Post

Assembly - Basic

Based on x86-64

Basic structure : opcode operand1 operand2 … ex) mov eax 3

kind of operand : Immediate value, Register, Memory(Size Directive TYPE PTR can added before it)


Data Transfer

mov dst, src : src->dst : transfers the value contained in the src register to the dst register

1
2
mov rdi, rsi ; rsi->rdi
mov QWORD PTR[rdi], rsi ; transfer value of rsi to memory address rdi point to  

lea dst, src : store Effective Address(EA) of src to dst

1
lea rsi, [rbx+8*rcx] ; store address of [rbx+8*rcx] to rsi

Arithmetic

1
2
3
4
add dst, src ; dst += src
sub dst, src ; dst -= src
inc op ; op += 1
dec op ; op -= 1

Logical

and, or, xor, neg ( bit calculation base)


Comparison

cmp op1, op2 : compare op1 and op2 by subtraction but no substition. Just set flag so that cpu read. For example, set ZF if op1=op2 test op1, op2 : compare op1 and op2 by taking And bit calculation but no substition. just set flag so taht cpu read


Branch : change execution flow by moving rip

jmp addr : move rip to addr

1
2
1: xor rax, rax
2: jmp 1

je addr : jump if just before comparison is true

1
2
3
4
1: mov rax, 0xcafebabe
2: mov rbx, 0xcafebabe
3: cmp rax, rbx ; rax == rbx ; <= prev compare
4: je 1 ; jump to 1

jg addr : jump if just before comparison, op1>op2

1
2
3
4
1: mov rax, 0x31337
2: mov rbx, 0x13337
3: cmp rax, rbx ; rax > rbx
4: jg 1  ; jump to 1

Stack

push val : rsp-=8; [rsp]=val;

1
2
3
4
5
;[Stack]
0x7fffffffc400 | 0x0  <= rsp
0x7fffffffc408 | 0x0
;[Code]
push 0x31337
1
2
3
4
5
; AFTER
;[Stack]
0x7fffffffc3f8 | 0x31337 <= rsp 
0x7fffffffc400 | 0x0
0x7fffffffc408 | 0x0

pop reg : reg=[rsp]; rsp+=8;

1
2
3
4
5
6
;[Stack]
0x7fffffffc3f8 | 0x31337 <= rsp 
0x7fffffffc400 | 0x0
0x7fffffffc408 | 0x0
;[Code]
pop rax
1
2
3
4
5
6
; AFTER
;[Register]
rax = 0x31337
;[Stack]
0x7fffffffc400 | 0x0 <= rsp 
0x7fffffffc408 | 0x0

Procedure

call addr : push return_address; jmp addr;

1
2
3
4
5
6
7
8
9
10
11
;[Register]
rip = 0x400000
rsp = 0x7fffffffc400 
;[Stack]
0x7fffffffc3f8 | 0x0
0x7fffffffc400 | 0x0 <= rsp
;[Code]
0x400000 | call 0x401000  <= rip
0x400005 | mov esi, eax
...
0x401000 | push rbp
1
2
3
4
5
6
7
8
9
10
11
;[Register] ;AFTER
rip = 0x401000
rsp = 0x7fffffffc3f8
;[Stack] 
0x7fffffffc3f8 | 0x400005  <= rsp
0x7fffffffc400 | 0x0
;[Code]
0x400000 | call 0x401000
0x400005 | mov esi, eax
...
0x401000 | push rbp  <= rip

leave(clear stack frame) : mov rsp, rbp; pop rbp

1
2
3
4
5
6
7
8
9
10
;[Register]
rsp = 0x7fffffffc400
rbp = 0x7fffffffc480
;[Stack]
0x7fffffffc400 | 0x0 <= rsp
...
0x7fffffffc480 | 0x7fffffffc500 <= rbp
0x7fffffffc488 | 0x31337 
;[Code]
leave
1
2
3
4
5
6
7
8
9
10
;[Register] ;AFTER
rsp = 0x7fffffffc488
rbp = 0x7fffffffc500
;[Stack]
0x7fffffffc400 | 0x0
...
0x7fffffffc480 | 0x7fffffffc500
0x7fffffffc488 | 0x31337 <= rsp
...
0x7fffffffc500 | 0x7fffffffc550 <= rbp

ret(return address) : pop rip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;[Register]
rip = 0x401021
rsp = 0x7fffffffc3f8
;[Stack]
0x7fffffffc3f8 | 0x400005    <= rsp
0x7fffffffc400 | 0x123456789abcdef
;[Code]
0x400000 | call 0x401000
0x400005 | mov esi, eax
...
0x401000 | push rbp
0x401001 | mov rbp, rsp
0x401004 | sub rsp, 0x30
0x401008 | mov BYTE PTR [RSP], 0x3
...
0x401020 | leave
0x401021 | ret <= rip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;[Register]
rip = 0x400005
rsp = 0x7fffffffc400
;[Stack]
0x7fffffffc3f8 | 0x400005
0x7fffffffc400 | 0x123456789abcdef    <= rsp
;[Code]
0x400000 | call 0x401000
0x400005 | mov esi, eax   <= rip
...
0x401000 | push rbp
0x401001 | mov rbp, rsp
0x401004 | sub rsp, 0x30
0x401008 | mov BYTE PTR [RSP], 0x3
...
0x401020 | leave
0x401021 | ret

Allocation and Free of stack frame

  1. Call function. At this time, nexp instruction address will be pushed in stack. : call func
  2. push rbp to stack to store original stack frame. : push rbp
  3. transfer rsp to rbp to create new stack frame.(then, rbp point same with rsp) : mov rbp rsp
  4. subtract 0x30 from rsp to expand stack frame space. : sub rsp 0x30
  5. allocate local variable to allocated stack frame. : mov BYTE PTR[rsp] 0x30
  6. use any calculation on this stack frame. : ...
  7. take stored rbp to go back to the original stack frame. : leave
  8. take stored return address to go back to the original execution flow. : ret

Update 2024-07-31

System Call

유저 모드에서 커널 모드의 시스템 소프트웨어에게 동작을 요청하기 위해 사용됨.

syscall

요청 : rax

인자 순서 : rdi -> rsi -> rdx -> rcx -> r8 -> r9 -> stack

syscall table은 검색하면 쉽게 찾을 수 있으므로 외울 필요는 없다. 셸코딩을 하다보면 자연스럽게 중요한 몇 가지는 익숙해질 것이다.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
read0x00unsigned int fdchar *bufsize_t count
write0x01unsigned int fdconst char *bufsize_t count
open0x02const char *filenameint flagsumode_t mode
close0x03unsigned int fd  
mprotect0x0aunsigned long startsize_t lenunsigned long prot
connect0x2aint sockfdstruct sockaddr *addrint addrlen
execve0x3bconst char *filenameconst char *const *argvconst char *const *envp
This post is licensed under CC BY 4.0 by the author.