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
- Call function. At this time, nexp instruction address will be pushed in stack. :
call func - push rbp to stack to store original stack frame. :
push rbp - transfer rsp to rbp to create new stack frame.(then, rbp point same with rsp) :
mov rbp rsp - subtract 0x30 from rsp to expand stack frame space. :
sub rsp 0x30 - allocate local variable to allocated stack frame. :
mov BYTE PTR[rsp] 0x30 - use any calculation on this stack frame. :
... - take stored rbp to go back to the original stack frame. :
leave - 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은 검색하면 쉽게 찾을 수 있으므로 외울 필요는 없다. 셸코딩을 하다보면 자연스럽게 중요한 몇 가지는 익숙해질 것이다.
| syscall | rax | arg0 (rdi) | arg1 (rsi) | arg2 (rdx) |
|---|---|---|---|---|
| read | 0x00 | unsigned int fd | char *buf | size_t count |
| write | 0x01 | unsigned int fd | const char *buf | size_t count |
| open | 0x02 | const char *filename | int flags | umode_t mode |
| close | 0x03 | unsigned int fd | ||
| mprotect | 0x0a | unsigned long start | size_t len | unsigned long prot |
| connect | 0x2a | int sockfd | struct sockaddr *addr | int addrlen |
| execve | 0x3b | const char *filename | const char *const *argv | const char *const *envp |