Architecture
일반적으로 그냥 '애플 칩과 인텔 칩이 따로 개발이 되어있고 호환이 어렵다'라는 것은 당연히 개발사가 다르니까 호환이 어렵겠다 정도로 알고있었다.
하지만, 정확히는 이루고 있는 Architecture가 다르기 때문에, Apple은 ARM 기반 Architecture, Intel은 X86 기반 Architecture를 사용하는 등 차이점을 보인다.
Architecture란?
호환되는 프로세서에서 사용하는 명령어 집합 구조를 통칭하는 말이며 아키텍쳐에 따라 레지스터와 명령어(Assembly)가 나뉜다 할 수 있다.
그럼 일부 역사가 긴 게임들에서 "이제 우리 게임도 32비트 뿐만 아니라 64비트 클라이언트를 지원합니다." 라고 했을때 이게 왜 좋은지 또한 막연하게 그냥 좋은거겠지 생각했었다.
1 WORD == 32 bit or 64 bit 는 컴퓨터가 한번에 처리할 수 있는 단위
이렇게 컴퓨터의 구조가 이루어져 있다 보니 64bit Client로 업그레이드할 경우 훨씬 빨라질 수 있는 것이다.
Register란?
- CPU의 기억장치
- 명령을 처리하는 데 필요한 데이터를 일시적으로 저장
- 크기는 작지만 기억장치중 속도가 가장 빠름
a = 1
b = 1
a + b
예를 들어, 위의 연산을 수행할 때에는 a + b 연산을 수행하기 위해 각각 a, b에 값을 저장해놓고 그 값을 더해주어야 하기 때문에 Register가 필요하다고 생각해 볼 수 있다.
Register의 종류
1. 범용 레지스터
| 이름 | 주용도 |
|---|
| rax | 함수의 반환값 |
| rbx | 메모리 주소 |
| rcx | 반복문의 반복 및 각종 연산의 시행 횟수 |
| rdx | 부호 확장 및 곱셈, 나눗셈 |
| rsi | 데이터를 옮길 때 원본을 가리키는 포인터 |
| rdi | 데이터를 옮길 때 목적지를 가리키는 포인터 |
| rsp | 사용중인 스택의 위치를 가리키는 포인터 |
| rbp | 스택의 바닥을 가리키는 포인터 |
| rip | 현재 실행중인 명령어의 위치를 가리킴 |
| 일반적 명명 규칙 | Full name | 사용 목적 |
|---|
| ax | Accumaulator register | 산술 연산 |
| bx | Base register | 데이터 주소 지정 |
| cx | Count register | 반복문 및 문자열 연산 |
| dx | Data register | I/O 연산 |
| 특수 명명 규칙 | Full name | 사용 목적 |
|---|
| si | Source Index | 데이터의 원본 위치를 가리킴 |
| di | Destination Index | 목적지 메모리 위치를 가리킴 |
| sp | Stack Pointer | 스택의 최상단, 함수 호출시 매개변수, 반환 주소 및 지역 변수 저장 |
| bp | Base Pointer | 함수 내 스택 프레임의 기준점, 함수의 지역 변수나 매개변수 참조시 사용 |
1. 산술 연산 레지스터 : RAX, RCX, RDX
| 이름 | 주용도 |
|---|
| rax | 함수의 반환값이나 산술 연산의 결과를 저장하는 데 사용 |
| rcx | 반복문의 횟수 계산 및 기타 연산에서 사용 |
| rdx | 보조 확장 및 곱셈, 나눗셈 연산에서 사용됩니다 |
2. 인덱스 레지스터
| 이름 | 주용도 |
|---|
| rsi | 데이터를 이동할 때 소스 데이터를 가리키는 인덱스 |
| rdi | 데이터를 이동할 때 목적지를 가리키는 인덱스 |
3. 포인터 레지스터
| 이름 | 주용도 |
|---|
| rsp | 현재 스택의 최상단(탑)을 가리키는 포인터 |
| rbp | 스택 프레임의 기준이 되는 포인터로, 함수 호출 시 기준 주소로 사용 |
| rbx | 범용 레지스터로 메모리 주소를 저장하는 데 사용되지만, 특정 용도로 포인터처럼 사용 |
| rip | 현재 실행중인 명령어의 위치를 가리킴(Instruction Pointer) |
세그먼트 레지스터
| 이름 | 주 용도 |
|---|
| cs | code section register (코드 섹션 레지스터) |
| ss | stack memory register (스택 메모리 레지스터) |
| ds | data section register (데이터 섹션 레지스터) |
| es | 운영체제마다 범용적인 용도로 사용 |
| fs | 운영체제마다 범용적인 용도로 사용 |
| gs | 운영체제마다 범용적인 용도로 사용 |
플래그 레지스터
| 이름 | 주 용도 |
|---|
| CF (carry flag) | unsigned의 연산 결과가 overflow 되었을 때 1로 설정 |
| ZF (zero flag) | 연산 결과가 0일 경우 1로 설정 |
| SF (sign flag) | 연산 결과가 음수일 경우 1로 설정 |
| OF (overflow flag) | signed의 연산 결과가 overflow 되었을 때 1로 설정 |
데이터의 단위
| 이름 | 크기 |
|---|
| bit | 1 bit |
| nibble | 4 bits |
| BYTE | 1 byte = 8 bits |
| WORD | 2 bytes |
| DWORD | 4 bytes (Double WORD) |
| QWORD | 8 bytes (Quad WORD) |
| Field | 여러 바이트로 구성된 데이터의 최소 단위 |
| Record | 여러 필드로 구성된 데이터의 한 행 |
| File | 여러 레코드로 구성된 데이터 집합 |
| Database | 여러 파일로 구성된 데이터베이스 |
Register 호환성
64bit에서 개발한 프로그램은 32bit 컴퓨터에서 실행할 수 없지만, 32bit에서 개발한 프로그램은 64bit 컴퓨터에서 실행할 수 있다. 현재 널리 사용되는 x86_64 아키텍쳐는 IA-32를 74비트로 확장하는 등의 구조를 가지고 있다.
Assembly?
| 단계 | 파일 확장자 | 설명 |
|---|
| 소스 코드 | .c | 개발자가 작성한 원시 소스 코드입니다. C 언어 소스 파일의 확장자는 .c입니다. |
| 전처리기 | .i | 전처리기는 소스 코드에서 매크로를 확장하고, include 파일을 삽입하며, 조건부 컴파일을 처리합니다. |
| 컴파일러 | .s | 전처리된 코드를 어셈블리 코드로 변환합니다. 이 단계에서 C 코드가 저수준 언어로 변환됩니다. |
| 어셈블러 | .o | 어셈블리 코드를 기계어 코드(목적 코드)로 변환합니다. 이 결과로 생성되는 파일의 확장자는 .o입니다. |
| 링커 | - | 여러 목적 파일(.o)을 결합하여 실행 가능한 파일을 생성합니다. 링커는 필요한 라이브러리와 함께 결합을 수행합니다. |
Reverse Engineering에서는 바이트 코드를 다시 C언어로 생각을 해 보아야 하기 때문에 Assembly를 알아야 한다.
Assembly 구조
| 기본 구조 | opcode | operand1 | operand2 |
|---|
| 예제 명령어 | MOV | RAX | 0x41 |
| 의미 | 대입하라 | RAX에 | 0x41을 |
Operation Code
| 용도 | 코드 | 설명 |
|---|
| 데이터 이동 | mov, lea | 데이터 이동 명령어로, mov는 데이터를 레지스터나 메모리 사이에 이동시키며, lea는 주소를 계산합니다. |
| 산술 연산 | inc, dec, add, sub | 산술 연산 명령어로, inc는 증가, dec는 감소, add는 더하기, sub는 빼기를 수행합니다. |
| 논리 연산 | and, or, xor | 논리 연산 명령어로, and, or, xor은 각각 논리곱, 논리합, 배타적 논리합 연산을 수행합니다. |
| 비교 | cmp, test | 비교 명령어로, cmp는 두 값을 비교하고, test는 두 값의 논리곱(AND)을 계산하여 플래그를 설정합니다. |
| 분기 | jmp, je, jg, jne, jng | 분기 명령어로, 조건에 따라 코드의 실행 흐름을 변경합니다. jmp는 무조건 분기, je는 같을 때, jg는 클 때 등 조건별 분기를 처리합니다. |
| 기타 | push, pop, call, ret, leave, syscall | 기타 명령어로, push와 pop은 스택 조작, call과 ret은 함수 호출과 반환, leave는 스택 프레임 정리, syscall은 시스템 호출을 수행합니다. |
Operand
Operand는 3가지 타입이 존재한다.
1. 상수
2. Register
3. memory : memory oprand는 [ ]로 둘러쌓아 표기한다. 앞부분에 참조할 크기를 지정해 주기도 한다.