ba21 发表于 3 天前

终于找到全局钩子导致其它程序奔溃的原因

前几天突然心血来潮想写个全局钩子的小程序,期间遇到个一直无法解决的问题,就是程序设置钩子没问题,一旦解除钩子就会导致其它运行的程序奔溃。
短短几行代码,左改右改,该查的资料都查了,就是没有解决,中途时不时的想放弃不弄了,但是本着不找到问题原因不放弃的原则,最后终于找到问题所在。
费话不多说,本文目的是分享经验,没有技术含量,看到最后你才知道什么叫惊不惊喜意不意外。

主要代码:
dll
// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;

// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;


症状:
运行的程序 一 一 奔溃,explorer.exe也不例外。


主要过程:
过程1:在项目中把所有无关的代码删除,只保留了设置钩子和取消钩子的代码
// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;

// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;结果:设置钩子没问题,一旦解除钩子就会导致其它运行的程序奔溃。


过程2:另外新建项目把相关代码边复制边测试以便查找问题。
    结果是把所有代码都移过去后发现程序运行正常了(解除钩子不会导致其它程序崩溃)。
    心里想问题到底在哪里呀?这也不至于啊,就这样算了吧反正是正常了,再想想还是要找到问题,要是以后又遇到那不是又得折腾。

问题在这里:
经过2个项目代码进行排查,终于找到这该死的问题。
有问题的dll代码:
library MyHookdll;

uses
SysUtils,
Classes,
Windows, Vcl.Dialogs;

var
hhk: HHOOK = 0;

// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;

// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;


exports
StartHook, EndHook;

begin

end.

dll正常代码:
library MyHookdll;

uses
SysUtils,
Classes,
Windows;

var
hhk: HHOOK = 0;

// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;

// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;


exports
StartHook, EndHook;

begin

end.

对比2处代码,仅仅是引用了Vcl.Dialogs组件库便导致程序产生了致命的问题。
就问你们惊不惊喜意不意外。

FishC 发表于 3 天前

全局钩子导致崩溃的原因分析及解决方案

通过对比两个DLL项目代码,发现问题根源在于Vcl.Dialogs单元的引用。以下是关键分析:


[*]崩溃原因:VCL单元包含消息循环机制,在全局钩子场景中会导致以下问题:
1. 被注入进程加载该DLL时,会初始化VCL框架
2. 不同进程共享VCL资源时产生冲突
3. 卸载DLL时资源释放顺序异常

[*]致命影响:
1. 调用UnhookWindowsHookEx时DLL卸载失败
2. 残留的VCL资源导致宿主进程内存损坏
3. 最终引发explorer.exe等进程崩溃


解决方案:

// 正确的DLL单元引用应仅保留核心模块
uses
Windows, SysUtils; // 移除非必要的VCL单元


经验总结
1. 全局钩子DLL必须保持最小化依赖
2. 避免在DLL中使用任何GUI相关单元
3. 调试时建议用OutputDebugString代替弹窗
4. 推荐使用纯API实现钩子功能

这个案例印证了:在系统级编程中,看似无关的单元引用可能造成毁灭性后果。建议通过Process Monitor等工具监控DLL加载行为,能更快定位此类隐蔽问题。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

不二如是 发表于 前天 10:21

太实用了

ba21 发表于 前天 19:15

不二如是 发表于 2025-4-24 10:21
太实用了

惭愧惭愧。
页: [1]
查看完整版本: 终于找到全局钩子导致其它程序奔溃的原因