미친해커

[Reversing] IAT Hooking Step 2 본문

Reversing/Hooking

[Reversing] IAT Hooking Step 2

미친해커 2022. 2. 3. 15:05
반응형

이번에는 본격적으로 IAT 후킹을 시도할 생각이다. 우리가 저번 포스팅에서 해당 함수의 IAT 주소를 구하는 부분까지 프로그래밍을 했었다. 그렇다면 이제 남은 단계는 딱 한가지이다. 해당 주소에 있는 주소 값을 후킹 함수의 주소로 바꾸면 후킹이 완료된다.

 

후킹을 하기 전에 후킹(제어권을 가로챌) 함수를 먼저 만들어야한다. 포스팅에서는 MessageBoxA 라는 함수를 대상으로 후킹을 할 것이다.

 

함수를 정했다면 그 함수의 호출 인자에 대한 구조를 알고 있어야 한다. (반드시 알아야 할 필요는 없다) MessageBoxA의 대한 정보는 MSDN에서 찾을 수 있다.

 

MessageBoxA function (winuser.h) - Win32 apps

Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message, such as status or error information. The message box returns an integer value that indicates which button the user clicked.

docs.microsoft.com

위 링크를 보고 함수의 인자들을 파악한 뒤 다음과 같이 프로그래밍한다.

IAT Hooking.zip
0.02MB

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

int (WINAPI *OriMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
    return OriMessageBoxA(hWnd, "Hook", "Hook", uType);
}

int main(int argc, char *argv[])
{
    MessageBoxA(NULL, "Not Hooked!", "Not Hooked!", 0);
    ULONGLONG ImageBase = GetModuleHandleA(NULL);

    IMAGE_DOS_HEADER *DOS = ImageBase;
    printf("Image Dos Header : 0x%p\n", DOS);

    IMAGE_NT_HEADERS *NT = ImageBase + DOS->e_lfanew;
    printf("Image Nt Header : 0x%p\n", NT);

    IMAGE_IMPORT_DESCRIPTOR (*IMPORT)[1] = ImageBase + NT->OptionalHeader.DataDirectory[1].VirtualAddress;
    printf("Import Address Table : 0x%p\n", IMPORT);

    for (int i = 0;; i++)
    {
        if (IMPORT[i]->OriginalFirstThunk == NULL)
            break;

        printf("[*] Get Module Name : %s\n", ImageBase + IMPORT[i]->Name);

        for (int j = 0;; j++)
        {
            IMAGE_THUNK_DATA *THUNK = ImageBase + IMPORT[i]->OriginalFirstThunk + j * sizeof(void *);

            if (THUNK->u1.AddressOfData == NULL)
            {
                break;
            }

            if (THUNK->u1.Ordinal < 0x80000000)
            {
                IMAGE_IMPORT_BY_NAME *IMPORT_NAME = ImageBase + THUNK->u1.AddressOfData;
                printf("[+] Function Name : %s\n", IMPORT_NAME->Name);
                printf("[+] IAT Address : 0x%p\n", ImageBase + IMPORT[i]->FirstThunk + j * sizeof(void *));

                if (strcmp(IMPORT_NAME->Name, "MessageBoxA") == 0)
                {
                    OriMessageBoxA = *(ULONG_PTR *)(ImageBase + IMPORT[i]->FirstThunk + j * sizeof(void *));
                    *(ULONG_PTR *)(ImageBase + IMPORT[i]->FirstThunk + j * sizeof(void *)) = MyMessageBoxA;
                    goto hook;
                }
            }
        }
    }
hook:
    printf("MessageBoxA is hooked!\n");
    printf("Please press the any key...");
    getchar();
    MessageBoxA(NULL, "Not Hooked!", "Not Hooked!", 0);
}

위 코드를 보면 저번 코드와 거의 유사하지만 몇가지 다른 점이 존재한다.  MyMessageBoxAOriMessageBoxA가 선언되어 있다.

 

 

MyMessageBoxA는 후킹 함수가 된다. MessageBoxA가 호출되면 해당 함수가 호출되지 않고 후킹 함수가 호출되게 된다.

OriMessageBoxAMessageBoxA 함수의 주소를 저장하는 함수 포인터다. 왜냐하면 MyMessageBoxA가 MessageBoxA 대신 호출되고나서 MessageBoxA를 호출한다면 IAT를 참조하여 호출하기 떄문에 자기 자신이 무한적으로 호출되며 프로그램이 DeadLock 상태에 걸리게 된다. 이러한 현상을 막고 MyMessageBoxA에서 원본 함수를 호출하기 위해서 IAT를 사용하지 않고 MessageBoxA 함수의 주소를 저장하여 IAT를 거치지 않고 함수를 호출하는 것이다.

 

해당 프로그램을 실행하면 처음과 마지막에 "Not Hooked!"라는 메시지 박스가 나오도록 되어있다. 처음에는 "Not Hooked!"로 정상적으로 함수가 호출되지만 MessageBoxA 함수가 후킹되고 MyMessageBoxA가 대신 호출되었을 때 MyMessageBoxA는 "Hook"라는 메시지가 출력되도록 인자를 조작하여 MessageBoxA 함수를 호출한다. 때문에 후킹이 완료된 이후로 IAT를 통해 호출되는 모든 MessageBoxA 함수는 항상 "Hook" 라는 메시지를 출력하게 된다.

반응형
Comments