1 Punkte von GN⁺ 2024-07-11 | 1 Kommentare | Auf WhatsApp teilen

Seltsame Dinge, die ich beim Schreiben eines x86-Emulators gelernt habe

  • Erläutert verschiedene Trivia und Seltsamkeiten, die beim Schreiben von x86- und amd64-Emulatoren aufgefallen sind
  • Beim Time Travel Debugging (TTD) wird ein CPU-Emulator verwendet, um die Ausführung eines Prozesses auf Instruktionsebene aufzuzeichnen
  • Die erste Version von TTD hieß iDNA, war in Assembler geschrieben, schnell, aber schwer wartbar
  • Die zweite Version wurde in C++ geschrieben und war dadurch besser wartbar

Nutzlose x86-Encoding-Trivia

  • Das x86-Encoding-Schema erlaubt es, dieselbe Instruktion auf verschiedene Arten zu encodieren
  • Die Instruktion int 3 kann als CD 03 oder CC encodiert werden
  • Das Register EAX wird „Akkumulator-Register“ genannt und unterscheidet sich tatsächlich beim Encoding
  • Das REX-Präfix ermöglicht in 64-Bit-Code den Zugriff auf einen größeren Registerbereich
  • Instruktionen können bis zu 15 Byte lang sein; bei Überschreitung wird eine Ausnahme ausgelöst
  • Das Address-Override-Präfix ermöglicht es, im 64-Bit-Modus 32-Bit-Adressen zu referenzieren

Seltsame Eigenschaften von Flags

  • Die Instruktion INC aktualisiert im Gegensatz zu ADD nicht das Carry-Flag
  • Die Instruktionen CMPXCHG8B/CMPXCHG16B ändern nur das Zero-Flag
  • Shift- und Rotate-Instruktionen lassen das Overflow-Flag undefiniert, wenn die Shift-Menge größer als 1 ist

Noch mehr Überraschungen bei Shift-Instruktionen

  • shr ax, 10h verschiebt das Register ax um 16 Bit und macht es zu 0
  • shr eax, 20h verschiebt das Register eax um 32 Bit, aber der Wert ändert sich nicht
  • Die Shift-Menge wird mit 1FH maskiert

Segment-Overrides

  • Segmente werden in 32-Bit- und 64-Bit-Code weiterhin verwendet, hauptsächlich für Thread Local Storage
  • Unter Windows wird über die Register FS oder GS auf den TEB (Thread Execution Block) verwiesen
  • In 32-Bit-Prozessen wird FS verwendet, in 64-Bit-Prozessen GS
  • Im 64-Bit-Modus ist der Wert des Segmentregisters selbst nicht wichtig

Segment-Overrides: noch mehr Trivia

  • Im 32-Bit-Modus verweist der tatsächliche Wert des Segmentregisters auf den Segment-Deskriptor
  • Im 64-Bit-Modus wird die Basis durch das MSR gesteuert
  • In WinDbg kann man die Segmentwerte eines 64-Bit-Prozesses nicht direkt auslesen

Fazit

  • Dieser Beitrag liefert eine zufällige Liste von x86-Trivia
  • Das Schreiben eines Emulators hilft dabei, ein tiefes Verständnis dafür zu entwickeln, wie eine CPU funktioniert
  • Auf der Website von Agner Fog gibt es hervorragende Ressourcen

Zusammenfassung von GN⁺

  • Erläutert verschiedene Trivia und Seltsamkeiten, die beim Schreiben von x86- und amd64-Emulatoren aufgefallen sind
  • Das Schreiben eines Emulators hilft dabei, die Funktionsweise einer CPU tief zu verstehen
  • Behandelt verschiedene Trivia wie unterschiedliche Encoding-Methoden für die Instruktion int 3, das REX-Präfix und Segment-Overrides
  • Weitere Ressourcen gibt es auf der Website von Agner Fog

1 Kommentare

 
GN⁺ 2024-07-11
Hacker-News-Kommentar
  • Das Intel SDM stellt klar, dass bei den Befehlen BSF/BSR der Zielwert bei Eingabe von 0 undefiniert ist. AMD dokumentiert, dass das Ziel in diesem Fall nicht verändert wird
    • glibc nutzt die inoffizielle Tatsache aus, dass das Ziel bei Intel nicht verändert wird
    • TZCNT/LZCNT sind BSF/BSR mit F3-Präfix, das auf älteren Prozessoren ignoriert wird. Derselbe Code kann sich auf unterschiedlichen CPUs unterschiedlich verhalten
  • Es gibt viele Beschwerden über Präfixe, aber das ist nicht das größte Problem. Die erweiterten Bits REX/VEX/EVEX.RXB werden ignoriert, wenn sie nicht angewendet werden
    • APX kann mit dem REX2-Präfix Register r16-r31 kodieren, aber nicht xmm16-xmm31
    • Das EVEX-Präfix hat je nach Opcode unterschiedliche Layouts
    • Je nach Registertyp wird die Erweiterungsbit-Nutzung unterschiedlich gehandhabt
  • Meinung von jemandem, der gern Assembler programmiert. Er mag die einfache, direkte ästhetische Qualität
    • Er teilt die Erfahrung, eine Mini-VM geschrieben zu haben, um einem JS-Freund den Stack zu erklären
    • Es wird erwähnt, dass der Freund mit Webentwicklung so beschäftigt ist, dass ihm die Zeit für tiefergehendes Lernen fehlt
  • Es wurde fälschlich erinnert, dass sich eine Salsa20-Variante und Maschinencode auf cryp.to befänden. Dan Bernsteins Website ist cr.yp.to
    • Es wird die Erfahrung geteilt, in einem Startup an Datenverschlüsselung gearbeitet und dabei verschiedene Implementierungen getestet zu haben
  • Justine Tunney und ihr Emulator werden empfohlen. Die Dokumentation erklärt gut, wie eine CPU funktioniert
  • Der Meinung, dass das Schreiben eines CPU-Emulators der beste Weg sei, eine CPU zu verstehen, wird widersprochen
    • Eine CPU auf Gate-Ebene zu bauen, sei der bessere Weg
  • Der Meinung, dass x86-Assembler mehr Probleme verursache als RISC, wird widersprochen
    • x86 ist leicht zu analysieren, MIPS dagegen schwierig
  • Es wird Respekt für Entwickler von x86-Prozessor-Emulatoren ausgedrückt
    • Bei der Entwicklung eines i386-Emulators wurde viel über Systemaufrufe und ELF gelernt
  • Es wird Erfahrung mit dem Schreiben eines x86-Emulators geteilt
    • Es wird an die Erfahrung erinnert, einen Spielzeug-Emulator geschrieben zu haben, der frühen BIOS-Code ausführt
  • Es wird die Meinung geteilt, dass Blogstil und Layout gefallen