함수 포인터
- C언어에서 함수의 이름은 실행코드가 저장되는 메모리의 주소
- 디버거로 대상 메모리 내용 확인 가능
- 디스어셈블 기능을 이용해서 명령 단위 실행으로 역추적 가능
- 함수 포인터는 대상 플랫폼에 따라 32bit 혹은 64bit로 달라질 수 있다.
- Callback 구조 적용에 적합하다.
- 소위 API라는것들은 모두 함수로 구현되며 일반 함수 포인터와 동일한 개념이 적용된다.
- calling convetion
- __cdecl (호출자가 스택정리)
- __stdcall (피호출자가 스택정리)
- __fastcall (매개변수에 레지스터 사용)
- 64bit에서는 요게 기본이다.
- int(__cdecl * pfTest)(int, int) = testFunc 처럼 함수포인터에서 써줘야한다.
함수 프롤로그 와 에필로그
- 함수 내부에서 사용된 자동변수는 스택을 사용한다.
- 필요한 스택의 크기는 컴파일 타임에 결정되어 반영된다.
- 함수 프롤로그는 스택을 증가시키는 코드
- 함수 에필로그는 스택을 되돌리는 코드
#include <stdio.h>
int testFunc(int a, int b)
{
int c = 0;
a + b;
return c;
}
int main()
{
int result = testFunc(1, 2);
return 0;
}
int testFunc(int a, int b)
{
00301000 push ebp
00301001 mov ebp,esp
00301003 push ecx
int c = 0;
00301004 mov dword ptr [c],0
a + b;
return c;
0030100B mov eax,dword ptr [c]
}
0030100E mov esp,ebp
00301010 pop ebp
00301011 ret
push ebp ~ push ecx 까지를 function prologue 라고 한다.
mov eax ~ ret 까지를 function epilogue
처음에는 move ebp esp 나갈때는 move esp ebp 반대이다.
caller ebp 를 push 를 통해 stack에 previous ebp memory value를 저장하고 ebp를 현재 top을 가르키는 esp value로 업데이트 한다.
Naked 함수
- MS 전용 C언어 확장 문법 (Storage class) 으로 함수에만 적용가능하다
- Function prologue / epilouge sequence 가 없다.
- x86(32bit) , ARM 환경에서만 사용할 수 있으며 x64 환경에서는 사용 불가능하다.
- 문법 특수성
- return 문 사용 불가
- 변수 선언 및 정의 불가 (반드시 분리)
- int a = 10 이거는 안됨
- int a; a = 10; 이거는 됨
#include <stdio.h>
__declspec(naked) void testFunc(void)
{
puts("testFunc()");
printf("a: %d\n", 10);
}
int main()
{
testFunc();
return 0;
}
__declspec(naked) 로 선언할 수 있다.
0099107F int 3
--- D:\C_Stuff\Study\ApiHook\04_nakedFunc\04_nakedFunc.c -----------------------
puts("testFunc()");
00991080 push 993020h
00991085 call dword ptr [__imp__puts (09920B8h)]
0099108B add esp,4
printf("a: %d\n", 10);
0099108E push 0Ah
00991090 push 993018h
00991095 call printf (0991040h)
0099109A add esp,8
--- 소스 파일이 없습니다. ---------------------------------------------------------------
0099109D int 3
stack pointer를 manage하는 pro ,epilouge 가 존재하지 않는다.
int 는 integer가 아니라 interrupt 이다. int 3은 일종의 브레이크 포인트 역할을 하게 된다.
0099109D 메모리주소의 값을 확인해보면 cc 이다.
인라인 어셈블리
- C언어 코드에서 __asm {} 키워드를 사용해서 어셈블리 코드를 작성할 수 있는 방법이다.
- x86 환경에서만 사용이 가능하다.
#include <stdio.h>
__declspec(naked) int testFunc(int a, int b)
{
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
}
int x;
x = a;
printf("naked testFunc(%d, %d)\n", a, b);
__asm {
mov eax, 5
}
__asm {
mov esp, ebp
pop ebp
ret
}
}
int main()
{
puts("main() - Begin");
int result = testFunc(1, 2);
printf("testFunc(): %d\n", result);
puts("main() - End");
return 0;
}
prolouge ,epilouge 를 inline assembly로 추가된 상황이다. 사실상 일반 함수처럼 동작하는것이다.
sub esp, __LOCAL_SIZE compiler가 필요한 stack 용량(변수 코드등) 을 계산해주고 그만큼 stack pointer에서 빼준다. stack pointer메모리 주소는 최대(기본 1MB) 에서 0으로 가니까
return 될때 eax register에 보통 값을 넣는다.
'Operating System > 이해하면 인생이 바뀌는 Windows API hook' 카테고리의 다른 글
DLL injection (0) | 2024.12.13 |
---|---|
Inline hook (0) | 2024.12.02 |
IAT hook (0) | 2024.12.02 |
사전지식 - 두 번째 (0) | 2024.11.27 |