使用WithDLL注入Detours钩子DLL失败:未导出序号#1错误解决咨询
问题:withdll.exe提示“DLL未导出序号#1”的解决方法
问题描述
使用Microsoft Detours实现进程挂钩DLL时,运行withdll.exe注入出现以下错误:
B:>withdll.exe /d:[...]\IniHook.dll HookTarget.exe withdll.exe: 错误:[...]\IniHook.dll 未导出序号#1。 请查看Detours.chm中的DetourCreateProcessWithDllEx帮助条目。
无法找到Detours.chm文档,现需明确问题原因及修复方案。
用户提供的挂钩DLL代码如下:
#include "pch.h" #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <detours.h> #include <iostream> #pragma comment(lib, "detours.lib") typedef DWORD(WINAPI* GetPrivateProfileStringW_t)(LPCWSTR app, LPCWSTR key, LPCWSTR def, LPWSTR result, DWORD size, LPCWSTR file_name); GetPrivateProfileStringW_t TrueGetPrivateProfileStringW = nullptr; DWORD WINAPI HookedGetPrivateProfileStringW(LPCWSTR app, LPCWSTR key, LPCWSTR def, LPWSTR result, DWORD size, LPCWSTR file_name) { std::wcout << L"GetPrivateProfileStringW called." << L"\n"; return TrueGetPrivateProfileStringW(app, key, def, result, size, file_name); } BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID _) { switch (reason) { case DLL_THREAD_ATTACH: case DLL_PROCESS_ATTACH: DetourTransactionBegin(); TrueGetPrivateProfileStringW = (GetPrivateProfileStringW_t)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetPrivateProfileStringW"); DetourAttach(&(PVOID&)TrueGetPrivateProfileStringW, HookedGetPrivateProfileStringW); DetourTransactionCommit(); break; case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourDetach(&(PVOID&)TrueGetPrivateProfileStringW, HookedGetPrivateProfileStringW); DetourTransactionCommit(); break; } return TRUE; }
问题原因
withdll.exe的/d注入参数有硬性要求:被注入的DLL必须导出序号为1的函数。这是因为它内部调用DetourCreateProcessWithDllEx时,默认会尝试调用DLL中序号1的导出函数完成初始化(未指定自定义入口点时)。当前DLL没有任何导出函数,因此触发该错误。
修复方案
方案一:给DLL添加序号为1的导出函数
不需要修改withdll的调用命令,只需在DLL中添加一个空的导出函数,并确保它的导出序号为1,两种实现方式任选其一:
方式1:使用.def配置文件
在项目中新建一个IniHook.def文件,内容如下:
LIBRARY IniHook EXPORTS DllInit @1
然后在代码中添加对应的空函数:
// 仅满足withdll的导出要求,无需实际逻辑 extern "C" __declspec(dllexport) void DllInit() { }
编译时链接器会自动读取.def文件,将DllInit函数导出为序号1。
方式2:通过代码直接指定导出序号
无需额外配置文件,在代码中添加如下函数声明与实现:
// 直接指定导出序号为1 extern "C" __declspec(dllexport) __declspec(allocate(".text$1")) void DllInit() { }
或者在代码中添加编译指令,同样可以实现导出:
#pragma comment(linker, "/EXPORT:DllInit,@1,NONAME") // 空实现的导出函数 extern "C" void DllInit() { }
方案二:修改withdll调用命令,指定自定义入口点
如果不想添加多余函数,可以修改withdll的命令格式,明确指定DLL的入口点(需确保该函数已导出):
withdll.exe /d:IniHook.dll,DllInit HookTarget.exe
注:此方式要求DllInit是已导出的函数,本质和方案一类似,只是显式指定了入口点而非依赖默认的序号1。
额外代码优化建议
当前代码在DLL_THREAD_ATTACH事件中也执行了挂钩操作,这会导致每个线程创建时重复调用DetourAttach,引发错误。应仅在DLL_PROCESS_ATTACH时进行挂钩,修改后的DllMain如下:
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID _) { switch (reason) { case DLL_PROCESS_ATTACH: DetourTransactionBegin(); TrueGetPrivateProfileStringW = (GetPrivateProfileStringW_t)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetPrivateProfileStringW"); DetourAttach(&(PVOID&)TrueGetPrivateProfileStringW, HookedGetPrivateProfileStringW); DetourTransactionCommit(); break; case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourDetach(&(PVOID&)TrueGetPrivateProfileStringW, HookedGetPrivateProfileStringW); DetourTransactionCommit(); break; } return TRUE; }
内容的提问来源于stack exchange,提问作者Thomas W.
相关产品推荐
相关产品推荐

