[Dreamhack] Stack Buffer Overflow: 1 - Calling Convention

securitykss·2022년 10월 26일
0
post-thumbnail

이 글은 https://dreamhack.io/lecture/courses/54 를 토대로 작성한 글입니다.

1. Introduction

이전 장에서 Calling Convention(함수 호출 규약)에 대해 언급했었다.

Calling Convention은 컴파일러에 따라 정해진다.
C언어를 컴파일 할 때, 윈도우에서는 MSCV, 리눅스에서는 gcc 컴파일러를 사용한다.

x86-64 아키텍처에서 MSCV 컴파일러는 MSx64 호출 규약을 적용하고,

gcc 컴파일러는 SYSTEM V호출 규약을 적용한다.

이번 시간에는 gcc에서 x86 바이너리를 컴파일 할 때 사용하는 cdecl, 그리고 x86-64 바이너리를 컴파일 할 때 사용하는 SYSTEM V 호출 규약에 대해 알아보자.

2. cdecl

2.1 설명

x86 아키텍처(32bit)는 레지스터의 수가 적다. 따라서 스택을 통해서 인자를 전달한다.

인자를 전달하기 위해 사용한 스택은 caller(호출자)가 정리를 한다.

스택을 통해 인자를 전달할 때는, 마지막 인자부터 첫 번째 인자까지 거꾸로 스택에 push한다.

2.2 예제

// Name: cdecl.c

// Compile: gcc -fno-asynchronous-unwind-tables -nostdlib -masm=intel \

//          -fomit-frame-pointer -S cdecl.c -w -m32 -fno-pic -O0

void __attribute__((cdecl)) callee(int a1, int a2){ // cdecl로 호출

}

void caller(){

   callee(1, 2);

}

caller(호출자)가 인자값 1과 2를 넘기면서 callee(피호출자)를 호출한다.

; Name: cdecl.s

	.file	"cdecl.c"

	.intel_syntax noprefix

	.text

	.globl	callee

	.type	callee, @function

callee:

	nop

	ret ; 스택을 정리하지 않고 리턴합니다.

	.size	callee, .-callee

	.globl	caller

	.type	caller, @function

caller:

	push	2 ; 2를 스택에 저장하여 callee의 인자로 전달합니다.

	push	1 ; 1를 스택에 저장하여 callee의 인자로 전달합니다.

	call	callee

	add	esp, 8 ; 스택을 정리합니다. (push를 2번하였기 때문에 8byte만큼 esp가 증가되어 있습니다.)

	nop

	ret

	.size	caller, .-caller

	.ident	"GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"

	.section	.note.GNU-stack,"",@progbits

caller에서 callee를 호출 할 때 가장 마지막 인자인 2부터 1까지 스택에 push하는 모습을 확인 할 수 있다.

3. SYSTEM V

3.1 설명

리눅스는 SYSTEM V(SYSV) Application Binary Interface(ABI)를 기반으로 만들어졌다.

SYSV ABI는 ELF 포맷, 링킹 방법, 함수 호출 규약 등의 내용을 담고 있다.

file 명령어로 바이너리 정보를 살펴 보면, SYSV를 확인 할 수 있다.

SYSV 함수 호출 규약

1. 6개의 인자를 RDI, RSI, RDX, RCX, R8, R9에 순서대로 저장하여 전달합니다. 더 많은 인자를 사용해야 할 때는 스택을 추가로 이용한다.

2. Caller에서 인자 전달에 사용된 스택을 정리한다.

3. 함수의 반환 값은 RAX로 전달한다

예제 보면서 이해해 보자.

3.2 예제

C코드

// Name: sysv.c

// Compile: gcc -fno-asynchronous-unwind-tables  -masm=intel \

//         -fno-omit-frame-pointer -S sysv.c -fno-pic -O0

#define ull unsigned long long

ull callee(ull a1, int a2, int a3, int a4, int a5, int a6, int a7) {

  ull ret = a1 + a2 + a3 + a4 + a5 + a6 + a7;

  return ret;

}

void caller() { callee(123456789123456789, 2, 3, 4, 5, 6, 7); }

int main() { caller(); }

컴파일

$ gcc -fno-asynchronous-unwind-tables -masm=intel -fno-omit-frame-pointer -o sysv sysv.c -fno-pic -O0

gdb 분석

3.2.1 인자 전달


caller에 break를 걸고 실행 한다.

void caller() { callee(123456789123456789, 2, 3, 4, 5, 6, 7); } 

코드를 보면 123456789123456789, 2, 3, 4, 5, 6, 7 인자값을 callee에 넣어준다.
이를 어셈블리로 보면
<caller+8>push 7부터 <caller+37> movabs rdi, 0x1b69b4acd05f15을 통해 인자들을 레지스터에 저장 후에 callee로 갈 준비를 한다.

레지스터에 어떤 값들이 저장되어 있는지 보자.

void caller() { callee(123456789123456789, 2, 3, 4, 5, 6, 7); } 

SYSV에 따라 rdi부터 r9까지 인자들이 저장된 모습을 확인할 수 있다. 그리고 총 7개의 인자이므로 push 7을 통해 스택에 인자를 저장하는 모습을 확인 할 수 있다.

3.2.2 반환 주소 저장


sicallee 함수 안으로 들어가보자.

stack에 0x5555555551b9이 저장된 모습을 볼 수 있다.
이는 caller+47에서 calle callee을 한 후 다음 주소 caller+52의 주소이다.

3.2.3 스택 프레임 저장


callee에서 push rbp를 한 후의 스택 상태를 보자

스택에 0x7fffffffdd60이 저장된다.
이는 caller로 돌아가려고 할 때, callerStack Frame Pointer(스택 프레임 포인터)를 유지하기 위해 스택에 저장한다.

3.2.4 스택 프레임 할당


mov rbp, rsp를 해서 calleeStack Frame을 할당할 준비를 한다.

ull callee(ull a1, int a2, int a3, int a4, int a5, int a6, int a7) {
  ull ret = a1 + a2 + a3 + a4 + a5 + a6 + a7;
  return ret;
}

callee에서는 지역변수를 사용하지 않기 때문에 Stack Frame을 할당 준비만 하고, 실제로 할당하지 않는다.
현재 스택 상태를 보자.

stack
rbp = rsp
0x7fffffffdd60(caller의 SFP)
0x5555555551b9(return 주소)

3.2.5 반환값 전달


ull callee(ull a1, int a2, int a3, int a4, int a5, int a6, int a7) {
  ull ret = a1 + a2 + a3 + a4 + a5 + a6 + a7;
  return ret;
} 

callee에서 인자들의 합을 rax에 저장을 한다. (SYSV 함수 호출 규약에 따라 반환값은 rax로 전달하기 때문)

3.2.6 반환

stack
0x7fffffffdd60(caller의 SFP)
0x5555555551b9(return 주소)

pop rbpcallerSFP(Stack Frame Pointer)rbp로 저장해서 caller로 돌아갈 준비를 한다.
rbp = 0x7fffffffdd60(caller의 SFP)

stack
0x5555555551b9(return 주소)

ret으로 caller+52로 돌아간다.

4. Conclusion

이렇게 cdecl, SYSV 호출 규약에 대해 알아 보았다.

다음 그림은 호출 규약을 정리한 표이다.

마치며

다음 시간엔 Stack Overflow에 대해 알아보자.

Reference

https://dreamhack.io/lecture/courses/54
https://learn.dreamhack.io/54#13 (사진 출처)

profile
보안 공부를 하는 학생입니다.

0개의 댓글