Durch ein sehr simples Demo-Programm aus dem lokalen IRC-Channel bin ich inspiriert worden, das ganze etwas zu verbessern, und ein paar simple Funktionen zu schreiben, mit denen man zur Laufzeit erkennen kann, ob innerhalb eines bestimmten Code ein Breakpoint gesetzt worden ist. Den Code dazu gibt's
hier zum anschauen, und hier eine kurze Session:
$ gcc -g antibreak.c -o antibreak
$ ./antibreak
i=0
i=1
i=2
$ gdb antibreak
GNU gdb 6.3-debian
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-linux"...Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) break antibreak.c:11
Breakpoint 1 at 0x8048408: file antibreak.c, line 11.
(gdb) break antibreak.c:19
Breakpoint 2 at 0x804843d: file antibreak.c, line 19.
(gdb) break antibreak.c:32
Breakpoint 3 at 0x80484ce: file antibreak.c, line 32.
(gdb) r
Starting program: /home/ak/antibreak/antibreak
found breakpoint at address 0x0x8048408
found breakpoint at address 0x0x804843d
found breakpoint at address 0x0x80484ce
Program exited with code 01.
(gdb) quit
$
Um das gleich mal am anfang zu klären: ja, das funktioniert auch ohne
-g, allerdings kann man dann im gdb keine Breakpoints auf Zeilennummern setzen.
Der Hintergrund: gdb setzt Breakpoints, indem der Maschinencode im Speicher so verändert wird, dass an der jeweiligen Stelle ein INT 3 (Opcode 0xCC) reingeschrieben wird.. Wird diese Stelle nun ausgeführt, löst dieser INT 3 einen Trap aus, bei dem sich gdb reinhängt, und vor einem Continue an die Stelle noch den vorherigen Opcode reinschreibt.
Die von mir geschriebenen Funktionen machen es nun so, dass man eine Start- und eine Endadresse angibt, und dieser Bereich von einem simplen x86 Opcode Decoder angeschaut wird, und wenn ein INT 3 gefunden wird, wird die Adresse der Instruktion zurrückgeliefert. Von diesem Punkt weg kann man dann solange weitere INT-3-Instruktionen suchen, bis man die Endadresse erreicht hatt. Den Opcode Decoder hab ich übrigens nicht selbstgeschrieben, sondern über Google gefunden, und für kompakt genug befunden, um den schnell mal via copy/paste zu verwenden. Als End-Adresse hab ich übrigens in allen Fällen die Startadresse der nächsten Funktion im Sourceode verwendet. Klar, das verlässt sich auf ein gcc-spezifisches Verhalten, aber hey, das ganze ist sowieso auch x86-spezifisch, also, was soll's?
Wer übrigens noch mehr über ein paar nette Anti-Debugging-Techniken unter Linux lesen möchste (kein Voodoo-Techniken, aber trotzdem ganz interessant für n00bs wie mich), dem kann ich nur
diesen Text empfehlen.
Wie ich übrigens aus gut informierten Kreisen erfahren habe, gibt es noch eine andere Debugging-Technik, die auf den Debug-Registern des x86 aufbauen, und das dürfte von neueren gdb-Versionen schon (teilweise) unterstützt werden. Insofern ist nicht garantiert, dass das in Zukunft auch nur irgendwie funktioniert.