미친해커

[Reversing] Trampoline(Inline) Hooking x86 Step 2 본문

Reversing/Hooking

[Reversing] Trampoline(Inline) Hooking x86 Step 2

미친해커 2022. 4. 29. 22:46
반응형

How to patch the top 5 bytes of the API

이제 후킹하고 싶은 API의 상위 5 바이트를 패치하는 것만 남았다. 기본적으로 DLL의 함수들은 .text 섹션에 존재한다. 해당 섹션의 메모리 보호 옵션은 보통 ER---쓰기 권한이 빠져있다. 그렇기 때문에 먼저 쓰기 권한을 부여해야만 한다. Windows API 중 특정 영역의 메모리 보호 옵션을 수정하는 함수VirtualProtect 함수가 있다.

 

VirtualProtectEx function (memoryapi.h) - Win32 apps

Changes the protection on a region of committed pages in the virtual address space of a specified process.

docs.microsoft.com

#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
    // GetModuleHandleA 함수로 hModule을 구한다.
    HMODULE hModule = GetModuleHandleA("user32.dll");
    
    // 만약 GetModuleHandleA 함수로 구하지 못하였다면 LoadLibraryA 함수로 DLL을 로드한다.
    if (hModule == NULL)
        hModule = LoadLibraryA("user32.dll");
    
    // GetProcAddress 함수를 이용해 주소를 가져옴
    PVOID pMessageBoxA = (PVOID)GetProcAddress(hModule, "MessageBoxA");
    
    // 원본 메모리 보호 옵션을 저장할 변수
    DWORD OldProtect;
    
    /*
    VirtualProtect 함수를 사용해 MessageBoxA 주소로부터 5바이트 만큼의 메모리 보호 옵션을
    PAGE_EXECUTE_READWRITE(ERW--)로 변경한다.
    */
    if (VirtualProtect(pMessageBoxA, 5, PAGE_EXECUTE_READWRITE, &OldProtect) == FALSE)
    {
        printf("VirtualProtect Failed\n");
        return -1;
    }
    
    printf("Changed the top 5 bytes memory protection option in MessageBoxA to ERW--\n");
}
Changed the top 5 bytes memory protection option in MessageBoxA to ERW--

위 문자열이 출력되면 정상적으로 MessageBoxA 함수의 상위 5바이트의 메모리 보호 옵션이 수정된 것이다. 수정된 이후에는 해당 영역의 데이터를 수정할 수 있게 된다. 이제 본격적으로 후킹을 시도해보자.

Let's try Trampoline Hooking!!

후킹 함수가 호출되었을 때 원본 함수를 호출하기 위해서는 꼭 다시 원본 코드로 복원 후 호출해야 하고 호출이 끝난 후에는 다시 후킹 코드로 패치해줘야 후킹이 유지될 수 있다.

#include <stdio.h>
#include <windows.h>

BYTE HookCode[5] = { 0xE9, 0x00, 0x00, 0x00, 0x00 };
BYTE OriginCode[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 };

int __stdcall NewMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
    // 상위 5 바이트를 원본 코드로 패치
    memcpy(MessageBoxA, OriginCode, 5);
    // lpText 변수를 조작해 함수를 정상적으로 실행
    int ret = MessageBoxA(hWnd, "Hooked..!", lpCaption, uType);
    // 다시 후킹 코드로 패치
    memcpy(MessageBoxA, HookCode, 5);

    // 반환값 전달
    return ret;
}

int main(int argc, char *argv[])
{
    // GetModuleHandleA 함수로 hModule을 구한다.
    HMODULE hModule = GetModuleHandleA("user32.dll");
    
    // 만약 GetModuleHandleA 함수로 구하지 못하였다면 LoadLibraryA 함수로 DLL을 로드한다.
    if (hModule == NULL)
        hModule = LoadLibraryA("user32.dll");

    if (hModule == NULL)
    {
        printf("[-] user32.dll not found\n");
        return -1;
    }

    printf("[+] user32.dll found!\n");

    // GetProcAddress 함수를 이용해 주소를 가져옴
    PVOID pMessageBoxA = (PVOID)GetProcAddress(hModule, "MessageBoxA");
    
    if (pMessageBoxA == NULL)
    {
        printf("[-] MessageBoxA not found\n");
        return -1;
    }

    printf("[+] MessageBoxA found!\n");

    // 원본 메모리 보호 옵션을 저장할 변수
    DWORD OldProtect;
    
    /*
    VirtualProtect 함수를 사용해 MessageBoxA 주소로부터 5바이트 만큼의 메모리 보호 옵션을
    PAGE_EXECUTE_READWRITE(ERW--)로 변경한다.
    */
    if (VirtualProtect(pMessageBoxA, 5, PAGE_EXECUTE_READWRITE, &OldProtect) == FALSE)
    {
        printf("VirtualProtect Failed\n");
        return -1;
    }
    
    printf("[*] Changed the top 5 bytes memory protection option in MessageBoxA to ERW--\n");

    printf("[+] Calculate relative jump length\n");
    // 점프할 상대거리 계산후 변수에 저장
    *(DWORD *)(HookCode + 1) = (ULONG_PTR)NewMessageBoxA - (ULONG_PTR)pMessageBoxA - 5;

    printf("[*] Patch code :    0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", 
        HookCode[0],
        HookCode[1],
        HookCode[2],
        HookCode[3],
        HookCode[4]
    );
    
    // 원본 5 바이트 백업
    memcpy(OriginCode, pMessageBoxA, 5);
    printf("[*] Original code : 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
        OriginCode[0],
        OriginCode[1],
        OriginCode[2],
        OriginCode[3],
        OriginCode[4]
    );

    // 후킹 코드로 패치
    memcpy(pMessageBoxA, HookCode, 5);

    printf("[+] Trampoline Hooking Success!\n");
    printf("Press the any key...");
    getchar();

    // 후킹된 MessageBoxA 함수 호출
    MessageBoxA(NULL, "Not Hooked...!", "Trampoline Hook", 0);

    return 0;
}

Trampoline Hook x86.exe
0.05MB

[+] user32.dll found!
[+] MessageBoxA found!
[*] Changed the top 5 bytes memory protection option in MessageBoxA to ERW--
[+] Calculate relative jump length
[*] Patch code :     0xE9 0x1B 0x09 0xAD 0x8A
[*] Original code :  0x8B 0xFF 0x55 0x8B 0xEC
[+] Trampoline Hooking Success!
Press the any key...

정상적으로 실행되었다면 Press the any key가 출력되었을 때 아무 키를 입력하게 되면 코드와 다르게 Not Hooked...! 가 아닌 Hooked...! 라는 메시지를 출력한다.

반응형
Comments