발자취
[Reverse Engineering] #05-1. 어셈블리어와 x86-64 - 이론 본문
[Reverse Engineering] #05-1. 어셈블리어와 x86-64 - 이론
해린 2024. 5. 29. 22:08본 게시물은 Dreamhack의 Reverse Engineering 로드맵 과정을 학습한 기록이다.
#01. 어셈블리 언어
: 컴퓨터의 기계어와 치환되는 언어. 기계어의 종류가 많은 만큼 많은 수의 어셈블리어가 존재한다.
#02. x64 어셈블리 언어
1. 기본 구조
- 명령어(Operation Code, Opcode): 한국어에서의 동사 역할과도 같음
- 피연산자(Operand): 한국어에서의 목적어 역할과도 같음
1) x86-64 어셈블리어 문법 구조의 예
mov eax, 3
- mov: 명령어(opcode) / 대입하라는 의미.
- eax: 첫번째 operand / 대입당할 피연산자
- 3: 두번째 operand / 대입할 피연산자
→ eax에 3을 대입하라
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 |
3. 피연산자
1) 종류
- 상수
- 레지스터
- 메모리
- []로 둘러싸임.
- 앞에 크기 지정자 TYPE PTR이 추가될 수 있음
- TYPE 에는 BYTE(1바이트), WORD(2바이트), DWORD(4바이트), QWORD(8바이트)가 올 수 있음.
2) 메모리 피연산자의 예
| 메모리 피연산자 | |
| QWORD PTR [0x8048000] | 0x8048000의 데이터를 8바이트만큼 참조 |
| DWORD PTR [0x8048000] | 0x8048000의 데이터를 4바이트만큼 참조 |
| WORD PTR [rax] | rax가 가리키는 주소에서 데이터를 2바이트 만큼 참조 |
#03. x64 어셈블리 명령어
1. 데이터 이동 명령어
: 어떤 값을 레지스터나 메모리에 옮기도록 지시
1) mov 명령어
| mov dst, src → src에 들어있는 값을 dst에 대입 | |
| mov rdi, rsi | rsi의 값을 rdi에 대입 |
| mov QWORD PTR[rdi], rsi | rsi의 값을 rdi가 가리키는 주소에 대입 |
| mov QWORD PTR[rdi+8*rcx], rsi | rsi의 값을 rdi+8*rcx가 가리키는 주소에 대입 |
2) lea 명령어
| lea dst, src → src의 유효 주소(Effective Address, EA)를 dst에 저장합니다. | |
| lea rsi, [rbx+8*rcx] | rbx+8*rcx를 rsi에 대입 |
2. 산술 연산 명령어
: 덧셈, 뺄셈, 곱셈, 나눗셈 연산을 지시하는 명령어. 여기서는 덧셈, 뺄셈만 다룸.
1) add
| add dst, src → dst에 src의 값을 더합니다. | |
| add eax, 3 | eax += 3 |
| add ax, WORD PTR[rdi] | ax += *(WORD *)rdi |
2) sub
| sub dst, src → dst에 src의 값을 뺍니다. | |
| sub eax, 3 | eax -= 3 |
| sub ax, WORD PTR[rdi] | ax -= *(WORD *)rdi |
3) inc
| inc op → op의 값을 1 증가시킴 | |
| inc eax | eax += 1 |
4) dec
| dec op → op의 값을 1 감소시킴 | |
| dec eax | eax -= 1 |
2. 논리 연산 명령어
: and, or, xor, neg 등의 비트 연산을 지시하는 명령어. 연산은 비트 단위로 이루어진다.
1) and dst, src
: dst와 src의 비트가 모두 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
and eax, ebx
[Result]
eax = 0xcafe0000
2) or dst, src
: dst와 src의 비트 중 하나라도 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
or eax, ebx
[Result]
eax = 0xffffbabe
3) xor dst, src
: dst와 src의 비트가 서로 다르면 1, 같으면 0
[Register]
eax = 0xffffffff
ebx = 0xcafebabe
[Code]
xor eax, ebx
[Result]
eax = 0x35014541
- eax = 0xffffffff -> 11111111111111111111111111111111
- ebx = 0xcafebabe -> 11001010111111101011101010111110
→ xor: 00110101000000010100010101000001
4) not op
: op의 비트 전부 반전
[Register]
eax = 0xffffffff
[Code]
not eax
[Result]
eax = 0x00000000
3. 비교 명령어
: 두 피연산자의 값을 비교하고, 플래그를 설정함.
1) cmp op1, op2
: op1과 op2를 비교
cmp는 두 피연산자를 빼서 대소를 비교함.
두 수를 뺐는데 결과가 0이면 ZF 플래그가 설정됨 (ZF=1)
2) test op1, op2
: op1과 op2를 비교
test는 두 피연산자에 AND 비트연산을 취함
역시 결과가 0이면 ZF플래그가 설정됨 (ZF=1)
4. 분기 명령어
: rip를 이동시켜 실행 흐름을 바꿈
1) jmp addr
: addr로 rip를 이동시킴
2) je addr
: 직전에 비교한 두 피연산자가 같으면 점프한다 (jump if equal)
(그렇다보니 cmp 연산자와 연달아 오는 경우가 대부분이다.)
3) jg addr
: 직전에 비교한 두 피연산자 중 전자가 더 크면 점프 (jump if greater)
5. 스택 명령어
1) push val
: val을 스택 최상단에 쌓음
*참고: rsp는 스택의 최상단을 가리키는 포인터
2) pop reg
: 스택 최상단 값을 꺼내서 reg에 대입
6. 프로시저 명령어
6-1. 프로시저란?
: 특정 기능을 수행하는 코드 조각
- 호출(Call): 프로시저를 부르는 행위
- 반환(Return): 프로시저에서 돌아오는 것
❣ 프로시저를 호출할 때는 프로시저를 실행하고 난 뒤에 원래의 실행 흐름으로 돌아와야 하므로, call 다음의 명령어 주소(Return Address, 반환 주소)를 ㅅ택에 저장하고 프로시저로 rip를 이동시킨다.
1) call addr
: addr에 위치한 프로시저 호출
2) leave
: 스택 프레임 정리
*스택 프레임: 함수별로 사용하는 스택의 영역을 명확히 구분하기 위해 사용하는 것.
3) ret
: return address로 반환
'Dreamhack Study > Reverse Engineering' 카테고리의 다른 글
| [Reverse Engineering] #06-1. Tools: IDA 이론 (0) | 2024.06.25 |
|---|---|
| [Reverse Engineering] #05-2. 어셈블리어와 x86-64 - Quiz (0) | 2024.05.30 |
| [Reverse Engineering] #04. Windows Memory Layout (0) | 2024.05.29 |
| [Reverse Engineering] #03. 컴퓨터 구조와 명령어 집합 구조 (0) | 2024.05.29 |
| [Reverse Engineering] #02. 동적 분석과 정적 분석 (0) | 2023.05.14 |