Reverse Engineering-Anwendungen nach Verschleierung (Teil 2)

Einführung


Diese Veröffentlichung zielt darauf ab, einige Reverse Engineering-Techniken zu untersuchen. Alle Materialien werden nur zu Informationszwecken präsentiert und sind nicht für persönliche Zwecke bestimmt.

Empfohlene Lektüre nach dem ersten Teil
Wenn einem Chirurgen beigebracht wird, wie eine Person arbeitet, und ihm ein Skalpell gibt, bedeutet dies nicht, dass er dieses Wissen zum Nachteil einer Person nutzt, und ein sachkundiger Assembler träumt nicht davon, einen Supervirus zu schreiben.
In diesen Lektionen sollten Sie also nicht nach Hinweisen auf Risse und Hacks suchen.

Forschungsgegenstand


Wir untersuchen weiterhin den Plug-In-Code für die Visual Studio Atomineer Pro-Dokumentation (im Folgenden: APD). Machen wir uns mit dem Tool und seinen Funktionen vertraut. Nehmen wir also an, wir haben eine Klasse in C ++.

class ClassForReadFile { public: ClassForReadFile(); }; 

Konfigurieren Sie die APD so, dass die Kommentare im Doxygen-Stil vorliegen. Wir bewegen den Cursor auf die Klasse und drücken STRG + UMSCHALT + D. Wir bekommen folgendes:

 /** The class for read file. */ class ClassForReadFile { public: ClassForReadFile(); }; 

Das Plugin hat eine schöne Beschreibung der Klasse hinzugefügt. Alles in Ordnung ist! Wir gehen weiter. Angenommen, eine Klasse gehört zu einer Bibliothek und wir müssen sie exportieren. Fügen Sie ein Makro hinzu und ändern Sie die Klassendefinition

 #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(); }; 

Für C ++ (Windows OS) ist die Situation Standard. Schauen Sie sich unser Plugin an. Drücken Sie STRG + UMSCHALT + D und erhalten Sie überhaupt nicht das, was wir erwartet haben

 /** A data reader DLL exports. */ class DATA_READER_DLL_EXPORTS ClassForReadFile { }; 

Der Name der Definition DATA_READER_DLL_EXPORTS wurde als Name der Klasse anstelle von ClassForReadFile definiert, und die Klassenbeschreibung wurde aus diesem Namen generiert. Das heißt, im Plugin-Code wird diese Situation, nämlich der Export der Klasse, entweder nicht oder fehlerhaft verarbeitet. Dies werden wir versuchen zu korrigieren.

Schritt 1


Wir werden nach Hinweisen suchen. Erstens, da der Export von Funktionen und Klassen aus C / C ++ eine Standardsituation ist, versuchen wir immer noch, das Plugin korrekt zu "erzwingen". Fügen Sie anstelle der Definition von DATA_READER_DLL_EXPORTS die Anweisung __declspec selbst ein und generieren Sie die Dokumentation

 /** The class for read file. */ class __declspec(dllexport) ClassForReadFile { }; 

Und siehe da, sie haben die richtige Klassenbeschreibung! Wir schließen daraus, dass es in APD einen Code gibt, der das Vorhandensein der Zeichenfolge "__declspec" in der Klassenbeschreibung überprüft und den weiteren Algorithmus zum Erstellen der Dokumentation ignoriert.

Wir dekompilieren die Bibliothek mit der Standarddatei ildasm.exe aus den Microsoft SDKs. Suchen Sie die Zeile "__declspec". Es wird in 2 Methoden CmdDocComment :: a und CmdDocComment :: b gefunden. Klasse eins. Wir werden es weiter untersuchen.

Schritt 2


Ich muss sofort sagen, dass das, wonach wir suchen, in der CmdDocComment :: a-Methode enthalten ist

Hier trifft sich __declspec . Es werden nur die interessantesten Zeilen angezeigt.

Zeichenfolge a (CmdDocComment.GeneratorInfo A_0)
 List<string> e = A_0.e; //....... List<string> list = A_0.e; int num3 = 0; while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (list[num3] == "__declspec") { if (num3 + 1 < list.Count) { num = list[num3 + 1].IndexOf(')'); if (num >= 0) { list[num3 + 1] = list[num3 + 1].Substring(num + 1).Trim(); } } list.RemoveAt(num3); num3--; } num3++; } if (list.Count > 0 && (list[0] == "struct" || list[0] == "union")) { if (list.Count == 1) { //...... 


Wir sind zu dem Schluss gekommen, dass nach Überprüfung
list [num3] == "__declspec"
Löschmethode wird aufgerufen
list.RemoveAt (num3);
Argumentation (laut denken):

  1. Die CmdDocComment :: a-Methode verfügt über eine lokale Variable, die ein Array von Zeichenfolgen enthält

     List<string> list = A_0.e; 
  2. Das erste Element dieses Arrays speichert den Anfang der Beschreibung der Funktion, Struktur, Klasse usw., dh des Schlüsselworts "Klasse", "Struktur", "Vereinigung".
     list[0] == "struct" 
  3. Jedes Element des Arrays enthält ein separates Wort. In unserem Fall ist es {"class", "DATA_READER_DLL_EXPORTS", "ClassForReadFile"}
  4. Es gibt eine Schleife, die alle Elemente des Arrays "e" umgeht, nach dem Element "__declspec" sucht und es und alles, was in Klammern steht, entfernt
  5. Es gibt eine zusätzliche Bedingung zum Verlassen des Zyklus. Dies ist die Position der Wörter "wo" oder ":". Das offizielle Wort "wo" ist, um ehrlich zu sein, nicht vertraut, aber ":" wird beim Erben von Klassen verwendet

Definieren Sie einen neuen Algorithmus und den Zweck der Änderungen:

1. Änderungen sollten den Rest der Funktionalität nicht beeinträchtigen

2. Wir werden die Elemente des "list" -Arrays gemäß dem Algorithmus löschen
- das erste Element überspringen;
- Wenn das nächste Element nicht ":" und nicht "where" und nicht das Ende des Arrays ist, löschen Sie es.

Schreiben Sie den gewünschten Zyklus

 //     ,          num2 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { // ,      .     if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } //  if (list[num3] == "__declspec"),  if (num3 != 0 && num3 < (list.Count - 1) && list[num3 + 1] != ":" && list[num3 + 1] != "where") { e.RemoveAt(index); --index; } num3++; } 

Es bleibt zu programmieren.

Schritt 3


Laut programmieren. Programmieren im allgemeinen Fall ist das Schreiben von Quellcodes, Kompilieren, Verknüpfen. Aber der Verschleierer hat uns eine solche Gelegenheit genommen. Wir werden das empfohlene dnSpy-Tool verwenden . Wir werden die HEX-Codes von CIL-Befehlen direkt in der Bibliothek ändern, was, wie sich herausstellte, sehr aufregend und informativ ist! Fangen wir an. Öffnen Sie dnSpy und laden Sie die Bibliothek.

Finden Sie unsere Methode
Bild

Wählen Sie while und ändern Sie die Ansicht in IL
Unser Zyklus
Bild

Ich werde auch eine Auflistung geben, obwohl es ziemlich sperrig ist

Unser Zyklus
 /* 0x00016710 07 */ IL_018C: ldloc.1 //         1. /* 0x00016711 1119 */ IL_018D: ldloc.s V_25 //          ( ). /* 0x00016713 6FF900000A */ IL_018F: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016718 72925E0070 */ IL_0194: ldstr "where" //       ,   ,   . /* 0x0001671D 287000000A */ IL_0199: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016722 3AAB000000 */ IL_019E: brtrue IL_024E //    ,   value  true,    null   . /* 0x00016727 07 */ IL_01A3: ldloc.1 //         1. /* 0x00016728 1119 */ IL_01A4: ldloc.s V_25 //          ( ). /* 0x0001672A 6FF900000A */ IL_01A6: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001672F 72A31D0070 */ IL_01AB: ldstr ":" //       ,   ,   . /* 0x00016734 287000000A */ IL_01B0: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016739 3A94000000 */ IL_01B5: brtrue IL_024E //    ,   value  true,    null   . /* 0x0001673E 07 */ IL_01BA: ldloc.1 //         1. /* 0x0001673F 1119 */ IL_01BB: ldloc.s V_25 //          ( ). /* 0x00016741 6FF900000A */ IL_01BD: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016746 03 */ IL_01C2: ldarg.1 //     1   . /* 0x00016747 7B12010004 */ IL_01C3: ldfld string Atomineer.Utils.CmdDocComment/GeneratorInfo::b //      ,       . /* 0x0001674C 287000000A */ IL_01C8: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016751 2C07 */ IL_01CD: brfalse.s IL_01D6 //    ,   value  false,    . /* 0x00016753 09 */ IL_01CF: ldloc.3 //         3. /* 0x00016754 16 */ IL_01D0: ldc.i4.0 //    0     int32. /* 0x00016755 2F03 */ IL_01D1: bge.s IL_01D6 //     ( ),        . /* 0x00016757 1119 */ IL_01D3: ldloc.s V_25 //          ( ). /* 0x00016759 0D */ IL_01D5: stloc.3 //                3. /* 0x0001675A 07 */ IL_01D6: ldloc.1 //         1. /* 0x0001675B 1119 */ IL_01D7: ldloc.s V_25 //          ( ). /* 0x0001675D 6FF900000A */ IL_01D9: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016762 729E5E0070 */ IL_01DE: ldstr "__declspec" //       ,   ,   . /* 0x00016767 287000000A */ IL_01E3: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x0001676C 2C51 */ IL_01E8: brfalse.s IL_023B //    ,   value  false,    . /* 0x0001676E 1119 */ IL_01EA: ldloc.s V_25 //          ( ). /* 0x00016770 17 */ IL_01EC: ldc.i4.1 //    1     int32. /* 0x00016771 58 */ IL_01ED: add //         . /* 0x00016772 07 */ IL_01EE: ldloc.1 //         1. /* 0x00016773 6FF700000A */ IL_01EF: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x00016778 2F37 */ IL_01F4: bge.s IL_022D //     ( ),        . /* 0x0001677A 07 */ IL_01F6: ldloc.1 //         1. /* 0x0001677B 1119 */ IL_01F7: ldloc.s V_25 //          ( ). /* 0x0001677D 17 */ IL_01F9: ldc.i4.1 //    1     int32. /* 0x0001677E 58 */ IL_01FA: add //         . /* 0x0001677F 6FF900000A */ IL_01FB: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016784 1F29 */ IL_0200: ldc.i4.s 41 //      int8     int32 ( ). /* 0x00016786 6FC800000A */ IL_0202: callvirt instance int32 [mscorlib]System.String::IndexOf(char) //             . /* 0x0001678B 0C */ IL_0207: stloc.2 //                2. /* 0x0001678C 08 */ IL_0208: ldloc.2 //         2. /* 0x0001678D 16 */ IL_0209: ldc.i4.0 //    0     int32. /* 0x0001678E 3221 */ IL_020A: blt.s IL_022D //     ( ),      . /* 0x00016790 07 */ IL_020C: ldloc.1 //         1. /* 0x00016791 1119 */ IL_020D: ldloc.s V_25 //          ( ). /* 0x00016793 17 */ IL_020F: ldc.i4.1 //    1     int32. /* 0x00016794 58 */ IL_0210: add //         . /* 0x00016795 07 */ IL_0211: ldloc.1 //         1. /* 0x00016796 1119 */ IL_0212: ldloc.s V_25 //          ( ). /* 0x00016798 17 */ IL_0214: ldc.i4.1 //    1     int32. /* 0x00016799 58 */ IL_0215: add //         . /* 0x0001679A 6FF900000A */ IL_0216: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001679F 08 */ IL_021B: ldloc.2 //         2. /* 0x000167A0 17 */ IL_021C: ldc.i4.1 //    1     int32. /* 0x000167A1 58 */ IL_021D: add //         . /* 0x000167A2 6FCB00000A */ IL_021E: callvirt instance string [mscorlib]System.String::Substring(int32) //             . /* 0x000167A7 6F8600000A */ IL_0223: callvirt instance string [mscorlib]System.String::Trim() //             . /* 0x000167AC 6FFF00000A */ IL_0228: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::set_Item(int32, !0) //             . /* 0x000167B1 07 */ IL_022D: ldloc.1 //         1. /* 0x000167B2 1119 */ IL_022E: ldloc.s V_25 //          ( ). /* 0x000167B4 6F6701000A */ IL_0230: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::RemoveAt(int32) //             . /* 0x000167B9 1119 */ IL_0235: ldloc.s V_25 //          ( ). /* 0x000167BB 17 */ IL_0237: ldc.i4.1 //    1     int32. /* 0x000167BC 59 */ IL_0238: sub //           . /* 0x000167BD 1319 */ IL_0239: stloc.s V_25 //                index ( ). /* 0x000167BF 1119 */ IL_023B: ldloc.s V_25 //          ( ). /* 0x000167C1 17 */ IL_023D: ldc.i4.1 //    1     int32. /* 0x000167C2 58 */ IL_023E: add //         . /* 0x000167C3 1319 */ IL_023F: stloc.s V_25 //                index ( ). /* 0x000167C5 1119 */ IL_0241: ldloc.s V_25 //          ( ). /* 0x000167C7 07 */ IL_0243: ldloc.1 //         1. /* 0x000167C8 6FF700000A */ IL_0244: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x000167CD 3F3EFFFFFF */ IL_0249: blt IL_018C //    ,     . 


Jetzt haben wir ein Fenster in CIL-Befehlen, deren HEX-Darstellung, Dateiversatz und Beschreibung. Alles an einem Ort. Sehr praktisch (danke CrazyAlex25 ).
Achten wir auf den Block, in dem "__declspec" erwähnt wird. Blockoffset 0x0001675A. Dies wird der Beginn unserer Änderungen sein. Suchen Sie als Nächstes die RemoveAt-Methode. Es wird uns unverändert nützlich sein. Das letzte Byte des Blocks ist 0x000167BF. Gehen Sie zum HEX-Editor Strg + X und schreiben Sie 0x00 in diesen Bereich. Wir werden speichern und prüfen, wozu die Änderungen geführt haben.
leere Schleife
 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; } 


Jetzt werden wir die neue Logik implementieren. Fügen Sie zunächst die Bedingung hinzu

  if (num3 != 0 && num3 < list.Count - 1) 

Die Tabelle zeigt die neuen Befehle und ihre Beschreibung.
1119ldloc.sLädt eine lokale Variable mit dem angegebenen Index auf den Berechnungsstapel (Kurzform).
2C61brfalse.sÜbergibt die Steuerung an die endgültige Anweisung, wenn der Wert false, eine Nullreferenz oder Null ist. Hinweis : Wenn num3 == 0 ist, fahren Sie mit dem Erhöhen des Schleifeniterators fort. Der Wert 0x64 ist der Adressoffset zur Anweisung 0x000167BF (siehe Auflistung)
1119ldloc.sLädt eine lokale Variable mit dem angegebenen Index auf den Berechnungsstapel (Kurzform)
07ldloc.1Lädt eine lokale Variable mit Index 1 auf den Berechnungsstapel
6FF700000Acallvirtget_Count () - Ruft eine spät gebundene Objektmethode auf und verschiebt den Rückgabewert auf den Berechnungsstapel
17ldc.i4.1Schiebt den ganzzahligen Wert 1 auf dem Berechnungsstapel als int32
59subSubtrahiert einen Wert von einem anderen und schiebt das Ergebnis auf den Berechnungsstapel.
2F55bge.s.Es überträgt die Kontrolle auf die endgültige Anweisung (Kurzform), wenn der erste Wert größer oder gleich dem zweiten ist. Hinweis : Wenn num3> list.Count - 1 ist, fahren Sie mit dem Schritt des Erhöhens des Schleifeniterators fort. Der Wert 0x55 ist der Adressoffset vor dem Befehl 0x000167BF

Wir schreiben diese Bytes ab dem Offset 0x0001675A. Speichern und erneut dekompilieren

Erste Bedingung
 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.RemoveAt(num3); num3--; } num3++; } 


Jetzt fügen wir die Zeichenfolgenprüfung "where" und ":" hinzu. Ich zitiere den folgenden HEX-Code ohne zusätzliche Kommentare:

 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 

dekompilieren und bekommen, was Sie geplant haben

Neuer Zyklus
 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++; } 


Mit diesen Änderungen generiert das Plugin die folgende Dokumentation des Codes:

 /** The class for read file. */ class DATA_READER_DLL_EXPORTS ClassForReadFile { }; 

Fazit


In dieser Lektion haben wir gelernt, wie wir unser Wissen anwenden können, um Fehler zu beheben. Natürlich spiegelt dieses Beispiel nicht die gesamte Vielfalt der Fehler und ihre Behandlung wider, aber dies ist kein „banaler Riss“. Wir haben den offensichtlichen Fehler behoben, ohne den Quellcode zu haben und ohne die Anwendung neu zu erstellen.

Source: https://habr.com/ru/post/de445126/


All Articles