ETC/Pwn 메모장

함수 인자와 Return 값 레지스터 (feat.32, 64 bit)

210_ 2019. 6. 25. 23:59
#include <stdio.h>

int tmp(int a, int b, int c, int d, int e, int f)
{
	printf("%d %d %d %d %d %d\n", a, b, c, d, e, f);
	return 1;
}

int main()
{
    printf("%d\n", tmp(1, 2, 3, 4, 5, 6));
}

위의 코드에서 32비트와 64비트의 Function Argument와 Function Return 값 저장 레지스터를 알아보자.


32 BITS

pwndbg> disas main
Dump of assembler code for function main:
   0x0804844a <+0>:     lea    ecx,[esp+0x4]
   0x0804844e <+4>:     and    esp,0xfffffff0
   0x08048451 <+7>:     push   DWORD PTR [ecx-0x4]
   0x08048454 <+10>:    push   ebp
   0x08048455 <+11>:    mov    ebp,esp
   0x08048457 <+13>:    push   ecx
   0x08048458 <+14>:    sub    esp,0x4
   0x0804845b <+17>:    sub    esp,0x8
   0x0804845e <+20>:    push   0x6
   0x08048460 <+22>:    push   0x5
   0x08048462 <+24>:    push   0x4
   0x08048464 <+26>:    push   0x3
   0x08048466 <+28>:    push   0x2
   0x08048468 <+30>:    push   0x1
   0x0804846a <+32>:    call   0x804841b <tmp>
   0x0804846f <+37>:    add    esp,0x20
   0x08048472 <+40>:    sub    esp,0x8
   0x08048475 <+43>:    push   eax
   0x08048476 <+44>:    push   0x8048533
   0x0804847b <+49>:    call   0x80482f0 <printf@plt>
   0x08048480 <+54>:    add    esp,0x10
   0x08048483 <+57>:    nop
   0x08048484 <+58>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x08048487 <+61>:    leave
   0x08048488 <+62>:    lea    esp,[ecx-0x4]
   0x0804848b <+65>:    ret
End of assembler dump.
pwndbg> disas tmp
Dump of assembler code for function tmp:
   0x0804841b <+0>:     push   ebp
   0x0804841c <+1>:     mov    ebp,esp
   0x0804841e <+3>:     sub    esp,0x8
   0x08048421 <+6>:     sub    esp,0x4
   0x08048424 <+9>:     push   DWORD PTR [ebp+0x1c]
   0x08048427 <+12>:    push   DWORD PTR [ebp+0x18]
   0x0804842a <+15>:    push   DWORD PTR [ebp+0x14]
   0x0804842d <+18>:    push   DWORD PTR [ebp+0x10]
   0x08048430 <+21>:    push   DWORD PTR [ebp+0xc]
   0x08048433 <+24>:    push   DWORD PTR [ebp+0x8]
   0x08048436 <+27>:    push   0x8048520
   0x0804843b <+32>:    call   0x80482f0 <printf@plt>
   0x08048440 <+37>:    add    esp,0x20
   0x08048443 <+40>:    mov    eax,0x1
   0x08048448 <+45>:    leave
   0x08048449 <+46>:    ret
End of assembler dump.

32비트에서 함수 호출 할 때에는 push 명령어를 통해 인자를 stack에 저장한 후에 DWORD PTR [ebp+인자위치] 에서 값을 들고 온다. 레지스터에 저장을 안하네? 뭐지? syscall 인자로 사용해야 해서 그런가? 아시는 분은 댓글 좀 달아주세요 ㅠㅠ 해결! 아래 주소에서 그 이유를 설명해준다.

https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-i386-and-x86-6

 

What are the calling conventions for UNIX & Linux system calls on i386 and x86-64

Following links explain x86-32 system call conventions for both UNIX (BSD flavor) & Linux: http://www.int80h.org/bsdasm/#system-calls http://www.freebsd.org/doc/en/books/developers-handbook/x86-

stackoverflow.com

그리고 tmp 함수의 return 값은 EAX에 저장되어 main으로 전달되는 것을 main+43(push eax)에서 printf의 인자 전달 부분에서 볼 수 있다.


ebp+인자위치 에서 값을 가져오는 이유

이때 첫번째 인자가 왜 ebp+0x8인지는 다음과 같이 설명할 수 있다.

main+32 -> call tmp

pwndbg> x/wx $ebp
0xfff535a8:     0x00000000

... code execute ...

tmp+1 -> mov ebp,esp

pwndbg> x/16wx $esp
0xfff53578:     0xfff535a8      0x0804846f      0x00000001      0x00000002
0xfff53588:     0x00000003      0x00000004      0x00000005      0x00000006
0xfff53598:     0xfff5365c      0xf761619d      0xf778d3c4      0xfff535c0
0xfff535a8:     0x00000000      0xf75fcad3      0x08048490      0x00000000
pwndbg> x/wx 0xff86ee38
0xfff535a8:     0x00000000
pwndbg> x/wx 0x0804846f
0x804846f <main+37>:    0x8320c483
pwndbg> x/wx $ebp
0xfff53578:     0xfff535a8
pwndbg> x/wx $ebp+8
0xfff53580:     0x00000001

tmp+1 까지 실행될 경우 stack은 main 함수의 ebp, main 함수에서 tmp 함수가 끝나고 실행할 명령어 주소 이 두 주소가 저장되어 있으므로 실질적인 인자는 tmp 함수의 ebp+8 에 존재한다.


64 BITS

pwndbg> disas main
Dump of assembler code for function main:
   0x000000000040058c <+0>:     push   rbp
   0x000000000040058d <+1>:     mov    rbp,rsp
   0x0000000000400590 <+4>:     mov    r9d,0x6
   0x0000000000400596 <+10>:    mov    r8d,0x5
   0x000000000040059c <+16>:    mov    ecx,0x4
   0x00000000004005a1 <+21>:    mov    edx,0x3
   0x00000000004005a6 <+26>:    mov    esi,0x2
   0x00000000004005ab <+31>:    mov    edi,0x1
   0x00000000004005b0 <+36>:    call   0x400536 <tmp>
   0x00000000004005b5 <+41>:    mov    esi,eax
   0x00000000004005b7 <+43>:    mov    edi,0x400667
   0x00000000004005bc <+48>:    mov    eax,0x0
   0x00000000004005c1 <+53>:    call   0x400410 <printf@plt>
   0x00000000004005c6 <+58>:    nop
   0x00000000004005c7 <+59>:    pop    rbp
   0x00000000004005c8 <+60>:    ret
End of assembler dump.
pwndbg> disas tmp
Dump of assembler code for function tmp:
   0x0000000000400536 <+0>:     push   rbp
   0x0000000000400537 <+1>:     mov    rbp,rsp
   0x000000000040053a <+4>:     sub    rsp,0x20
   0x000000000040053e <+8>:     mov    DWORD PTR [rbp-0x4],edi
   0x0000000000400541 <+11>:    mov    DWORD PTR [rbp-0x8],esi
   0x0000000000400544 <+14>:    mov    DWORD PTR [rbp-0xc],edx
   0x0000000000400547 <+17>:    mov    DWORD PTR [rbp-0x10],ecx
   0x000000000040054a <+20>:    mov    DWORD PTR [rbp-0x14],r8d
   0x000000000040054e <+24>:    mov    DWORD PTR [rbp-0x18],r9d
   0x0000000000400552 <+28>:    mov    r8d,DWORD PTR [rbp-0x14]
   0x0000000000400556 <+32>:    mov    edi,DWORD PTR [rbp-0x10]
   0x0000000000400559 <+35>:    mov    ecx,DWORD PTR [rbp-0xc]
   0x000000000040055c <+38>:    mov    edx,DWORD PTR [rbp-0x8]
   0x000000000040055f <+41>:    mov    eax,DWORD PTR [rbp-0x4]
   0x0000000000400562 <+44>:    sub    rsp,0x8
   0x0000000000400566 <+48>:    mov    esi,DWORD PTR [rbp-0x18]
   0x0000000000400569 <+51>:    push   rsi
   0x000000000040056a <+52>:    mov    r9d,r8d
   0x000000000040056d <+55>:    mov    r8d,edi
   0x0000000000400570 <+58>:    mov    esi,eax
   0x0000000000400572 <+60>:    mov    edi,0x400654
   0x0000000000400577 <+65>:    mov    eax,0x0
   0x000000000040057c <+70>:    call   0x400410 <printf@plt>
   0x0000000000400581 <+75>:    add    rsp,0x10
   0x0000000000400585 <+79>:    mov    eax,0x1
   0x000000000040058a <+84>:    leave
   0x000000000040058b <+85>:    ret
End of assembler dump.

64비트에서 호출할 때에는 EDI, ESI, EDX, ECX, R8, R9에 차곡차곡 저장한 후에 stack에 옮긴 뒤, 다시 레지스터에 그 포인터 값을 넣는다.

그리고 tmp 함수의 return 값은 EAX에 저장되어 main으로 전달되는 것을 main+41(mov esi,eax)에서 printf의 인자 전달 부분에서 볼 수 있다.


Function Argument

32bits 64bits
In Stack & use [ebp+@] RDI, RSI, RDX, RCX, R8, R9

Function Return Value

32bits 64bits
EAX EAX

 


참고문헌

https://tribal1012.tistory.com/13

 

32bit와 64bit의 차이

32bit와 64bit의 차이 CPU 레지스터(Registers) Calling Conventions(함수 호출 규약) 1. CPU Registers CPU의 레지스터란 처리의 지연 시간을 줄이기 위해 사용하고 있는 CPU 내부의 임시 기억장치이다. 컴퓨터가..

tribal1012.tistory.com

https://docs.microsoft.com/ko-kr/cpp/build/x64-calling-convention?view=vs-2019

 

x64 호출 규칙

ABI 기본 x64 호출 규칙의 세부 정보입니다.

docs.microsoft.com

https://www.lazenca.net/pages/viewpage.action?pageId=16810038&src=contextnavpagetreemode

 

03.RTL(Return to libc) - TechNote - Lazenca.0x0

페이지 … TechNote 02.TechNote 06.Exploit tech 배너의 맨 끝으로 배너의 맨 처음으로 03.RTL(Return to libc) 메타 데이터의 끝으로 건너뛰기 Lazenca.0x0님이 작성, 4월 11, 2018에 최종 변경 메타 데이터의 시작으로 이동