6. Flags
Welcome to one of the most important concepts in assembly programming! Flags are the CPU’s way of telling you what happened during the last operation. They’re the secret sauce that makes decision-making and loops possible.
What are Flags?
The RFLAGS register is a 64-bit register where each bit represents a different condition or status. Its kinda the CPU’s dashboard with status eg. Negative, Zero, Carry, Overflow. These flags are automatically set after most arithmetic and logical operations.
The RFLAGS Register
Here are the most important flags we’ll work with:
Refer this for more detailed view.
| Flag | Bit | Name | Purpose |
|---|---|---|---|
| CF | 0 | Carry Flag | Set if unsigned overflow occurred |
| PF | 2 | Parity Flag | Set if result has even number of 1 bits |
| AF | 4 | Adjust Flag | BCD operations (rarely used) |
| ZF | 6 | Zero Flag | Set if result was zero |
| SF | 7 | Sign Flag | Set if result was negative |
| OF | 11 | Overflow Flag | Set if signed overflow occurred |
How Flags Get Set
After every arithmetic operation, the CPU automatically updates these flags:
1
2
3
4
5
6
7
8
9
10
mov rax, 10
mov rbx, 20
; After this addition, flags will be:
add rax, rbx ; ZF=0 (result ≠ 0), SF=0 (positive), CF=0 (no carry), OF=0 (no overflow)
sub rax, 30 ; ZF=1 (result = 0), SF=0, CF=0, OF=0
mov rcx, -1
add rcx, 1 ; ZF=1 (result = 0), SF=0, CF=1 (unsigned overflow), OF=0
Let’s this practically in rappel we discussed in previous blogs.
After we execute the sub rax, 30 instruction, we can see ZF is set.
> sub rax, 30
rax=0000000000000000 rbx=0000000000000014 rcx=0000000000000000
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=0000000000400005 rsp=00007fffda9cd5f0 rbp=0000000000000000
r8=0000000000000000 r9=0000000000000000 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
[cf=0, zf=1, of=0, sf=0, pf=1, af=0, df=0]
In GDB/Pwndbg, you can see the current flags:
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> info registers eflags
eflags = 0x202 [ CF PF IF ]
pwndbg> p $eflags
$1 = [ CF ZF ]
# Set flags
# setflag <flag_name> <value>
# To set the Zero Flag (ZF):
pwndbg> setflag ZF 1
# To clear the Carry Flag (CF):
pwndbg> setflag CF 0
The CMP Instruction: Comparison Without Storage
The cmp instruction is specifically designed for comparisons. It subtracts the operands but doesn’t store the result - it only sets the flags!
1
2
3
4
5
mov rax, 10
mov rbx, 20
cmp rax, rbx ; Sets flags as if we did: 10 - 20
; Result: ZF=0 (not equal), SF=1 (negative), CF=1 (borrow needed)
You can run this in rappel and check. But if you want to make it full assembly program then you need to add assembler directives and entry point _start
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
section .data
; No data needed for this simple comparison
section .bss
; No uninitialized data
section .text
global _start
_start:
mov rax, 10 ; Load 10 into RAX
mov rbx, 20 ; Load 20 into RBX
cmp rax, rbx ; Compare RAX and RBX (sets flags as if RAX - RBX)
mov rax, 60 ; syscall: exit
syscall
1
2
3
$ nasm -f elf64 cmp.s -o cmp.o
$ ld cmp.o -o cmp
$ ./cmp
CMP is equivalent to SUB, but doesn’t change the operands.
TEST Instruction: Bitwise Comparison
test performs a bitwise AND without storing the result, only setting flags:
1
2
3
4
5
6
mov rax, 5 ; Binary: 101
test rax, 1 ; Check if least significant bit is set
; ZF=0 (result of AND is not zero), so number is odd
test rax, rax ; Common idiom: check if RAX is zero
; ZF=1 if RAX is zero, ZF=0 otherwise
Why Flags Matter: Conditional Jumps
Flags become incredibly powerful when combined with conditional jumps (next chapter). They enable if statements and loops.
Some instructions don’t affect flags:
mov,lea,push,pop
Some instructions do affect flags:
add,sub,cmp,test