IAT 隐藏和混淆

一、介绍

导入地址表 (IAT) 包含有关 PE 文件的信息,例如使用过的函数和导出它们的 DLL。此类信息可用于对二进制文件进行签名和检测,如下图所示PE 文件导入被认为高度可疑的函数

二、隐藏混淆方法

(1)IAT 隐藏和混淆—方法 1 自定义函数

  可以在运行时使用 GetProcAddressGetModuleHandle 或 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

请登录后发表评论

    没有回复内容