CONTENTS
1. 어셈블리어와 x86-64
1-1. 어셈블리언어
1-2. x86-64 어셈블리
1-3. 피연산자
2. x86-64 어셈블리 명령어
2-1. Opcode : 데이터 이동
2-2. Opcode : 산술 연산
2-3. Opcode : 논리 연산 - and, or
2-4. Opcode : 논리 연산 - xor, not
2-5. Opcode : 비교/분기
3. 요약 정리
1. 어셈블리어와 x86-64
1-1. 어셈블리언어
- 어셈블리 언어는 컴퓨터의 기계어와 치환되는 언어
1-2. x86-64 어셈블리
1) 기본 구조
- 동사에 해당하는 명령어(Operation Code, Opcode)와 목적어에 해당하는 피연산자(Operand)로 구성

2) 명령어
| 명령 코드 | |
| 데이터 이동(Data Transfer) | mov, lea |
| 산술 연산(Arithmetic) | inc, dec, add, sub |
| 논리 연산(Logical) | and, or, xor, not |
| 비교(Comparison) | cmp, test |
| 분기(Branch) | jmp, je, jg |
| 스택(Stack) | push, pop |
| 프로시져(Procedure) | call, ret, leave |
| 시스템 콜(System call) | syscall |
1-3. 피연산자
- 메모리 피연산자는 []으로 둘러싸인 것으로 표현되며, 앞에 크기 지정자(Size Directive) TYPE PTR이 추가될 수 있다
- TYPE에는 BYTE, WORD, DWORD, QWORD가 있으며, 각각 1바이트, 2바이트, 4바이트, 8바이트의 크기를 지정
1) 종류
- 상수(Immediate Value)
- 레지스터(Register)
- 메모리(Memory)
[예시]
| 메모리 피 연산자 | |
| QWORD PTR [0x8048000] | 0x8048000의 데이터를 8바이트만큼 참조 |
| DWORD PTR [0x8048000] | 0x8048000의 데이터를 4바이트만큼 참조 |
| WORD PTR [rax] | rax가 가르키는 주소에서 데이터를 2바이트만큼 참조 |
2. x86-64 어셈블리 명령어
2-1. Opcode : 데이터 이동
- 데이터 이동 명령어는 어떤 값을 레지스터나 메모리에 옮기도록 지시
| mov dst, src : src에 들어있는 값을 dst에 대입 | |
| mov rdi, rsi | rsi의 값을 rdi에 대입 |
| mov QWORD PTR[rdi], rsi | rsi의 값을 rdi가 가리키는 주소에 대입 |
| mov QWROD PTR[rdi+8*rcx], rsi | rsi의 값을 rdi+8*rcx가 가리키는 주소에 대입 |
| lea dst.src : src의 유효 주소(Effective Addres, EA)를 dst에 저장 | |
| lea rsi, [rbx+8*rcx] | rbx+8*rcx를 rsi에 대입 |
[예시] - 데이터 이동
▷ 레지스터, 메모리 및 코드가 다음과 같을 때, 아래에서 적절한 값을 채우시오.
[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에 저장된 값은 ( )이다.
0xCOFFEE
2. Code를 2까지 실행했을 때, rax에 들어있는 값은 ( )이다.
0x401A48
2-2. Opcode : 산술 연산
- 산술 연산 명령어는 덧셈, 뺄셈, 곱셈, 나눗셈 연산을 지시
| add dst, src : dst에 src의 값을 더한다 | |
| add eax, 3 | eax += 3 |
| add ax, WORD PTR[rdi] | ax += *(WORD *)rdi |
| sub dst, src " dst에서 src의 값을 뺍니다 | |
| sub eax, 3 | eax -= 3 |
| sub ax, WORD PTR[rdi] | ax -= *(WORD *)rdi |
| inc op : op의 값을 1 증가시킴 | |
| inc eax | eax += 1 |
| dec op : op의 값을 1 감소시킴 | |
| dec eax | eax -=1 |
[예시] - 덧셈과 뺄셈
- 레지스터, 메모리 및 코드가 다음과 같을 때, 아래에서 적절한 값을 채우시오
[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에 저장된 값은 ( )이다.
0x3133A
해설 : rax의 값은 rbx+0x10인 0x555555554010에 저장된 0x3만큼 증가
2. Code를 3까지 실행했을 때, rax에 저장된 값은 ( )이다.
0
해설 : rax의 값은 rbx+0x20에 저장된 0x3133A 만큼 감소
3. Code를 4까지 실행했으 때, rax에 저장된 값은 ( )이다.
1
rax의 값은 1 증가
2-3. Opcode : 논리 연산 - and, or
- 논리 연산 명령어는 and, or, xor, neg 등의 비트 연산을 지시
- 비트 단위로 연산
1) and dst, src: dst와 src의 비트가 모두 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
and eax, dbx
[Result]
eax = 0xcafe0000
2) or dst, src: dst와 src의 비트 중 하나라도 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
or eax, ebx
[Result]
eax = 0xffffbabe
[예시] - 논리 연산 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에 저장된 값은 ( )이다.
0x1234567800000000
2. Code를 2까지 실행했을 때, rbx에 저장된 값은 ( )이다.
0x000000009abcdef0
3. Code를 3까지 실행했을 때, rax에 저장된 값은 ( )이다.
0x123456789abcdef0
2-4. Opcode : 논리 연산 - xor, not
1) xor dst, src: dst와 src의 비트가 서로 다르면 1, 같으면 0
[Register]
eax = 0xffffffff
ebx = 0xcafebabe
[Code]
xor eax, ebx
[Result]
eax = 0x35014541
2) not op: op의 비트 전부 반전
[Register]
eax = 0xffffffff
[Code]
not eax
[Result]
eax = 0x00000000
[예시] - 논리 연산 xor, not
- 레지스터, 메모리 및 코드가 다음과 같을 때, 아래에서 적절한 값을 채우시오
[Register]
rax = 0x35014541
rbx = 0xdeadbeef
=============================
[Code]
1: xor rax, rbx
2: xor rax, rbx
3: not eax
[문제]
1. Code를 1까지 실행했을 때, rax에 저장되는 값은 ( )이다.
0xa96c79ae
2. Code를 2까지 실행했을 때, rax에 저장되는 값은 ( )이다.
0x35014541
해설 : xor 연산을 동일한 값으로 두번 실행할 경우, 원래 값으로 돌아간다
(이를 기반으로 개발된 암호 사이트 → https://en.wikipedia.org/wiki/XOR_cipher)
3. Code를 3까지 실행했을 때, rax에 저장되는 값은 ( )이다.
0xcafebabe
해설 : 3번에서 rax가 아닌 eax를 not 하여도 괜찮은 이유는 eax가 rax의 하위 32비트를 가리키는 부분이기 때문
만약 not rax를 수행했다면 답은 0xffffffffcafebabe
2-5. Opcode : 비교/분기
1) 비교
- 비교 명령어는 두 피연산자의 값을 비교하고, 플래그를 설정
① cmp op1, op2: op1과 op2를 비교
- cmp는 두 피연산자를 빼서 대소를 비교합니다. 연산의결과는 op1에 대입하지 않는다.
- 예를 들어, 서로 같은 두 수를 빼면 결과가 0이 되어 ZF플래그가 설정되는데,
이후에 CPU는 ZF플래그를 보고 두 값이 같았는지 판단할 수 있다.
[Code]
1: mov rax, 0xA
2: mov rbx, 0xA
3: cmp rax, rbx ; ZF=1
② test op1, op2: op1과 op2를 비교
- test는 두 피연산자에 AND 비트연산을 취한다. 연산의 결과는 op1에 대입하지 않는다.
- 예를 들어, 아래 코드에서 처럼 0이 된 rax를 op1과 op2로 삼아 test를 수행하면, 결과가 0인 ZF플래그가 설정된다.
이후에 CPU는 이 ZF플래그를 보고 rax가 0이었는지 판단할 수 있다.
[Code]
1: xor rax, rax
2: test rax, rax ; ZF=1
2) 분기
- 분기 명령어는 rip를 이동시켜 실행 흐름을 바꾼다.
① jmp addr: addr로 rip를 이동시킨다.
[Code]
1: xor rax, rax
2: jmp 1 ; jump to 1
② je addr: 직전에 비교한 두 피연산자가 같으면 점프 (jump if equal)
[Code]
1: mov rax, 0xcafebabe
2: mov rbx, 0xcafebabe
3: cmp rax, rbx ; rax == rbx
4: je 1 ; jump to 1
③ jg addr: 직전에 비교한 두 연산자 중 전자가 더 크면 점프 (jump if greater)
[Code]
1: mov rax, 0x31337
2: mov rbx, 0x13337
3: cmp rax, rbx ; rax > rbx
4: jg 1 ; jump to 1
3. 요약 정리
1) 데이터 이동 연산자
- mov dst, src: src의 값을 dst에 대입
- lea dst, src: src의 유효 주소를 dst에 대입
2) 산술 연산
- add dst, src: src의 값을 dst에 더함
- sub dst, src: src의 값을 dst에서 뺌
- inc op: op의 값을 1 더함
- dec op: op의 값을 1 뺌
3) 논리 연산
- and dst, src: dst와 src가 모두 1이면 1, 아니면 0
- or dst, src: dst와 src중 한 쪽이라도 1이면 1, 아니면 0
- xor dst, src: dst와 src가 다르면 1, 같으면 0
- not op: op의 비트를 모두 반전
4) 비교
- cmp op1, op2: op1에서 op2를 빼고 플래그를 설정
- test op1, op2: op1과 op2에 AND 연산을 하고, 플래그를 설정
5) 분기
- jmp addr: addr로 rip 이동
- je addr: 직전 비교에서 두 피연산자의 값이 같을 경우 addr로 rip 이동
- jg addr: 직전 비교에서 두 피연산자 중 전자의 값이 더 클 경우 addr로 rip 이동
※ 이 게시글은 DREAMHACK 강의를 참조하여 작성되었습니다.
'시스템해킹' 카테고리의 다른 글
| Syetem Hacking - [02] Computer Science - (3-2) x86 Assembly (0) | 2022.12.02 |
|---|---|
| Syetem Hacking - [02] Computer Science - (2) Computer Architecture (0) | 2022.12.01 |
| Syetem Hacking - [02] Computer Science - (1) Linux Memory Lauout (0) | 2022.11.30 |
| Syetem Hacking - [01] 리눅스 환경 구축 (0) | 2022.11.30 |