Windows API Hooking
2022년 4월 3일
User-Mode Hooking #
IAT(Import Address Table) Hooking #
Inline Function Hooking (Detour Hooking) #
사용할 API의 첫 5byte(x86)를 자신이 만든 가짜 함수로 JMP 하는 코드로 바꾸는 방식.
가짜 함수에서 여러 조작을 한 뒤 다시 API의 시작 위치로 돌려줄 수도 있고, 가짜 함수 내부적으로 원본 API를 호출한뒤 리턴하는 방법도 있다.
JMP 의 OPCode는 “E9"이며, 사용되는 주소는 상대주소이다. 코드의 주소는 명령어의 시작 위치이고 점프는 그 명령어 다음부터 세기때문에 E9 01000000 이라고 해도 명령어 크기(5byte)만큼은 일단 이동하기때문에 총 6byte를 움직이는걸 알 수 있다.
주소가 0x410000 이고, 0x420000으로 JMP 하는 코드인데, E9 FBFF0000 => FFFB(0x10000 - 0x5) 만큼만 점프하는것을 알 수 있다.
JMP 상대주소 = 0x420000 - 0x410000 - 5 임을 알 수 있다.
제자리로 점프하는 코드도 -5byte 점프해야하고, 만약 도착지가 더 낮은주소라도 계산방법은 같다.
1typedef int (__stdcall *tFunction)(int, int, double, char*); // 함수포인터 별칭지정
2tFunction oFunction = NULL;
3
4int __stdcall hkFunction(int a, int b, double c, char *d)
5{
6 int ret;
7 printf("before Execute Function\n");
8 ret = oFunction(a, b, c, d);
9 printf("after Execute Function\n");
10 return (ret);
11}
12
13void *SetHook(BYTE *src, const BYTE *dst)
14{
15 BYTE *org_gadget = malloc(10);
16 DWORD dwBack;
17
18 VirtualProtect(src, 5, PAGE_EXECUTE_READWRITE, &dwBack);
19 memcpy(org_gadget, src, 5); // 기존 코드 저장
20 *src = 0xE9;
21 *(DWORD*)(src + 1) = dst - src - 5; // E9 __ __ __ __ 점프할 주소 값을 채워야한다. LittleEndian
22
23 // 후킹 이후 사용할 원본함수 가젯
24 *(DWORD*)(org_gadget + 5) = 0xE9;
25 *(DWORD*)(org_gadget + 6) = (src + 5) - (org_gadget + 5) - 5; // 현재위치에서 원본함수로 돌아가기
26
27 // src는 더이상 코드수정이 필요없으니 권한원복, 가젯은 원본호출전 실행해야되기 때문에 권한줌
28 VirtualProtect(src, 5, dwBack, &dwBack);
29 VirtualProtect(org_gadget, 10, PAGE_EXECUTE_READWRITE, &dwBack);
30 return (org_gadget);
31}
32
33oFunction = SetHook(src, hkFunction);
후킹을 위해 JMP코드를 원본 함수에 삽입했기때문에 그 코드는 손실되어버린다.
후킹함수에서 원하는 코드를 실행하고난 뒤 다시 원본함수를 호출해야하는데, 손실된 원본 코드와 원본함수 + 5 위치로 점프하는 코드를 같이 가젯에 담고 실행시켜주면 손실된 코드도 실행되며 원본 함수로 복구된다.
만약 후킹을 단 한번만 한다면 후킹함수에서 원본 코드를 복원하는 방법도 사용할 수 있을것이다.