基于C#实现简易的键盘记录器
作者:Csharp 发布时间:2023-07-02 21:32:41
标签:C#,键盘,记录器
利用HOOK技术来做一个键盘记录器,看看一天下来,我们点击了多少次键盘,哪些键的使用频率最高。
实现功能
使用C#实现一个键盘记录器
开发环境
开发工具: Visual Studio 2013
.NET Framework版本:4.5
实现代码
public class HookUtil
{
#region windows api
/// <summary>
/// 安装钩子
/// </summary>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
/// <summary>
/// 继续下一个钩子
/// </summary>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
/// <summary>
/// 卸载钩子
/// </summary>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
/// <summary>
///获取当前线程编号(线程钩子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
/// <summary>
/// 获取当前实例的函数
/// </summary>
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
/// <summary>
/// 获取按键的状态
/// </summary>
/// <param name="pbKeyState"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern int GetKeyboardState(byte[] pbKeyState);
/// <summary>
/// 将指定的虚拟键码和键盘状态翻译为相应的字符或字符串
/// </summary>
[DllImport("user32")]
public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState,byte[] lpwTransKey,int fuState);
#endregion
/// <summary>
/// 键盘结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
{
public int vkCode; //定一个虚拟键码。该代码必须有一个价值的范围1至254
public int scanCode; // 指定的硬件扫描码的关键
public int flags; // 键标志
public int time; // 指定的时间戳记的这个讯息
public int dwExtraInfo; // 指定额外信息相关的信息
}
//定义为键盘钩子
public int WH_KEYBOARD_LL = 13;
//相关键盘事件
public event KeyEventHandler KeyDownEvent;
public event KeyPressEventHandler KeyPressEvent;
public event KeyEventHandler KeyUpEvent;
//相关动作
private const int WM_KEYDOWN = 0x100;//KEYDOWN
private const int WM_KEYUP = 0x101;//KEYUP
private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN
private const int WM_SYSKEYUP = 0x105;//SYSKEYUP
//hookid
private int hookID = 0;
//向下传递数据
public Keys NoNextKeyCode;
/// <summary>
/// 安装钩子
/// </summary>
public void StartHook()
{
if (hookID == 0)
{
HookProc hookProc = new HookProc(KeyboardHookProc);
hookID = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
if (hookID == 0)
{
StopHook();
throw new Exception("安装键盘钩子失败");
}
}
}
public void StopHook()
{
bool isStop = true;
if (hookID != 0)
{
isStop = UnhookWindowsHookEx(hookID);
hookID = 0;
}
if (!isStop) throw new Exception("卸载键盘钩子失败!");
}
/// <summary>
/// 监听事件
/// </summary>
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
//按下处理
if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDownEvent(this, e);
//阻止向下传递
if (NoNextKeyCode == keyData)
{
return hookID;
}
}
//按下并抬起处理
if (KeyPressEvent != null && wParam == WM_KEYDOWN)
{
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
{
KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
KeyPressEvent(this, e);
}
}
// 抬起处理
if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUpEvent(this, e);
}
}
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
~HookUtil()
{
StopHook();
}
}
HookUtil keyHook = new HookUtil();
private void btnBegin_Click(object sender, EventArgs e)
{
keyHook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//钩住按下事件
keyHook.StartHook();
btnBegin.Enabled = false;
btnEnd.Enabled = true;
}
private void btnEnd_Click(object sender, EventArgs e)
{
keyHook.StopHook();
btnBegin.Enabled = true;
btnEnd.Enabled = false;
}
private void btnInfo_Click(object sender, EventArgs e)
{
string path = System.AppDomain.CurrentDomain.BaseDirectory + "log\\" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
if (!File.Exists(path))
{
MessageBox.Show("还未监听到数据,请操作后再查看");
return;
}
var list = File.ReadAllLines(path).ToList().GroupBy(s => s).Select(s => new { s.Key, s.ToList().Count }).OrderByDescending(s => s.Count);
FrmInfo frm = new FrmInfo();
frm.Show();
foreach (var item in list)
{
frm.addItems(new string[] { "", item.Key, item.Count + "" });
}
}
private void hook_KeyDown(object sender, KeyEventArgs e)
{
if (!listKey.Contains(e.KeyData))
{
if (Control.ModifierKeys != Keys.None)
{
WriteLog(Control.ModifierKeys + "+" + e.KeyData);
}
else
{
WriteLog(e.KeyData + "");
}
}
else
{
WriteLog(e.KeyData + "");
}
}
实现效果
来源:https://blog.csdn.net/qq_27410185/article/details/126374859


猜你喜欢
- Mybatis的Dao层实现传统开发方式编写UserDao接口public interface UserDao {  
- 本文实例为大家分享了Android实现旋转动画的具体代码,供大家参考,具体内容如下旋转动画(可加速、减速)1、准备工作首先需要有一个用于旋转
- 最近由于工作要求:前端采用vue开发,后端采用springboot开发,前后端分离开发,最后前端页面又整合到后端来。经历多次采坑,总结以下方
- 一、AtomicReference 基本使用我们这里再聊起老生常谈的账户问题,通过个人银行账户问题,来逐渐引入 AtomicReferenc
- 1. 引言在 Java 应用程序中,异常类对于正确捕获和处理错误至关重要。我们常常在编写异常处理的重复代码上花费时间,而不是关注应用程序的其
- 前言在项目开发中,日志系统是必不可少的,用AOP在Web的请求做入参和出参的参数打印,同时对异常进行日志打印,避免重复的手写日志,完整案例见
- Android PopWindow 设置背景亮度的实例设置背景 /** * 设置添加屏幕的背景透明度 * @param bgAl
- 这是调用相机 public static File getImageFromCamer(Context context, File
- Java中,将字节数组转成图片的有很多种方式,今天在这里记录其中一种,方便以后查询,也可以提供给没有接触的童鞋做一个参考。首先是将图片转成字
- 本文实例讲述了Android数据库中事务操作方法之银行转账功能。分享给大家供大家参考,具体如下:主javapackage com.ithei
- 1.把springboot项目打包成三个jar包,并指定端口为14341,14342,143432.下载腾讯云免费ssl证书,解压后会出现如
- 用了MyBatis的同行,应该见过foreach,它一般是这样用的:<select id="foreachTest"
- 随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、先后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远
- 本文详细地介绍了Java内存管理的原理,以及内存泄露产生的原因,同时提供了一些列解决Java内存泄露的方案,希望对各位Java开发者有所帮助
- 现在基于信息安全问题,特别是版本是23以上权限越严格。特别是拍照,读,写权限一般权限允许过,下次就不用询问了的,所以很多应用都喜欢在首页或者
- Android中实现全屏、无标题栏的两种办法,另附Android系统自带样式的解释实现全屏无标题栏:1.在xml文件中进行配置 Androi
- 需求winForm 程序输出类型为 windows 程序(不是命令行程序)在运行时想输入一些信息编译开发调试,如何实现这一功能解答:Allo
- 在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的、不可预知的异
- 传输层安全性协议(英语:Transport Layer Security,缩写作 TLS),及其前身安全套接层(Secure So
- 这篇山寨一个新版QQ的列表滑动删除,上篇有说到QQ的滑动删除,推测原理就是ListView本身每个item存在一个Button,只不过普通的