도혀릐 2021. 11. 22. 22:00

Introduction

디스어셈블 과정을 통해 얻은 어셈블리 코드를 이해하기 위해 어셈블리 코드 공부

 

기계 코드가 실제로 동작할 CPU에 따라 기계 코드가 바뀌는데 앞서 배운 것처럼 어셈블리 코드는 기계 코드와 1:1 대응이므로 CPU의 종류에 의해서 어셈블리 코드도 달라진다. 

그럼 가장 널리 쓰이는 IIntel 구조의 64bit 버전 명령어 집합(64-bit version IA32 Intruction Set, 이하 x64 )에서 쓰이는 x64 명령어 집합(x86-64 Instruction Set)에 대해 알아보자

 

들어가기에 앞서

InStruction Cycle

CPU는 기본적으로 다음 실행할 명령어를 읽어옴(Fetch) -> 읽어온 명령어를 해석(Decode) ->

 -> 해석한 결과를 실행(Execute) 과정을 반복하는 장치(Instruction Cycle을 반복)    

이렇게 한 개의 명령어, 기계코드가 실행되는 과정을 Instruction Cycle이라고 한다. 

 

레지스터(Register)와 명령어(Instruction)

CPU는 InStruction Cycle을 수행하기 위해 기계 코드에 해당하는 각종 명령어를 해석하기 위한 구성 요소 외에도 읽어온 명령어가 저장된 공간을 임시로 기억해 둘 구성 요소나, 명령어를 실행한 결과를 저장해 둘 구성요소가 필요하다.

이렇게 CPU의 동작에 필수적인 저장 공간의 역할을 하는 CPU의 구성요소를 레지스터(Register)라고 한다.

 

레지스터

레지스터와 그 종류

레지스터는 CPU가 사용하는 저장공간이다. 레지스터들은 대개 쓰임새가 정해져 있지 않지만

관행적으로 그 용도를 정해놓는 레지스터도 있고, 용도가 정해져 그 용도로만 쓰이는 레지스터가 있기도 하다

하나씩 차례로 알아보자

 

 

범용 레지스터(General-Purpose Registers, GPR)

범용 레지스터는 말 그대로 특별히 정해두지 않고 다양하게 쓸 수 있는 레지스터이지만 예외적으로

그 쓰임새가 관행적으로 정해져 있는 경우도 있다.

 

[rax] : 함수가 실행된 후 리턴값을 저장 즉, 어떤 함수의 실행이 종료되고 나면 해당 함수의 결괏값이 반환될 때 이 rax 레지스터에 담겨 반환한다. 그렇다고 rax가 리턴 값을 위해서만 쓰이는 것이 아니라 함수가 반환되기 전까지는 자유롭게 사용되다 종료 후 리턴 값을 반환하기 위한 레지스터로 rax만 사용된다.

 

x64의 범용 레지스터들 중에서는 함수가 실행될 때 필요한 인자들을 저장하는 용도로 사용하는 레지스터들도 있다. 

이를 함수 호출 규약(Calling Convention)이라고 부르며, 운영체제의 종류나 함수의 종류에 따라 조금씩 다르다. 

 

[rcx, rdx, r8, r9]는 Windows 64bit에서 함수를 호출할 때 필요한 인자들을 순서대로 저장

 

rax와 마찬가지로, 함수 호출 규약에서 쓰이는 레지스터들 역시 함수를 호출할 때 인자를 전달하는 용도

당연히 함수 호출 이후 범용 레지스터로 자유롭게 사용가능

 

[rsp]는 특별히 그 용도가 정해져있다. 

rsp는스택포인터(Stack Pointer)로, 스택의 가장 위쪽 주소를 가르킨다. 

 

( 스택은 함수가 사용할 지역 변수들을 저장하기 위한 저장공간 )

 

 

명령어 포인터

명령어 포인터(Instruction Pointer)는 용도가 엄격히 정해져 있는 레지스터이다.

명령어 포인터인 [rip]는 다음에 실행될 명령어의 주소를 저장

( 프로그램의 실행 흐름과 관련된 중요한 레지스터라 범용으로 사용되지 않음 )

 

 

Data Size

CPU가 사용하는 크기단위를 WORD라고 한다. 처음엔 cpu가 사용하는 값인 16bit를 WORD라 불렀고 이 단위를 처리할 수 있는 범용 레지스터의 이름이 [ax, cx, dx, bx]로 붙은 것이 현재 우리가 사용하는 레지스터의 이름의 기원이 되었다. 하지만 시간이 흘러 [16bit -> 32bit -> 64bit] cpu가 사용하는 값이 단위가 커지며 이를 처리할 레지스터 이름과 워드의 이름이 다양하게 바뀌었고 레지스터가 담을 수 있는 저장공간이 커졌고 

 

그렇게 우리가 사용하는 64bit CPU인 레지스터들이 담을수 있는 값의 크기는 64bit(8byte, QWORD)이다. 값의 크기가 8byte라고 해서 꼭 그 단위로만 값을 저장해야하는 것은 아니다. 32bit(4byte, DWORD)만 연산에 사용할 수 있고 

16bit(2byte, WORD)나 8bit(1byte, BYTE)만 사용하는 것도 가능하다 허나 이렇게 접근 할 때는 저장할 용량에 맞는 

레지스터의 이름을 사용해야한다.

 

 

FLAGS

마지막으로 알아볼 레지스터는 상태 레지스터인 FLAGS이다.

깃발은 현재 상태나 조건을 0과 1로 나타내는 레지스터이다. 앞서 본 레지스터들과 다르게 FLAGS 레지스터를 구성하는 64개의 비트들 각각이 서로 다른 의미를 가진다. 플래그들 중 몇 가지 중요한 플래그들에 대해 짚고 넘어가자

 

 

 

 

 

[!] 리턴 값은 rax 레지스터에만 저장된다 [!]

https://dreamhack.io에 들어가 직접해보기를 추천합니다.