Operating System/이해하면 인생이 바뀌는 Windows API hook

사전 지식 - 첫 번째

Tony Lim 2024. 11. 22. 14:50

함수 포인터

  • 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