Files
2025-09-25 04:02:19 +08:00

63 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### 如何不在编码时直接导入相关 API 的前提下进行攻击
从攻击者的角度来看,**不在编码时直接导入相关 API** 是一个核心的规避手段。这种技术通常被称为**动态 API 调用**或**运行时 API 解析**,其主要目的是:
1. **绕过签名检测:** 传统的杀毒软件和安全工具会扫描可执行文件中的**导入表**。如果导入表里有 `CreateRemoteThread``WriteProcessMemory``LoadLibrary` 等高危函数,文件就会被标记为可疑。动态调用 API 可以让导入表看起来非常“干净”,从而躲过静态扫描
2. **增加逆向分析难度:** 逆向工程师通常会从导入表入手,快速了解程序的功能。如果导入表是空的或只导入了少数几个基础函数,逆向分析师就必须花费大量时间去跟踪程序的运行时行为,才能发现其真正意图
3. **支持多操作系统版本和架构:** 有些 API 的地址在不同版本的 Windows 上可能会有细微差异。动态获取 API 地址可以确保代码在不同系统上都能正确运行,提高攻击的通用性
4. **按需加载:** 只有在需要执行特定恶意行为时才去获取和调用相应的 API,这可以减少不必要的代码和数据,使恶意程序更小、更精简
下面是几种具体的技术实现,从初级到高级:
**1. 使用 `LoadLibrary` 和 `GetProcAddress`**
这是最基础、最常见的方法。攻击者只需要在代码中静态导入 `LoadLibraryA/W``GetProcAddress` 这两个函数,然后用它们来动态获取所有其他需要的 API
**实现步骤:**
1. **加载 DLL** 调用 `LoadLibraryA`,传入需要加载的 DLL 名称(例如 "kernel32.dll")。这个函数会返回该 DLL 在内存中的基址
2. **获取函数地址:** 调用 `GetProcAddress`,传入 DLL 的基址和需要获取的函数名称(例如 "CreateRemoteThread")。这个函数会返回该 API 的内存地址
3. **函数指针调用:** 将获取到的地址赋值给一个函数指针,然后通过这个指针像调用普通函数一样来调用它
```c
// 伪代码
#include <windows.h>
#include <stdio.h>
int main() {
HMODULE hKernel32 = LoadLibraryA("kernel32.dll");
if (hKernel32 == NULL) {
// 处理错误
return 1;
}
// 定义一个函数指针类型
typedef HANDLE (WINAPI* CreateRemoteThread_t)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
// 获取 CreateRemoteThread 的地址
CreateRemoteThread_t pCreateRemoteThread = (CreateRemoteThread_t)GetProcAddress(hKernel32, "CreateRemoteThread");
if (pCreateRemoteThread == NULL) {
// 处理错误
return 1;
}
// 现在可以使用 pCreateRemoteThread 来调用 CreateRemoteThread 函数了
// pCreateRemoteThread(..., ..., ...);
return 0;
}
```
这种方法虽然简单,但 `LoadLibrary``GetProcAddress` 依然会出现在程序的导入表中,因此安全软件仍然可以进行识别
**2. 手动解析 PEB**
这是更高级、更隐蔽的方法。其核心是**完全不依赖任何静态导入**,通过**手动遍历内存中的数据结构**来找到所需的 API 地址
**实现步骤:**
1. **获取 PEB 地址:** 在 32 位系统上,PEB 的地址可以通过 `FS:[0x30]` 寄存器来获取;在 64 位系统上,可以通过 `GS:[0x60]` 来获取
2. **遍历 PEB LDR 数据结构:** PEB 结构体中包含一个指向已加载模块列表的指针(`LDR_DATA`)。攻击者可以遍历这个列表,找到 `ntdll.dll``kernel32.dll` 等已加载的 DLL 模块
3. **解析 DLL 的导出表(EAT):** 找到目标 DLL 的基址后,手动解析其**导出地址表 (EAT)**。EAT 是一个包含所有导出函数名称和地址的结构
4. **哈希值匹配:** 为了避免在代码中硬编码函数名称字符串(字符串会暴露恶意意图),攻击者通常会为每个函数名称计算一个哈希值。遍历 EAT 中的函数名称,计算哈希值,然后与预设的目标哈希值进行匹配。如果匹配成功,就找到了所需 API 的地址
5. **函数指针调用:** 获取到地址后,同样通过函数指针进行调用