Opcodes zu mnemonics

Hallo zusammen,
wenn ich ein Programm mit OllyDbg öffne, zeigt er mir ja den Assembler Code an, der sich aus den Opcodes ergibt. Mich interessiert jetzt, wie Olly das macht, gibt es da ein System hinter?
Angenommen ich möchte alle push eax in push ecx umwandeln, dann kann ich ja nicht einfach in dem Programm nach allen vorkommen von 0x50 suchen und gegen 0x51 ersetzen. Schließlich könnte der Wert auch zu einem Jump oder etwas anderem gehören. Demnach müsste ich das erste Byte der Opcodes doch einlesen und entscheiden, wie viele weitere Bytes noch zum Mnemonic vom ersten Byte gehören. Falls es sich um ein Mnemonic handelt, dass nur aus einem Byte besteht und dem Wert 50 entspricht, könnte ich es in 51 umwandeln.

Oder Angenommen, ich möchte alle CALL aufrufe auflisten. Da besteht das selbe Problem. Demnach müsste ich die Opcodes analysieren und herausfinden, zu welchem Mnemonic sie gehören. Wie würdet ihr das angehen?

gruß seux
 
Hierfür gibt es schon fertige Libraries:
zB.
BeaEngine
HDE
diStorm

Das Problem besteht eher darin Opcode-Folgen zu finden und diese von Daten zu unterscheiden.

MfG
Inliferty

Edit:
Ich persönlich finde die BeaEngine am besten und ist die mir einzig bekannte Engine mit x64 Support.
 
Daten und Code zu unterscheiden ist ein nicht zu unterschätzendes Problem. Eine naive Methode wäre ja einfach ab dem Entrypoint (und allen exports+TLS-Callbacks) alles als Code zu interpretieren und sich durch den ganzen Code durchzuhangeln, indem man jeweils die Länge jeder Instruktion weiterstepped und auch Calls und Jumps folgt.
Das Problem: Manche "execution-paths" ergeben sich erst dynamisch während der Ausführung. Beispielsweise Callbacks, die man als Funktionspointer einer anderen Funktion übergibt, oder Exception Handler, ThreadProcs usw.. Oder solche Konstrukte wie JMP/CALL <REGISTER> oder JMP DWORD PTR auf uninitialisierte Funktionspointer.
Aber allgemein klappt das ansonsten ganz gut und es reicht für die normalen Disassembly Zwecke. Aber um sowas wie PUSH EAX in PUSH ECX auszutauschen braucht man schon quasi eine 100% detection bzw. zumindest keine false positives, weil man ansonsten Daten korrumpieren könnte.

Siehe auch dieses Paper:
http://www.utdallas.edu/~hamlen/wartell-pkdd11.pdf
 
Vielen Dank euch beiden schomal für die schnellen Antworten.
@Inliferty: Ich mache das kleine Projekt in Java, da ich diese Sprache auch in der Uni lerne und sie später auch fürs Software-Projekt können sollte. Ich denke mal, dass ich die Engines dann nicht nutzen kann (auf der Homepage von BeaEngine stand jedenfalls C).

@+++ATH0: Ich möchte es ja vorerst einfach halten. Gehen wir mal von nur einer Section mit Code aus, und diesen Code habe ich schon in ein Byte Array eingelesen. Demnach dürfte das Problem mit den Daten/Code ja nicht bestehen (Okay, es können sich auch Daten im Code Segment befinden, aber das möchte ich hier erstmal vernachlässigen). Ich habe mich jetzt dazu entschlossen, einmal alle CALLs aufzulisten. Ein CALL hat den Wert 0xE8 gefolgt von einem DWORD. Die Idee mit den Längen der entsprechenden Instruktions finde ich ganz gut, an sowas hatte ich auch schon gedacht. Ich habe mich mal nach einer Liste umgeschaut und das hier gefunden:
coder32 edition | X86 Opcode and Instruction Reference 1.11
Jedoch habe ich massive schwierigkeiten, das zu verstehen. Die Opcodes können ja nur von 0x00 bis 0xFF gehen. Im Grunde genommen bräuchte ich nur eine Liste, in der sich ablesen lässt, wie viele Bytes der Mnemonic für 0x?? noch benötigt und würde dann diese Anzahl weiterspringen. Sobald ich dann 0xE8 eingelesen hab, könnte ich ich die nächsten 4 Bytes ausgeben.

Lässt sich aus dem obigen Link diese Länge rauslesen? Oder kennt ihr eine andere Liste, wo ich die Länge übernehmen könnte?

PS: Das Dokument, was du gepostet hast, hab ich bisher nur überflogen. Ich werde es mir später nochmal genauer angucken ;).
 
Gehen wir mal von nur einer Section mit Code aus, und diesen Code habe ich schon in ein Byte Array eingelesen. Demnach dürfte das Problem mit den Daten/Code ja nicht bestehen

Zusätzlich zu "interleaved data" (beispielsweise sehr häufig bei Delphi Executables) können auch padding bytes drin sein, die das alignment zerstören. Man kann also zwar einmal von oben bis unten durchgehen und hoffen, dass das padding nicht das alignment zerstört, aber darauf vertrauen sollte man nicht. Sicherer ist es vom Entrypoint auszugehen und Sprüngen und Calls zu folgen. Dadurch findet man halt nicht alles, aber dafür ist das Ergebnis sicherer.

Zu den Längen von Instruktionen: Das ist nicht so ganz einfach. Bei manchen Opcodes gibt es nur eine fixe Länge, die man springen muss. Bei anderen Opcodes folgt allerdings ein MOD R/M Byte, welches je nach Kodierung darüber entscheidet was nun folgt. Also entweder gar nichts mehr, ein Byte, ein DWORD oder ein SIB Byte, welches wiederum darüber entscheidet ob noch ein Byte oder DWORD als Basis draufgerechnet wird. Dann gibt es noch die 16-Bit Prefixes, die auf WORD-Größe statt DWORDS schaltet.

Google mal nach "Length Disassembler" und schau dir am besten ein Beispiel an, oder benutz es gleich. Je nach dem, was gerade interessant für dich ist. :wink:
 
Zurück
Oben