0x01 钩子

钩子,英文Hook,泛指钓取所需东西而使用的一切工具。后来延伸为“偷看或截取信息时所用的手段或工具”。

  • 挂钩:为了偷看或截取来往信息而在中间设置岗哨的行为
  • 钩取:偷看或操作信息的行为

0x02 消息钩子

敲击键盘时,消息会从OS(Operating System的简称)移动到应用程序,而消息钩子就是在这个过程中偷看信息。

下面以键盘信息为例

常规Windows消息流:

1、发生键盘输入事件时,WM_KEYDOWN消息被添加到[OS message queue];

2、OS判断哪个应用程序中发生了事件,然后从[OS message queue]中取出消息,添加到相应应用程序的[application message queue]中;

3、应用程序监视自身的[application message queue],发现新添加的WM_KEYDOWN消息后,调用相应的事件处理程序处理。

附带钩子的信息流:

  1. 发生键盘输入事件,WM_KEYDOWN消息被添加到OS消息队列;
  2. OS判断哪个应用程序发生了事件,从OS消息队列中取出消息,发送给应用程序;
  3. 钩子程序截取信息,对消息采取一定的动作(因钩子目的而定);
  4. 如钩子程序不拦截消息,消息最终传输给应用程序,此时的消息可能经过了钩子程序的修改。

如下图,OS消息队列和应用程序消息队列之间存在一条钩链(Hook Chain),设置好键盘消息钩子后,处于钩链中的键盘消息钩子会比应用程序先一步看到相应信息。在键盘消息钩子函数的内部,除了可以查看消息之外,还可以修改消息本身,而且还能对消息实施拦截,阻止消息传递。可以同时设置多个相同的键盘消息钩子,按照设置的顺序依次调用,从而组成的链条称为钩链。

0x03 SetWindowsHookEx()

在Windows中可以使用SetWindowsHookEx()API来设置消息钩子,这个函数除了可以设置当前进程的钩子之外,它还可以设置全局钩子。全局钩子,顾名思义,即当前正在运行的进程都会被设置相应的钩子。

Windows API作用类似是一个个功能函数。

定义如下

HHOOK SetWindowsHookExA(
int idHook, //hook type 钩子类型
HOOKPROC lpfn, //hook procedure 回调函数地址
HINSTANCE hMod, //hook procedure所属的dll句柄
DWORD dwThreadId //想要挂钩的线程PID
);

第一个参数表征钩子的类型,但钩子的类型是微软规定好的,你只能选一种,自己不能乱写

第二个参数是钩子执行程序,即当钩子勾到所需信息时运行的程序

第三个参数是要注入的dll句柄

第四个参数是想要挂载的线程ID,如果该参数为0,则表明钩子是一个全局钩子

HHOOK:返回值,钩子句柄,需要保留,等不使用钩子时通过UnhookWindowsHookEx函数卸载钩子。

idHook:钩子的拦截消息类型,选择钩子程序的拦截范围,具体值参考文章结尾的消息类型。

Lpfn:消息的回调函数地址,钩子子程的地址指针,一般是填函数名。

hMod:钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。在dll中可通过AfxInitExtensionModule(MousehookDLL, hInstance)获得DLL句柄。

dwThreadId:钩子所监视的线程的线程号,可通过GetCurrentThreadId()获得线程号。对于全局钩子,该参数为NULL(或0)。

使用SetWindowsHookEx()设置好钩子后,在某个进程中生成指定消息时,OS会将相关的DLL文件强制注入相应的进程,然后调用注册的钩子过程。

0x04 键盘消息钩取练习

下面做一个简单练习,先看图



KeyHook.dll文件是一个含有钩子过程(KeyboardProc)的DLL文件,HookMain.exe是最先加载KeyHook.dll并安装键盘钩子的程序。HookMain.exe加载KeyHook.dll后使用SetWindowsHookEx()安装键盘钩子;若其他进程(如图中所示)发生键盘输入事件,OS就会强制将KeyHook.dll加载到像一个进程的内存,然后调用KeyboardProc()函数。

先分析源码

keyHook.cpp

//KeyHook.cpp

#include "stdio.h"
#include "windows.h" //定义目标进程名为notepad.exe
#define DEF_PROCESS_NAME "notepad.exe" //定义全局变量
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL; //DllMain()函数在DLL被加载到进程后会自动执行
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved){
switch( dwReason ){
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break; case DLL_PROCESS_DETACH:
break;
} return TRUE;
} //
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam){
char szPath[MAX_PATH] = {0,};
char *p = NULL; if( nCode >= 0 ){ //释放键盘按键时,bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) ){
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\'); //比较当前进程名称,若为notepad.exe,则消息不会传递给应用程序或下一个钩子函数
//_stricmp()函数用于比较字符串,i表示不区分大小写,若两个值相等则返回0
if( !_stricmp(p + 1, DEF_PROCESS_NAME) ){
return 1;
}
}
} //比较当前进程名称,若非notepad.exe,则消息传递给应用程序或下一个钩子函数
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
} //在C++中调用C的库文件,用extern "C"告知编译器,因为C++支持函数重载而C不支持,两者的编译规则不同
#ifdef __cplusplus
extern "C"{
#endif
//__declspec,针对编译器的关键字,用于指出导出函数
//当调用导出函数HookStart()时,SetWindowsHookEx()函数就会将KeyboardProc()添加到键盘钩链
__declspec(dllexport) void HookStart(){
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
} __declspec(dllexport) void HookStop(){
if(g_hHook){
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif

因为要生成的是KeyHook.dll文件,因而在开始创建项目时应先选择Win 32控制台应用程序,再到应用程序类型中勾选DLL,编写好cpp文件后选择Release再生成文件即可得到DLL文件。

当调用导出函数HookStart()时,SetWindowsHookEx()函数就会将KeyboardProc()添加到键盘钩链。安装好键盘钩子后,无论哪个进程,只要发生键盘输入事件,OS都会强制将KeyHook.dll注入相应的进程中。

KeyboardProc()函数中发生键盘输入事件时,会比较当前进程名称和“notepad.exe”是否一致,若一致则返回1,终止KeyboardProc()函数,即截获并删除消息,从而实现对notepad.exe程序的键盘输入事件进行钩取并截获删除、键盘消息不能传递到notepad.exe的消息队列中。

KeyboardProc()函数定义如下:

LRESULT CALLBACK KeyboardProc(
int code, //HC_ACTION(0), HC_NOREMOVE(3)
WPARAM wParam, //virtual-key code
LPARAM lParam //extra information
);

其中wParam指用户按下的键盘按键的虚拟键值。

HookMain.cpp

//HookMain

#include "stdio.h"
#include "windows.h"
//Console Input/Output,定义了通过控制台进行数据输入和数据输出的函数
//主要是一些用户通过按键盘产生的对应操作,比如getch()函数等等
#include "conio.h" //定义一些常量
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop" //定义两个参数为空、返回值为void即没有的函数指针
typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)(); void main(){
//定义及初始化句柄变量和函数指针
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL; //加载KeyHook.dll
hDll = LoadLibraryA(DEF_DLL_NAME); //若加载不成功,则输出错误信息
if( hDll == NULL ){
printf("[-]无法加载%s [%d]\n", DEF_DLL_NAME, GetLastError());
return;
} //获取导出函数地址
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP); //开始钩取
HookStart(); //直至用户输入“q”退出钩取
printf("[*]等待输入 'q' 来停止钩取...\n");
while( _getch() != 'q' ); //终止钩取
HookStop(); //卸载KeyHook.dll
FreeLibrary(hDll);
}

先加载KeyHook.dll,再调用HookStart()函数开始钩取,当获取到用户输入“q”后调用HookStop()函数终止钩取。

代码流程分析概括

安装好键盘钩子后,无论在哪个进程中,只要发生了键盘输入事件,OS就会强制将KeyHook.dll注入到进程中,加载了KeyHook.dll的进程,发生键盘事件时会首先调用执行KeyHook.KetyboardProc()

KetyboardProc()函数中发生键盘输入事件时,会比较当前进程的名称与“notepad.exe”是否相同,相同返回1,终止KetyboardProc()函数,意味着截获并删除了消息,这样键盘消息就不会传递到notepad.exe程序的消息队列。

练习示例HookMain.exe

注:HookMain.exe 要和 KeyHook.dll放在一个目录下

运行HookMain.exe之后,再打开notepad.exe,输入键盘信息,发现打不上去,notepad.exe卡死。

我使用的系统是win10 64位系统,经过多方面搜索,终于确定问题,版本不匹配,dll因为CPU架构不匹配不能注入时,程序会通过SendMessage来调用你的KeyBoardProc,然后程序就卡住了,一直卡在对话框界面,卡死…

直接编译成64位的程序解决问题,同时,注入程序和dll都要是64位.

(参考知乎回答https://www.zhihu.com/question/64221483)

这里就不便示例了,就是大概这样

0x05 调试HookMain.exe

使用Ollydbg打开,可以看到是典型的VC++启动函数,图中显示的是HookMain.exe的EP(Entry Point,程序入口地址)代码

查找核心代码

我们关心的是核心的键盘钩取部分的代码,如何查找核心代码?

  1. 逐步跟踪(除非迫不得已!)
  2. 检索相关API (Ctrl+G)
  3. 检索相关字符串

我们已经知道程序的功能,会在控制台显示字符串“press ‘q’ to quit!”,所以先检查程序导入的字符串(Search for -All referencen text strings):



地址40104d处引用了要查找的字符串,双击跳转:



来到main函数处。

调试main函数

401000双击下断F9运行,到断点停下来后,开始F8/F7调试,了解main函数中主要的代码流。401006地址处调用LoadLibraryA(Keyhook.dll),然后由40104b地址处的CALL EBX指令调用KeyHook.HookStart()函数。在40104b处,F7单步跟进查看:



这里的代码是被加载到HookMain.exe进程中的KeyHook.dll的HookStart()函数,第一句就是调用SetWindowsHookExW()函数,在进行参数入栈操作后,我们可以在栈中看到函数的4个参数值。

再附上一张作者写的流程

参考博客

dll注入

看雪原创Windows消息钩取

CSDN—Windows消息钩取

关于逆向工程核心原理-Windows消息钩取

关于《逆向工程核心原理》键盘钩子的总结

DLL注入-Windows消息钩取的更多相关文章

  1. DLL注入之windows消息钩取

    DLL注入之windows消息钩取 0x00 通过Windows消息的钩取 通过Windows消息钩取可以使用SetWindowsHookEx.该函数的原型如下: SetWindowsHookEx( ...

  2. 《逆向工程核心原理》Windows消息钩取

    DLL注入--使用SetWindowsHookEx函数实现消息钩取 MSDN: SetWindowsHookEx Function The SetWindowsHookEx function inst ...

  3. Reverse Core 第三部分 - 21章 - Windows消息钩取

    @author: dlive @date: 2016/12/19 0x01 SetWindowsHookEx() HHOOK SetWindowsHookEx( int idHook, //hook ...

  4. Windows消息钩取

    @author: dlive @date: 2016/12/19 0x01 SetWindowsHookEx() HHOOK SetWindowsHookEx( int idHook, //hook ...

  5. 逆向学习-Windows消息钩取

    钩子 Hook,就是钩子.偷看或截取信息时所用的手段或工具. 消息钩子 常规Windows流: 1.发生键盘输入事件时,WM_KEYDOWN消息被添加到[OS message queue]. 2.OS ...

  6. SetWindowsHookEx 消息钩取进程卡死

    <逆向工程核心原理> windows消息钩取部分的例子在win10下卡死,失败.通过搜索发现,要保证钩取的进程与注入的dll要保持cpu平台相同 SetWindowsHookEx可用于将d ...

  7. dll注入及卸载实践

    三种方法:具体详见<逆向工程核心原理>. 1.创建远程线程CreateRemoteThread() 2.使用注册表AppInit_DLLs 3.消息钩取SetWindowsHookEx() ...

  8. 逆向学习-DLL注入

    DLL注入技术,可以实现钩取API,改进程序,修复Bug. DLL注入指的是向运行中的其他进程强制插入特定的DLL文件. DLL注入命令进程自行调用LoadLibrary()API,加载用户指定的DL ...

  9. Dll注入:Windows消息钩子注入

    SetWindowsHook() 是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的.当消息到达后,在目标窗口处理函数之 ...

  10. 通过注入DLL后使用热补丁钩取API

    通过注入DLL后使用热补丁钩取API 0x00 对比修改API的前五个字节钩取API 对前一种方法钩取API的流程梳理如下: 注入相应的DLL 修改原始AI的函数的前五个字节跳往新函数(钩取API) ...

随机推荐

  1. Java学习笔记06

    1. 类和对象 1.1 类和对象 ​ 客观存在的事物皆为对象,所以我们也常常说万物皆对象. 类 类的理解 类是对现实生活中一类具有共同属性和行为的事物的抽象 类是对象的数据类型,类是具有相同属性和行为 ...

  2. 基于Mongodb分布式锁简单实现,解决定时任务并发执行问题

    前言 我们日常开发过程,会有一些定时任务的代码来统计一些系统运行数据,但是我们应用有需要部署多个实例,传统的通过配置文件来控制定时任务是否启动又太过繁琐,而且还经常出错,导致一些异常数据的产生 网上有 ...

  3. 没有杯子的世界:OOP设计思想的应用实践

    最近看到一个有趣的问题:Person类具有Hand,Hand可以操作杯子Cup,但是在石器时代是没有杯子的,这个问题用编程怎么解决? 简单代码实现 我们先用简单代码实现原问题: @Data publi ...

  4. 机器视觉基本理论(opencv)

    1. 什么是图像采样 采样是按照某种时间间隔或空间间隔,将空间上连续的图像变换成离散点的操作称为图像采样 2. 什么是图像分变率 采样 得到的离散图像的尺寸称为图像分辨率.分辨率是数字图像可辨别的最小 ...

  5. Spring之丐版IOC实现

    文章目录 IOC控制反转 依赖注入 Bean的自动装配方式 丐版IOC实现 BeanDefinition.java ResourceLoader.java BeanRegister.java Bean ...

  6. rnacos——用rust重新实现的nacos开源配置、注册中心服务

    1. 简介 rnacos 是一个用rust实现的nacos服务. rnacos是一个轻量.快速.稳定的服务,包含注册中心.配置中心.web管理控制台功能. rnacos兼容nacos client s ...

  7. Django笔记三十九之settings配置介绍

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十九之settings配置介绍 这一篇笔记介绍 Django 里 settings.py 里一些常用的配置项,这些配置有一些是在之前的笔 ...

  8. Prompt learning 教学[进阶篇]:简介Prompt框架并给出自然语言处理技术:Few-Shot Prompting、Self-Consistency等;项目实战搭建知识库内容机器人

    Prompt learning 教学[进阶篇]:简介Prompt框架并给出自然语言处理技术:Few-Shot Prompting.Self-Consistency等:项目实战搭建知识库内容机器人 1. ...

  9. SQL课设之报刊订阅管理

    E-R图: 一些常用命令: 1.登录mysql 2.选择进入数据库  相关SQL代码如下: CREATE TABLE Administrator( Gname varchar(50)PRIMARY K ...

  10. < Python全景系列-4 > 史上最全文件类型读写库大盘点!什么?还包括音频、视频?

    欢迎来到我们的系列博客<Python全景系列>!在这个系列中,我们将带领你从Python的基础知识开始,一步步深入到高级话题,帮助你掌握这门强大而灵活的编程语言! 本文系列第四篇,介绍史上 ...