Tody, Sept 21, 2025, I’m learning assembly from Pwn.college.

Intro

Come on, Assembly is named “Assembly” because it is assembled (not compiled) into binary code.
The inventor is Kathleen Booth.
Assembly is the simplest programming language.
So You can master assembly in a week!
Assembly tells computer what to do, with a Noun/Operands, and Verbs/Operators.
There are tow syntaxes of x86 assembly, AT&T and Intel.

Data

Binary

A binary digit is called a bit.
And a byte is a 8-bits group.
Then there’s word here, first composed of 2bytes, then 4, and now 8bytes typically for modern computer.

Registers

Registers are very fast, temporary stores for data.
Registers are (typically) the same size as the word width of the architecture

Control Flow

Conditional jumps check Conditions stored in the “flags” register: rflags

There is a bunch of bits in rflags, each indicates a “flag”.

Flags are updated by:

  • Most arithmetic instructions
  • Comparison instruction cmp(sub, but discards result)
  • Comparison instruction test(and, but discards result)

Main conditional flags:

  • Carry Flag: was the 65th bit 1?
  • Zero Flag: was the result 0?
  • Overflow Flag: did the result “wrap” between positive to negative?
  • Signed Flag: was the result’s signed bit set (i.e, was it negative)?

Common patterns:

1
2
3
4
cmp rax, rbx; ja STAY_LEET # unsigned rax > rbx. 0xffffffff >= 0
cmp rax, rbx; jle STAY_LEET # signed rax <= rbx. 0xffffffff = -1 < 0
test rax, rax; jnz STAY_LEET # rax != 0
cmp rax, rbx; je STAY_LEET # rax == rbx

image.png

Function Calls

Assembly code is split into functions with call and ret.
call pushes rip (address of the next instruction after the call) and jumps away!
ret pops rip and jumps to it!

An example:

1
2
3
4
5
6
7
8
9
10
int check_leet(int authed) {
if(authed) return 1337;
else return 0;
}

int main() {
check_leet(0);
check_leet(1);
exit();
}

In assembly, it looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.intel_syntax noprefix

.global _start
_start:

mov rdi, 0
call FNC_CHECK_LEET
mov rdi, 1
call FNC_CHECK_LEET
call FNC_EXIT

FUNC_CHECK_LEET:
push rbp
mov rbp, rsp
test rdi, rdi
jnz LEET
mov ax, 0
ret
LEET:
mov ax, 1337
mov rsp, rbp
pop rbp
ret

FUNC_EXIT:
???

Practicing

Set Registers

1
mov dest_reg, src_reg

Add to Registers

1
add reg1, reg2

This means add reg2 to reg1 and store the sum in reg1;

Linear Equation of Register

1
2
3
imul reg1, reg2
add reg1, reg3
mov reg4, reg1

This means reg4 = reg1 * reg2 + reg3

Integer Division

For the instruction div reg, the following happens:

  • rax = rdx:rax / reg
  • rdx = remainder

Access Lower Bits

We could access eht lower bytes of each register using different register names.

1
2
3
4
5
6
7
8
9
10
MSB                                    LSB
+----------------------------------------+
| rax |
+--------------------+-------------------+
| eax |
+---------+---------+
| ax |
+----+----+
| ah | al |
+----+----+

Byte Extraction

Shifting has the nice side effect of doing quick multiplication (by2) or division (by2), and can also be used to compute modulo.

  • shl reg1, reg2 <=> Shift reg1 left by the amount in reg2
  • shr reg1, reg2 <=> Shift reg1 right by the amount in reg2

Bitwise Operation

We have four kinds of logic instructions to implement bitwise operation: and, or, not, xor.

Memory size access

The breakdown of the names of memory sizes:

  • Quad Word = 8 Bytes = 64 bits
  • Double Word = 4 Bytes = 32 bits
  • Word = 2 bytes = 16 bits
  • Byte = 1 Byte = 8 bits

Jump

Absolute Jump

1
2
lea reg, [mem]
jmp reg

Relative Jump

1
2
3
4
5
jmp END
.rept 0x50
nop
.endr
END:

This snippet does jmp to [0x50 + the_addr_of_jmp_END].

Indirect Jump

Here we do not jump directly to an address or a symbol, we could make a jump table, look like:

1
2
3
4
[0x1337] = address of do_thing_0
[0x1337+0x8] = address of do_thing_1
[0x1337+0x10] = address of do_thing_2
[0x1337+0x18] = address of do_default_thing

Here 0x1337 is the address of the jump table, and we jump like this:

1
jmp [jump_table_address + number * 0x8]

This kind of jump always be used to write a switch like snippet, looks like:

1
2
3
4
5
6
switch(x) {
case 0: do_thing_0;
case 1: do_thing_1;
case 2: do_thing_2;
default: do_thing_default;
}