시스템 해킹

Stack Overflow

kchabin 2022. 8. 18. 23:02

함수 호출 규약

함수의 호출 및 반환에 대한 약속.

한 함수에서 다른 함수 호출 시, 프로그램 실행 흐름이 이동한다. 호출한 함수가 반환하면, 다시 원래의 함수로 돌아와서 기존의 실행 흐름을 이어나간다. 

함수를 호출할 때는 반환된 이후를 위해 호출자(Caller)의 상태(Stack Frame)및 반환 주소( Return Address)를 저장해야 한다.

호출자는 피호출자(Callee)가 요구하는 인자를 전달해줘야 하며, 피호출자의 실행이 종료될 때는 반환 값을 전달받아야 한다.

 

함수 호출 규약의 종류

x86(32bit) 아키텍처 = 레지스터의 수가 적음. 스택으로 인자를 전달함.

x86-64 아키텍처 = 인자가 적으면 레지스터만, 인자가 너무 많을 때는 스택 사용.

 

CPU의 아키텍처가 같아도 컴파일러가 다르면 적용하는 호출 규약이 다를 수 있다.

 

C언어 컴파일

윈도우 = MSVC

리눅스 = gcc

 

x86-64 

- MSVC = MS x64 호출 규약 적용 

- gcc = SYSTEM V 호출 규약 적용

 

다양한 함수 호출 규약

x86
  • cdecl
  • stdcall
  • fastcall
  • thiscall
x86-64
  • System V AMD64 ABI의 Calling Convention
  • MS ABI의 Calling Convention

cdecl

x86 아키텍처는 레지스터의 수가 적음.

-스택으로 인자 전달

-인자 전달한 스택을 호출자가 정리함.

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

 

SYSV

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

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

 

1. 6개의 인자를 RDI, RSI, RDX, RCX, R8, R9에 순서대로 저장하여 전달함.

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

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

 

 

x64 레지스터의 종류

- 레지스터 = CPU에서 사용하는 변수라고 생각한다.

 

지금 RAX, EAX, RSI, ESI 이런게 굉장히 헷갈리는 상태라 한 번 정리해보기로 했다.

데이터 레지스터 (RAX, RBX, RCX, RDX)

 

-RAX(accumulator) : 사칙연산 명령어에서 자동으로 사용. 함수의 return 값 저장

시스템콜 함수를 사용하려면 rax에 함수의 syscall 번호를 넣어준다.

 

- RBX : 메모리 주소 저장 용도

 

- RCX : CPU는 루프카운터로 ECX를 자동으로 사용

고급언어 for 문의 i 역할. 

차이점 : 미리 반복 값을 정해두고 명령어를 사용할 때마다 값이 하나씩 줄어든다.

 

* 루프 카운터 = 컴퓨터 시스템 따위에서, 반복하여 실행되는 문장에서 이제까지 실행된 횟수를 기록하는 기기.

하드웨어 계수기보단 기억 장소와 레지스터를 할당하여 그 곳에 값을 기록한다.

 

- RDX : 다른 레지스터를 서포트하는 여분의 레지스터. EAX와 같이 사용됨.

 

포인터 레지스터(RSI, RDI, RBP, RSP)

 

- RSI / RDI

문자열 출발지/목적지 주소, 확장 소스 인덱스, 확장 목적지 인덱스 레지스터.

각각 메모리 출발지와 목적지를 나타냄.

 

rsi = source index : src 데이터, 즉 복사할 데이터의 주소가 저장됨.

rdi = destination index : 데이터를 복사할 때, 복사된 dest 데이터의 주소가 저장됨. 

 

- RSP

현재 스택 주소. 스택 맨 윗쪽 주소. 

- 데이터가 계속 쌓일 때, 스택의 가장 높은 곳을 가리킨다.

스택에 있는 데이터의 주소를 지정.

 

- push, pop 명령을 통해 RSP 값이 위아래로 8바이트씩 이동하면서 스택 프레임의 크기를 변경하게 된다.

계산, 데이터 전송에는 거의 사용되지 않음.

 

-RBP (Extended Base Pointer)

함수가 호출되면 스택 프레임이 형성된다. 

스택 복귀 주소. = 스택 프레임의 시작 지점 주소

 

고급 언어에서 스택에 있는 함수 매개변수와 지역 변수를 참조하기 위해 사용.

고급 수준의 프로그래밍 이외 사용 x.

 

- RIP

현재 명령 실행 주소

 

기타

R8~R15

일반적으로 함수의 매개변수로 사용

 

하나의 레지스터는 아래 그림처럼 크기에 따라 적절히 쪼개 사용할 수 있다. 

 

 

스택 프레임 : EBP(베이스 포인터) 레지스터를 사용하여 스택 내의 로컬 변수, 파라미터, 복귀 주소에 접근하는 기법

 

ESP 레지스터의 값은 프로그램 안에서 수시로 변경됨 - >  이를 기준으로 하는 프로그램을 만들기 어려움.

CPU가 정확한 위치 참고 어렵다. 

어떤 기준 시점(함수 시작)의 ESP 값을 EBP에 저장하고 이를 함수 내에서 유지해주면, ESP 값이 아무리 변하더라도 EBP를 기준으로 안전하게 해당 함수의 변수, 파라미터, 복귀 주소에 접근할 수 있다.

-> 이것이 EBP 레지스터의 베이스 포인터 역할.

 

 

 

PUSH EBP ; 함수시작(현재 EBP를 스택에 저장)
MOV EBP, ESP ; 현재의 스택 포인터를 EBP에 저장


MOV ESP, EBP ;ESP 정리 - 함수 시작했을 때의 값으로 복원시킴
POP EBP ; 리턴 되기 전에 저장해 놓았던 원래 EBP 값으로 복원
RETN ; 함수 종료

 

 

스택 프레임

 

버퍼

변수

RBP

스택이 시작하는 베이스 포인터

RET

 

가장 아래쪽에 RET이 존재해 함수가 끝났을 때 돌아갈 곳을 지정.

메인함수의 스택 프레임이 이렇다면, 메인함수가 다른 함수를 불러왔을 때 버퍼 위에 데이터가 차곡차곡 쌓인다.

이때 변수 - RET - RBP - 버퍼 순.

해당 함수가 다 실행되면 쌓였던 스택이 사라진다.

 

'시스템 해킹' 카테고리의 다른 글

basic exploitaion 001 문제 풀이  (0) 2022.08.19
pwnable.kr bof 문제 풀이  (0) 2022.08.19
gdb 분석 실습  (0) 2022.08.18
3주차 질문  (0) 2022.08.13
basic_exploitation_000 문제 풀이  (0) 2022.08.13