FreeLibraryAndExitThread 在卸载注入的 DLL 时使程序崩溃

Posted

技术标签:

【中文标题】FreeLibraryAndExitThread 在卸载注入的 DLL 时使程序崩溃【英文标题】:FreeLibraryAndExitThread crashes program when unloading injected DLL 【发布时间】:2017-02-08 09:16:57 【问题描述】:

我正在编写一个注入到游戏中的 DLL,以便进行一些逆向工程。有一段时间,当我对程序进行更改时,我能够成功地注入、弹出和重新注入。我正在使用FreeLibraryAndExitThread 卸载。

将 XInput 添加到程序中以便我可以捕获用户输入后,当我调用 FreeLibraryAndExitThread 时,游戏因访问冲突而崩溃。从this post 开始,我猜想当我卸载时使用 XInput 会在程序中留下一些 'live' 内容,这就是导致崩溃的原因。老实说,我不知道如何解决这个问题。

这是在退出时使程序崩溃的代码:

    XINPUT_STATE state;
    ZeroMemory(&state, sizeof(XINPUT_STATE));

    //The problematic line of code
    bool gamepad = XInputGetState(0, &state) == ERROR_SUCCESS; 

    WORD buttonsHeld = state.Gamepad.wButtons;
    WORD buttonsPressed = (~previousButtonState) & state.Gamepad.wButtons;
    WORD buttonsReleased = previousButtonState & (~state.Gamepad.wButtons);

当我删除对 XInputGetState 的调用时,一切正常,我能够卸载 DLL 而不会崩溃。

这就是我要求程序卸载和退出的方式

    FreeLibraryAndExitThread(hDLL, 0);

其中hDLL 是来自DllMain 的参数hinstDLL。我也尝试过GetModuleHandleEx,而不是使用hinstDLL

我的猜测是:

使用XInputGetState 使我的程序为XInput 加载第二个DLL,或者

XInputGetState 在调用时创建了对我的 DLL 的某种引用,当我删除我的 DLL 时,它试图访问不再存在的内存。

编辑:我做了一些挖掘,问题似乎是添加对XInputGetState 的调用会导致我的DLL 加载XINPUT1_4.dll。我尝试使用FreeLibrary 卸载它,但它不起作用。

编辑:我进一步缩小了范围 - 事实证明,访问冲突是由游戏中的某个线程试图返回 XINPUT1_4.dll 的部分代码引起的,该部分代码被卸载,使其崩溃。我不知道如何解决这个问题。

最终编辑:这是一个简单的修复,我必须为导致问题的 DLL 调用 LoadLibrary(L"XINPUT1_4.dll")

【问题讨论】:

如果游戏已经将 XInput 用于自己的目的,那么它应该有自己的对 xinput1_4.dll 的开放引用,因此加载和释放您自己的引用不应该从内存中卸载该 DLL 作为其引用计数不会下降到 0。 尝试在您的 DLL(LoadLibrary、GetProcAddress)中显式加载xinput1_4.dll,而不是静态链接到它。如果您在 DLL 卸载时保持加载状态,则可能没有问题。 游戏实际上加载了旧版本的 XInput,它加载的是 1.3 版本。这有什么不同吗? 实际上,这行得通! DllMain 创建一个线程,该线程调用LoadLibrary("XINPUT1_4.dll");,当我卸载时它正在工作!非常感谢! :) 【参考方案1】:

解决办法如下:

问题是调用XInputGetState 导致我的DLL 自动加载XINPUT1_4.dll,当我调用FreeLibraryAndExitThread 时,我的DLL 卸载迫使XInput DLL 也卸载。程序中的代码(可能来自 XInput 1.4 中的线程)试图执行不再存在的代码,从而导致访问冲突。

所以解决方案是在我初始化我的 DLL 的线程之后调用LoadLibrary(L'XINPUT1_4.dll'),这样当我的 DLL 被卸载时,XInput DLL 会留在内存中,因为LoadLibrary 增加了引用计数。

(当一个DLL的引用计数达到0时,它会卸载。当你第一次加载它时它被初始化为1,LoadLibrary将它加1,调用FreeLibraryAndExitThread将它减1。所以当一切都说完了,它的引用count 大于 0 并且在我的 DLL 被卸载时仍保留在内存中)

【讨论】:

以上是关于FreeLibraryAndExitThread 在卸载注入的 DLL 时使程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

游戏辅助 -- 获取人物属性代码编写

如Process Hacker那样卸载dll

如何安全地调用 TerminateThread 和 FreeLibrary (WinAPI, C++)

远程线程DLL注入, 如何释放DLL和结束DLL的线程