Anti-Reversing-Techniken (Teil 2)

Dec 05 2022
Im ersten Teil haben wir einige gängige Techniken besprochen, die von Malware-Autoren verwendet werden, um ihre Anwendungen vor Reverse Engineering zu schützen. In diesem zweiten Teil werfen wir einen Blick auf weitere Methoden und Techniken zur Erkennung und Verhinderung von Reverse Engineering.

Im ersten Teil haben wir einige gängige Techniken besprochen, die von Malware-Autoren verwendet werden, um ihre Anwendungen vor Reverse Engineering zu schützen. In diesem zweiten Teil werfen wir einen Blick auf weitere Methoden und Techniken zur Erkennung und Verhinderung von Reverse Engineering.

Debugger-Erkennung:

Code-Ausführungs-Timing-Technik:

Wenn wir einen Debugger verwenden, um eine ausführbare Datei zu analysieren, verwenden wir manchmal eine Einzelschrittausführung, um einige Assembleranweisungen durchzugehen, die Ausführungszeit ist viel länger als die normale Ausführung, nehmen wir diesen Code als Beispiel:

Am Anfang des Codes haben wir einen Aufruf der Funktion „ GetTickCount “, die die Anzahl der Millisekunden zurückgibt, die seit dem Systemstart vergangen sind. Der Code bis zur Ausführung der Funktion „ ShellExecuteW “ dauert 30 bis 47 Millisekunden, wie ich auf meinem Computer gemessen habe. Danach rufen wir die Funktion „ GetTickCount “ erneut auf, um die Zeit zu erhalten, und erhalten dann die Differenz zwischen den beiden Aufrufwerten, und wenn es länger als 70 Millisekunden dauert, beenden Sie den Prozess, andernfalls drucken Sie die Nachricht.

Es gibt andere Windows-APIs, die zum Abrufen von Zeit verwendet werden können, z. B.:

  • GetLocalTime().
  • GetSystemTime().
  • QueryPerformanceCounter().

Software-Breakpoints sind eine gängige Anti-Debugging-Technik, die von böswilligen Akteuren verwendet wird, um das Reverse Engineering ihrer Malware zu erschweren. Diese Technik beinhaltet das Einfügen einer bestimmten Codeanweisung, die als Haltepunkt bezeichnet wird, an einer bestimmten Stelle in das Programm. Wenn das Programm ausgeführt wird, bewirkt der Haltepunkt, dass die Ausführung des Programms beendet wird, wodurch es für einen Debugger schwierig wird, das Verhalten des Programms zu analysieren.

Eine Möglichkeit, einen Haltepunkt mithilfe der Assemblersprache zu implementieren, besteht darin, die int 3Anweisung zu verwenden. Diese Anweisung ist ein Software-Interrupt, der bewirkt, dass das Programm anhält und die Steuerung an den Debugger übergibt. Hier ist ein Beispiel für die Verwendung der int 3Anweisung in C++:

Hardware-Haltepunkte:

Hardware-Haltepunkte sind eine Art von Anti-Debugging-Technik, die spezielle Hardwarefunktionen der CPU verwendet, um das Debuggen eines Programms zu erkennen und zu verhindern. Bei dieser Technik wird ein Haltepunkt auf eine bestimmte Speicheradresse oder ein Register gesetzt und dann die CPU angewiesen, einen Interrupt auszulösen, wenn das Programm auf die angegebene Adresse oder das Register zugreift oder diese ändert.

Nachfolgend finden Sie ein Beispiel, wie Sie überprüfen können, ob ein Hardware-Haltepunkt gesetzt ist:

„Auf Hardware-Haltepunkt prüfen“

Ein Wert ungleich Null in einem der Debug-Register könnte darauf hindeuten, dass der Prozess unter einem Debugger mit einem gesetzten Hardware-Haltepunkt ausgeführt wird.

Selbst-Debugging:

Ist eine Technik, die von Malware-Autoren verwendet wird, um zu verhindern, dass sich ein Debugger an den Prozess anfügt oder prüft, ob der Prozess unter einem Debugger läuft. Es beinhaltet die Verwendung eines Debuggers zum Debuggen des Programms selbst, um andere Debugger zu erkennen und zu verhindern, dass sie sich an das Programm anhängen.

Unten sehen Sie ein Beispiel für eine Instanz, die eine zweite Instanz des Prozesses erstellt und ein benanntes Ereignis mit dem Namen „debugger_present“ erstellt. Die zweite Instanz des Prozesses versucht, einen Debugger an den übergeordneten Prozess anzuhängen, und wenn dies fehlschlägt, setzt sie das Ereignis, das dem übergeordneten Prozess anzeigt, dass ein anderer Debugger an ihn angehängt ist:

Elternprozess
Kindprozess

Unbehandelter Ausnahmefilter ()

Wenn ein Programm auf eine Ausnahme stößt, die von keinem registrierten Ausnahmehandler behandelt wird, wird die Funktion kernel32!UnhandledExceptionFilter() aufgerufen. Es ist möglich, einen benutzerdefinierten Filter für unbehandelte Ausnahmen mithilfe von kernel32!SetUnhandledExceptionFilter() zu registrieren. Wenn das Programm jedoch debuggt wird, wird der benutzerdefinierte Filter nicht aufgerufen und die Ausnahme stattdessen an den Debugger übergeben. Wenn die Kontrolle an den Filter für unbehandelte Ausnahmen übergeben wird, kann daher gefolgert werden, dass das Programm nicht unter einem Debugger ausgeführt wird:

vielen Dank, dass Sie sich die Zeit genommen haben, dies bis zum nächsten Mal zu lesen.

Von Infosec Writeups: In der Infosec kommt jeden Tag eine Menge auf, mit der es schwer ist, Schritt zu halten. Abonnieren Sie unseren wöchentlichen Newsletter, um KOSTENLOS die neuesten Infosec-Trends in Form von 5 Artikeln, 4 Threads, 3 Videos, 2 GitHub-Repos und -Tools sowie 1 Jobbenachrichtigung zu erhalten!