개요
지난 시간 우리는 아키텍쳐에 대해서 배운바가 있다.
오늘은 고수준의 언어에서 기계어로 가기위한 중간 단계인 assembly에 대해서 알아보도록 하자.
아키텍쳐
프로세서에서 사용한 명령어 집합 구조들을 통칭하는 말로, 아키텍쳐에 따라 레지스터와 명령어(Assembly)가 나뉜다.
x86을 예를 들어 보자면 32bit / 64bit는 컴퓨터가 한번에 처리할 수 있는 단위를 말한다.
이를 우리는 1WORD라 정의한다.
레지스터
CPU의 기억장치로 명령을 처리하는데 필요한 데이터를 일시적으로 저장한다.
크기는 작지만 기억장치 중에 속도가 가장 빠르다.
종류
- 범용레지스터
- 산술 연산 레지스터
- 인덱스 레지스터
- 포인터 레지스터
- 세그먼트 레지스터
- 플래그 레지스터
범용레지스터
이름 | 주 용도 |
---|---|
RAX | 함수의 반환값 |
RBX | 메모리 주소 |
RCX | 반복문의 반복 및 각종연산의 시행 횟수 |
RDX | 부호 확장 및 곱셈, 나눗셈 |
RSI | 데이터를 옮길 때 원본을 가리키는 포인터 |
RDI | 데이터를 옮길 때 목적지를 가리키는 포인터 |
RSP | 사용중인 스택의 위치를 가리키는 포인터 |
RBP | 스택의 바닥을 가리키는 포인터 |
RIP | 현재 실행중인 명령어의 위치를 가리킴 |
대체적으로 이렇게 쓰일뿐 이런 상황에서 무조건 쓰인다는 내용은 아니다.
함수 호출 규약에 따라 레지스터는 사용되어진다.
세그먼트 레지스터
각 섹션의 주소를 가리킴
이름 | 주 용도 |
---|---|
cs | 코드 섹션 레지스터 |
ss | 스택 메모리 레지스터 |
ds | 데이터 섹션 레지스터 |
es | 운영체제마다 범용적인 용도로 사용 |
fs | 운영체제마다 범용적인 용도로 사용 |
gs | 운영체제마다 범용적인 용도로 사용 |
플래그 레지스터
이름 | 주 용도 |
---|---|
CF(Carry Flag) | Unsigned의 연산 결과가 Overflow 되었을 때 1로 설정 |
ZF(Zero Flag) | 연산 결과가 0일 경우 1로 설정 |
SF(Sign Flag) | 연산 결과가 음수일 경우 1로 설정 |
OF(Overflow Flag) | Signed의 연산결과가 Overflow 되었을 때 1로 설정 |
전체가 플래그 레지스터이고, 이것이 잘게 1비트씩 쪼개어져서 각각의 플래그를 나타내고 있는 사진이다.
레지스터 호환성
EAX는 32bit 운영체제에서 동작하는 레지스터이고, AX 16bit가 확장된 것이다.
그림상으로 보면 AX도 AH와 AL로 각각 8bit 씩 구성된 것으로 볼 수 있다.
데이터 단위
이름 | 크기 |
---|---|
bit | 1bit |
byte | 8bit |
WORD | 2byte = 16bit |
DWORD | 2WORD(Double WORD) = 4byte = 32bit |
QWORD | 4WORD(Quater WORD) = 8byte = 64bit |
ASSEMBLY
어셈블리어는 연산자를 뜻하는 Opcode와 피연산자인 Operand로 이루어져있다.
가령 MOV RAX, 0x41이라는 명령어가 있다면, Opcode는 MOV, Operand는 RAX와 0x41이다.
용도 | 코드 |
---|---|
데이터 이동 | mov, lea |
산술 연산 | inc, dec, add, sub |
논리 연산 | and, or, xor |
비교 | cmp, test |
분기 | jmp, je, jg, jne, jng |
기타 | push, pop, call, ret, leave, syscall |
명령어
- mov operand1, operand2 : 피연산자 2를 피연산자 1에 대입
- lea operand1, operand2 : 피연산자2의 주소를 피연산자 1에 대입
- 또는, lea rsi, [rax x 3 + 2]의 경우 rax x 3 + 2의 값을 rsi에 대입
- add : 더하기
- sub : 빼기
- inc : 1 더하기
- dec : 1 빼기
- cmp rax, rbx : rax와 rbx를 뺀 값을 확인하여 비교
- ZF = 1일 시 두 값은 같은 값이 된다.
- test rax, rbx : rax와 rbx를 and 연산하고 두 값에서 같은 비트가 있는 지 확인할 수 있다.
- test rax, rax와 같은 명령어는 실질적으로 rax가 0인지 아닌지 확인하는 작업이다.
- cmp는 두 값을 뺄셈 연산을 통해 비교하는 반면 test는 단순히 and 연산만 수행하기 때문에 계산 오버헤드가 낮고 저장을 하지 않기 때문에 기존 값을 변경하지 않으면서, 플래그만 업데이트를 한다.
- jmp 0x00001001: 해당 주소로 점프
- je(jmp equal) : 비교 했을 때, 두 값이 같으면 해당 주소로 점프
- jg(jmp greater) : 두 값일 비교했을 때, op1이 더 크면 해당 주소로 점프
- jl(jmp less) : op1이 더 작으면 해당 주소로 점프
operand
피연산자에는 상수, 레지스터, 메모리 총 3가지 종류가 존재한다.
특히 메모리일 경우 mov rax BYTE PTR [0x501000]과 같이 [ ]로 둘러싸져있고, 앞부분에 참조할 크기를 지정해주기도 한다.
이 경우 0x501000의 주소에서 1byte만 참조해서 rax에 대입하겠다는 뜻이다.
'STUDY > Reverse Engineering' 카테고리의 다른 글
[Dreamhack] Embedded Hacking (0) | 2024.03.18 |
---|