【问题标题】:Powershell / C# keyhook script NullReferenceException externallyPowershell / C# keyhook 脚本 NullReferenceException 外部
【发布时间】:2019-04-04 14:26:13
【问题描述】:

我正在尝试使用 powershell 脚本(使用 C#)来收听击键。它在出错之前工作了很短的时间。有趣的是,在我添加了一些控制台写入后,错误似乎发生在我的代码外部。

这是 powreshell 脚本:

Add-Type -TypeDefinition @"
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    namespace KeyLogger {
        public static class Program {
            private const int WH_KEYBOARD_LL = 13;
            private static IntPtr hookId = IntPtr.Zero;

            public static void Begin() {
                hookId = SetHook();
                Application.Run();
                UnhookWindowsHookEx(hookId);
            }

            private static IntPtr SetHook() {
                IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
                return SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, moduleHandle, 0);
            }

            private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
                Console.WriteLine('0');
                if (nCode == 0) {
                    Console.WriteLine('1');
                    Console.WriteLine(Marshal.ReadInt32(lParam) + " " + wParam + " ");
                }
                Console.WriteLine('2');
                IntPtr x = CallNextHookEx(hookId, nCode, wParam, lParam);
                Console.WriteLine('3');
                return x;
            }

            private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

            [DllImport("user32.dll")]
            private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
            [DllImport("user32.dll")]
            private static extern bool UnhookWindowsHookEx(IntPtr hhk);
            [DllImport("user32.dll")]
            private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
            [DllImport("kernel32.dll")]
            private static extern IntPtr GetModuleHandle(string lpModuleName);
        }
    }
"@ -ReferencedAssemblies System.Windows.Forms

[KeyLogger.Program]::Begin();

错误是:

发生了未正确处理的错误。附加信息如下所示。 Windows PowerShell 进程将退出。 未处理的异常:System.NullReferenceException:对象引用未设置为对象的实例。

注意 HookCallback 方法中的 Console.WriteLine 。但是最后打印的调试行是3,表示错误发生在HookCallback之外。更有趣的是,如果我有多个脚本实例正在运行,它们不会同时出错;一个可能会崩溃,但其余的会继续运行一段时间。

我对 powershell 或 C# 不太熟悉,所以想知道如何获取更多调试信息?我希望出现错误的行号或文件或库名称,但不确定如何获取该信息。

免责声明,我从这里https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-keyboard-hook-in-c/ 获取脚本(稍作修改)。

编辑:我接受了 Stuartd 的回答,因为它非常有帮助,但正如我在 cmets 中所说,它需要进行一些小的修改才能工作。 为了方便未来的读者,以下是有效的修改:

 private static HookProc callback;
 ...

 private static IntPtr SetHook() {
     callback = HookCallback;
     IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
     return SetWindowsHookEx(WH_KEYBOARD_LL, callback, moduleHandle, 0);
 }
 ...

我在 C# 上做了更多的搜索和阅读,似乎原因是 SetWindowsHookEx 没有管理/保持活动它的 hookProc 委托参数,并且传递一个静态方法直接创建一个本地(方法的本地) 委托,一旦 SetHook 方法返回就可以删除了。

【问题讨论】:

  • 如果重要的话,x 似乎指向地址 0; Console.WriteLine("3 [" + x + "]");3 [0].
  • 我的猜测是,在应用程序中使用了相同的代码后,委托正在被释放。在您致电SetWindowsHookEx 后添加GC.KeepAlive(HookProc); - 正如我必须做的here
  • 您是否考虑过在 Visual Studio 中构建并调试它?
  • @stuartd, GC.KeepAlive(HookProc); 给了我编译错误'KeyLogger.Program.HookProx' is a 'type' but is used like a 'variable'。但我认为我的问题可能与 GC 有关,因为当我有大事运行时,它似乎会更快发生。
  • 确实如此。嗯.. 一秒钟-

标签: c# windows powershell keylogger keyhook


【解决方案1】:

这可能会做到:创建一个引用HookProc 的字段,并告诉 GC 使其保持活动状态:

public static class Program
{
    private const int WH_KEYBOARD_LL = 13;
    private static IntPtr hookId = IntPtr.Zero;
    private static HookProc proc;

    public static void Begin()
    {
        proc = HookCallback;
        hookId = SetHook();
        GC.KeepAlive(proc);
        Application.Run();
        UnhookWindowsHookEx(hookId);
    }

     … etc


【讨论】:

  • 看看这个,如果 proc 在一个字段中被引用,我认为它不会被处理。但是当我不得不这样做时,那是十多年前的事了,当时它“有效”,所以我没有进一步挖掘。
  • 我不想过早开枪,但它似乎 1)在调用SetHook之前移动proc分配,然后2)传递@ 987654325@ 而不是HookCallback 作为SetHookSetWindowsHookEx 的第二个参数可能已经解决了这个问题。你说得对,GC.KeepAlive 似乎没有必要。
猜你喜欢
  • 2021-12-26
  • 2021-06-21
  • 1970-01-01
  • 2021-12-24
  • 2015-06-29
  • 1970-01-01
  • 2023-03-22
  • 2017-08-04
  • 2019-09-30
相关资源
最近更新 更多