#include "header.h"
void print_image_import_descriptor(FILE* fp, u_char* buf, operand operand_type)
{
IMAGE_DOS_HEADER* piid_idh = (IMAGE_DOS_HEADER*)buf;
switch (operand_type)
{
unsigned int raw = 0;
case OPERAND_IID32:
{
IMAGE_NT_HEADERS32* piid_inh32 = (IMAGE_NT_HEADERS32*)(buf + piid_idh->e_lfanew);
// IID 구조체 배열의 크기
int piid_size = piid_inh32->OptionalHeader.DataDirectory[1].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소 RVA 값을 RAW로 변환
raw = (int)convert_rva_to_raw(buf, &(piid_inh32->OptionalHeader.DataDirectory[1].VirtualAddress), OPERAND_DWORD);
// IMPORT Directory 파일에서의 주소
printf("IMPORT DESCRIPTOR\t: 0x%X(RVA), 0x%X(RAW)\n\n", piid_inh32->OptionalHeader.DataDirectory[1].VirtualAddress, raw);
// IID 목록 개수
printf("IMPORT DESCRIPTOR count\t: 0x%X(%d)\n\n", piid_size, piid_size);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 실제 주소를 지정
IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)(buf + raw);
// IMPORT DLL 파일들 목록 출력 후 각 IMPORT DLL 파일 속 함수들을 출력하기 위해 다시 초기화해야 하므로 백업
IMAGE_IMPORT_DESCRIPTOR* piid = iid;
// IMPORT DLL 파일들 출력
for (int i = 0; i < piid_size; i++, piid++)
{
// IMAGE_IMPORT_DESCRIPTOR 구조체의 마지막 부분은 0으로 되어 있음
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("Name : [ %s ]\n", buf + raw);
printf("[%08X] - OriginalFirstThunk[%zdbyte]\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->OriginalFirstThunk), piid->OriginalFirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->OriginalFirstThunk));
printf("[%08X] - TimeDateStamp[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->TimeDateStamp), piid->TimeDateStamp);
offset = get_file_offset(fp, sizeof(piid->TimeDateStamp));
printf("[%08X] - ForwarderChain[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->ForwarderChain), piid->ForwarderChain);
offset = get_file_offset(fp, sizeof(piid->ForwarderChain));
printf("[%08X] - Name[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->Name), piid->Name, raw);
offset = get_file_offset(fp, sizeof(piid->Name));
printf("[%08X] - FirstThunk[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n\n", offset, sizeof(piid->FirstThunk), piid->FirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->FirstThunk));
printf("-----------------------------------------------\n\n");
}
// dll 라이브러리들 이름은 출력했으니 각 라이브러리의 함수들의 hint와 이름 출력
printf("-------------- (IMAGE NAME TABLE, IMAGE IAT TABLE) --------------\n\n");
piid = iid;
// IMAGE_IMPORT_DESCRIPTOR 구조체의 크기만큼 반복한다.
for (int i = 0; i < piid_size; i++, piid++)
{
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
// IMPORT 함수가 어떤 dll 라이브러리에 속해있는지 확인하기 위해 라이브러리 이름 출력
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("[%08X] - [***** %s *****]\n\n", raw, buf + raw);
// IMAGE IMPORT Descriptor 구조체의 OriginalFirstThunk 구조체에 있는 값(RVA)은 IMAGE_THUNK_DATA의 멤버 변수의 값이다.
raw = (int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA32* itd_oft32 = (IMAGE_THUNK_DATA32*)(buf + raw);
raw = (int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA32* itd_ft32 = (IMAGE_THUNK_DATA32*)(buf + raw);
// IMAGE THUNK DATA 구조체의 마지막은 0x00000000 값이다.
// 해당 값이 아닐 동안 반복하여 dll 라이브러리 속 함수들 출력
for (; itd_oft32->u1.AddressOfData != 0x00000000; itd_oft32++, itd_ft32++)
{
raw = (int)convert_rva_to_raw(buf, &(itd_oft32->u1.AddressOfData), OPERAND_DWORD);
IMAGE_IMPORT_BY_NAME* iibn32 = (IMAGE_IMPORT_BY_NAME*)(buf + raw);
// name
fseek(fp, raw, SEEK_SET);
offset = get_file_offset(fp, sizeof(iibn32->Hint));
printf("[%08X] - Name : %s()\n", offset, iibn32->Name);
// hint value
offset = get_file_offset(fp, -2);
printf("[%08X] - Hint : 0x%X\n", ftell(fp), iibn32->Hint);
// IAT 영역 출력
fseek(fp, (long)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD), SEEK_SET);
offset = ftell(fp);
printf("[%08X] - IAT : %08X(RVA), %08X(RAW)\n\n", offset, itd_ft32->u1.Function, (unsigned int)convert_rva_to_raw(buf, &(itd_ft32->u1.Function), OPERAND_DWORD));
}
printf("----------------------------------------\n\n");
}
printf("-----------------------------------------------------------------\n\n");
break;
}
case OPERAND_IID64:
{
ULONGLONG ull_raw = 0;
IMAGE_NT_HEADERS64* piid_inh64 = (IMAGE_NT_HEADERS64*)(buf + piid_idh->e_lfanew);
// IID 구조체 배열의 크기
int piid_size = piid_inh64->OptionalHeader.DataDirectory[1].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소 RVA 값을 RAW로 변환
raw = (int)convert_rva_to_raw(buf, &(piid_inh64->OptionalHeader.DataDirectory[1].VirtualAddress), OPERAND_DWORD);
// IMPORT Directory 파일에서의 주소
printf("IMPORT DESCRIPTOR\t: 0x%X(RVA), 0x%X(RAW)\n\n", piid_inh64->OptionalHeader.DataDirectory[1].VirtualAddress, raw);
// IID 목록 개수
printf("IMPORT DESCRIPTOR count\t: 0x%X(%d)\n\n", piid_size, piid_size);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 실제 주소를 지정
IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)(buf + raw);
// IMPORT DLL 파일들 목록 출력 후 각 IMPORT DLL 파일 속 함수들을 출력하기 위해 다시 초기화해야 하므로 백업
IMAGE_IMPORT_DESCRIPTOR* piid = iid;
// IMPORT DLL 파일들 출력
for (int i = 0; i < piid_size; i++, piid++)
{
// IMAGE_IMPORT_DESCRIPTOR 구조체의 마지막 부분은 0으로 되어 있음
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("Name : [ %s ]\n", buf + raw);
printf("[%08X] - OriginalFirstThunk[%zdbyte]\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->OriginalFirstThunk), piid->OriginalFirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->OriginalFirstThunk));
printf("[%08X] - TimeDateStamp[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->TimeDateStamp), piid->TimeDateStamp);
offset = get_file_offset(fp, sizeof(piid->TimeDateStamp));
printf("[%08X] - ForwarderChain[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->ForwarderChain), piid->ForwarderChain);
offset = get_file_offset(fp, sizeof(piid->ForwarderChain));
printf("[%08X] - Name[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->Name), piid->Name, raw);
offset = get_file_offset(fp, sizeof(piid->Name));
printf("[%08X] - FirstThunk[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n\n", offset, sizeof(piid->FirstThunk), piid->FirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->FirstThunk));
printf("-----------------------------------------------\n\n");
}
// dll 라이브러리들 이름은 출력했으니 각 라이브러리의 함수들의 hint와 이름 출력
printf("-------------- (IMAGE NAME TABLE, IMAGE IAT TABLE) --------------\n\n");
piid = iid;
// IMAGE_IMPORT_DESCRIPTOR 구조체의 크기만큼 반복한다.
for (int i = 0; i < piid_size; i++, piid++)
{
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
// IMPORT 함수가 어떤 dll 라이브러리에 속해있는지 확인하기 위해 라이브러리 이름 출력
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("[%08X] - [***** %s *****]\n\n", raw, buf + raw);
// IMAGE IMPORT Descriptor 구조체의 OriginalFirstThunk 구조체에 있는 값(RVA)은 IMAGE_THUNK_DATA의 멤버 변수의 값이다.
raw = (int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA64* itd_oft64 = (IMAGE_THUNK_DATA64*)(buf + raw);
raw = (int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA64* itd_ft64 = (IMAGE_THUNK_DATA64*)(buf + raw);
// IMAGE THUNK DATA 구조체의 마지막은 0x00000000 값이다.
// 해당 값이 아닐 동안 반복하여 dll 라이브러리 속 함수들 출력
for (; itd_oft64->u1.AddressOfData != 0x00000000; itd_oft64++, itd_ft64++)
{
ull_raw = (ULONGLONG)convert_rva_to_raw(buf, &(itd_oft64->u1.AddressOfData), OPERAND_ULONGLONG);
IMAGE_IMPORT_BY_NAME* iibn64 = (IMAGE_IMPORT_BY_NAME*)(buf + ull_raw);
// name
_fseeki64(fp, ull_raw, SEEK_SET);
offset = get_file_offset(fp, sizeof(iibn64->Hint));
printf("[%08X] - Name : %s()\n", offset, iibn64->Name);
// hint value
offset = get_file_offset(fp, -2);
printf("[%08X] - Hint : %X\n", ftell(fp), iibn64->Hint);
// IAT 영역 출력
_fseeki64(fp, convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD), SEEK_SET);
offset = ftell(fp);
printf("[%08X] - IAT : %llX(RVA), %llX(RAW)\n\n", offset, itd_ft64->u1.Function, convert_rva_to_raw(buf, &(itd_ft64->u1.Function), OPERAND_DWORD));
}
printf("----------------------------------------\n\n");
}
printf("-----------------------------------------------------------------\n\n");
break;
}
}
}
매개변수
void print_image_import_descriptor(FILE* fp, u_char* buf, operand operand_type)
인자로 파일 포인터, 파일의 내용이 담긴 동적 메모리 주소, 32bit 모드인지 64bit 모드인지에 따른 정의된 값을 받는다.
정의된 값은 header.h 파일에 선언되어 있다.
IMAGE_IMPORT_DESCRIPTOR 구조체 정의
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
IMAGE_IMPORT_DESCRIPTOR 구조체는 winnt.h 파일에 위와 같이 되어 있다.
일반적인 프로그램에서는 보통 여러 개의 라이브러리를 Import 하기 때문에 라이브러리의 개수만큼 위 구조체의 배열 형식으로 존재하며, 구조체 배열의 마지막은 NULL 구조체로 끝나게 된다.
typedef struct _IMAGE_THUNK_DATA32
{
union
{
DWORD ForwarderString;// PBYTE
DWORD Function;// PDWORD
DWORD Ordinal;
DWORD AddressOfData;// PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef struct _IMAGE_THUNK_DATA64
{
union
{
ULONGLONG ForwarderString;// PBYTE
ULONGLONG Function;// PDWORD
ULONGLONG Ordinal;
ULONGLONG AddressOfData;// PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;
/*
ForwarderString : 실제 Import 한 함수가 Forwarding된 함수일 경우. Forwarding에 대해서는 Export에서 설명
Function : FirstThunk의 경우 메모리에 로딩이 되면 실제 함수 주소를 가리키는 IMAGE_THUNK_DATA의 RVA값을 표시한다고 했는데, 이때 실제 Function의 주소가 포함된 부분
Ordinal : Import를 함수 명이 아니라 서수(Ordinal)로 한 경우. 0x80000000 값으로 마스크하면 최 상위 비트를 구할 수 있는데 이것이 1로 셋팅된 경우 서수로 판단
AddressOfData : 실제 Import 된 함수의 이름이 포함된 IMAGE_IMPORT_BY_NAME 구조체에 대한 RVA 주소 포함
*/
위의 IMAGE_IMPORT_DESCRIPTOR 구조체의 멤버 OriginalFirstThunk와 FirstThunk 멤버의 값을 RAW로 변환하여 해당 위치로 가면 위의 IMAGE_THUNK_DATA 구조체 형식으로 되어 있다.
IMAGE_THUNK_DATA 구조체의 멤버는 union 공용체 형태로, 가장 큰 자료형의 크기만큼 할당하고 그 메모리를 공유해서 사용하는데, 상황에 따라 한 개의 변수가 4가지 경우로 해석된다고 보면 된다.
typedef struct _IMAGE_IMPORT_BY_NAME
{
WORD Hint;// ordinal
BYTE Name[1];// function name string
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
위의 IMAGE_THUNK_DATA 구조체의 값을 RAW로 변환하여 해당 위치로 가면 위의 IMAGE_IMPORT_BY_NAME 구조체 형식으로 되어있다.
주요 구조체 멤버
- OriginalFirstThunk : INT(Import Name Table)의 주소(RVA)
- Name : Library 이름 문자열의 주소(RVA)
- FirstThunk : IAT(Import Address Table)의 주소(RVA)
PE 헤더에서 ‘Table’이라고 하면 ‘배열’을 뜻한다.
INT와 IAT는 long type(4byte 자료형)배열이고, NULL로 끝난다. (IAT도 같은 값을 가지는 경우가 있다.)
INT와 IAT의 크기는 같아야 한다.
IAT 입력 순서
아래의 사진에서는 INT와 IAT의 각 원소가 동시에 같은 주소를 가리키지만 그렇지 않은 경우도 많다.
PE 로더가 Import 함수 주소를 IAT에 입력하는 기본적인 순서를 설명하면 아래와 같다.
- IID의 Name 멤버를 읽어서 라이브러리의 이름 문자열(”kernel32.dll”)을 얻는다.
- 해당 라이브러리를 로딩한다. → LoadLibrary(”Kernel32.dll”)
- IID의 OriginalFirst Thunk 멤버를 읽어서 INT 주소를 얻는다.
- INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻는다.
- IMAGE_IMPORT_BY_NAME의 Hint(ordinal) 또는 Name 항목을 이용하여 해당 함수의 시작 주소를 얻는다. → GetProcAddress(”GetCurretThreadId”)
- IID의 FirstThunk(IAT) 멤버를 읽어서 IAT 주소를 얻는다.
- 해당 IAT 배열 값에 위 5번에서 구한 함수 주소를 입력한다.
- INT가 NULL을 만나 끝날 때까지 4 ~ 7 과정을 반복한다.
실제 IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 위치
실제 IMAGE_IMPORT_DESCRIPTOR 구조체 배열은 PE 파일의 PE 헤더가 아닌 PE 바디에 위치한다.
그곳을 찾아가기 위한 정보는 PE 헤더에 있는데, IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress 값이 실제 IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소이다.(RVA)
출력 코드
IMAGE_NT_HEADERS32* piid_inh32 = (IMAGE_NT_HEADERS32*)(buf + piid_idh->e_lfanew);
// IID 구조체 배열의 크기
int piid_size = piid_inh32->OptionalHeader.DataDirectory[1].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소 RVA 값을 RAW로 변환
raw = (int)convert_rva_to_raw(buf, &(piid_inh32->OptionalHeader.DataDirectory[1].VirtualAddress), OPERAND_DWORD);
// IMPORT Directory 파일에서의 주소
printf("IMPORT DESCRIPTOR\t: 0x%X(RVA), 0x%X(RAW)\n\n", piid_inh32->OptionalHeader.DataDirectory[1].VirtualAddress, raw);
// IID 목록 개수
printf("IMPORT DESCRIPTOR count\t: 0x%X(%d)\n\n", piid_size, piid_size);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 실제 주소를 지정
IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)(buf + raw);
// IMPORT DLL 파일들 목록 출력 후 각 IMPORT DLL 파일 속 함수들을 출력하기 위해 다시 초기화해야 하므로 백업
IMAGE_IMPORT_DESCRIPTOR* piid = iid;
// IMPORT DLL 파일들 출력
for (int i = 0; i < piid_size; i++, piid++)
{
// IMAGE_IMPORT_DESCRIPTOR 구조체의 마지막 부분은 0으로 되어 있음
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("Name : [ %s ]\n", buf + raw);
printf("[%08X] - OriginalFirstThunk[%zdbyte]\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->OriginalFirstThunk), piid->OriginalFirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->OriginalFirstThunk));
printf("[%08X] - TimeDateStamp[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->TimeDateStamp), piid->TimeDateStamp);
offset = get_file_offset(fp, sizeof(piid->TimeDateStamp));
printf("[%08X] - ForwarderChain[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->ForwarderChain), piid->ForwarderChain);
offset = get_file_offset(fp, sizeof(piid->ForwarderChain));
printf("[%08X] - Name[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->Name), piid->Name, raw);
offset = get_file_offset(fp, sizeof(piid->Name));
printf("[%08X] - FirstThunk[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n\n", offset, sizeof(piid->FirstThunk), piid->FirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->FirstThunk));
printf("-----------------------------------------------\n\n");
}
// dll 라이브러리들 이름은 출력했으니 각 라이브러리의 함수들의 hint와 이름 출력
printf("-------------- (IMAGE NAME TABLE, IMAGE IAT TABLE) --------------\n\n");
piid = iid;
// IMAGE_IMPORT_DESCRIPTOR 구조체의 크기만큼 반복한다.
for (int i = 0; i < piid_size; i++, piid++)
{
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
// IMPORT 함수가 어떤 dll 라이브러리에 속해있는지 확인하기 위해 라이브러리 이름 출력
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("[%08X] - [***** %s *****]\n\n", raw, buf + raw);
// IMAGE IMPORT Descriptor 구조체의 OriginalFirstThunk 구조체에 있는 값(RVA)은 IMAGE_THUNK_DATA의 멤버 변수의 값이다.
raw = (int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA32* itd_oft32 = (IMAGE_THUNK_DATA32*)(buf + raw);
raw = (int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA32* itd_ft32 = (IMAGE_THUNK_DATA32*)(buf + raw);
// IMAGE THUNK DATA 구조체의 마지막은 0x00000000 값이다.
// 해당 값이 아닐 동안 반복하여 dll 라이브러리 속 함수들 출력
for (; itd_oft32->u1.AddressOfData != 0x00000000; itd_oft32++, itd_ft32++)
{
raw = (int)convert_rva_to_raw(buf, &(itd_oft32->u1.AddressOfData), OPERAND_DWORD);
IMAGE_IMPORT_BY_NAME* iibn32 = (IMAGE_IMPORT_BY_NAME*)(buf + raw);
// name
fseek(fp, raw, SEEK_SET);
offset = get_file_offset(fp, sizeof(iibn32->Hint));
printf("[%08X] - Name : %s()\n", offset, iibn32->Name);
// hint value
offset = get_file_offset(fp, -2);
printf("[%08X] - Hint : 0x%X\n", ftell(fp), iibn32->Hint);
// IAT 영역 출력
fseek(fp, (long)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD), SEEK_SET);
offset = ftell(fp);
printf("[%08X] - IAT : %08X(RVA), %08X(RAW)\n\n", offset, itd_ft32->u1.Function, (unsigned int)convert_rva_to_raw(buf, &(itd_ft32->u1.Function), OPERAND_DWORD));
}
printf("----------------------------------------\n\n");
}
printf("-----------------------------------------------------------------\n\n");
코드 설명은 32bit 모드일 때의 코드로 설명한다.
(64bit 모드일 때의 코드와는 별 차이 없이 중간에 자료형의 크기만 달라질 뿐이다.)
// IID 목록 개수 구하기
int piid_size = piid_inh32->OptionalHeader.DataDirectory[1].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
// IID 목록 개수 출력
printf("IMPORT DESCRIPTOR count\t: 0x%X(%d)\n\n", piid_size, piid_size);
import 하는 라이브러리의 개수를 알기 위해서 optional header의 datadirectory[1]의 멤버 Size 값에 IMAGE_IMPORT_DESCRIPTOR 구조체의 크기를 나누어 구한다.
그리고 개수를 출력해준다.
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소 RVA 값을 RAW로 변환
raw = (int)convert_rva_to_raw(buf, &(piid_inh32->OptionalHeader.DataDirectory[1].VirtualAddress), OPERAND_DWORD);
// IMPORT Directory 파일에서의 주소
printf("IMPORT DESCRIPTOR\t: 0x%X(RVA), 0x%X(RAW)\n\n", piid_inh32->OptionalHeader.DataDirectory[1].VirtualAddress, raw);
// IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 실제 주소를 지정
IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)(buf + raw);
// IMPORT DLL 파일들 목록 출력 후 각 IMPORT DLL 파일 속 함수들을 출력하기 위해 다시 초기화해야 하므로 백업
IMAGE_IMPORT_DESCRIPTOR* piid = iid;
IMAGE_IMPORT_DESCRIPTOR의 시작 오프셋 값을 구해 출력하고, 해당 시작 주소를 백업해둔다.
이유는 import 하는 라이브러리의 목록을 출력하고 나서 다시 import 하는 첫 라이브러리에서 함수들의 목록을 출력해야 하기 때문이다.
// IMPORT DLL 파일들 출력
for (int i = 0; i < piid_size; i++, piid++)
{
// IMAGE_IMPORT_DESCRIPTOR 구조체의 마지막 부분은 0으로 되어 있음
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("Name : [ %s ]\n", buf + raw);
printf("[%08X] - OriginalFirstThunk[%zdbyte]\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->OriginalFirstThunk), piid->OriginalFirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->OriginalFirstThunk));
printf("[%08X] - TimeDateStamp[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->TimeDateStamp), piid->TimeDateStamp);
offset = get_file_offset(fp, sizeof(piid->TimeDateStamp));
printf("[%08X] - ForwarderChain[%zdbyte]\t: %08X(RVA)\n", offset, sizeof(piid->ForwarderChain), piid->ForwarderChain);
offset = get_file_offset(fp, sizeof(piid->ForwarderChain));
printf("[%08X] - Name[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n", offset, sizeof(piid->Name), piid->Name, raw);
offset = get_file_offset(fp, sizeof(piid->Name));
printf("[%08X] - FirstThunk[%zdbyte]\t\t: %08X(RVA), %08X(RAW)\n\n", offset, sizeof(piid->FirstThunk), piid->FirstThunk, (unsigned int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD));
offset = get_file_offset(fp, sizeof(piid->FirstThunk));
printf("-----------------------------------------------\n\n");
}
import 하는 라이브러리들의 정보를 출력한다.
// dll 라이브러리들 이름은 출력했으니 각 라이브러리의 함수들의 hint와 이름 출력
printf("-------------- (IMAGE NAME TABLE, IMAGE IAT TABLE) --------------\n\n");
piid = iid;
// IMAGE_IMPORT_DESCRIPTOR 구조체의 크기만큼 반복한다.
for (int i = 0; i < piid_size; i++, piid++)
{
if (piid->Characteristics == 0x00000000 && piid->OriginalFirstThunk == 0x00000000 && piid->TimeDateStamp == 0x00000000 && piid->ForwarderChain == 0x00000000 && piid->Name == 0x00000000 && piid->FirstThunk == 0x00000000)
break;
// IMPORT 함수가 어떤 dll 라이브러리에 속해있는지 확인하기 위해 라이브러리 이름 출력
raw = (int)convert_rva_to_raw(buf, &(piid->Name), OPERAND_DWORD);
printf("[%08X] - [***** %s *****]\n\n", raw, buf + raw);
// IMAGE IMPORT Descriptor 구조체의 OriginalFirstThunk 구조체에 있는 값(RVA)은 IMAGE_THUNK_DATA의 멤버 변수의 값이다.
raw = (int)convert_rva_to_raw(buf, &(piid->OriginalFirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA32* itd_oft32 = (IMAGE_THUNK_DATA32*)(buf + raw);
raw = (int)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD);
IMAGE_THUNK_DATA32* itd_ft32 = (IMAGE_THUNK_DATA32*)(buf + raw);
// IMAGE THUNK DATA 구조체의 마지막은 0x00000000 값이다.
// 해당 값이 아닐 동안 반복하여 dll 라이브러리 속 함수들 출력
for (; itd_oft32->u1.AddressOfData != 0x00000000; itd_oft32++, itd_ft32++)
{
raw = (int)convert_rva_to_raw(buf, &(itd_oft32->u1.AddressOfData), OPERAND_DWORD);
IMAGE_IMPORT_BY_NAME* iibn32 = (IMAGE_IMPORT_BY_NAME*)(buf + raw);
// name
fseek(fp, raw, SEEK_SET);
offset = get_file_offset(fp, sizeof(iibn32->Hint));
printf("[%08X] - Name : %s()\n", offset, iibn32->Name);
// hint value
offset = get_file_offset(fp, -2);
printf("[%08X] - Hint : 0x%X\n", ftell(fp), iibn32->Hint);
// IAT 영역 출력
fseek(fp, (long)convert_rva_to_raw(buf, &(piid->FirstThunk), OPERAND_DWORD), SEEK_SET);
offset = ftell(fp);
printf("[%08X] - IAT : %08X(RVA), %08X(RAW)\n\n", offset, itd_ft32->u1.Function, (unsigned int)convert_rva_to_raw(buf, &(itd_ft32->u1.Function), OPERAND_DWORD));
}
printf("----------------------------------------\n\n");
}
printf("-----------------------------------------------------------------\n\n");
각 라이브러리의 함수들의 INT와 IAT 값 그리고 hint 값을 출력한다.
'toy project > Reversing' 카테고리의 다른 글
[Toy project] PE Viewer 툴 만들기(9) main.c (0) | 2023.07.28 |
---|---|
[Toy project] PE Viewer 툴 만들기(8) print_ied.c (0) | 2023.07.27 |
[Toy Project] PE Viewer 툴 만들기(6) convert_rva_to_raw.c (0) | 2023.07.26 |
[Toy Project] PE Viewer 툴 만들기(5) print_section_header.c (0) | 2023.07.26 |
[Toy Project] PE Viewer 툴 만들기(4) print_inh_ioh_datadirectory.c (0) | 2023.07.26 |