Présentation
Cette publication vise à étudier certaines techniques d'ingénierie inverse. Tous les documents sont présentés à titre informatif uniquement et ne sont pas destinés à être utilisés à des fins personnelles.Lecture recommandée après la
première partieSi un chirurgien apprend comment fonctionne une personne et lui donne un scalpel, cela ne signifie pas qu'il utilisera ces connaissances au détriment de quelqu'un, et un assembleur compétent ne rêve pas d'écrire un super virus.
Donc, dans ces leçons, vous ne devez pas rechercher des indices de fissures et de hacks.
Sujet de recherche
Nous continuons d'étudier le code du plug-in pour la documentation de Visual Studio Atomineer Pro (ci-après APD). Familiarisons-nous avec l'outil et ses capacités.
Supposons donc que nous ayons une classe en C ++.
class ClassForReadFile { public: ClassForReadFile(); };
configurer l'APD pour que les commentaires soient dans le style Doxygen. Nous obtenons le curseur sur la
classe et appuyez sur
CTRL + MAJ + D. Nous obtenons ce qui suit:
class ClassForReadFile { public: ClassForReadFile(); };
Le plugin a ajouté une belle description de la classe. Tout est super! Nous continuons. Supposons qu'une classe appartient à une bibliothèque et que nous devons l'exporter. Ajouter une macro et modifier la définition de classe
#ifdef DLL_EXPORTS #define DATA_READER_DLL_EXPORTS __declspec(dllexport) #else #define DATA_READER_DLL_EXPORTS __declspec(dllimport) #endif class DATA_READER_DLL_EXPORTS ClassForReadFile { public: ClassForReadFile(); };
Pour C ++ (Windows OS), la situation est standard. Découvrez notre plugin. Appuyez sur
CTRL + MAJ + D et n'obtenez pas du tout ce que nous attendions
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
le nom de la
définition DATA_READER_DLL_EXPORTS a
été défini comme le nom de la classe, au lieu de
ClassForReadFile , et la description de la classe a été générée à partir de ce nom. Autrement dit, dans le code du plugin, cette situation, à savoir l'exportation de la classe, n'est pas traitée ou est traitée avec une erreur. C'est ce que nous allons essayer de corriger.
Étape 1
Nous chercherons des indices. Premièrement, étant donné que l'exportation de fonctions et de classes depuis C / C ++ est une situation standard, nous essayons toujours de «forcer» le plugin correctement. Au lieu de la
définition DATA_READER_DLL_EXPORTS
, insérez l'instruction
__declspec elle -
même et générez la documentation
class __declspec(dllexport) ClassForReadFile { };
Et voilà, ils ont obtenu la bonne description de classe! Ainsi, nous concluons que dans APD il y a du code qui vérifie la présence de la chaîne "__declspec" dans la description de la classe et ignore son autre algorithme pour construire la documentation.
Nous décompilons la bibliothèque avec le ildasm.exe standard des SDK Microsoft. Recherchez la ligne "__declspec". Il se trouve dans 2 méthodes CmdDocComment :: a et CmdDocComment :: b. Classe un. Nous la soumettrons à un complément d'étude.
Étape 2
Je dois dire tout de suite que ce que nous recherchons est dans la méthode CmdDocComment :: a
C'est là que
__declspec se réunit. Seules les lignes les plus intéressantes sont affichées.
chaîne a (CmdDocComment.GeneratorInfo A_0) Nous avons conclu cela sur la base qu'après vérification
list [num3] == "__declspec"
la méthode de suppression est appelée
list.RemoveAt (num3);
Raisonnement (penser à haute voix):
- La méthode CmdDocComment :: a a une variable locale contenant un tableau de chaînes
List<string> list = A_0.e;
- Le premier élément de ce tableau stocke le début de la description de la fonction, la structure, la classe, etc., c'est-à-dire le mot-clé "class", "struct", "union"
list[0] == "struct"
- Chaque élément du tableau contient un mot distinct. Dans notre cas, ce sera {"class", "DATA_READER_DLL_EXPORTS", "ClassForReadFile"}
- Il y a une boucle qui fait le tour de tous les éléments du tableau "e", recherche l'élément "__declspec", et le supprime ainsi que tout ce qui est entre crochets
- Il existe une condition supplémentaire pour quitter le cycle. Il s'agit de l'emplacement des mots "où" ou ":". Le mot officiel "où" est, pour être honnête, pas familier, mais ":" est utilisé lors de l'héritage des classes
Définissez un nouvel algorithme et le but des modifications:
1. les modifications ne devraient pas affecter le reste de la fonctionnalité
2. nous allons supprimer les éléments du tableau "list" selon l'algorithme
- sauter le premier élément;
- si l'élément suivant n'est pas ":" et non "où" et non la fin du tableau, supprimez.
Écrivez le cycle souhaité
Reste à le programmer.
Étape 3
Programmez fort. La programmation dans le cas général est l'écriture de codes sources, la compilation, la liaison. Mais l'obfuscateur nous a privés d'une telle opportunité. Nous utiliserons l'
outil dnSpy recommandé. Nous allons changer les codes HEX des commandes CIL directement dans la bibliothèque, ce qui s'est avéré très excitant et instructif! Commençons. Ouvrez dnSpy, chargez la bibliothèque.
sélectionnez tout et changez la vue en IL
Je donnerai également une liste, bien qu'elle soit plutôt volumineuse
Nous avons maintenant une fenêtre dans les commandes CIL, leur représentation HEX, décalage de fichier et description. Tout en un seul endroit. Très pratique (merci
CrazyAlex25 ).
Faisons attention au bloc mentionnant "__declspec". Décalage de bloc 0x0001675A. Ce sera le début de nos modifications. Ensuite, recherchez la méthode RemoveAt. Il nous sera utile sans changement. Le dernier octet du bloc est 0x000167BF. Accédez à l'éditeur HEX
Ctrl + X et écrivez 0x00 dans cette plage. Nous enregistrerons et vérifierons les conséquences de ces changements.
boucle vide while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; }
Nous allons maintenant mettre en œuvre la nouvelle logique. Tout d'abord, ajoutez la condition
if (num3 != 0 && num3 < list.Count - 1)
Le tableau présente les nouvelles commandes et leur description.
1119 | ldloc.s | Charge une variable locale avec l'index spécifié sur la pile de calcul (forme courte). |
---|
2C61 | brfalse.s | Passe le contrôle à l'instruction finale si la valeur est fausse, une référence nulle ou zéro. Remarque : Si num3 == 0, passez à l'étape d'augmentation de l'itérateur de boucle. La valeur 0x64 est le décalage d'adresse vers l'instruction 0x000167BF (voir liste) |
---|
1119 | ldloc.s | Charge une variable locale avec l'index spécifié dans la pile de calcul (forme courte) |
---|
07 | ldloc.1 | Charge une variable locale d'index 1 sur la pile de calcul |
---|
6FF700000A | callvirt | get_Count () - Appelle une méthode d'objet à liaison tardive et pousse la valeur de retour sur la pile de calcul |
---|
17 | ldc.i4.1 | Pousse la valeur entière 1 sur la pile de calcul comme int32 |
---|
59 | sous | Soustrait une valeur d'une autre et pousse le résultat dans la pile de calcul. |
---|
2F55 | bge.s | Il transfère le contrôle à l'instruction finale (forme abrégée) si la première valeur est supérieure ou égale à la seconde. Remarque : Si num3> list.Count - 1, passez à l'étape d'augmentation de l'itérateur de boucle. La valeur 0x55 est le décalage d'adresse avant l'instruction 0x000167BF |
---|
Nous écrivons ces octets à partir de l'offset 0x0001675A. Enregistrez et décompilez à nouveau
Première condition while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; }
Maintenant, nous ajoutons la vérification de chaîne "où" et ":". Je cite le code HEX suivant sans commentaires supplémentaires:
07 11 19 17 58 6F F9 00 00 0A 72 A3 1D 00 70 28 70 00 00 0A 2D 3F 07 11 19 17 58 6F F9 00 00 0A 72 92 5E 00 70 28 70 00 00 0A 2D 29
décompiler et obtenir ce que vous avez prévu
Nouveau cycle while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (num3 != 0 && num3 < list.Count - 1 && !(list[num3 + 1] == ":") && !(list[num3 + 1] == "where")) { list.RemoveAt(num3); num3--; } num3++; }
Avec ces modifications, le plugin générera la documentation suivante du code:
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
Conclusion
Dans cette leçon, nous avons appris à appliquer nos connaissances pour corriger les bogues. Bien sûr, cet exemple ne reflète pas toute la variété des erreurs et de leur traitement, mais ce n'est pas une «fissure banale». Nous avons corrigé le bogue évident sans avoir le code source et sans reconstruire l'application.