如何不在编码时直接导入相关 API 的前提下进行攻击

从攻击者的角度来看,不在编码时直接导入相关 API 是一个核心的规避手段。这种技术通常被称为动态 API 调用运行时 API 解析,其主要目的是:

  1. 绕过签名检测: 传统的杀毒软件和安全工具会扫描可执行文件中的导入表。如果导入表里有 CreateRemoteThreadWriteProcessMemoryLoadLibrary 等高危函数,文件就会被标记为可疑。动态调用 API 可以让导入表看起来非常“干净”,从而躲过静态扫描
  2. 增加逆向分析难度: 逆向工程师通常会从导入表入手,快速了解程序的功能。如果导入表是空的或只导入了少数几个基础函数,逆向分析师就必须花费大量时间去跟踪程序的运行时行为,才能发现其真正意图
  3. 支持多操作系统版本和架构: 有些 API 的地址在不同版本的 Windows 上可能会有细微差异。动态获取 API 地址可以确保代码在不同系统上都能正确运行,提高攻击的通用性
  4. 按需加载: 只有在需要执行特定恶意行为时才去获取和调用相应的 API,这可以减少不必要的代码和数据,使恶意程序更小、更精简

下面是几种具体的技术实现,从初级到高级:

1. 使用 LoadLibraryGetProcAddress

这是最基础、最常见的方法。攻击者只需要在代码中静态导入 LoadLibraryA/WGetProcAddress 这两个函数,然后用它们来动态获取所有其他需要的 API

实现步骤:

  1. 加载 DLL: 调用 LoadLibraryA,传入需要加载的 DLL 名称(例如 "kernel32.dll")。这个函数会返回该 DLL 在内存中的基址
  2. 获取函数地址: 调用 GetProcAddress,传入 DLL 的基址和需要获取的函数名称(例如 "CreateRemoteThread")。这个函数会返回该 API 的内存地址
  3. 函数指针调用: 将获取到的地址赋值给一个函数指针,然后通过这个指针像调用普通函数一样来调用它
// 伪代码
#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;
}

这种方法虽然简单,但 LoadLibraryGetProcAddress 依然会出现在程序的导入表中,因此安全软件仍然可以进行识别

2. 手动解析 PEB

这是更高级、更隐蔽的方法。其核心是完全不依赖任何静态导入,通过手动遍历内存中的数据结构来找到所需的 API 地址

实现步骤:

  1. 获取 PEB 地址: 在 32 位系统上,PEB 的地址可以通过 FS:[0x30] 寄存器来获取;在 64 位系统上,可以通过 GS:[0x60] 来获取
  2. 遍历 PEB LDR 数据结构: PEB 结构体中包含一个指向已加载模块列表的指针(LDR_DATA)。攻击者可以遍历这个列表,找到 ntdll.dllkernel32.dll 等已加载的 DLL 模块
  3. 解析 DLL 的导出表(EAT): 找到目标 DLL 的基址后,手动解析其导出地址表 (EAT)。EAT 是一个包含所有导出函数名称和地址的结构
  4. 哈希值匹配: 为了避免在代码中硬编码函数名称字符串(字符串会暴露恶意意图),攻击者通常会为每个函数名称计算一个哈希值。遍历 EAT 中的函数名称,计算哈希值,然后与预设的目标哈希值进行匹配。如果匹配成功,就找到了所需 API 的地址
  5. 函数指针调用: 获取到地址后,同样通过函数指针进行调用
Copyright © 版权信息 all right reserved,powered by Gitbook该文件修订时间: 2025-09-25 03:13:04

results matching ""

    No results matching ""