我正试图在Windows操作系统上挂钩CBT钩子。我目前正在使用Windows 7 x64。
我已经阅读了许多关于这个问题的线索,但没有一个解决了我的问题。应用程序运行良好;挂钩已安装,我可以看到一些通知即将到来。
实际上出现的问题是应用程序未被通知在同一台机器上运行的其他进程的CBT挂钩。
应用程序是用C#编写的(使用Microsoft .NET)。这是一个正在运行的样本:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsHook
{
class Program
{
[STAThread]
static void Main(string[] args)
{
uint thid = (uint)AppDomain.GetCurrentThreadId();
bool global = true;
mHookDelegate = Marshal.GetFunctionPointerForDelegate(new HookProc(ManagedCallback));
if (global == true) {
mNativeWrapperInstance = LoadLibrary("Native_x64.dll");
thid = 0;
} else {
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
mNativeWrapperInstance = GetModuleHandle(curModule.ModuleName);
}
}
mNativeWrappedDelegate = AllocHookWrapper(mHookDelegate);
mHookHandle = SetWindowsHookEx(/*WH_CBT*/5, mNativeWrappedDelegate, mNativeWrapperInstance, thid);
if (mHookHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
Application.Run(new Form());
if (FreeHookWrapper(mNativeWrappedDelegate) == false)
throw new Win32Exception("FreeHookWrapper has failed");
if (FreeLibrary(mNativeWrapperInstance) == false)
throw new Win32Exception("FreeLibrary has failed");
if (UnhookWindowsHookEx(mHookHandle) == false)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
static int ManagedCallback(int code, IntPtr wParam, IntPtr lParam)
{
Trace.TraceInformation("Code: {0}", code);
if (code >= 0) {
return (0);
} else {
return (CallNextHookEx(mHookHandle, code, wParam, lParam));
}
}
delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
static IntPtr mHookHandle;
static IntPtr mHookDelegate;
static IntPtr mNativeWrapperInstance = IntPtr.Zero;
static IntPtr mNativeWrappedDelegate = IntPtr.Zero;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int hook, IntPtr callback, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")]
internal static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("Native_x64.dll")]
private static extern IntPtr AllocHookWrapper(IntPtr callback);
[DllImport("Native_x64.dll")]
private static extern bool FreeHookWrapper(IntPtr wrapper);
[DllImport("Native_x64.dll")]
private static extern int FreeHooksCount();
}
}
AllocHookWrapper和FreeHookWrapper是来自x64平台的DLL(Native_x64.dll)编译器的导入例程,位于应用程序的同一目录中。 AllocHookWrapper存储函数指针(托管例程)并返回调用函数指针的DLL例程。
以下是DLL的代码:
#include "stdafx.h"
#include "iGecko.Native.h"
#ifdef _MANAGED
#pragma managed(push, off)
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define WRAPPER_NAME(idx) Wrapper ## idx
#define WRAPPER_IMPLEMENTATION(idx) \
LRESULT WINAPI WRAPPER_NAME(idx)(int code, WPARAM wparam, LPARAM lparam) \
{ \
if (sHooksWrapped[idx] != NULL) \
return (sHooksWrapped[idx])(code, wparam, lparam); \
else \
return (0); \
}
#define WRAPPER_COUNT 16
HOOKPROC sHooksWrapped[WRAPPER_COUNT] = { NULL };
WRAPPER_IMPLEMENTATION(0x00);
WRAPPER_IMPLEMENTATION(0x01);
WRAPPER_IMPLEMENTATION(0x02);
WRAPPER_IMPLEMENTATION(0x03);
WRAPPER_IMPLEMENTATION(0x04);
WRAPPER_IMPLEMENTATION(0x05);
WRAPPER_IMPLEMENTATION(0x06);
WRAPPER_IMPLEMENTATION(0x07);
WRAPPER_IMPLEMENTATION(0x08);
WRAPPER_IMPLEMENTATION(0x09);
WRAPPER_IMPLEMENTATION(0x0A);
WRAPPER_IMPLEMENTATION(0x0B);
WRAPPER_IMPLEMENTATION(0x0C);
WRAPPER_IMPLEMENTATION(0x0D);
WRAPPER_IMPLEMENTATION(0x0E);
WRAPPER_IMPLEMENTATION(0x0F);
const HOOKPROC sHookWrappers[] = {
WRAPPER_NAME(0x00),
WRAPPER_NAME(0x01),
WRAPPER_NAME(0x02),
WRAPPER_NAME(0x03),
WRAPPER_NAME(0x04),
WRAPPER_NAME(0x05),
WRAPPER_NAME(0x06),
WRAPPER_NAME(0x07),
WRAPPER_NAME(0x08),
WRAPPER_NAME(0x09),
WRAPPER_NAME(0x0A),
WRAPPER_NAME(0x0B),
WRAPPER_NAME(0x0C),
WRAPPER_NAME(0x0D),
WRAPPER_NAME(0x0E),
WRAPPER_NAME(0x0F)
};
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
return (TRUE);
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
extern "C" IGECKONATIVE_API HOOKPROC WINAPI AllocHookWrapper(HOOKPROC wrapped)
{
for(int i = 0; i < WRAPPER_COUNT; i++) {
if (sHooksWrapped[i] == NULL) {
sHooksWrapped[i] = wrapped;
return sHookWrappers[i];
}
}
return (NULL);
}
extern "C" IGECKONATIVE_API BOOL WINAPI FreeHookWrapper(HOOKPROC wrapper)
{
for(int i = 0; i < WRAPPER_COUNT; i++) {
if (sHookWrappers[i] == wrapper) {
sHooksWrapped[i] = NULL;
return TRUE;
}
}
return (FALSE);
}
extern "C" IGECKONATIVE_API INT WINAPI FreeHooksCount()
{
int c = 0;
for(int i = 0; i < WRAPPER_COUNT; i++) {
if (sHooksWrapped[i] == NULL)
c++;
}
return (c);
}
实际上我对某个系统上与窗口相关的事件(创建,破坏)感兴趣,但我实际上无法通过操作系统通知...
发生了什么事?我错过了什么?
请注意,我正在与Administratos小组一起运行。
我在page
中找到了这个有趣的部分.NET Framework不支持全局挂钩 您无法在Microsoft .NET Framework中实现全局挂钩。要安装全局钩子,钩子必须具有本机DLL导出才能将自身插入另一个需要有效,一致的函数调用的进程中。此行为需要DLL导出。 .NET Framework不支持DLL导出。托管代码没有函数指针的一致值的概念,因为这些函数指针是动态构建的代理。
我想通过实现包含钩子回调的本机DLL来实现这个技巧,该回调调用托管回调。但是,只在调用SetWindowsHookEx例程且不被其他进程调用的进程中调用托管回调。
有哪些可行的解决方法?
可能会分配存储进程ID(被管理者)的堆内存,并发送描述被挂钩函数的用户消息吗?
我想要实现的是系统范围的监视器,它可以检测执行的新进程,检测创建的窗口位置和大小,以及关闭的窗口,移动的窗口,最小化/最大化的窗口。监视器应连续检测鼠标和键盘事件(总是在系统范围内),还必须“模拟”鼠标和键盘事件。
同一桌面中的每个进程都必须由存档(32位或64位)和底层框架(本机或托管)独立监视。
监视器应强制进程窗口的位置,大小和移动,并且应能够充当本地用户,以便允许远程用户充当本地用户(如VNC)。
答案 0 :(得分:7)
很抱歉,但我不理解“包装”非托管DLL以及使用ManagedCallback
作为托管EXE内部钩子的感觉。
您应该明白,您用作系统范围CBT挂钩(SetWindowsHookEx
的参数)的回调的方法必须加载到all process
的地址空间中(它将完成一个DLL)注入实现钩子功能的模块)。在Windows SDK(MSDN)中,您可以阅读以下内容(请参阅http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx上的注释):
SetWindowsHookEx可用于注入 一个DLL进入另一个进程。一个32位 DLL无法注入64位 进程,一个64位DLL不能 注入32位进程。如果 应用程序需要使用钩子 在其他过程中,它是必需的 一个32位的应用程序调用 SetWindowsHookEx注入一个32位 DLL进入32位进程,和 64位应用程序调用 SetWindowsHookEx注入一个64位 DLL进入64位进程。 32位 和64位DLL必须有不同 名。
此外,您在关于系统范围的钩子的问题中写下,并且不使用0作为SetWindowsHookEx
的最后一个参数。还有一个问题:作为SetWindowsHookEx
(HINSTANCE hMod)的第三个参数,您使用的实例而不是带有钩子代码的dll (您当前在EXE中的钩子代码)
所以我的建议是:你必须编写一个新的本机代码来实现系统范围的CBT钩子并将其放在DLL中。我建议你也为DLL选择一个基地址(链接器开关),这不是减少DLL变基的标准值。它不是强制性的,但这将节省内存资源。
对于坏消息感到抱歉,但在我看来,您当前的代码应该被完全重写。
根据问题中的更新 更新:我再说一遍,如果你在一个进程SetWindowsHookEx
中调用来设置CBT挂钩,你应该作为参数给出模块DLL的实例(起始地址)和实现钩子的DLL中的函数的地址。从哪个进程调用SetWindowsHookEx
函数并不重要。用作参数的DLL将在使用User32.dll的同一Windows工作站的所有进程中加载(注入)。所以你有一些原生的限制。如果要支持32位和64位平台,则必须实现两个 dll:一个32位和64位DLL。此外,在同一进程中使用不同的.NET版本存在问题。从理论上讲,只有使用.NET 4.0才能实现这一点。一般来说这是一个非常复杂的问题。你应该理解它我写的关于DLL的意思不仅是DLL,还有它的所有依赖。因此,如果您实现调用托管DLL(.NET DLL)的本机DLL,则无法实现。
因此,如果你想使用全局CBT钩子你必须实现,如果作为两个本机DLL (一个32位和64位)并设置安装钩子内部的两个进程(一个32位和64位)。因此,请准确说明SetWindowsHookEx
文档http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx的注释中所描述的内容(参见上面的引用)。我认为没有更简单的方法了。