X64 레지스터에 대하여

2021. 5. 21. 17:57Layer7/Reverse Engineering

728x90

- X64란?

 

Intel, AMD의 64bit CPU가 사용하는 ISA를 뜻합니다.

X86 ISA의 상위 호환이라 보면 됩니다.

 

X64의 다른 이름은 아래와 같이 3가지가 있습니다.

 

  • X86-64
  • AMD64
  • Intel64

 

- X64 레지스터의 크기 구조

 

레지스터 크기 구조

여기서 dword(32)라고 써져있는 부분까지는 위에서 말한 X86 레지스터입니다.

qword(64)까지가 우리가 주로 보고 있는 X64 레지스터입니다.

 

X64 레지스터는 여러가지로 나눠집니다.

 

  • 범용 레지스터 (범용으로 쓰이는 레지스터)
  • 인덱스 레지스터 (주소와 관련된 레지스터)
  • 포인터 레지스터 (특정한 위치를 가리키는 레지스터)

 

이 세 가지의 레지스터는 아래에서 자세하게 설명하겠습니다.

설명은 괄호 안과 뒤에 추가적으로 적어놓겠습니다.

 

 

- 범용 레지스터, 인덱스 레지스터, 포인터 레지스터, 플래그 레지스터

 

범용 레지스터

 

  • RAX(Accumulator) - 덧셈, 뺄셈 등의 산술/논리 연산을 수행. 함수의 return 값이 저장.
  • RBX(Base) - 메모리 주소를 저장하기 위한 용도로 사용.
  • RCX(Count) - 반복문에서 카운터로 사용.
  • RDX(Data) - 여분의 레지스터이며 큰 수의 곱셈, 나눗셈 연산에서 RAX와 함께 사용.
  • R8-R15 - 8에서 15까지의 번호가 붙은 여분의 레지스터이며 다용도로 사용. (X86에는 존재하지 않는 레지스터)

 

인덱스 레지스터

 

  • RSI(Source Index) - 데이터를 복사할 때 *src 데이터의 주소, 즉 복사할 데이터의 주소가 저장.
  • RDI(Destination Index) - 데이터를 복사할 때 src 데이터가 위치할 *dest 주소, 즉 값이 저장될 주소가 저장.

* source 데이터의 줄임말, 원본 데이터

* destination 데이터의 줄임말, 복사 데이터

 

 

포인터 레지스터

 

  • RSP(Stack Point) - 현재 사용 중인 스택의 제일 위를 가리킴.
  • RBP(Base Point) - 스택 프레임의 시작 지점을 나타냄.
  • RIP(Instruction Point) - 다음에 실행해야 할 명령어가 존재하는 메모리 주소가 저장됨.

 

플래그 레지스터

 

  • CF(Carry Flag) - 부호 없는 수 끼리의 연산 결과에서 자리 올림(carry)이 생기는 경우에 CF=1
  • ZF(Zero Flag) - 연산 결과가 0이면 ZF=1
  • SF(Sing Flag) - 산술 연산의 결과가 0(양수)이냐 1(음수)이냐에 따라 값이 결정됨.
  • OF(Overflow Flag) - *부호 있는 수 끼리의 연산 결과가 범위를 넘어가면 OF=1

 

정말 많죠?

이쯤 되면 " ?....이걸 다 외워야 한다고? "라는 생각이 들 겁니다.

 

네....외워야 하더라구요....^ㅡ^....

 

그래도 그나마 쉽게 외우는 방법을 소개해보겠습니다.

 

  1. RSI, RIP처럼 축약된 단어 외우기
  2. 가운데 글자의 뜻을 외우기 (RBX에서 B는 Base...처럼)
  3. 판별법 외우고 종류 분류해보기 (맨 뒷글자 주목)
    • 맨 뒷글자가 X 거나 R8-R15이면 범용 레지스터
    • 맨 뒷글자가 I면 인덱스 레지스터
    • 맨 뒷글자가 P면 포인터 레지스터
    • 맨 뒷글자가 F면 플래그 레지스터

이렇게 외우면 그나마 빠르게 외울 수 있습니다.

꽤 괜찮은 팁인 것 같죠..?

 

 

- 함수 호출 규약

 

함수 호출 규약 : 서브루틴이 어떻게 호출자로부터 변수를 받고, 어떻게 결과를 반환하는지에 대한 규약.


X64 환경에서는 RAX, RBX, RCX, RDX, RBP, RSP, RSI, RDI, R8-R15

이렇게 총 16개의 레지스터를 갖습니다.

 

리눅스에서는 System V AMD64 ABI라는 함수 호출 규약을 사용합니다.

이 함수 호출 규약의 구조는 아래와 같습니다.

 

int test(int a, int b, int c, int d, int e, int f, int g)

 

a : RDI

b : RSI

c : RCX

d : RDX

e : R8

f : R9

g : 스택

 

스택이 무엇인지에 대해서는 아래에서 자세하게 설명하겠습니다.

 

 

- 스택이란?

 

정의 : 지역 변수를 담는 데이터 영역. 위의 주소부터 사용된다는 특징이 있다.

 

C/C++을 해보신 분들은 스택이라는 단어를 굉장히 익숙하게 받아들이실 겁니다.

X64에서는 스택을 메모리 관리를 위해 사용합니다.

또한, 스택은 LIFO(Last In First Out)의 특징을 가지고 있습니다.

 

+) LIFO를 이해 하기 힘들다면 프링글스 감자칩 통을 생각해보세요!
    프링글스 통에 감자칩이 들어갈 때 가장 먼저 들어간 감자칩이 제일 바닥에 깔려있을거고

    그 감자칩이 가장 마지막에 나오는 것과 같은 원리입니다.

 

스택의 구조 (가장 마지막에 들어간 C가 가장 마지막에 나옴)

프로그램이 커지면 커질 수록 함수의 수도 많아질텐데

함수와 관련된 지역 변수들은 모두 어떻게 관리해야할까요?

 

이런걸 스택으로 관리해야 합니다.

그럼 스택을 어떻게 활용하고, 어떤 방법을 써야 효율적인 메모리 관리가 가능할까요?

 

 

- 스택 프레임

 

스택 프레임은 함수가 호출될 때, 그 함수만의 스택 영역을 구분하기 위하여 생성되는 공간입니다.

함수 하나를 실행 할 때 하나의 스택 프레임이 생성됩니다.

스택은 여러개의 스택 프레임으로 구성됩니다.

 

 

리눅스에서 가장 먼저 스택에 채워지는 스택 프레임은

그림과 같이 __libc_start_main입니다.

 

__libc_start_main은 main 함수를 부르기 전에 불러오는 함수입니다.

이 함수 안에는 main의 주소가 들어있습니다.

 

그 다음에는 main 함수를 불러옵니다.

 

이후에 차례차례 여러가지 사용자 정의 함수를 부르게 됩니다.

예시로 test라는 함수를 불러보겠습니다.

 

이 함수를 불렀을 때 스택에는 test 함수의 스택 프레임이 채워집니다.

여기서 RSP와 RBP는 무엇일까요?

 

분명 RSP는 현재 스택의 가장 윗부분, RBP는 가장 아랫부분이라고 대답하시는 분들이 있을겁니다.

(제가 그랬거든요)

 

RSP와 RBP의 정의부터 다시 보겠습니다.

  • RSP(Stack Point) - 현재 사용 중인 스택의 제일 위를 가리킴.
  • RBP(Base Point) - 스택 프레임의 시작 지점을 나타냄.

RSP는 현재 스택의 가장 윗부분이 맞습니다.

 

하지만 RBP의 정의를 보면 "스택 프레임의 시작 지점을 나타냄."이라고 나와있습니다.

스택 프레임의 시작 지점이라는건 test의 시작 지점이라는거니까

main 함수와 test 함수의 사이 지점입니다.

 

 

- X64의 여러가지 명령어

 

흐름 제어 명령어

  • call : 함수 호출
  • ret : 함수에서 빠져나옴
  • jmp : 해당 주소로 RIP를 설정
  • je : 비교한 값이 A==B일 때 jmp
  • jne : 비교한 값이 A!=B일 때 jmp
  • ja : 비교한 값이 A>B일 때 jmp
  • jb : 비교한 값이 A<B일 때 jmp
  • jae : 비교한 값이 A>=B일 때 jmp
  • jbe : 비교한 값이 A<=B일 때 jmp

 

데이터 이동 명령어

 

  • push : RSP에 값을 넣고 RSP가 한 칸 올라감
  • pop : RSP에 값을 넣고 RSP가 한 칸 내려감
  • mov A B : B의 값을 A에 복사
  • *lea A B : B의 주소를 A에 복사

* load effective address

 

산술 명령어

 

  • add A B : A+=B (A+B의 값을 A에 저장)
  • sub A B : A-=B
  • inc A : A++ (A의 값을 1 증가)
  • dec A : A--
  • cmp A B : A-B를 한 값을 대상으로 비교하여 Flag 값을 설정 (크냐, 작냐, 같냐)
  • test : A&B를 한 값을 대상으로 비교하여 ZF 레지스터만 설정 (0이냐, 아니냐)
728x90