吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5763|回复: 33
收起左侧

[原创] x64dbg插件无驱动过vmp3.9.4反调试

  [复制链接]
yoyoRev 发表于 2026-4-3 16:14
最近研究了一下vmp3.8 3.9两个版本的反调试,大差不差,有微小差异,过的手法都一致,在此给出3.9的大致流程

3.9反调试流程

3.9反调试流程


高版本vmp使用rdtsc指令进行随机路径的syscall调用,我使用unicorn进行模拟跟踪,遇到rdtsc指令之后,设置硬断到该指令的下一条指令,然后运行调试器到断点处,再次模拟,直至遇到syscall或者api调用,
看一下日志

模拟日志

模拟日志


syscall调用

syscall调用

可以看到遇到syscall之后模拟会停止,此时也会运行调试器到断点处,再利用断点回调删除硬断,此时就可以对syscall进行干预,这就是大致流程

接下来开始对反调试进行分析,首先就是peb,没啥说的,调试有个高级选项,隐藏调试器,也就是清空debugflag还有一个调试堆的标志,然后就可以进行
跟踪,建议直接在ntclose下断(主要不要在头部下断,或者使用硬断),隐藏调试器之后直接在ntclose下断,可以避免一部分不影响调试器的api调用,当然如果
在ntclose下断之后直接弹窗,那么老实进行跟踪,看上图,直接syscall调用 rax是调用号,就知道具体调用的是哪个函数,这里对应的是NtSetInformationProcess,rdx是0x28
问了一下ai,设置这个是为了防止对syscall进行跟踪,如果设置了回调,那么syscall之后回三环的落脚点就是自己设置的回调,不再是下条指令,建议自行尝试复现,我已经进行了
复现,这里不再赘述

接下来调用NtOpenFile 检测\??\TitanHide驱动 由于这个电脑上没有驱动,所以不需要管,有的话尝试置C0000034进行绕过,看图

检测\??\TitanHide驱动

检测\??\TitanHide驱动


此时rax是0x33,通过r8可以找到\??\TitanHide驱动字符串。

接下来第三处NtQueryInformationProcess 查询DebugPort 修改输出参数值,将-1(0xffffffffffffffff)置0绕过

查询DebugPort

查询DebugPort


可以看到0xffffffffffffffff,置0就行

接下来就是第四处 NtQueryInformationProcess 查询DebugObjectHandle rax置C0000353绕过

查询DebugObjectHandle

查询DebugObjectHandle


接下来在又调用了一些api,在rtlfreeheap下断,无需模拟(注意,只针对本程序,我这也是模拟了好几次进行偷懒,其他仍需进行模拟,可以尝试进行在rtlfreeheap下断,检测到就重新模拟就好,多尝试)

看第五处反调试ZwSetInformationThread hidefromdebugger

ZwSetInformationThread hidefromdebugger

ZwSetInformationThread hidefromdebugger


将rdx置0即可,然后就是对壳代码进行自校验,这里最好不要模拟,因为时间比较久,直接在壳代码所在段第一个字节下硬断

壳代码所在段

壳代码所在段


011e2968-dc3a-41a8-b1c7-084811888f59.png

程序中断在这里,此时找jne 也就是下面的指令,有的程序会有call jne jmp啥的混淆,不重要,一定要找对jne,这个jne一定会跳转到movzx esi, byte ptr ds:[rcx] 这里或者上面一两条指令,
在这个jne下一条指令下硬断,即图中mov eax, 0x88BAC81D 这条指令,程序会在这里中断n(具体的n每个程序不一样)次,对n如何进行判断,一直f9,直到弹出检测到调试器窗口即可,
记住n的次数,然后重新开始,本程序在这里中断5次既可,然后继续模拟执行,(也可以不中断,持续模拟执行,但是时间比较长,我这是省时法)与其说省时法,不如说是我踩坑踩出来的


9a537dc1-f31a-4b01-a19d-f9bd90d84df9.png

接下来就是四处检测

NtQueryInformationProcess 查询DebugPort 置0绕过   

ZwSetInformationThread hidefromdebugger rdx置0绕过  

NtClose 关闭无效句柄 rax置C0000008,直接ret绕过(3.8版本则是closehandle,要将rax置0)

触发单步异常 检测硬断

前两处和上面过的方法一致,不再赘述,在执行完ZwSetInformationThread hidefromdebugger之后,3.9直接在ntclose下断。3.8直接在closehandle下断,


ntclose

ntclose

rax置C0000008,将rip改到ret指令就行。然后删除所有硬断,直接f9,程序就运行起来了

程序运行

程序运行


最后给出模拟执行代码,以及插件和demo程序,如果你能成功运行程序,恭喜你,过vmp的反调试已经轻而易举了

让ai写的,觉得不好的可以不使用,大致思路是这样,仅供参考

#include "pch.h"

#include <windows.h>
#include <vector>
#include <string>
#include "pluginsdk/bridgemain.h"
#include "pluginsdk/_plugins.h"
#include "pluginsdk/_scriptapi_module.h"
#include "pluginsdk/_scriptapi_memory.h"
#include "pluginsdk/_scriptapi_debug.h"
#include "unicorn/unicorn.h"

#pragma comment(lib, "unicorn.lib")
#pragma comment(lib, "x64dbg.lib")      // x64dbg 的库(64位)
#pragma comment(lib, "x64bridge.lib")   // x64bridge 的库(64位)

#define MENU_UNICORN_ID 1

int pluginHandle;
duint mainModuleBase = 0;
duint mainModuleSize = 0;
int hMenu;

// ---------------------- 全局控制变量 ----------------------
HANDLE g_hSimulateEvent = NULL;        // 唤醒工作线程的事件
HANDLE g_hSimulateThread = NULL;       // 工作线程句柄
BOOL   g_bStopSimulation = FALSE;      // 是否停止工作线程
BOOL   g_bWaitingForBreakpoint = FALSE;// 是否在等待 rdtsc 断点
duint  g_rdtscNextRip = 0;             // rdtsc 下一条指令地址
int    g_stop_reason = 0;              // 模拟停止原因:1=rdtsc, 2=syscall, 3=out_of_module
BOOL   gsreg = FALSE;
BOOL   api = FALSE;

// ---------------------- Unicorn 钩子函数 ----------------------
static void hook_code(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {

    WORD code = 0;
    uc_mem_read(uc, address, &code, 2);
    if (code == 0x310f) {  // rdtsc
        _plugin_logprintf(u8"rdtsc at address: %llx\n", address);
        g_stop_reason = 1;
        g_rdtscNextRip = address + size;  // 下一条指令地址
        uc_emu_stop(uc);
        return;
    }
    if (code == 0x50f) {   // syscall
        _plugin_logprintf(u8"syscall at address: %llx\n", address);
        g_stop_reason = 2;
        uc_emu_stop(uc);
        return;
    }
    // 如果指令地址超出主模块范围,停止模拟
    if (address < mainModuleBase || address >= (mainModuleBase + mainModuleSize)) {
        _plugin_logprintf(u8"[Unicorn] 停止模拟,交回控制权给 x64dbg。api address:%llx\n", address);
        g_stop_reason = 3;
        uc_emu_stop(uc);
    }
}

static bool hook_mem_unmapped(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data) {
    uint64_t current_rip;
    uc_reg_read(uc, UC_X86_REG_RIP, ¤t_rip);
    _plugin_logprintf(u8"[Unicorn] MEM_UNMAPPED at RIP=0x%llX: type=%d, addr=0x%llX, size=%d\n",
        current_rip, type, address, size);

    duint pageBase = (duint)address & ~0xFFF;
    if (!Script::Memory::IsValidPtr(pageBase)) {
        _plugin_logprintf(u8"[Unicorn] 致命错误:尝试访问无效指针 0x%llX \n", pageBase);
        uc_emu_stop(uc);
        return false;
    }

    _plugin_logprintf(u8"[Unicorn] 触发缺页中断,懒加载映射内存页: 0x%llX\n", pageBase);

    uc_err err = uc_mem_map(uc, pageBase, 0x1000, UC_PROT_ALL);
    if (err != UC_ERR_OK && err != UC_ERR_MAP) {
        _plugin_logprintf(u8"[Unicorn] 内存映射失败: %s\n", uc_strerror(err));
        return false;
    }

    unsigned char pageBuf[0x1000] = { 0 };
    if (Script::Memory::Read(pageBase, pageBuf, 0x1000, nullptr)) {
        uc_mem_write(uc, pageBase, pageBuf, 0x1000);
        return true;
    }
    return false;
}

// ---------------------- 工作线程:执行模拟 ----------------------
DWORD WINAPI SimulationWorker(LPVOID lpParam) {
    while (!g_bStopSimulation) {
        // 等待事件触发
        WaitForSingleObject(g_hSimulateEvent, INFINITE);
        ResetEvent(g_hSimulateEvent);

        // 如果是因为断点唤醒,需要删除之前设置的硬件断点
        if (g_bWaitingForBreakpoint) {
            Script::Debug::DeleteHardwareBreakpoint(g_rdtscNextRip);
            g_bWaitingForBreakpoint = FALSE;
        }

        // 重新初始化 Unicorn 引擎
        uc_engine* uc;
        uc_err err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
        if (err != UC_ERR_OK) {
            _plugin_logprintf(u8"uc_open 失败: %u\n", err);
            continue;
        }

        // 1. 获取主模块范围(每次重新获取,因为调试器可能已加载新模块)
        mainModuleBase = Script::Module::GetMainModuleBase();
        mainModuleSize = Script::Module::SizeFromAddr(mainModuleBase);

        // 2. 映射所有已提交的内存页到 Unicorn
        MEMMAP memoryMap = { 0 };
        DbgMemMap(&memoryMap);
        for (int i = 0; i < memoryMap.count; i++) {
            auto& page = memoryMap.page[i];
            if (page.mbi.State == MEM_COMMIT) {
                duint base = (duint)page.mbi.BaseAddress;
                SIZE_T size = page.mbi.RegionSize;
                duint alignedBase = base & ~0xFFF;
                duint alignedSize = (size + 0xFFF) & ~0xFFF;
                uc_mem_map(uc, alignedBase, alignedSize, UC_PROT_ALL);
                std::vector<uint8_t> buffer(size);
                if (Script::Memory::Read(base, buffer.data(), size, nullptr)) {
                    uc_mem_write(uc, base, buffer.data(), size);
                }
            }
        }

        // 3. 同步寄存器状态(64位)
        REGDUMP_AVX512 regdump = { 0 };
        if (!DbgGetRegDumpEx(®dump, sizeof(regdump))) {
            _plugin_logprintf(u8"[Unicorn] 无法获取寄存器,模拟跳过\n");
            uc_close(uc);
            continue;
        }

        uint64_t rax = regdump.regcontext.cax;
        uint64_t rbx = regdump.regcontext.cbx;
        uint64_t rcx = regdump.regcontext.ccx;
        uint64_t rdx = regdump.regcontext.cdx;
        uint64_t rsi = regdump.regcontext.csi;
        uint64_t rdi = regdump.regcontext.cdi;
        uint64_t rsp = regdump.regcontext.csp;
        uint64_t rbp = regdump.regcontext.cbp;
        uint64_t rip = regdump.regcontext.cip;
        uint64_t rflags = regdump.regcontext.eflags;
        rflags &= ~0x100;  // 清除 TF 位

        uint64_t r8 = regdump.regcontext.r8;
        uint64_t r9 = regdump.regcontext.r9;
        uint64_t r10 = regdump.regcontext.r10;
        uint64_t r11 = regdump.regcontext.r11;
        uint64_t r12 = regdump.regcontext.r12;
        uint64_t r13 = regdump.regcontext.r13;
        uint64_t r14 = regdump.regcontext.r14;
        uint64_t r15 = regdump.regcontext.r15;

        uc_reg_write(uc, UC_X86_REG_RAX, &rax);
        uc_reg_write(uc, UC_X86_REG_RBX, &rbx);
        uc_reg_write(uc, UC_X86_REG_RCX, &rcx);
        uc_reg_write(uc, UC_X86_REG_RDX, &rdx);
        uc_reg_write(uc, UC_X86_REG_RSI, &rsi);
        uc_reg_write(uc, UC_X86_REG_RDI, &rdi);
        uc_reg_write(uc, UC_X86_REG_RSP, &rsp);
        uc_reg_write(uc, UC_X86_REG_RBP, &rbp);
        uc_reg_write(uc, UC_X86_REG_RIP, &rip);
        uc_reg_write(uc, UC_X86_REG_RFLAGS, &rflags);
        uc_reg_write(uc, UC_X86_REG_R8, &r8);
        uc_reg_write(uc, UC_X86_REG_R9, &r9);
        uc_reg_write(uc, UC_X86_REG_R10, &r10);
        uc_reg_write(uc, UC_X86_REG_R11, &r11);
        uc_reg_write(uc, UC_X86_REG_R12, &r12);
        uc_reg_write(uc, UC_X86_REG_R13, &r13);
        uc_reg_write(uc, UC_X86_REG_R14, &r14);
        uc_reg_write(uc, UC_X86_REG_R15, &r15);

        // 4. 设置钩子
        uc_hook trace_code, trace_mem;
        uc_hook_add(uc, &trace_code, UC_HOOK_CODE, hook_code, NULL, 1, 0);
        uc_hook_add(uc, &trace_mem, UC_HOOK_MEM_UNMAPPED, hook_mem_unmapped, NULL, 1, 0);

        // 5. 开始模拟
        _plugin_logprintf(u8"[Unicorn] 开始模拟,起始 RIP: 0x%llX\n", rip);
        g_stop_reason = 0;   // 清除停止原因
        err = uc_emu_start(uc, rip, UINT64_MAX, 0, 0);

        if (err != UC_ERR_OK) {
            _plugin_logprintf(u8"[Unicorn] 模拟停止,原因: %s\n", uc_strerror(err));
        }
        else {
            _plugin_logprintf(u8"[Unicorn] 模拟正常完成。\n");
        }

        // 获取停止时的 RIP
        uint64_t stop_rip = 0;
        uc_reg_read(uc, UC_X86_REG_RIP, &stop_rip);
        _plugin_logprintf(u8"[Unicorn] 停止时 RIP: 0x%llX, 停止原因: %d\n", stop_rip, g_stop_reason);

        // 根据停止原因决定后续动作
        if (g_stop_reason == 1) {   // rdtsc
            // 在 rdtsc 下一条指令设置硬件断点
            g_rdtscNextRip = stop_rip+2;  // 实际上 stop_rip 就是 rdtsc 地址,但 hook_code 中已存储 g_rdtscNextRip
            Script::Debug::SetHardwareBreakpoint(g_rdtscNextRip);
            g_bWaitingForBreakpoint = TRUE;
            // 让调试器继续执行到断点
            DbgCmdExec("go");
        }
        else if (g_stop_reason == 2) {   // syscall
            _plugin_logprintf(u8"[Unicorn] 遇到 syscall,模拟结束。\n");
            Script::Debug::SetHardwareBreakpoint(stop_rip);
            DbgCmdExec("go");
            // 不再继续模拟,线程回到等待状态(等待用户再次触发)
        }
        else if (g_stop_reason == 3) {   // 超出模块范围
            _plugin_logprintf(u8"[Unicorn] 超出主模块,模拟结束。\n");
            api = TRUE;
            Script::Debug::SetHardwareBreakpoint(stop_rip);
            DbgCmdExec("go");
            // 不再继续模拟,线程回到等待状态
        }
        else {
            // 其他未知停止原因(可能 uc_emu_start 错误),也不继续
            gsreg = TRUE;
            BASIC_INSTRUCTION_INFO info;
            DbgDisasmFastAt(stop_rip, &info);
            Script::Debug::SetHardwareBreakpoint(stop_rip+info.size);
            DbgCmdExec("go");

            _plugin_logprintf(u8"[Unicorn] 模拟因未知原因停止。\n");
        }

        uc_close(uc);
    }
    return 0;
}

// ---------------------- 断点回调 ----------------------
BOOL g_bDebugMenuActive = FALSE;  // 标记是否处于调试模式(菜单 DEBUG_ID 按下后)

extern "C" __declspec(dllexport) void CBBREAKPOINT(CBTYPE cbType, PLUG_CB_BREAKPOINT * info) {
    // 处理调试模式下的硬件断点(仅当 g_bDebugMenuActive 为真)
    if (g_bDebugMenuActive && info->breakpoint->type == bp_hardware) {
        // 如果是之前为 rdtsc 设置的下一条指令断点
        if (info->breakpoint->addr == g_rdtscNextRip) {
            _plugin_logprintf(u8"[Unicorn] rdtsc 下一条指令断点命中: 0x%llX\n", info->breakpoint->addr);
            // 唤醒工作线程继续模拟
            Script::Debug::DeleteHardwareBreakpoint(g_rdtscNextRip);
            SetEvent(g_hSimulateEvent);

            // 注意:断点命中后调试器处于暂停状态,工作线程会重新获取寄存器并模拟
            // 此时不要执行 go,否则工作线程还没准备好就继续运行了
            return;
        }

        WORD op = 0;
        if (DbgMemRead(info->breakpoint->addr, &op, 2)) {
            if (op == 0x50f) {
                _plugin_logprintf(u8"syscall:%llx\n", info->breakpoint->addr);
                Script::Debug::DeleteHardwareBreakpoint(info->breakpoint->addr);
                return;
            }
            /*else {
                DbgCmdExec("StepInto");
            }*/

        }

    }
    if (gsreg && info->breakpoint->type == bp_hardware) {
        gsreg = FALSE;
        Script::Debug::DeleteHardwareBreakpoint(info->breakpoint->addr);
        SetEvent(g_hSimulateEvent);
        return;
    }
    if (api && info->breakpoint->type == bp_hardware) {
        api = FALSE;
        Script::Debug::DeleteHardwareBreakpoint(info->breakpoint->addr);

        return;
    }

}

// ---------------------- 菜单回调 ----------------------
extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY * info) {
    if (info->hEntry == MENU_UNICORN_ID) {
        // 启动模拟(唤醒工作线程)
        g_bDebugMenuActive = TRUE;          // 确保不在调试模式下
        g_bWaitingForBreakpoint = FALSE;     // 清除等待断点标志
        SetEvent(g_hSimulateEvent);
        return;
    }

}

// ---------------------- 插件入口函数 ----------------------
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT * initStruct) {
    initStruct->pluginVersion = 1;
    initStruct->sdkVersion = PLUG_SDKVERSION;
    strcpy_s(initStruct->pluginName, "UnicornPlugin");
    pluginHandle = initStruct->pluginHandle;
    return true;
}

extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT * setupStruct) {
    hMenu = setupStruct->hMenu;
    _plugin_menuaddentry(hMenu, MENU_UNICORN_ID, "sys_trace");

    // 创建事件和常驻工作线程
    g_hSimulateEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_hSimulateThread = CreateThread(NULL, 0, SimulationWorker, NULL, 0, NULL);
    _plugin_logputs("[UnicornPlugin] plugsetup called, worker thread started.");
}

extern "C" __declspec(dllexport) bool plugstop() {
    // 停止工作线程
    if (g_hSimulateEvent) {
        g_bStopSimulation = TRUE;
        SetEvent(g_hSimulateEvent);
        if (g_hSimulateThread) {
            WaitForSingleObject(g_hSimulateThread, INFINITE);
            CloseHandle(g_hSimulateThread);
            g_hSimulateThread = NULL;
        }
        CloseHandle(g_hSimulateEvent);
        g_hSimulateEvent = NULL;
    }
    _plugin_logputs("[UnicornPlugin] plugstop called, worker thread stopped.");
    return true;
}

// ---------------------- DllMain ----------------------
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


通过网盘分享的文件:plugin.zip
链接: https://pan.baidu.com/s/1KIxIQjQCCzhqMfhRXNfklQ?pwd=b228 提取码: b228
--来自百度网盘超级会员v8的分享

免费评分

参与人数 12吾爱币 +18 热心值 +10 收起 理由
Sound + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
eec + 2 + 1 我很赞同!
冥界3大法王 + 4 + 1 感谢源码起步!
hiplease + 1 用心讨论,共获提升!
wapj152321 + 1 + 1 我很赞同!
evea + 1 + 1 谢谢@Thanks!
sht281 + 1 + 1 谢谢@Thanks!
DNLINYJ + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
HongHu106 + 1 + 1 谢谢@Thanks!
秋名山 + 1 我很赞同!
m_h + 1 + 1 谢谢@Thanks!
xpiao + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Sound 发表于 2026-5-20 11:47
本帖最后由 Sound 于 2026-5-20 16:33 编辑
dongdaiyu 发表于 2026-5-20 11:06
感谢分享,key是啥没key用不了
delete...
Sound 发表于 2026-5-19 18:45
本帖最后由 Sound 于 2026-5-20 16:32 编辑
dongdaiyu 发表于 2026-5-19 12:04
大佬,这是你写的吗?求

好多年前写的了,写着玩的,拿去玩.

Sound 发表于 2026-5-19 09:51
本帖最后由 Sound 于 2026-5-20 16:32 编辑




看了一遍真的太详细了 简直是手把手教别人,好文章....不过我还是喜欢用插件,方便 :)


 楼主| yoyoRev 发表于 2026-4-3 22:55

先隐藏peb,这是第一步,然后再在ntclose下断,运行到ntclose之后 运行到用户代码,然后再使用插件
小南跑 发表于 2026-4-3 18:03
谢谢大师的分享,感觉能研究VMP的都好强啊!
以前看到VMP都放弃,现在随着大佬们开发的工具越来越完善,能不能破解先不说,至少可以拖进去调试器单步跟一跟了
而不是一上来碰到它的反调试警告就无可奈何,开发不易,向大师致敬!

这个好像是64位的,也发布个配套的32位吧
xpiao 发表于 2026-4-3 20:18
感谢大佬的工具,正在找过vmp3.9的
xpiao 发表于 2026-4-3 21:06
大佬我在入口断点运行插件直接闪退,有解决方法吗
 楼主| yoyoRev 发表于 2026-4-3 21:09
xpiao 发表于 2026-4-3 21:06
大佬我在入口断点运行插件直接闪退,有解决方法吗

看看有没有tls回调,闪退有没有啥日志输出啥的
 楼主| yoyoRev 发表于 2026-4-3 21:10
小南跑 发表于 2026-4-3 18:03
谢谢大师的分享,感觉能研究VMP的都好强啊!
以前看到VMP都放弃,现在随着大佬们开发的工具越来越完善,能 ...

x86的还没研究,等假期过了上班看看
zjdals 发表于 2026-4-3 21:10
看不懂但是大受震感!
xpiao 发表于 2026-4-3 21:27
有日志显示
4e0ad7874abdf0fb5bb343c2c2b521df.png
flea033 发表于 2026-4-4 12:05
感谢分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-14 08:21

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表