미친해커

[Reversing] IAT Hooking Step 1 본문

Reversing/Hooking

[Reversing] IAT Hooking Step 1

미친해커 2022. 1. 22. 19:51
반응형

이번 포스팅부터 본격적으로 IAT Hooking에 대해서 알아보도록 하자

 

[Reversing] IAT Hooking Step 0

IAT(Import Address Table) Hooking 이란 IAT에 적혀있는 API의 주소를 조작하여 후킹하는 기법을 말한다. IAT에는 응용 프로그램이 호출하는 API의 함수명, 함수 주소 등이 기록되어 있다. 해당 응용 프로그

crazyhacker.tistory.com

저번 포스팅에서 간단히 PE 구조와 IAT의 구조에 대해서 배웠었다. 이번엔 IAT에서 등록되어 있는 함수들의 이름과 주소를 불러오는 프로그램을 만들어보도록 한다.

Source Code

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

int main(int argc, char *argv[])
{
    // 현재 프로그램의 ImageBase를 가져온다
    ULONGLONG ImageBase = GetModuleHandleA(NULL);

    // ImageBase는 Dos Header의 주소와 같기에 포인터로 연결해준다.
    IMAGE_DOS_HEADER *DOS = ImageBase;
    printf("Image Dos Header : 0x%p\n", DOS);

    // Dos Header로부터 NT Header의 주소를 계산한다.
    IMAGE_NT_HEADERS *NT = ImageBase + DOS->e_lfanew;
    printf("Image Nt Header : 0x%p\n", NT);

    /*
    NT Header에 멤버 변수중 DataDirectory에 Import Descriptor의 주소가 저장되어 있다.
    해당 주소는 VirtualAddress라는 변수에 담겨져 있으며 해당 변수명의 의미는
    변수에 담겨져 있는 주소가 상대주소라는 것을 의미한다. 실제 주소를 계산하기 위해서는
    IamgeBase + VirtualAdress를 해주면 구할 수 있다.
    또 Import Descriptor의 갯수는 미리 알아낼 수 없으며 마지막 Import Descriptor에는
    모든 데이터가 NULL로 설정되어 있다. 그 점을 이용해 마지막 Import Descriptor를
    구할 수 있다.
    */
    IMAGE_IMPORT_DESCRIPTOR (*IMPORT)[1] = ImageBase + NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    printf("Import Address Table : 0x%p\n", IMPORT);
	
    for (int i = 0;; i++)
    {
        // 현재 Import Descriptor가 마지막인지 확인
        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 *);

            // 해당 Thunk Data가 마지막인지 확인
            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);
            }
        }
    }
}

위 소스코드를 컴파일 해 실행시켜보면 다음과 같이 해당 응용 프로그램이 로드하는 DLL 이름과 API 함수 이름들이 출력된다. (출력되는 DLL 이름과 API 함수 이름들은 아래 예시와 다를 수 있다)

[*] Get Module Name : KERNEL32.dll
[+] Function Name : DeleteCriticalSection
[+] Function Name : EnterCriticalSection
-------------중략-------------
[+] Function Name : fprintf
[+] Function Name : free
[+] Function Name : fwrite
[+] Function Name : malloc
[+] Function Name : memcpy
[+] Function Name : printf
[+] Function Name : signal
[+] Function Name : strlen
[+] Function Name : strncmp
[+] Function Name : vfprintf

How can I get IAT Address?

여기서 해당 함수의 주소를 저장하고 있는 IAT의 주소를 구하기 위해서는 한줄만 추가하면 된다.

printf("[+] Function Name : %s\n", IMPORT_NAME->Name);
printf("[+] IAT Address : 0x%p\n", ImageBase + IMPORT[i]->FirstThunk + j * sizeof(void *));

이렇게 밑에 한줄만 추가하면 해당 함수의 주소를 가지고있는 주소를 구할 수 있다.

Description of source code

소스코드에 대한 간단한 설명은 다음 그림을 보고 이해하자.

GetModuleHandleA 함수는 현재 로르된 모듈들 중, 인자로 들어온 모듈 이름의 ImageBase 주소를 반환해준다. 만약 인자로 NULL 을 주면 현재 exe 프로그램의 ImageBase를 반환한다. 그리고 해당 주소는 IMAGE_DOS_HEADER의 주소이다.

typedef struct _IMAGE_DOS_HEADER
{
     WORD e_magic;
     WORD e_cblp;
     WORD e_cp;
     WORD e_crlc;
     WORD e_cparhdr;
     WORD e_minalloc;
     WORD e_maxalloc;
     WORD e_ss;
     WORD e_sp;
     WORD e_csum;
     WORD e_ip;
     WORD e_cs;
     WORD e_lfarlc;
     WORD e_ovno;
     WORD e_res[4];
     WORD e_oemid;
     WORD e_oeminfo;
     WORD e_res2[10];
     LONG e_lfanew;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

IMAGE_DOS_HEADERe_lfanew 멤버 변수의 값과 ImageBase의 주소를 더하면 IMAGE_NT_HEADER의 주소를 구할 수 있다.

 

 IMAGE_NT_HEADER는 x86, x64 이렇게 2가지로 나뉜다. x86과 x64의 큰 차이는 없기 때문에 여기서 설명은 x64 하겠다.

typedef struct _IMAGE_NT_HEADERS64 {
     DWORD                   Signature;
     IMAGE_FILE_HEADER       FileHeader;
     IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
     WORD                 Magic;
     BYTE                 MajorLinkerVersion;
     BYTE                 MinorLinkerVersion;
     DWORD                SizeOfCode;
     DWORD                SizeOfInitializedData;
     DWORD                SizeOfUninitializedData;
     DWORD                AddressOfEntryPoint;
     DWORD                BaseOfCode;
     ULONGLONG            ImageBase;
     DWORD                SectionAlignment;
     DWORD                FileAlignment;
     WORD                 MajorOperatingSystemVersion;
     WORD                 MinorOperatingSystemVersion;
     WORD                 MajorImageVersion;
     WORD                 MinorImageVersion;
     WORD                 MajorSubsystemVersion;
     WORD                 MinorSubsystemVersion;
     DWORD                Win32VersionValue;
     DWORD                SizeOfImage;
     DWORD                SizeOfHeaders;
     DWORD                CheckSum;
     WORD                 Subsystem;
     WORD                 DllCharacteristics;
     ULONGLONG            SizeOfStackReserve;
     ULONGLONG            SizeOfStackCommit;
     ULONGLONG            SizeOfHeapReserve;
     ULONGLONG            SizeOfHeapCommit;
     DWORD                LoaderFlags;
     DWORD                NumberOfRvaAndSizes;
     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_DATA_DIRECTORY {
     DWORD VirtualAddress;
     DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_DIRECTORY_ENTRY_EXPORT          0
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8
#define IMAGE_DIRECTORY_ENTRY_TLS             9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11
#define IMAGE_DIRECTORY_ENTRY_IAT            12
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14

IMAGE_IMPORT_DESCRIPTOR의 주소를 구하는 방법은 다음 코드와 같다.

IMAGE_NT_HEADER *NT;
IMAGE_IMPORT_DESCRIPTOR *IMPORT = ImageBase + NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

IMAGE_OPTIONAL_HEADER의 멤버 변수인 IMAGE_DATA_DIRECTORY의 첫번째 인덱스의
VirtualAddress + ImageBase를 계산하면 IMAGE_IMPORT_DESCRIPTOR의 주소가 나온다.

여기서 IMAGE_IMPORT_DESCRIPTOR를 포인터 배열로 선언한 이유는 n개의 갯수가 존재하고 마지막 인덱스에는 모든 값이 0으로 초기화되어 있다. 이것을 기준으로 유효한 개체만 찾으면 된다. 이 외에 INTIAT가 존재하는데 각각 Import Name TableImport Address Table이라 불린다. 이에 대한건 소스코드와 저번 포스팅에서 설명했기 때문에 여기서의 설명은 생략하도록 하겠다.

 

후킹할 함수를 찾고 해당 함수의 IAT 주소를 구해서 후킹 함수의 주소로 변경한다. 그러면 IAT Hooking 성공이다. 다음 포스팅에서 그림으로 보다 쉽게 설명하도록 하겠다.

반응형
Comments