Je suis toujours au collège pour un diplôme en sécurité informatique et j'ai pris mon premier cours basé sur le langage d'assemblage le semestre dernier. Nous avons abordé le sujet de la rétro-ingénierie et pourquoi c'est un élément important de la lutte contre les logiciels malveillants et les applications indésirables.
Pendant mon cours, nous avons principalement utilisé IDA pro, mais nous avons également consulté des applications similaires et gratuites basées sur un navigateur.
Dans ces applications, nous avons pu obtenir tellement d'informations sur les instructions et le code de bas niveau que je me demandais pourquoi nous avions même besoin d'un humain pour le parcourir et recréer les langages de niveau supérieur (comme écrire une version 'C' d'un morceau de logiciels malveillants).
Voici ma question:
Pourquoi un programme ne peut-il pas utiliser les informations présentes dans le code Assembly et les transformer automatiquement en un langage simpliste?
Je comprends qu'il ne ressemblerait pas exactement de la même façon qu'il a été écrit pour la première fois, mais ne devrait-il pas être possible de le recréer de manière à le rendre plus facile à lire et à suivre?
C'est juste quelque chose que je ne peux pas envelopper ma tête, merci!
C'est tout à fait possible, mais la précision et la lisibilité sont une question complètement différente. Une précision à apporter: la rétro-ingénierie n'est pas une décompilation.
Reverse Engineering est généralement le processus par lequel vous démontez quelque chose (quoi que ce soit vraiment) pour voir comment cela fonctionne. Démontage est lorsque vous prenez un fichier au format binaire et que vous interprétez le code machine dans son code d'assemblage. Décompilation interprète le code Assembly dans un langage de niveau supérieur.
Je crois que votre question est vraiment, Pourquoi la décompilation d'un programme ne peut-elle pas être automatisée? Eh bien, ça peut l'être!
Il existe plusieurs décompilateurs Java . Java byte-code est complètement réversible en raison de son indépendance d'architecture. Ce qui devient délicat, c'est la décompilation d'un langage comme C. Hex Rays fournit un décompilateur C, mais C est une langue compliquée. Il y a 10 façons différentes d'accomplir la même tâche. Ce qui peut être fait en 20 lignes, peut être fait en 3 ou 10. C'est la interprétation de la langue qui fait la automatisation de la décompilation C difficile.
Bien sûr, vous pouvez décompiler C selon ses instructions les plus simplistes. Ensuite, vous obtenez des lignes comme **(*var1) = 3;
ou (*bytecode)(param1)
qui peut être un appel à un pointeur de fonction. Le pire, c'est que vous devez vous rappeler que ce ne sont encore qu'une interprétation . Je ne saurais trop insister là-dessus. Et si l'interprétation est fausse? C'est quelque chose dont vous devez vous soucier au niveau du démontage, mais au moins il y a une quantité raisonnable de résultats pour 5-6 octets pour une instruction. Vous devez maintenant interpréter 15-20 octets afin de comprendre un appel de fonction ou une boucle for. S'il existe des techniques anti-reverse engineering, cela rend l'interprétation encore plus difficile.
Le contexte joue un rôle énorme. Quelle est la différence entre un pointeur de fonction, un char *
un pointeur et un uint32
? Absolument rien, sauf le contexte dans lequel il est utilisé. Les optimisations du compilateur peuvent utiliser __fastcall
plutôt que __stdcall
. Ce qui signifie maintenant que vous devez interpréter où les paramètres des fonctions vont être; soit sur la pile, soit dans un registre? Les fonctions en ligne, les macros, #defines feront toutes partie d'un sous-programme plus large. Il n'y a aucun moyen réel d'interpréter ces types de contextes.
Il est possible de recréer automatiquement quelque chose qui ressemble à du code C à partir d'Assembly, mais la quantité de devinettes que le décompresseur aurait à faire est monumentale.
Les compilateurs sont des choses très compliquées qui font une transformation compliquée sur le code source. Optimisations, substitutions de macro/précompilateur, alignement de code, vérification du type et des erreurs, liaison statique, etc. sur quel système d'exploitation il a été compilé, sur quelles bibliothèques il a été compilé, etc.
Donc, si vous preniez du code C, le respectiez, puis le décompiliez, le résultat ressemblerait rien à l'original.
Et c'est juste pour produire du code C qui s'exécute, nous n'avons même pas encore parlé de lisibilité. Les noms de variables, les noms de fonctions, etc. sont extraits par le compilateur et remplacés par des adresses brutes, donc les décompilateurs comme celui que vous imaginez nomment généralement vos fonctions A()
, B()
, C()
... et toutes les variables a
, b
, c
car il n'a aucun moyen de connaître la sémantique (c'est-à-dire ce que ces choses sont censées représenter ).
L'essentiel est que toute personne ayant un peu d'expérience en assembleur dirait que la lecture du code décompilé est en fait plus difficile que la lecture de l'assemblage brut. (À quelques exceptions près: Java, par exemple, se décompile en fait assez proprement).
Je n'ai pas encore trouvé de papier ou de logiciel qui automatise complètement l'ingénierie inverse, mais il y a certains domaines qui sont particulièrement intéressés par l'automatisation de l'ingénierie inverse , comme la criminalistique une analyse. L'automatisation de la rétro-ingénierie n'est pas (au moins actuellement) destinée à automatiser totalement l'ensemble du processus de rétro-ingénierie, mais au moins certaines parties afin que vous puissiez adapter votre procédure à un système de fichiers complet. Ceci est par exemple décrit dans cet article :
Introduction
Cet article abordera le besoin croissant d'automatisation en rétro-ingénierie (Reverse Engineering Automation), comment l'introduction de l'automatisation dans le processus de recherche peut gagner un temps précieux et faciliter la récupération d'informations sans lesquelles, soit nos recherches seraient moins approfondies, soit serait menée à une échelle beaucoup plus petite.
Dans cet article, je présenterai également des exemples de scripts d'automatisation et les rendrai accessibles via mon compte Github. Je vous invite également à ajouter d'autres exemples, au-delà des exemples trouvés dans cet article, et à m'envoyer des demandes d'extraction, afin que je puisse les ajouter.
Pourquoi l'automatisation est-elle nécessaire en ingénierie inverse?
Tout d'abord, je pense qu'il est important de souligner que Reverse Engineering Automation fait gagner du temps à l'enquête mais ne remplace pas le reste du processus, et deuxièmement, pour des raisons telles que les suivantes:
- Actions répétées.
- Fragments de code dynamiques détectés à un stade ultérieur du programme (Crypters, Packers).
- Contournement des protections logicielles, telles que SSDT.
- Allocation de mémoire du logiciel exécuté dans Ollydbg
L'article continue en décrivant quelques outils comme OllyDBG-Python et OllyDBG-Playtime, puis quelques extraits de code qui sont également disponibles ici et ici dans opensource.