【免杀技术】WinApi规避 - 字符串HASH与动态调用 (完美的IAT隐藏技术)
字数 1511 2025-08-22 12:23:30
WinAPI规避技术:字符串HASH与动态调用实现完美IAT隐藏
1. 技术概述
本文介绍一种高级的WinAPI规避技术,通过字符串HASH与动态调用相结合的方式实现完美的IAT(Import Address Table)隐藏。该技术不仅能有效规避杀毒软件的静态分析,还能避免敏感信息在可执行文件中暴露。
2. 技术核心组件
2.1 CRT库删除
必要性:
- 标准CRT库会引入大量不必要的API调用(如GetModuleHandle和GetProcAddress)
- CRT库会残留大量字符串硬编码在可执行文件中
- 删除CRT可减少被溯源的风险
Visual Studio编译参数设置:
/GS- /Zl /Oy- /Oi /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /MT /W3 /nologo /c /Zi /Fd"Debug\vc141.pdb" /errorReport:none
影响:
- 标准库函数如memcpy、memset、printf等将无法使用
- 需要自行实现这些功能或寻找替代API
示例:自实现memset
void* __cdecl my_memset(void* Destination, int Value, size_t Size) {
unsigned char* p = (unsigned char*)Destination;
while (Size > 0) {
*p = (unsigned char)Value;
p++;
Size--;
}
return Destination;
}
2.2 自定义HASH算法
HASH算法设计:
#define INITIAL_HASH 520
#define SEED 6
// ANSI字符串版本
DWORD HashStringLoseLoseA(_In_ PCHAR String) {
ULONG Hash = INITIAL_HASH;
INT c;
while (c = *String++) {
Hash += c;
Hash *= c + SEED; // 更新哈希值
}
return Hash;
}
// 宽字符版本
DWORD64 HashStringLoseLoseW(_In_ PWCHAR String) {
ULONG Hash = INITIAL_HASH;
INT c;
while (c = *String++) {
Hash += c;
Hash *= c + SEED;
}
return Hash;
}
特点:
- INITIAL_HASH和SEED可自定义
- 算法简单高效,适合运行时计算
- 提供ANSI和Unicode两个版本
3. 核心API自实现
3.1 自实现GetModuleHandleH
功能:通过HASH值而非明文字符串查找模块基址
实现代码:
HMODULE GetModuleHandleH(DWORD dwModuleNameHash) {
if (dwModuleNameHash == NULL) return NULL;
// 64位下PEB,进程环境块
PPEB pPeb = (PPEB)(__readgsqword(0x60));
// 获取LDR
PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr);
// 获取链表第一个元素(包含第一个模块的信息)
PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->InMemoryOrderModuleList.Flink);
while (pDte) {
if (pDte->FullDllName.Length != NULL) {
// 比较HASH值而非明文字符串
if (HashStringLoseLoseW(pDte->FullDllName.Buffer) == dwModuleNameHash) {
return pDte->Reserved2[0];
}
} else {
break;
}
pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte);
};
return NULL;
}
关键点:
- 通过PEB结构遍历已加载模块
- 使用
__readgsqword(0x60)获取PEB地址(x64架构) - 比较模块名的HASH值而非明文字符串
3.2 自实现GetProcAddressH
功能:通过HASH值查找导出函数地址
实现代码:
FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) {
if (hModule == NULL || dwApiNameHash == NULL) return NULL;
PBYTE pBase = (PBYTE)hModule;
PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;
if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
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]]);
// 比较函数名的HASH值
if (dwApiNameHash == HashStringLoseLoseA(pFunctionName)) {
return pFunctionAddress;
}
}
return NULL;
}
关键点:
- 解析PE文件结构定位导出表
- 遍历导出函数名称并计算HASH
- 匹配HASH值返回对应函数地址
4. 技术使用示例
4.1 HASH值计算
在使用前需要预先计算API名称和DLL名称的HASH值,可以编写一个简单的计算工具:
// 示例:计算MessageBoxA的HASH
DWORD hashMessageBoxA = HashStringLoseLoseA("MessageBoxA");
DWORD hashUser32 = HashStringLoseLoseW(L"user32.dll");
printf("MessageBoxA hash: 0x%X\n", hashMessageBoxA);
printf("user32.dll hash: 0x%X\n", hashUser32);
4.2 动态调用示例
// 定义函数类型
typedef int (WINAPI* MessageBoxAType)(HWND, LPCSTR, LPCSTR, UINT);
void ShowMessage() {
// 获取模块句柄
HMODULE hUser32 = GetModuleHandleH(hashUser32);
if (!hUser32) return;
// 获取函数地址
MessageBoxAType pMessageBoxA = (MessageBoxAType)GetProcAddressH(hUser32, hashMessageBoxA);
if (!pMessageBoxA) return;
// 调用函数
pMessageBoxA(NULL, "Hello", "World", MB_OK);
}
5. 技术优势分析
5.1 IAT表隐藏效果
- 完全消除了IAT表中的API名称
- 没有显式的GetModuleHandle/GetProcAddress调用
- 静态分析工具无法识别导入的函数
5.2 字符串隐藏效果
- 可执行文件中不包含API名称和DLL名称的明文字符串
- 所有敏感字符串都以HASH形式存在
- 极大增加了逆向工程难度
5.3 反检测能力
- 规避基于字符串特征的静态检测
- 规避基于IAT表的导入函数分析
- 规避常见的API调用模式检测
6. 技术局限性
-
兼容性问题:
- 不同Windows版本PEB/LDR结构可能有差异
- 需要针对不同架构(x86/x64)调整偏移量
-
开发复杂度:
- 需要预先计算所有API的HASH值
- 缺少标准库支持,需要自行实现基础功能
-
调试难度:
- 错误难以追踪和调试
- 需要额外的日志机制辅助开发
7. 进阶优化建议
-
HASH算法增强:
- 使用更复杂的HASH算法(如MurmurHash)
- 增加随机种子提高混淆效果
-
延迟加载:
- 仅在需要时加载DLL
- 使用异常处理机制应对加载失败
-
反调试措施:
- 结合反调试技术保护关键代码
- 动态修改HASH算法参数
-
代码混淆:
- 结合控制流混淆技术
- 动态生成部分代码
8. 总结
本文介绍的WinAPI规避技术通过结合字符串HASH与动态调用,实现了完美的IAT隐藏效果。该技术核心在于:
- 完全移除CRT库依赖
- 自实现关键API函数(GetModuleHandleH/GetProcAddressH)
- 使用HASH值替代明文字符串
- 通过PEB遍历实现模块查找
这种技术能有效规避杀毒软件的静态分析,显著提高恶意代码的隐蔽性,但同时也会增加开发和维护的复杂度。在实际应用中,建议根据具体需求进行适当调整和优化。