我正在尝试挂钩目标应用程序的键盘和鼠标事件。 我遵循了SO问题How to hook external process with SetWindowsHookEx and WH_KEYBOARD,并且第一次正确安装和卸载了钩子。但是,在我一次卸载该挂钩然后再次安装它之后,第二次尝试卸载该挂钩会使目标应用程序崩溃。挂钩的目的是监视应用程序的空闲时间,以便我可以在应用程序空闲时执行一些任务。 对于这个问题的长度,我深表歉意,但我尝试输入所有可能会有所帮助的细节 谢谢
我需要能够根据系统任务栏图标中的菜单命令安装和卸载挂钩。我有一个控制台应用程序[HookApp],该应用程序在DLL [HookDLL]中调用安装和卸载方法。控制台应用程序还创建一个窗口来处理菜单事件。我使用窗口的线程实际安装和卸载钩子,因为安装钩子的同一线程必须将其卸载。控制台和窗口都必须不可见。只有系统任务栏图标及其相关菜单必须可见。 我从命令行使用参数启动挂钩应用程序 一世。目标进程的ProcessId ii。工艺名称 iii。空闲时间[以秒为单位]-在DLL中开始操作之前的空闲时间 我想知道我在HookProcs中启动的计时器是否是导致崩溃的原因。
事件查看器日志 错误的应用程序名称:notepad.exe,版本:10.0.17134.1,时间戳:0x9d4727c2 错误的模块名称:HookDLL_x64.dll_unloaded,版本:0.0.0.0,时间戳:0x5c31aabd 异常代码:0xc0000005 故障偏移量:0x00000000000ba505 错误进程ID:0x2bac
异常代码似乎表明存在内存访问冲突。
我尝试了以下方法 一世。 一种。从HookApp使用HWND_BROADCAST调用SendMessageTimeout API b。从HookDLL使用HWND_BROADCAST调用SendMessageTimeout API 基于Unloading DLL from all processes after unhooking global CBT hook
II。 一种。在DLL_PROCESS_DETACH和DLL_THREAD_DETACH上的DLLMain方法中调用FreeLibraryAndExitThread API-两者一起并单独 基于http://www.rohitab.com/discuss/topic/42505-unloading-dll-crashes-exe/
III。使HookApp控制台和窗口可见和隐藏,以查看它是否有所不同
IV。由于64位架构,无法尝试以下操作 https://www.unknowncheats.me/forum/programming-for-beginners/73377-unload-injected-dll-thread-process.html
我提到的其他链接 一种。 Several programs crash when unhooking with UnhookWindowsHookEx() b。 How to correctly use SetWindowsHookEx & CallNextHookEx C。 Unloading an Injected DLL d。 FreeLibraryAndExitThread crashes program when unloading injected DLL
//Code in the DLL
extern "C" __declspec(dllexport) void install(unsigned long threadID,int _nIdleTime) {
nIdleTime = _nIdleTime;
Log(L"install proc called from app: _nIdleTime", _nIdleTime);
hhKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hinst, threadID);
hhMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, hinst, threadID);
logger->Log("Install");
logger->Log("Keyboard", (LONG)hhKeyboard);
logger->Log("Mouse", (LONG)hhMouse);
}
//Uninstall the dll from the Target Process
DWORD WINAPI UnInjectDLLFromTarget() {
uninstall();
//DWORD_PTR dwResult = 0;
//SendMessageTimeout(HWND_BROADCAST, WM_NULL, 0, 0, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, &dwResult);
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {
if (code < 0) {
return CallNextHookEx(0, code, wParam, lParam);
}
StartTimer();
Beep(1000, 20);
return CallNextHookEx(hhKeyboard, code, wParam, lParam);
}
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
if (code < 0) {
return CallNextHookEx(0, code, wParam, lParam);
}
// StartTimer();
//Beep(1000, 20);
return CallNextHookEx(hhMouse, code, wParam, lParam);
}
BOOL WINAPI DllMain(__in HINSTANCE hinstDLL, __in DWORD fdwReason, __in LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_DETACH || fdwReason==DLL_THREAD_DETACH) {
FreeLibraryAndExitThread(hinstDLL, 0);
return 0;
}
}
//Code in the Application [HookApp]
Similar to [But not exactly the same - It is a bit more involved because of the need to create the message pump and the menu event handling window]
HINSTANCE hinst = LoadLibrary(_T("MyDLL.dll"));
if (hinst) {
typedef void (*Install)(unsigned long);
typedef void (*Uninstall)();
Install install = (Install) GetProcAddress(hinst, "install");
Uninstall uninstall = (Uninstall) GetProcAddress(hinst, "uninstall");
install(threadID);
Sleep(20000);
uninstall();
}
请帮助。
答案 0 :(得分:1)
用于卸载钩子并卸载您所需的所有dll-对先前调用UnhookWindowsHookEx
所获得的每个钩子句柄调用SetWindowsHookEx
。所有。您不需要自己致电FreeLibrary[AndExitThread]
。在FreeLibrary
调用结束后,钩子dll上的系统自动调用UnhookWindowsHookEx
,当目标应用程序线程接收到第一个(任何)消息时,此线程调用GetMessage
或PeekMessage
。因此,仅需在几个UnhookWindowsHookEx
之后卸载dll,就需要在线程上发布消息,并为此安装钩子。如果您针对具体的单线程(dwThreadId
)执行此操作-您需要PostThreadMessageW(dwThreadId, WM_NULL, 0, 0);
-系统自己调用FreeLibrary
(从user32!__ClientFreeLibrary
调用,而从ntdll!KiUserCallbackDispatcher
调用-已调用线程(或它的窗口)收到任何消息时,在GetMessage
或PeekMessage
里面
您自己从dll调用FreeLibrary
-始终出错-如果假定该调用将卸载dll-调用后返回到哪里?到卸货的地方。在某些情况下,调用FreeLibraryAndExitThread
存在意义,但只能从您自己创建的线程中进行。在所有致命错误中都从dll入口点调用FreeLibraryAndExitThread
-您不杀死自己的线程,要杀死的线程-保留了被称为dll入口点的临界区(加载器锁)-致命错误。并在此处进行绝对无意义的呼叫-如果您说收到DLL_PROCESS_DETACH
,则意味着该dll已经在卸载过程中。 DLL_THREAD_DETACH
-任意点,为什么要在这里尝试多少次自我调用?退出线程,同时保留整个进程的关键部分而不释放它。致命错误。
也,因为安装了挂钩的线程必须将其卸载。-这是不正确的。另一个线程也可以做到这一点。
您的代码中的StartTimer();
在这里看起来也很可疑。这段代码是做什么的?在哪里取消计时器?