一、介绍
导入地址表 (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));
来源链接:https://www.cnblogs.com/websecyw/p/18683543










没有回复内容