ich hab vor ein paar Jahren einen interessanten Quelltext gesehen.
Dabei wurde beim Start des Programms manuell ein neuer Structured Exception Handler installiert, der also als erstes aufgerufen wird, wenn irgendwo eine Exception geworfen wird.
Dann wurde das Trap-Flag des Prozessors gesetzt, welches bewirkt, dass nach Ausführung der nächsten Anweisung eine Single-Step-Exception geworfen wird. Es wird also nur die nächste (Assembler-)Anweisung ausgeführt, danach springt das Programm sofort in den Exception-Handler.
Nun war es so, dass das eigentliche Programm von Beginn an verschlüsselt war (zB primitiv mit XOR). Der Exception-Handler hat dann die gerade ausgeführte, bereits entschlüsselte Anweisung wieder verschlüsselt und die nächste Anweisung entschlüsselt, wieder das Trap-Flag gesetzt und ist mit der Ausführung des Programms fortgefahren. Das hat zur Folge gehabt, dass immer nur genau eine Anweisung gleichzeitig unverschlüsselt im RAM war.
Ich möchte nun ein Programm schreiben, dass eine beliebige Exe-Datei nimmt und auf diese Weise verschlüsselt. Dabei bin ich auf folgendes Problem gestoßen:
Den Exception-Handler steht an der Speicheradresse FS:[0] (unter Windows). Um meinen Exception-Handler zu setzen, rufe ich folgende Anweisungen auf:
Code: Alles auswählen
PUSH MyHandler
PUSH FS:[0]
MOV FS:[0], ESP
1. Ein Pointer zur nächsten Struktur
2. Der Zeiger auf die eigentliche Handler-Funktion.
Wie auch immer. Wenn ich nun beliebige Programme verschlüsseln möchte, muss ich davon ausgehen, dass diese auch mit SEH arbeiten. Das heißt, auch diese Programme werden einen Befehl verwenden, um den Pointer an FS:[0] zu überschreiben.
Das Trap-Flag hat zur Folge, dass eine Anweisung erst komplett ausgeführt wird, und *anschließend* zum Handler gesprungen wird. Das ist schlecht, denn dann sucht Windows den Handler (wie immer) an FS:[0], da steht nun aber nicht mehr mein Wert, sondern der von dem anderen Programm. Das hat zur Folge, dass der Rest des Programms nicht richtig entschlüsselt wird und früher oder später abstürzt.
Also habe ich mir gedacht: Ok, dann musst Du den Fall abfangen, dass der Wert überschrieben wird.
Und hier kommt ihr ins Spiel: Wie?
Ich habe probiert, die CPU-Register Dr0 und Dr7 mit anschließenden Aufruf von SetThreadContext() durchzuführen. Dieser Hardware-Breakpoint wird korrekt aufgerufen, aber ebenfalls erst, *nachdem* der Pointer überschrieben wurde, was wieder dazu führt, dass nicht mehr mein Handler aufgerufen wird.
Wenn ich in OllyDbg einen Software-Breakpoint setze, dann hält er an, bevor der Wert überschrieben wird. Das ist genau das Verfahren, was ich brauche. Leider weiß ich nicht, wie man das umsetzt. Muss ich dafür das Hauptprogramm unbedingt in einer Debug-Umgebung laufen lassen, also meinen eigenen "Debugger" schreiben?