어셈블리 언어는 기계어와 치환되는 언어입니다.
기본 구조는 명령어(Opcode)
와 피연산자(Operand)
로 구성됩니다.
opcode operand1 operand2
Ex) mov eax, 3
opcode = mov
operand1 = eax
operand2 = 3
메모리 피연산자는 [ ]
로 둘러싸인 것으로 표현되며, 앞에 크기 지정자는 TYPE PTR
이 추가될 수 있습니다.
타입에는 BYTE, WORD, DWORD, QWORD가 올 수 있으며, 각각 1BYTE, 2BYTE, 4BYTE, 8BYTE의 크기를 지정합니다.
어떤 값을 레지스터나 메모리에 옮기도록 지시합니다.
예제 - 데이터 이동
[Register]
rbx = 0x401A40
=================================
[Memory]
0x401a40 | 0x0000000012345678
0x401a48 | 0x0000000000C0FFEE
0x401a50 | 0x00000000DEADBEEF
0x401a58 | 0x00000000CAFEBABE
0x401a60 | 0x0000000087654321
=================================
[Code]
1: mov rax, [rbx+8]
2: lea rax, [rbx+8]
1. Code를 1까지 실행했을 때, rax에 저장된 값은?
rax = [rbx+8]
rax = 0xC0FFEE
2. Code를 2까지 실행했을 때, rax에 들어있는 값은?
rax = [rbx+8]
rax = 0x401a48
덧셈, 뻴셈, 곱셈, 나눗셈 연산을 지시합니다.
예제 - 덧셈과 뺄셈
[Register]
rax = 0x31337
rbx = 0x555555554000
rcx = 0x2
=================================
[Memory]
0x555555554000| 0x0000000000000000
0x555555554008| 0x0000000000000001
0x555555554010| 0x0000000000000003
0x555555554018| 0x0000000000000005
0x555555554020| 0x000000000003133A
==================================
[Code]
1: add rax, [rbx+rcx*8]
2: add rcx, 2
3: sub rax, [rbx+rcx*8]
4: inc rax
1. Code를 1까지 실행했을 때, rax에 저장된 값은?
rax = rax + [0x555555554010]
rax = 0x31337 + 0x00003 = 0x3133A
2. Code를 3까지 실행했을 때, rax에 저장된 값은?
rcx = rcx + 0x2
rcx = 0x2 + 0x2 = 0x4
rax = 0x3133A
rax = rax - [0x555555554020]
rax = 0x3133A - 0x3133A = 0
3. Code를 4까지 실행했을 때, rax에 저장된 값은?
rax = 0x0
rax = rax + 1
rax = 0 + 1 = 1
논리 연산 명령어는 and, or, xor, neg 등의 비트 연산을 지시합니다.
and dst, src: dst ← dst and src
Ex) and eax, ebx ; eax = eax and ebx
or dst, src: dst ← dst or src
Ex) or eax, ebx ; eax = eax or src
예제 - 논리 연산(and & or)
[Register]
rax = 0xffffffff00000000
rbx = 0x00000000ffffffff
rcx = 0x123456789abcdef0
==================================
[Code]
1: and rax, rcx
2: and rbx, rcx
3: or rax, rbx
1. Code를 1까지 실행했을 때, rax에 저장된 값은?
rax = rax and rcx
rax = 0xffffffff00000000 and 0x123456789abcdef0 = 0x1234567800000000
2. Code를 2까지 실행했을 때, rbx에 저장된 값은?
rbx = rbx and rcx
rbx = 0x00000000ffffffff and 0x123456789abcdef0 = 0x000000009abcdef0
3. Code를 3까지 실행했을 때, rax에 저장된 값은?
rax = rax or rbx
rax = 0x1234567800000000 or 0x000000009abcdef0 = 0x123456789abcdef0
xor dst, src: dst ← dst xor src
Ex) xor eax, ebx ; eax = eax xor ebx
not op: !op
Ex) not eax ; !eax
예제 - 논리 연산(xor, not)
[Register]
rax = 0x35014541
rbx = 0xdeadbeef
==================================
[Code]
1: xor rax, rbx
2: xor rax, rbx
3: not eax
1. Code를 1까지 실행했을 때, rax에 저장되는 값은?
rax = rax xor rbx
rax = 0x35014541 xor 0xdeadbeef = 0xebacfbae
2. Code를 2까지 실행했을 때, rax에 저장되는 값은?
rax = rax xor rbx
rax = 0xebacfbae xor 0xdeadbeef = 0x35014541
→ xor 연산을 동일 값으로 두 번 실행할 경우, 원래 값으로 돌아갑니다.
3. Code를 3까지 실행했을 때, rax에 저장되는 값은?
!eax
rax = 0xcafebabe
두 피연산자의 값을 비교하는 플래그를 설정합니다.
cmp op1, op2: op1과 op2를 비교 (op1 - op2 연산 실행)
Ex)
mov rax, 0xA
mov rbx, 0xA
cmp rax, rbx ; ZF=1 ; Zero Flag가 1이기 때문에, op1 - op2 = 0 → 두 값이 같다
test op1, op2: op1과 op2를 비교 (op1 and op2 연산 실행)
Ex)
xor rax, rax
test rax, rax ; ZF=1 ; Zero Flag가 설정됬기 때문에, rax and rax = 0 → rax는 0이다.
rip를 이동시켜 실행 흐름을 바꿉니다.
jmp addr: addr로 rip를 이동시킵니다.
Ex)
xor rax, rax
jmp 1 ; jump to 1
je addr: 직전에 비교한 두 피연산자가 같으면 점프 (jump if equal)
Ex)
mov rax, 0xcafebabe
mov rbx, 0xcafebabe
cmp rax, rbx ; rax == rbx
je 1 ; jump to 1
jg addr: 직전에 비교한 두 연산자 중 전자가 더 크면 점프 (jump if greater)
Ex)
mov rax, 0x31337
mov rbx, 0x13337
cmp rax, rbx ; rax > rbx
jg 1 ; jump to 1