Le choix d'un compilateur a des effets minimes sur la difficulté de rétro-ingénierie de votre code. Les éléments importants à minimiser sont tous liés aux fuites d'informations de votre code. Vous voulez au moins désactiver toutes les informations de type d'exécution (RTTI). La fuite des informations de type et la simplicité du jeu d'instructions de la machine virtuelle sont l'une des raisons pour lesquelles le code CLR et JVM est plus facile à rétro-ingérer. Ils ont également un JIT qui applique des optimisations au code qui peuvent réduire la force de l'obfuscation. L'obscurcissement est fondamentalement l'opposé de l'optimisation et de nombreux obfuscations sont résolus en appliquant d'abord une passe d'optimisation.
Informations de débogage
Je vous conseille également désactiver toutes les informations de débogage, même si elles ne fuient aucune information très importante aujourd'hui, elles pourraient le faire demain. La quantité de fuite d'informations à partir des informations de débogage varie d'un compilateur à l'autre et du format binaire au format binaire. Par exemple, Microsoft Visual C ++ conserve toutes les informations de débogage importantes dans une base de données externe, généralement sous la forme d'un PDB. Le plus que vous pourriez fuir est le chemin que vous avez utilisé lors de la construction de votre logiciel.
Chaînes
En ce qui concerne les chaînes, vous devez absolument les chiffrer si vous en avez besoin pas du tout. Je viserais à remplacer tous ceux qui sont destinés au traçage des erreurs et à la journalisation des erreurs par des énumérations numériques. Les chaînes qui révèlent toute sorte d'informations sur ce qui se passe actuellement dans votre binaire doivent être indisponibles. Si vous cryptez les chaînes, elles seront décryptées. Essayez de les éviter autant que possible.
API système
Les importations d'API système constituent une autre source importante de fuite d'informations. Vous voulez vous assurer que toute fonction importée qui a une signature connue est bien cachée et ne peut pas être trouvée à l'aide de l'analyse automatique. Donc, un tableau de pointeurs de fonction de quelque chose comme LoadLibrary / GetProcAddress est hors de question. Tous les appels aux fonctions importées doivent passer par une fonction à sens unique et doivent être intégrés dans un bloc obscurci.
Bibliothèques d'exécution standard
Quelque chose de beaucoup des personnes oublient de prendre en compte les informations divulguées par les bibliothèques standard, telles que le moteur d'exécution de votre compilateur C ++. J'éviterais complètement son utilisation. En effet, la plupart des rétro-ingénieurs expérimentés auront des signatures préparées pour de nombreuses bibliothèques standard.
Obfuscation
Vous devriez également couvrir tout code critique avec une sorte d'obscurcissement lourd. Certains des masques les plus lourds et les moins chers actuellement sont CodeVirtualizer / Themida et VMProtect. Sachez cependant que ces packages ont une abondance de défauts. Ils transformeront parfois votre code en quelque chose qui ne sera pas l'équivalent de l'original ce qui peut conduire à une instabilité. Ils ralentissent également considérablement le code obscurci. Un facteur 10000 fois plus lent n'est pas rare. Il y a aussi le problème du déclenchement de plus de faux positifs avec un logiciel antivirus. Je vous conseille de signer votre logiciel en utilisant une autorité de certification réputée.
Séparation des blocs fonctionnels
La séparation du code en fonctions est une autre chose qui facilite le reverse engineering d'un programme. Cela s'applique en particulier lorsque les fonctions sont obscurcies, car cela crée des limites autour desquelles l'ingénieur inverse peut raisonner sur votre logiciel. De cette façon, l'ingénierie inverse peut résoudre votre programme de manière divisée et conquérir. Idéalement, vous voudriez que votre logiciel se trouve dans un bloc efficace avec une obfuscation appliquée uniformément à tout le bloc en un seul. Réduisez donc le nombre de blocs, utilisez l'inlining très généreusement et enveloppez-les dans un bon algorithme d'obscurcissement. Le compilateur peut facilement effectuer de lourdes optimisations et ordonner la pile, ce qui rendra le bloc plus difficile à rétroconcevoir.
Exécution
Lorsque vous masquez des informations, c'est important que les informations sont également bien cachées lors de l'exécution. Un ingénieur inverse compétent examinera l'état de votre programme pendant son exécution. Donc, utiliser des variables statiques qui déchiffrent lors du chargement ou en utilisant un emballage qui est complètement décompressé lors du chargement conduira à une recherche rapide. Faites attention à ce que vous allouez sur le tas. Toutes les opérations de tas passent par des appels API et peuvent être facilement enregistrées dans un fichier et raisonnées. Les opérations de pile sont généralement plus difficiles à suivre en raison de leur fréquence. L'analyse dynamique est tout aussi importante que statique. Vous devez être conscient de l'état de votre programme à tout moment et de quelles informations se trouvent où.
Anti-débogage
L'anti-débogage est sans valeur. Ne passez pas de temps dessus. Passez du temps à vous assurer que vos secrets sont bien cachés, que votre logiciel soit au repos ou non.
Emballage et chiffrement du segment de code
Je vais regrouper le chiffrement et le conditionnement dans la même catégorie. Ils servent tous les deux le même objectif et ils ont tous les deux les mêmes problèmes. Pour exécuter le code, la CPU a besoin de voir le texte brut. Vous devez donc fournir la clé dans le binaire. Le seul moyen efficace à distance de crypter et de compresser des segments de code est de les crypter et de les décrypter à des limites fonctionnelles et uniquement si le décryptage se produit lors de l'entrée de la fonction, puis le rechiffrement se produit lorsque vous quittez la fonction. Cela fournira une petite barrière contre le dumping de votre binaire pendant son exécution, mais il doit être associé à une forte obfuscation.
Enfin
Étudiez votre logiciel dans quelque chose comme la version gratuite d'IDA. Votre objectif est de vous assurer qu'il devient pratiquement impossible pour l'ingénieur inverse de trouver une base mentale stable. Moins vous divulguez d'informations et plus l'environnement change, plus il sera difficile d'étudier. Si vous n'êtes pas un ingénieur inverse expérimenté, il est presque impossible de concevoir quelque chose de difficile à inverser.
Si vous concevez un système de protection contre la copie, préparez-vous à le casser mentalement. Assurez-vous d'avoir un plan sur la façon dont vous allez gérer la pause et comment vous assurer que la prochaine version de votre logiciel ajoute suffisamment de valeur pour conduire les mises à niveau. Construisez votre système sur une base solide qui ne peut pas être cassée, ne recourez pas à la génération de vos propres clés de licence en utilisant un algorithme personnalisé caché de la manière que j'ai décrite ci-dessus. Le système doit être construit sur une base cryptographique solide pour l'imprévisibilité des messages.