Eine Einführung in x86-Assembly (Teil 1 von X)

Mit dieser mehrteiligen Einführung möchte ich eine leichte, verständliche Einführung in x86-Assembly geben. Falls du dieses Tutorial also liest und etwas falsch, schwer verständlich oder interessant findest, gib mir gern Feedback oder stell mir deine Frage. Da ich hoffe auch Anfänger mit dieser Einführung zu erreichen, beginnen wir heute mit den grundlegenden Begriffen und Funktionsweisen.

Die x86-Instruction-Set Architektur schlägt im Herzen vieler moderner Prozessoren(CPUs). Ein Befehlssatz enthält alle Maschinenbefehle die ein bestimmter Prozessor ausführen kann. x86-Assembly steht für die Befehlssatzarchitekturen von Intel und AMD Prozessoren. Es gibt noch ARM-, RISC und weitere Befehlssätze je nachdem mit welcher Prozessorarchitektur man sprechen möchte.

Heutzutage kann man komplexe Programme und Systeme beschreiben, auch ohne Assembly schreiben zu können. Befasst man sich jedoch mit dem Reverse-Engineering von Malware oder anderem bereits kompilierten Code, kommt man an Assembly nicht vorbei. Auch bei Systemen die sehr schnell sein müssen, kitzelt man das letzte bisschen Performance am effizientesten mit Assembly Code raus.

Von Bits und Bytes

Die grundlegende Recheneinheit eines Computers heißt Bit. Ein Bit hat
zwei Zustände 1 für AN und 0 für AUS. Da wir nur zwei Zustände kennen, nutzen wir zur Repräsentation das Dual- oder Binärsystem. Ein Prozessor fasst acht Bits zu einem Byte zusammen. Zwei Byte ergeben ein Wort (WORD) und vier Bytes ergeben ein Doppelwort (DWORD). Haben wir 1024 Bytes sprechen wir von einem Kilobyte (KB) und bei 1.048.576 Byte von einem Megabyte (MB).

Bits werden also immer in Achtergruppen oder Vielfachen davon verarbeitet. Hat man ein WORD 11111111 ist dieses äquivalent zu Dezimalwert 255.

Bitwert11111111
Bitposition76543210
Position als Exponent zur Basis 21286432168421

Die Summe der Positionen des Bits als Exponent mit der Basis zwei ist 255 und das ist dasselbe wie (2^8)-1. Ein Byte kann also jeden Wert zwischen 0 und 255 annehmen, ein WORD kann von 0-65.535 und ein DWORD einen maximalen Wert von 4.294.967.295 annehmen.

Schaut man in der Praxis Daten an, beispielsweise in einem Wireshark-Mitschnitt, so sind diese meist Hexadezimal dargestellt. Ein WORD wird meist aus Komfortgründen zur Basis 16 dargestellt. Dies bedeutet, dass Werte von 0-15 durch 0123456789ABCDEF dargestellt werden, das ganze ist in der nachfolgenden Tabelle dargestellt:

DezimalBinärHexadezimal
000000
100011
200102
300113
401004
501015
601106
701117
810008
910019
101010A
111011B
121100C
131101D
141110E
151111F

Um eine Hexadezimale Zahl als Binärzahl darzustelen, schreiben wir jedes Element in sein vierstelliges binäres Äquivalent um: Das WORD A43F ist also 1010 0100 0011 1111 in binärer Schreibweise.

Register in der CPU

Ein Register ist ein Ort an dem Daten auf der CPU abgelegt werden können, um damit zu in Zukunft etwas zu machen, beispielsweise rechnen. Register können in unterschiedliche Klassen aufgeteilt werden: Klassischerweise gibt es folgende acht Mehrzweck Register(General Purpose) in x86 Architekturen:

EAX, EBX, ECD, EDX, ESI, EDI, EBP, ESP.

Ein Mehrzweck-Register hat eine Größe von 32 Bit, kann also ein DWORD enthalten. Es gibt außerdem noch Segmentregister und Statusregister.
Segmentregister werden wir im nächsten Teil näher betrachten hier gibt es nur eine kurze Einführung: Der Hexadezimale Wert 0x40 kann entweder eine Instruktion oder ein Datenwert sein. Die CPU weiß nur dank des Segmentregisters, wie der Wert zu verarbeiten ist.

Ein Statusregister markiert (flaggt) Werte um einen Status zu repräsentieren. Das Zero-Flag zeigt beispielsweise, dass das Resultat der letzten Rechnung null ist. Ein Vorzeichen (Sign) Flag, zeigt ob Daten mit einem Vorzeichen zu interpretieren sind oder nicht. Bedingte Sprunganweisungen, beispielsweise
if-Konstruktionen in C, Python oder anderen Hochsprachen, hängen meist von den Flags ab.

Außerdem gibt es einen Extended Instruction Pointer. Dieser hat in der CPU eine ähnliche Funktion wie die das sprechende Navi im Straßenverkehr
wenn wir abbiegen sollen: Er zeigt auf die nächste Instruction, die auszuführen ist.

Abschluss der Einführung in x86-Assembly

Das wars für den ersten Teil. Wenn du Fragen oder sonstiges Feedback hast schreib mich einfach an.  Das nächste Mal machen wir dann mit Segmentregistern weiter.