You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在EXE中使用Detours替换LoadLibrary函数遇ERROR_INVALID_BLOCK问题求助

解决DetourDetach返回ERROR_INVALID_BLOCK的问题

首先咱们得先搞明白ERROR_INVALID_BLOCK这个错误的核心原因:Detours找不到对应已经挂载的钩子实例,所以没法完成卸载操作。结合你是在自身进程内Hook LoadLibrary系列函数(而非注入其他进程)的场景,大概率是以下几个问题导致的:

1. 未执行对应DetourAttach,或Real_LoadLibraryA指针不匹配

Detours有个硬性要求:必须先调用DetourAttach挂载钩子,才能调用DetourDetach卸载,而且DetourDetach的第一个参数必须是你当初DetourAttach时用的同一个PVOID*变量(也就是保存原始函数地址的那个指针)。

给你贴个正确的完整流程示例,你可以对照自己的代码排查:

// 先定义原始函数指针和自定义钩子函数
typedef HMODULE(WINAPI* pLoadLibraryA)(LPCSTR lpLibFileName);
pLoadLibraryA Real_LoadLibraryA = nullptr;

HMODULE WINAPI Mine_LoadLibraryA(LPCSTR lpLibFileName) {
    // 你的黑名单检查逻辑
    if (isBlacklisted(lpLibFileName)) {
        return nullptr; // 阻止加载黑名单DLL
    }
    return Real_LoadLibraryA(lpLibFileName); // 调用原始函数
}

// 挂载钩子的正确代码
void AttachLoadLibraryHooks() {
    // 注意:优先从kernelbase.dll获取,避免系统转发导致地址不匹配
    Real_LoadLibraryA = (pLoadLibraryA)GetProcAddress(GetModuleHandleA("kernelbase.dll"), "LoadLibraryA");
    
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    LONG attachResult = DetourAttach(&(PVOID&)Real_LoadLibraryA, Mine_LoadLibraryA);
    if (attachResult != NO_ERROR) {
        __debugbreak();
    }
    LONG commitResult = DetourTransactionCommit(nullptr);
    if (commitResult != NO_ERROR) {
        __debugbreak();
    }
}

// 卸载钩子的正确代码
void DetachLoadLibraryHooks() {
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    LONG detachResult = DetourDetach(&(PVOID&)Real_LoadLibraryA, Mine_LoadLibraryA);
    if (detachResult != NO_ERROR) {
        __debugbreak();
    }
    PVOID* ppbFailedPointer = nullptr;
    LONG commitResult = DetourTransactionCommitEx(&ppbFailedPointer);
    if (commitResult != NO_ERROR) {
        __debugbreak();
    }
}

特别注意:DetourAttach执行后,Real_LoadLibraryA会被Detours内部修改为真正的原始函数跳转地址,所以你绝对不能在卸载钩子前重新给这个指针赋值(比如再次调用GetProcAddress),否则指针不匹配就会直接返回ERROR_INVALID_BLOCK

2. 线程上下文的潜在问题

你调用DetourUpdateThread(GetCurrentThread())是对当前线程生效,虽然在自身进程内操作时,只要是进程内的活跃线程都可以,但最好确保执行钩子操作(挂载/卸载)的是进程的主线程,或者没有被挂起的线程,避免线程状态异常导致的问题。

3. 是不是搞反了挂载和卸载的顺序?

你贴的代码直接调用了DetourDetach,如果你的代码执行流程里,根本没先成功执行过对应的DetourAttach,那肯定会触发这个错误。先确认你的代码是先挂载钩子,再执行卸载逻辑。

额外小提示

因为LoadLibrary系列函数在不同Windows版本下可能存在导出转发(比如从kernel32.dll转发到kernelbase.dll),所以获取原始函数地址时,直接从kernelbase.dll获取能避免很多地址不匹配的问题,这也是我上面示例里这么写的原因。

如果还是排查不出问题,可以在挂载钩子后打印Real_LoadLibraryA的地址,卸载前再打印一次,确认两个地址完全一致,这样就能快速排除指针不匹配的问题。

内容的提问来源于stack exchange,提问作者M. Twombley

火山引擎 最新活动