jjzjj

c++ - 32 位程序无法捕获在 32 位进程上进行的击键,但能够捕获在 64 位进程上进行的击键

coder 2024-06-17 原文

我的环境详细信息:

  • 操作系统:Windows 7 Enterprise Service Pack 1(64 位操作系统)
  • 编译器:Microsoft Visual Studio 2005(Microsoft (R) 32 位 C/C++ 优化编译器版本 14.00.50727.762,适用于 80x86)
  • 我的程序 main.exe 和 hook.dll 是 32 位的
  • Internet Explorer (iexplore.exe) 是 64 位
  • Chrome (chrome.exe) 是 32 位的

我编写了一个名为 main.exe 的 C++ 程序,它进行以下调用:

HOOKPROC callback = (HOOKPROC) GetProcAddress(dll, "keyboardHook");
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, callback, dll, NULL);

keyboardHook 函数定义在名为 hook.dll 的 DLL 中。此函数记录控制台上的每次击键以及名为 out.txt 的文件。

我进行了以下实验。

  1. 在命令提示符下执行 main.exe。
  2. 将 Internet Explorer(64 位)设置为我的事件窗口,然后按 ABC
  3. 将 Chrome(32 位)设为我的事件窗口,然后按 XYZ
  4. 使命令提示符成为我的事件窗口并按 Ctrl + C 终止 main.exe。

我在控制台上只看到以下输出。

C:\lab\keyhook>main,exe
hinstDLL: 10000000; fdwReason: 1; lpvReserved: 00000000
); scan code: 28; transition state: 1
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 0
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 1
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 0
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 1
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 1
nCode: 0; wParam: 17 (◄); scan code: 29; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
hinstDLL: 10000000; fdwReason: 2; lpvReserved: 00000000
hinstDLL: 10000000; fdwReason: 0; lpvReserved: 00000001

我在登录的文件中只看到以下输出。

C:\lab\keyhook>type out.txt
); scan code: 28; transition state: 1
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 0
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 1
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 0
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 1
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 1
nCode: 0; wParam: 17 (◄); scan code: 29; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0

您可以看到在 32 位 Chrome 上进行的击键没有被捕获到控制台或日志文件中。

为什么我用 32 位 C++ 编译器编译的 32 位程序可以成功捕获在 64 位 iexplore.exe 上进行的击键,但无法捕获在 32 位 chrome.exe 上进行的击键?

SetWindowsHookEx documentation在 MSDN 上似乎表明我的 32 位程序应该只能捕获在其他 32 位程序上进行的击键。

SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names.

Because hooks run in the context of an application, they must match the "bitness" of the application. If a 32-bit application installs a global hook on 64-bit Windows, the 32-bit hook is injected into each 32-bit process (the usual security boundaries apply). In a 64-bit process, the threads are still marked as "hooked." However, because a 32-bit application must run the hook code, the system executes the hook in the hooking app's context; specifically, on the thread that called SetWindowsHookEx. This means that the hooking application must continue to pump messages or it might block the normal functioning of the 64-bit processes.

下面是调用 SetWindowsHookEx 的主程序的完整代码。

// Filename: main.cc
#include <iostream>
#include <windows.h>

int main(int argc, char **argv)
{
    HMODULE dll = LoadLibrary("hook.dll");
    if (dll == NULL) {
        std::cerr << "LoadLibrary error " << GetLastError() << std::endl;
        return 1;
    }

    HOOKPROC callback = (HOOKPROC) GetProcAddress(dll, "keyboardHook");
    if (callback == NULL) {
        std::cerr << "GetProcAddress error " << GetLastError() << std::endl;
        return 1;
    }

    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, callback, dll, NULL);
    if (hook == NULL) {
        std::cerr << "SetWindowsHookEx error " << GetLastError() << std::endl;
        return 1;
    }

    MSG messages;
    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    UnhookWindowsHookEx(hook);
}

这是DLL代码。

// Filename: hook.cc
#include <iostream>
#include <fstream>
#include <windows.h>

extern "C" __declspec(dllexport)
LRESULT keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    std::ofstream outputTxt("out.txt", std::ofstream::out | std::ofstream::app);
    outputTxt << "nCode: " << nCode << "; wParam: " << wParam
              <<" (" << char(wParam) << "); scan code: "
              << ((lParam & 0xFF0000) >> 16)
              << "; transition state: " << ((lParam & 0x80000000) >> 31)
              << std::endl;
    outputTxt.close();

    std::cout << "nCode: " << nCode << "; wParam: " << wParam
              <<" (" << char(wParam) << "); scan code: "
              << ((lParam & 0xFF0000) >> 16)
              << "; transition state: " << ((lParam & 0x80000000) >> 31)
              << std::endl;
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    std::cout << "hinstDLL: " << hinstDLL
              << "; fdwReason: " << fdwReason
              << "; lpvReserved: " << lpvReserved << std::endl;

    return TRUE;
}

这就是我编译这个项目的方式:

vcvars32.bat
cl /LD hook.cc /link user32.lib
cl main.cc /link user32.lib

这就是流程的样子。请参阅 chrome.exe 和 main.exe 是 32 位进程,而 iexplore.exe 的实例是 64 位进程。

您能解释一下为什么我的观察结果与 MSDN 文档不符吗?

最佳答案

Hans Passant 对这个问题的评论和 this answer by manuell帮助我理解和修复我的代码。

相对路径 “out.txt” 是所有困惑的根源。

我的 32 位程序成功地将其 32 位钩子(Hook)注入(inject) 32 位 Chrome。因此,钩子(Hook)在 Chrome 的上下文中运行,并且“out.txt”文件写入 Chrome 的工作目录中。因此,作为事件窗口在 Chrome 上进行的击键 XYZ 记录在“C:\Program Files (x86 )\Google\Chrome\Application\31.0.1650.63\out.txt"。

C:\>type "C:\Program Files (x86)\Google\Chrome\Application\31.0.1650.63\out.txt"
nCode: 0; wParam: 88 (X); scan code: 45; transition state: 0
nCode: 0; wParam: 88 (X); scan code: 45; transition state: 1
nCode: 0; wParam: 89 (Y); scan code: 21; transition state: 0
nCode: 0; wParam: 89 (Y); scan code: 21; transition state: 1
nCode: 0; wParam: 90 (Z); scan code: 44; transition state: 0
nCode: 0; wParam: 90 (Z); scan code: 44; transition state: 1

此输出未出现在控制台中,因为没有控制台与 Chrome 关联。

但是,我的 32 位程序无法将其 32 位 Hook 注入(inject) 64 位 Internet Explorer。因此, Hook 在我的程序上下文中执行,并且当它记录在 64 位 Internet Explorer 上进行的击键时,“out.txt”文件在我的程序的工作目录中创建。此输出也记录在控制台上,因为有一个控制台与我的程序相关联。这就是您在我的问题中看到的内容。

我的代码的一个简单修复是将击键记录到绝对路径 "C:\\out.txt" 而不是 hook 中的相对路径 "C:\out.txt".cc,这样我就可以在同一个文件中看到在 32 位程序和 64 位程序上进行的击键。

关于c++ - 32 位程序无法捕获在 32 位进程上进行的击键,但能够捕获在 64 位进程上进行的击键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20910872/

有关c++ - 32 位程序无法捕获在 32 位进程上进行的击键,但能够捕获在 64 位进程上进行的击键的更多相关文章

  1. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  9. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  10. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

    我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

随机推荐