一、介绍
导入地址表 (IAT) 包含有关 PE 文件的信息,例如使用过的函数和导出它们的 DLL。此类信息可用于对二进制文件进行签名和检测,如下图所示PE 文件导入被认为高度可疑的函数
二、隐藏混淆方法
(1)IAT 隐藏和混淆—方法 1 自定义函数
可以在运行时使用 GetProcAddress
、GetModuleHandle
或 LoadLibrary
动态加载这些函数。下面的代码段将动态加载 VirtualAllocEx
,因此在检查时它不会出现在 IAT 中
typedef LPVOID (WINAPI* fnVirtualAllocEx)(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); fnVirtualAllocEx pVirtualAllocEx = GetProcAddress(GetModuleHandleA("KERNEL32.DLL"), "VirtualAllocEx");
但该方法有个缺点,VirtualAllocEx
字符串存在于二进制文件中,GetProcAddress
和 GetModuleHandleA
会出现在 IAT 中。
(2)IAT 隐藏和混淆 - 方法 2 自定义GetProcAddress
通过解析PE结构当中的导出表获取函数的地址,代码当中
FunctionNameArray:包含函数名称的地址数组。
FunctionAddressArray:包含函数地址的数组。
FunctionOrdinalArray:包含每个函数的序号。
对于每个函数,首先获取它的名字(通过 FunctionNameArray)。
然后通过它的序号在 FunctionOrdinalArray 中找到它的序号。
使用序号在 FunctionAddressArray 中查找它的地址。
#include <windows.h> #include <iostream> PVOID GetProcAddressReplacement(IN HMODULE hModule, IN LPCSTR lpApiName) { // 这样做是为了避免每次使用 hModule 时进行强制转换 PBYTE pBase = (PBYTE)hModule; // 获取 DOS 头并进行签名检查 PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase; if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE) return NULL; // 获取 NT 头并进行签名检查 PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew); if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE) return NULL; // 获取可选头 IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader; // 获取映像导出表 PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取函数名数组指针 PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->AddressOfNames); // 获取函数地址数组指针 PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->AddressOfFunctions); // 获取函数序号数组指针 PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->AddressOfNameOrdinals); // 遍历所有导出的函数 for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) { // 获取函数名 CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]); // 通过其序号获取函数地址 PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[FunctionOrdinalArray[i]]); // 查找指定的函数 if (strcmp(lpApiName, pFunctionName) == 0) { // printf("[ %0.4d ] FOUND API -\t NAME: %s -\t ADDRESS: 0x%p -\t ORDINAL: %d\n", i, pFunctionName, pFunctionAddress, FunctionOrdinalArray[i]); return pFunctionAddress; } } return NULL; } int main() { LPVOID lpadder = GetProcAddressReplacement(GetModuleHandleA("ntdll.DLL"), "NtAllocateVirtualMemory"); printf("GetProcAddressReplacement is: %p\n", lpadder); LPVOID lpPadder = GetProcAddress(GetModuleHandleA("ntdll.DLL"), "NtAllocateVirtualMemory"); printf("GetProcAddress is: %p\n", lpPadder); }
(3)IAT 隐藏和混淆-自定义 GetModuleHandle
GetModuleHandle
函数获取指定 DLL 的句柄。此函数返回 DLL 的句柄,如果调用进程中不存在此 DLL,则返回 NULL
。
HMODULE
数据类型是加载的 DLL 的基地址,表示 DLL 在进程地址空间中的位置。可以利用进程环境块 (PEB) 获取加载的 DLL 相关的信息,特别是 PEB 结构的 PEB_LDR_DATA Ldr
成员。因此,第一步是通过 PEB 结构访问此成员。
在 64 位系统中获取 PEB
64 位系统中的 PEB结构的指针位于线程环境块 (TEB) 结构中
可以使用 Visual Studio 中的 __readgsqword(0x60)宏从 GS 寄存器读取 0x60
字节)来直接获取 PEB 结构
PPEB pPeb2 = (PPEB)(__readgsqword(0x60));
32 位系统中的 PEB
在 32 位系统中,指向 TEB 结构的偏移量存储在 FS
寄存器中
PPEB pPeb2 = (PPEB)(__readfsdword(0x30));标签:混淆,pBase,函数,DLL,获取,IAT,隐藏,PEB From: https://www.cnblogs.com/websecyw/p/18683543