어제에 이어 오늘은 2강
오늘은 가장 널리 쓰이는 Intel 구조의 64bit 버전 명령어 집합에서 쓰이는 x64 명령어 집합(x86-68 Instruction Set)에 대한 주제이다.
내용이 워낙 많아서 2강에만 꼬박 하루가 걸렸다,,
<2강> : x64 기초
< 들어가기에 앞서 >
1. CPU의 기본 동작
: < 실행할 명령어 읽어오기(Fetch) → 읽어온 명령어 해석하기(Decode) → 해석한 결과 실행하기(Execute) >
: 위와 같이 기계 코드가 실행되는 한 번의 과정을 Instruction Cycle이라고 한다.
2. 레지스터(Register)와 명령어(Instruction)
- CPU는 Instrcution Cycle을 수행하기 위해 기계 코드에 해당하는 각종 명령어를 해석하기 위한 구성 요소 외에도
읽어온 명령어가 저장된 공간을 임시로 기억해 둘 구성 요소나, 명령어를 실행한 결과를 저장해 둘 구성 요소
필요
- 레지스터(Register) : CPU의 동작에 필수적인 저장 공간의 역할을 하는 CPU의 구성 요소
- 명령어(Instruction) : CPU가 실행 / 수행하는 동작에 따라 형태가 조금씩 다름
< 레지스터(Register) >
: CPU가 사용하는 저장 공간
: 대개의 경우 각각의 레지스터들은 특별히 쓰임새가 정해져 있지 않지만, 관행적으로 용도를 정해놓고
사용하는 레지스터도 있고, 엄격히 정해진 용도로만 쓰이는 레지스터도 있다.
1. 범용 레지스터 (General-Purpose Registers, GPR)
:용도를 특별히 정해두지 않고 다양하게 쓸 수 있는 레지스터
: CPU에게 연습장과 같은 레지스터로, x64의 범용 레지스터는 총 16개이다.
위 사진의 빨간 글씨로 표시된 레지스터들이 이에 해당한다.
(범용 레지스터는 원칙적으로 용도가 정해져 있진 않지만, 관행적으로 쓰임새가 정해져 있는 경우도 존재함)
< 범용 레지스터의 종류 >
1. rax
: 함수가 실행된 후 리턴값을 저장하기 위해 사용
(어떤 함수의 실행이 종료되고 난 후, 해당 함수의 결과값이 반환될 때, rax 레지스터에 담겨 반환됨.)
: 단, 리턴값을 위해서만 사용되지는 않고, 함수 반환전까지는 범용 레지스터로 자유롭게 사용되다가,
종료 후 리턴값을 반환하기 위한 레지스터로는 rax만이 사용됨
2. rcx, rdx, r8, r9 ~ r15
: Windows 64bit에서 함수를 호출할 때 필요한 인자들을 순서대로 저장
(첫번째 인자는 rcx, 두번째 인자는 rdx에, ····)
: 함수 호출 규약(Calling Convention) : 함수가 실행될 때 필요한 인자들을 저장하는 용도로 사용
3. rsp
: 다른 범용 레지스터들과 달리 용도가 정해져 있음
: 스팩 포인터(Stack Pointer)로, 스택의 가장 위쪽 주소를 가리킴
(스택 : 함수가 사용할 지역 변수들을 저장하기 위해 준비해놓는 공간)
2. 명령어 포인터 (Instruction Pointer)
: 범용 레지스터와 달리 그 용도가 엄격히 정해져 있는 레지스터
: rip (다음에 실행될 명령어가 위치한 주소를 가리킴
3. Data Size
: WORD : CPU가 사용하는 값의 크기 단위 / "16bit = 2byte = WORD"
4. FLAGS
: 상태 레지스터 (현재 상태나 조건을 0과 1로 나타냄)
: FLAGS 레지스터를 구성하는 64개의 비트들 각각은 서로 다른 의미를 지님
< 중요 플래그들 >
1. CF (Carry Flag)
: +,- 등의 산술연산 혹은 bit shift/rotate 등의 연산이 일어났을 때, 자리 올림(carry)이 생기는 경우 CF의
값은 1이 됨
: 특징 : 연산에 사용된 값들에 부호가 없음(Unsigned)
2. ZF (Zero Flag)
: 연산의 결과가 0일때 ZF는 1이 됨
: 두 값이 같은 경우, ZF가 1이 되므로 비교한 두 대상의 값이 같은지를 확인할 수 있음
3. SF (Sign Flag)
: 부호가 있는(Signed) 값의 연산에서 사용되어 결과가 음수/양수인지를 가리킴
: 최상위 비트가 0(결과가 양수)이면 SF=0, 최상위 비트가 1(결과가 음수)이면 SF=1
4. OF (Overflow Flag)
: 부호가 있는(Signed) 값의 연산에서 CF의 역할을 함 (자리 올림이 생김)
: "표시할 수 있는 값의 범위를 넘어갔다는 것 (Overflowed)"
< Instruction Format >
1. Opcode (Operation Code, 명령 코드)
: 명령어에서 실제로 어떤 동작을 할지를 나타내는 부분 (자료 이동, 산술 연산, 자료 제어 등)
- 기계 코드 (Machine Code) or 명령 코드(Opcode)
: CPU가 실제로 수행할 작업을 나타내는 숫자 (CPU의 종류별로 다른 값일 수 있음)
- 어셈블리 코드 (Assembly Code)
: 숫자로 이루어진 명령코드를 문자로 작성한(Mnemonic) 코드 (명령 코드와 1:1 대응)
: 명령 코드와 피연산자를 묶어 하나의 명령어(Instruction)이 됨
: CPU의 동작을 그대로 옮겨놓은 것에 가깝기 때문에 메우 직관적이고 단순함
2. Operand (피연산자)
: 명령 코드가 연산할 대상
: Intel 방식의 어셈블리를 읽을 때에는 명령 코드에 따라 연산한 결과가 왼쪽 피연산자에 저장된다고
이해하는 것이 일반적
: 피연산자의 지정은 그 값을 사용할 방식에 따라 다양함
(상수, 레지스터 값, 레지스터가 가리키고 있는 메모리의 주소)
< Operand Type >
1. 상수값 (Immediate)
2. 레지스터 (Register)
: 레지스터에 들어있는 값이 피연산자로 사용됨
3. 레지스터에 저장된 메모리 주소를 참조한 값 (Addressing Modes)
: 메모리 주소를 참조한 값이 피연산자로 사용
< Instrcutions >
1. Data Movement
: 값을 레지스터나 메모리 주소에 옮기는 명령어
- mov : mov a, b (b의 값을 a로 옮긴다.)
- lea : lea c, addr (c에 주소를 저장한다.)
2. Arithmetic Operations (산술 연산)
- Unary Instructions
- inc, dec : 값을 1 증가시키거나 감소
- neg : 값의 부호를 바꿈 (2의 보수)
- not : 값의 비트를 반전
- Binary Instrcutions (명령어 a, b)
- add : a에 b를 더한다
- sub : a에서 b를 뺀다
- imul : a에 b를 곱한다
- and : a의 값과 b를 AND 논리연산 한 결과를 a에 저장해라
- or : a의 값과 b를 OR 논리연산 한 결과를 a에 저장해라
- xor : a의 값과 b를 XOR 논리연산 한 결과를 a에 저장해라
- Shift Instructions (명령어 a, k)
- shl, shr : a의값을 k만큼 오른쪽이나 왼쪽으로 shift
(shr의 경우 오른쪽으로 shift할 때 빈 bit 자리에는 0이 채워짐)
- sal, sar : a의 값을 k만큼 왼쪽으로 shift하지만, 부호가 보전됨
(따라서 sar의 경우 최상위비트가 shift 이후에도 보전됨)
3. Conditional Operations (코드의 실행 흐름 제어)
- test : a와 b를 AND 논리연산 한 결과
음수이면(최상위비트가 1) SF=1,
0이면 ZF=1
- cmp : a에서 b를 뺀 결과
a=b ZF=1,CF=0,
a<b : ZF=0, CF=1
a>b : ZF=0, CF=0
- jmp, jcc (조건부 jmp를 묶어서 부르는 이름)
+ 추가
- jae : >=
4. Stack Operation
: 지역 변수들은 스택에 저장되며, 스택은 레지스터가 아닌 메모리에 준비된다.
: rsp 레지스터와 밀접한 관련이 있음
: Intel x86-64 아키텍처에서 스택은 낮은 주소(더 작은 숫자)를 향해 자라기 때문에, 스택이 자랄수록 rsp에
저장된 메모리 주소는 점점 낮아진다.
- push, pop : 스택에 새로운 데이터를 추가하거나 뺄 때 사용
5. Procedure Call Instructions (함수 호출, 함수 종료)
- call : 함수 실행 (call은 피연산자로 실행할 함수의 주소를 받는다)
- ret : 함수를 종료한 뒤 Return Address로 돌아가는 역할
<추가>
2강을 듣다보니 16진수 표기법이 부족한 것 같아서, 아스키 코드표도 첨부한다.
리버싱을 제대로 하기 위해서는 마스터해야 할 것 같다.
'Reversing > 개념' 카테고리의 다른 글
[Reversing] Handray (0) | 2022.08.15 |
---|---|
PE 파일 & API & ELF 파일 (0) | 2021.06.25 |
[DreamHack] 5강 : hello-world.exe로 배우는 x64dbg 사용법 (0) | 2021.03.29 |
[DreamHack] 3강, 4강 : puts("hello world!\n"); → x86_64 asm (0) | 2021.03.28 |
[DreamHack] 1강 : 리버스 엔지니어링이란 (0) | 2021.03.27 |