본문 바로가기
운영체제/[ecourse] Windows Programming

7-2. API Hooking

by 헛둘이 2022. 9. 21.
API Hooking이란?
  • API의 호출을 가로채는 기술
  • 예를 들면, A라는 함수를 호출했는데 내가 만든 B라는 함수를 호출하는 것.

 

IAT Hook, DLL 함수의 호출 원리
  • DLL이 로드되는 위치를 알고 있고, 그 DLL이 가진 함수의 Offset을 알고 있다면?
  • 어셈블리 명령으로 call 0x주소로 호출할 수 있을 것이다.
  • 그러나 컴파일/링크 시 DLL 안의 함수 주소는 알 수 없다 (ASLR 때문)

출처: 위키백과

 

ASLR(Address space layout randomization)이란?

ASLR은 실행 시 exe, dll, stack, heap등의 주소를 임의로 변경하는 보안 기법

 

ASLR을 끄고 MessageBoxA의 주소 덮어쓰기
#include <iostream>
#include <Windows.h>
using namespace std;

typedef UINT(__stdcall* f)(HWND, const char*, const char*, UINT);

UINT __stdcall foo(HWND hwnd, const char* s1, const char* s2, UINT btn)
{
    printf("foo : %s, %s\n", s1, s2);

    HMODULE hDll = GetModuleHandle(L"user32.dll");
    f proc = (f)GetProcAddress(hDll, "MessageBoxA");
    
    return proc(hwnd, s2, s1, btn);
}

int main()
{
    //MessageBoxA(0, "API Hook", "aAA", 0);
    DWORD old;
    VirtualProtect((void*)0x041B098, sizeof(void*), PAGE_READWRITE, &old);
    *((int*)0x041B098) = (int)foo;
    VirtualProtect((void*)0x041B098, sizeof(void*), old, 0);

    MessageBoxA(0, "API HOOK", "AAA", MB_OK);

    return 0;
}
  • 비주얼스튜디오에서 ASLR을 끄고 MessageBoxA의 주소를 알아내어 테스트해본 결과 MessageBoxA가 아닌 foo가 실행
  • VirtualProtect는 페이지 보안 속성을 변경해주는 함수. IAT영역은 보안 속성이 기본적으로 READONLY이기 때문
  • 주소를 덮어쓰기 위해서 PAGE_READWRITE로 변경해줌

 

 

 

 

 

 

 


코드를 이용한 IAT 후킹
  • 32비트 기준으로 작성

 

후킹 전 알아야 할 정보들
  • 메모리에 매핑된 실행 파일의 주소를 알아야 함 (그래야 IAT의 주소를 아니까)
  • 후킹할 함수가 어떤 DLL에 있는지 알아야 함 (user32.dll에 MessageBox가 있듯이)
  • 어떤 함수를 어떤 함수로 바꿀건지 (MessageBox -> TestFunction)
가상 주소에 매핑된 실행 파일의 Import table의 주소를 구하는 방법

=> DbgHelp.dll에 있는 ImageDirectoryEntryToData를 사용하면 IAT의 위치를 구할 수 있음
=> 함수의 반환값으로 IMAGE_IMPORT_DESCRIPTOR의 주소가 반환되는데 그게 IAT

 

IMPORT 섹션
  • IMPORT 섹션은 IMAGE_IMPORT_DESCRIPTOR의 배열로 구성되어 있다.
  • IMAGE_IMPORT_DESCRIPTOR는 총 5개 항목으로 구성됨

출처 : https://osb330.tistory.com/40

  • 여기서 중요한 건 Name과 FirstThunk(IAT)
  • IAT 구조체의 항목은 DLL 하나당 하나씩 만들어짐
  • 어떤 구조체가 user32.dll을 관리하는지 찾아야 함
  • Name 항목이 DLL의 이름이 있는 주소
  • 코드에서는 구조체의 각 Name 멤버를 _stricmp로 user32.dll과 비교하고 있다.
  • FirstThunk(IAT)는 DLL의 함수가 시작되는 시작 주소
  • FirstThunk(IAT)는 Offset이므로 실행 파일의 주소 + FirstThunk로 봐야 함
  • 이 주소부터 4바이트씩 읽어서 그 주소가 MessageBoxA의 주소와 같은지 비교하면 됨
  • 찾으면 그 주소의 보호속성을 변경하고 덮어씀

 

#include <iostream>
#include <Windows.h>
#include <DbgHelp.h>

#pragma comment(lib, "DbgHelp.lib")
using namespace std;

UINT __stdcall TestFunction(DWORD p1, const char* p2, const char* p3, DWORD p4)
{
    cout << "Fake!!" << endl;
    return 0;
}

int main()
{
    ULONG sz;
    
    //실행 파일의 시작 주소
    HMODULE hExe = GetModuleHandle(0);

    // DbgHelp.dll의 ImageDirectoryEntryToData를 사용해 IAT를 구해온다.
    // 반환값은 IMAGE_IMPORT_DESCRIPTOR의 주소로 들어오는데 그게 IAT의 주소
    IMAGE_IMPORT_DESCRIPTOR* pImage =
        (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToData(
            hExe, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &sz
        );

    printf("Address Import Directory : %p\n", pImage);



    for (; pImage->Name; pImage++)
    {
        char* s = ((char*)hExe + pImage->Name);
        if (_strcmpi("user32.dll", s) == 0) break;
    }

    if (pImage->Name == 0)
    {
        cout << "user32.dll을 찾지 못했음" << endl;
        return 0;
    }

    // pThunk
    // pImage->Name이 0이 아니라면 DLL을 찾은 것이고,
    // 그 DLL을 관리하는 IAT 구조체의 Thunk에 접근하는 것
    IMAGE_THUNK_DATA* pThunk =
        (IMAGE_THUNK_DATA*)((char*)hExe + pImage->FirstThunk);

    for (; pThunk->u1.Function; pThunk++)
    {
        // pThunk->u1.Function이 내가 찾는 함수라면 if문 안으로 진입
        if (pThunk->u1.Function == (DWORD)MessageBoxA)
        {
            // 주소를 구해놓기
            DWORD* addr = &(pThunk->u1.Function);

         
            DWORD old;

            //IAT의 보호 속성이 READONLY 속성이므로 READWRITE로 변경
            VirtualProtect(addr, sizeof(DWORD), PAGE_READWRITE, &old);

            // 이거보단 WriteProcessMemory로 하는게 더 좋다.
            *addr = (DWORD)TestFunction;

            DWORD len;


            VirtualProtect(addr, sizeof(DWORD), old, &old);
            break;
        }
    }

    MessageBoxA(0, "Caption Test", "Error", MB_OK);

    return 0;
}

'운영체제 > [ecourse] Windows Programming' 카테고리의 다른 글

7-1. DllMain 기본 개념  (0) 2022.09.21
6-2-3. Critical Section 실습  (0) 2022.09.20
6-2-2. Event 실습  (0) 2022.09.20
6-2-1. Semaphore 실습  (0) 2022.09.20
6-2. 동기화 개념과 Critical Section  (1) 2022.09.20

댓글