카테고리 없음

1. x64 어셈블리 - 레지스터

rmagur1203 2021. 5. 22. 02:01

x64 Cheat Sheet

 

설명 64bit 32bit 16bit 8bit 8bit
Accumulator RAX EAX AX AH AL
Base RBX EBX BX BH BL
Counter RCX ECX CX CH CL
Data RDX EDX DX DH DL
  RDI EDI DI DIL  
  RSI ESI SI SIL  
Numbered(8~15) R$n$ R$n$D R$n$W R$n$B  
Stack pointer RSP ESP SP SPL  
Frame pointer RBP EBP BP BPL  
Name Notes Type 64-bit
long
32-bit
int
16-bit
short
8-bit
char
rax 함수의 반환값을 이 레지스터에 저장해둡니다. scratch rax eax ax ah and al
rcx 전형적인 Scratch 레지스터.  몇몇 명령어에선 카운터로도 사용합니다. scratch rcx ecx cx ch and cl
rdx Scratch 레지스터. scratch rdx edx dx dh and dl
rbx
Preserved 레지스터: 저장 없이 사용하지 마세요! preserved rbx ebx bx bh and bl
rsp
스택 포인터.  스택의 맨 위를 가리킵니다.
preserved rsp esp sp spl
rbp
Preserved 레지스터.  때로 스택 포인터의 이전 값 또는 "스택 프레임의 베이스"를 저장하는 데 사용됩니다. preserved rbp ebp bp bpl
rsi 64bit 리눅스에선 scratch 레지스터이자 두번째 함수의 인자를 넘겨줍니다.
64bit 윈도우에선 preserved 레지스터 입니다.
scratch rsi esi si sil
rdi 64bit 리눅스에선 scratch 레지스터와 함수의 첫번째 인자값을 넘겨줍니다.
64bit 윈도우에선 preserved 레지스터 입니다.
scratch rdi edi di dil
r8
Scratch 레지스터.  이것들은 64비트 모드에서 추가되었습니다. 그래서 이름 대신 숫자를 가지고 있습니다. scratch r8 r8d r8w r8b
r9
Scratch 레지스터. scratch r9 r9d r9w r9b
r10
Scratch 레지스터. scratch r10 r10d r10w r10b
r11
Scratch 레지스터. scratch r11 r11d r11w r11b
r12
Preserved 레지스터.  사용은 할 수 있지만, 저장하고 사용 후 복구해야 합니다.
preserved r12 r12d r12w r12b
r13
Preserved 레지스터. preserved r13 r13d r13w r13b
r14
Preserved 레지스터. preserved r14 r14d r14w r14b
r15
Preserved 레지스터. preserved r15 r15d r15w r15b

Scratch 레지스터 : 원하는 모든 용도로 사용할 수 있습니다.

Preserved 레지스터 : 다른 곳에서 중요한 용도로 사용되므로 사용하는 경우 원래 값을 저장해 두고 사용한 후에 다시 원래 값으로 돌려놔야 합니다.

빨간색 : 64비트에서만 존재하는 레지스터입니다.

 

참고로 함수 호출할때 인자를 넘겨주는데 어셈블리로 바꾸면 함수를 호출할 때 여러가지의 값을 넘겨줄 수 없기 때문에어떤 레지스터에 몇번째 인자를 저장해야 하는지 정해진 호출 규약이 있습니다.x86-64에는 두가지의 호출규약이 있습니다.

마이크로소프트 x64 호출 규약시스템 V AMD64 ABI이 있습니다.

마이크로소프트 x64 호출 규약

마이크로소프트 x64 호출 규약은 윈도우(마이크로소프트 비주얼 C++, 인텔 C++ 컴파일러, 엠바카데로 컴파일러), UEFI 등에서 사용합니다.

1. RCX/XMM0

2. RDX/XMM1

3. R8/XMM2

4. R9/XMM3

이 이후 인자부터는 스택에 들어갑니다.

인자 타입 1번째 2번째 3번째 4번째 5번째 이상
부동 소수점 XMM0 XMM1 XMM2 XMM3 스택
정수 RCX RDX R8 R9 스택
자료형(8, 16, 32 또는 64비트 자료형), __m64 RCX RDX R8 R9 스택
모든 포인터 RCX RDX R8 R9 스택

XMM$n$은 128bit짜리 대형 레지스터 입니다.

시스템 V AMD64 ABI

시스템 V AMD64 ABI는 GNU/리눅스, BSD, OS X(GCC, 인텔 C++ 컴파일러) 등에서 사용합니다.

인자 타입 1번째 2번째 3번째 4번째 5번째 6번째 7번째 이상
부동 소수점, __m256 XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6~XMM7
나머지 자료형 RDI RSI RDX RCX R8 R9 스택
포인터 RDI RSI RDX RCX R8 R9 스택

레지스터 설명
호출 규약 예제

보기 좀 불편하게 예제가 되어있지만 정리해보자면

s.a, s.b = int(4byte) + int(4byte) = 8byte(64bit)

s.d = double(8byte)

 

ld = long double(16 or 8byte)(여기선 16byte, 128bit)

long double 형식은 X87 클래스에 속한다. X87 클래스는 스택에 저장되는것 같다.

 

일반적인 레지스터

XMM, YMM

스택

func (e, f, s, g, h, ld, m, y, n, i, j, k)

e, f, (s.a, s.b), g, h, i 순서대로 6개의 레지스터에 값이 저장되게 됩니다.

그 다음에 나오는 j k는 레지스터를 다 사용했으므로 스택 프레임에 저장되게 됩니다.

s.d 는 부동소수점이기 때문에 XMM부분에 들어가고

m, y, n도 순차적으로 들어가게 됩니다.

근데 y만 YMM에 저장되어 있는데 y가 __m256 형식이라서 128bit 크기의 XMM에는 넣을 수 없기 때문에 YMM에 넣게 됩니다.

ld는 정확히는 모르지만 여러가지 이유로 스택에 저장되는것 같다.

 

YMM은 256bit 짜리 레지스터 입니다.

 

이러한 코드를 짜고 test 함수에 breakpoint 를 걸고 진행해봤다.

예상대로면

i1 = RDI = 1

i2 = RSI = 2

i3 = RDX = 3

이런 형태가 나올것이다.

 RAX  0x3ff8000000000000
 RBX  0x555555555210 (__libc_csu_init) ◂— endbr64
 RCX  0x7ffff7edf1e7 (write+23) ◂— cmp    rax, -0x1000 /* 'H=' */
 RDX  0x3
 RDI  0x1
 RSI  0x2
 R8   0x16
 R9   0x7c
 R10  0x7ffff7fb9be0 (main_arena+96) —▸ 0x5555555596a0 ◂— 0x0
 R11  0x246
 R12  0x555555555060 (_start) ◂— endbr64
 R13  0x7fffffffe4e0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe3d0 ◂— 0x0
 RSP  0x7fffffffe358 —▸ 0x5555555551e8 (main+113) ◂— add    rsp, 0x40
 RIP  0x555555555149 (test) ◂— endbr64

 

RDI = 0x1

RSI = 0x2

RDX = 0x3

예상한 그대로 나왔다!

 

부동 소수점은 계산하기 힘드니까 제외했다.

728x90