Assembly Fundamental

Reverse Engineering — Part 1: x86 Assembly Fundamentals

A beginner-friendly intro to 32-bit x86 assembly in Intel syntax: registers, mov, arithmetic, status flags, and signed multiplication and division.

01/11/2026Super Admin5 min read

Welcome to my first reverse engineering blog post! This series documents my learning journey, one topic at a time.

Who this is for: anyone with basic programming experience (C helps, but isn't required) who wants to start reading disassembly. Goal of the series: to build, step by step, the skills needed to read and understand compiled code.

In this post, I'll cover common registers, data movement, basic arithmetic, and a first look at status flags on Intel x86 in 32-bit mode, using Intel syntax.

I. Common Registers

Each general-purpose register below holds 32 bits (4 bytes). These are their common roles, although their actual use depends on the program. Many of these roles are historical: certain instructions use specific registers implicitly (for example, loop uses ECX, and string instructions like movs use ESI and EDI).

RegisterCommon role
EAXArithmetic and function return values
EBXGeneral-purpose data
ECXCounters (loop, rep, shift counts in cl)
EDXGeneral-purpose data; also used in multiplication and division
ESISource pointer (string instructions)
EDIDestination pointer (string instructions)
ESPStack pointer
EBPOften used as a stack frame pointer
EIPInstruction pointer: address of the next instruction to execute

EIP is special: you cannot use it as an operand of mov. It changes through jumps, calls, and returns, which we'll cover later.

Smaller parts of a register

When reading disassembly you will constantly see smaller names. Each register can be accessed in parts:

Plain text
EAX (32 bits)
  AX (low 16 bits)
    AH (bits 8–15)  AL (bits 0–7)

EBX, ECX, and EDX work the same way (BX/BH/BL, CX/CH/CL, DX/DH/DL). ESI, EDI, ESP, and EBP have 16-bit forms (SI, DI, SP, BP) and, in 32-bit mode, no separate 8-bit high/low halves.

II. Moving Data

The mov instruction copies data from a source to a destination. The source remains unchanged.

Syntax:

ASM
mov destination, source

Common ways to copy data:

ASM
mov eax, ebx                 ; Register → register
mov DWORD PTR [esi], eax     ; Register → memory
mov eax, DWORD PTR [esi]     ; Memory → register
mov eax, 10                  ; Constant → register
mov DWORD PTR [esi], 10      ; Constant → memory

2.1. Address vs. Value

When ESI holds a memory address, esi gives us the address, while [esi] accesses the data stored at that address.

For example, suppose ESI = 0x1000 and the 32-bit value stored at that address is 42:

ASM
mov eax, esi       ; EAX = 0x1000 — copy the address
mov eax, [esi]     ; EAX = 42     — read the value at that address

ESI is a general-purpose register, so it can also hold an ordinary number rather than an address.

(A related instruction, lea, computes an address like [esi + 4] without reading memory. It deserves its own post, but keep in mind that it looks like a memory access and isn't one.)

2.2. Memory Operand Size

DWORD PTR specifies a 4-byte memory operand. It is needed when the assembler cannot otherwise determine the operand size:

ASM
mov DWORD PTR [esi], 10      ; Write a 32-bit value

Ordinary mov cannot copy directly between two memory locations. Use a register in between:

ASM
mov eax, DWORD PTR [esi]
mov DWORD PTR [edi], eax

These examples assume valid memory addresses with the required read or write permissions.

III. Arithmetic Instructions

These instructions perform basic arithmetic:

InstructionMeaningExample
addAdd to the destinationadd eax, 5
subSubtract from the destinationsub eax, 5
negCompute zero minus the operandneg eax
incIncrease by oneinc eax
decDecrease by onedec eax

Example:

ASM
mov eax, 10
add eax, 5      ; EAX = 15
sub eax, 3      ; EAX = 12
neg eax         ; EAX = -12 (0xFFFFFFF4)
inc eax         ; EAX = -11 (0xFFFFFFF5)
dec eax         ; EAX = -12 (0xFFFFFFF4)

Negative numbers are stored in two's complement, which is why -12 appears as 0xFFFFFFF4 in a 32-bit register.

Status flags

These instructions also update the CPU's status flags. Four matter most for now:

FlagSet when…
ZF (Zero)the result is zero
SF (Sign)the result's top bit is 1 (negative as a signed value)
CF (Carry)an unsigned operation carried out or borrowed
OF (Overflow)a signed result did not fit in the destination

IV. Signed Multiplication — IMUL

imul performs signed multiplication. It has three common forms:

ASM
imul source
imul destination, source
imul destination, source, immediate

The source can be a register or memory operand. In the two- and three-operand forms, the destination must be a register.

4.1. One-Operand Form

With a 32-bit source, the CPU implicitly multiplies EAX by the source and stores the full 64-bit result in EDX:EAX.

ASM
mov eax, 6
mov ebx, 7
imul ebx            ; EDX:EAX = EAX × EBX = 42

The result is split into two halves:

Plain text
[ EDX: upper 32 bits ][ EAX: lower 32 bits ]

For this example:

Plain text
EDX = 0x00000000
EAX = 0x0000002A     ; 42

EDX:EAX means the two registers are joined to represent one 64-bit value. In this form, CF and OF are set when EDX is not just the sign extension of EAX, i.e. when the result needs more than 32 bits.

4.2. Two-Operand Form

The destination is multiplied by the source. Only the lower 32 bits of the result are stored in the destination.

ASM
mov eax, 6
mov ebx, 7
imul eax, ebx       ; EAX = 42

4.3. Three-Operand Form

The second operand (the source) is multiplied by the third (an immediate constant), and the product is stored in the destination. Only the lower 32 bits of the result are kept.

ASM
mov ebx, 6
imul eax, ebx, 7    ; EAX = 42

The two- and three-operand forms do not write to EDX. If the full signed result cannot fit in the destination, the Carry and Overflow Flags are set.

V. Signed Division — IDIV

Syntax:

ASM
idiv divisor

The divisor must be a register or memory operand. idiv does not accept an immediate constant or support a two-operand form.

With a 32-bit divisor, the operation is:

Plain text
EDX:EAX ÷ divisor
EAX = quotient
EDX = remainder

When dividing a signed 32-bit value held in EAX, use cdq to sign-extend it into EDX:EAX first:

ASM
mov eax, -20
cdq                 ; Sign-extend EAX into EDX:EAX
mov ebx, 3
idiv ebx            ; EAX = -6, EDX = -2

We can check the result: -20 = (-6 × 3) + (-2). The quotient is truncated toward zero. A nonzero remainder has the same sign as the dividend.

A few things to remember:

  • cdq sets EDX to 0 for a nonnegative EAX, or 0xFFFFFFFF for a negative EAX, without changing EAX.
  • If EDX:EAX already contains the intended 64-bit dividend, do not use cdq: it would overwrite the upper half.