HOOK学习笔记与心得

一、  Hook介绍
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
 我们知道Windows系统API函数都是被封装到DLL中,在某个应用程序要调用一个API函数的时候,如果这个函数所在的DLL没有被加载到本进程中则加载它,然后保存当前环境(各个寄存器和函数调用完后的返回地址等)。接着程序会跳转到这个API函数的入口地址去执行此处的指令。由此看来,我们想在调用真正的API之前先调用我们的函数,那么可以修改这个API函数的入口处的代码,使他先跳转到我们的函数地址,然后在我们的函数最后再调用原来的API函数。
简单来说HOOK API 可以理解成对程序将要执行系统函数的一个拦截, 拦截后执行自己写的代码以达到完成某种特定的目的,再恢复程序继续执行,很多PJ中的Patch 机器码,盗号木马等都是用这个方法。
二、  Hook API实战
OK,既然我们需要实现一个这样的一个HOOK。当然需要两样东西,一是目标程序,一是我们的代码。
1.  程序:
新建一个dll工程文件目录如图:
附件 93152
我们自己新建一个Add.def文件,然后添加到工程中即可,如图我是添加到Source Files里。
跟exe有个main或者WinMain入口函数一样,DLL也有它自己的一个入口函数,就是DllMain。
我们打开dllmain.cpp  代码如下:

代码:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"int WINAPI add(int a, int b)
{return a + b;
}BOOL APIENTRY DllMain(HANDLE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{return TRUE;
}



DEF文件是模块定义文件,模块定义 (.def) 文件为链接器提供有关被链接程序的导出、属性及其他方面的信息。生成 DLL 时,.def 文件最有用。由于存在可代替模块定义语句使用的链接器选项,通常不需要 .def 文件。也可以将 __declspec(dllexport) 用作指定导出函数的手段。在链接器阶段可以使用 /DEF(指定模块定义文件)链接器选项调用 .def 文件。如果生成的 .exe 文件没有导出,使用 .def 文件将使输出文件较大并降低加载速度。
  在VC++中,生成DLL可以不使用.def文件。只需要在VC++的函数定义前要加__declspec(dllexport)修饰就可以了。但是使用__declspec(dllexport)和使用.def文件是有区别的。如果DLL是提供给VC++用户使用的,你只需要把编译DLL时产生的.lib提供给用户,它可以很轻松地调用你的DLL。但是如果你的DLL是供其他程序如VB、delphi,以及.NET用户使用的,那么会产生一个小麻烦。因为VC++对于__declspec(dllexport)声明的函数会进行名称转换,如下面的函数:
__declspec(dllexport) int __stdcallIsWinNT()
会转换为IsWinNT@0,这样你在VB中必须这样声明:
Declare Function IsWinNT Lib "my.dll" Alias "IsWinNT@0" () As Long
@的后面的数由于参数类型不同而可能不同。这显然不太方便。所以如果要想避免这种转换,就要使用.def文件方式。
EXPORTS后面的数可以不给,系统会自动分配一个数。对于VB、PB、Delphi用户,通常使用按名称进行调用的方式,这个数关系不大,但是对于使用.lib链接的VC程序来说,不是按名称进行调用,而是按照这个数进行调用的,所以最好给出。
  .def 文件中的第一条 LIBRARY 语句不是必须的,但LIBRARY 语句后面的 DLL 的名称必须正确,即与生成的动态链接库的名称必须匹配。此语句将 .def 文件标识为属于 DLL。链接器将此名称放到 DLL 的导入库中。
EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
如果使用 MFC DLL 向导创建 MFC DLL,则向导将为您创建主干 .def 文件并将其自动添加到项目中。添加要导出到此文件的函数名。对于非 MFC DLL,必须亲自创建 .def 文件并将其添加到项目中。
如果导出 C++ 文件中的函数,必须将修饰名放到 .def 文件中,或者通过使用外部“C”定义具有标准 C 链接的导出函数。如果需要将修饰名放到 .def 文件中,则可以通过使用 DUMPBIN 工具或 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与 DLL 的 .def 文件中的导出名相匹配。
因此def文件代码如下:

代码:

LIBRARY  Add
DESCRIPTION "ADD LA"
EXPORTS
add  @1;

void CMFCApplication5Dlg::OnBnClickedButton1()
{// TODO: Add your control notification handler code hereHINSTANCE hAddDll = NULL;typedef int (WINAPI*AddProc)(int a, int b);//函数原型定义  AddProc add;if (hAddDll == NULL){hAddDll = ::LoadLibrary(_T("Win32DLL.dll"));//加载dll  }add = (AddProc)::GetProcAddress(hAddDll, "add");//获取函数add地址  int a = 123;int b = 456;int c = add(a, b); CString tem;tem.Format(_T("%d+%d=%d"), a, b, c);AfxMessageBox(tem);}

代码:

// Hook.cpp : Defines the initialization routines for the DLL.
#include "stdafx.h"
#include "Hook.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//变量定义  #pragma data_seg("SHARED") //不同Instance共享的该变量   
static HHOOK  hhk = NULL; //鼠标钩子句柄  
static HINSTANCE hinst = NULL; //本dll的实例句柄 (hook.dll)  
#pragma data_seg()  
#pragma comment(linker, "/section:SHARED,rws")  
//以上的变量为共享  CString temp; //用于显示错误的临时变量  
bool bHook = false; //是否Hook了函数  
bool m_bInjected = false; //是否对API进行了Hook  
BYTE OldCode[5]; //原程序API入口代码  
BYTE NewCode[5]; //新跳转的API代码 (jmp xxxx)  
typedef int (WINAPI*AddProc)(int a, int b);//add.dll中的add函数定义  
AddProc add; //add.dll中的add函数  
HANDLE hProcess = NULL; //所处进程的句柄  
FARPROC pfadd;  //指向add函数的远指针  
DWORD dwPid;  //所处进程ID  
//end of 变量定义  
// CHookAppBEGIN_MESSAGE_MAP(CHookApp, CWinApp)
END_MESSAGE_MAP()//鼠标钩子过程,什么事情也不做,目的是注入dll到程序中  
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{return CallNextHookEx(hhk, nCode, wParam, lParam);
}
//开启钩子的函数  
void HookOn()
{ASSERT(hProcess != NULL);DWORD dwTemp = 0;DWORD dwOldProtect;//将内存保护模式改为可写,老模式保存入dwOldProtect  VirtualProtectEx(hProcess, pfadd, 5, PAGE_READWRITE, &dwOldProtect);//将所属进程中add()的前5个字节改为Jmp Myadd   WriteProcessMemory(hProcess, pfadd, NewCode, 5, 0);//将内存保护模式改回为dwOldProtect  VirtualProtectEx(hProcess, pfadd, 5, dwOldProtect, &dwTemp);bHook = true;
}
//关闭钩子的函数  
void HookOff()//将所属进程中add()的入口代码恢复  
{ASSERT(hProcess != NULL);DWORD dwTemp = 0;DWORD dwOldProtect;VirtualProtectEx(hProcess, pfadd, 5, PAGE_READWRITE, &dwOldProtect);WriteProcessMemory(hProcess, pfadd, OldCode, 5, 0);VirtualProtectEx(hProcess, pfadd, 5, dwOldProtect, &dwTemp);bHook = false;
}
//然后,写我们自己的Myadd()函数  
int WINAPI Myadd(int a, int b)
{//截获了对add()的调用,我们给a,b都加上一定的数a = a + 987;b = b + 654;HookOff();//关掉Myadd()钩子防止死循环  int ret;ret = add(a, b);HookOn();//开启Myadd()钩子  return ret;
}
//好,最重要的HOOK函数: 
void Inject()
{if (m_bInjected == false){ //保证只调用1次  m_bInjected = true;//获取add.dll中的add()函数  HMODULE hmod = ::LoadLibrary(_T("Win32DLL.dll"));add = (AddProc)::GetProcAddress(hmod, "add");pfadd = (FARPROC)add;if (pfadd == NULL){AfxMessageBox(L"cannot locate add()");}// 将add()中的入口代码保存入OldCode[]  _asm{lea edi, OldCodemov esi, pfaddcldmovsdmovsb}NewCode[0] = 0xe9;//实际上0xe9就相当于jmp指令  //获取Myadd()的相对地址  _asm{lea eax, Myaddmov ebx, pfaddsub eax, ebxsub eax, 5mov dword ptr[NewCode + 1], eax}//填充完毕,现在NewCode[]里的指令相当于Jmp Myadd  HookOn(); //可以开启钩子了  }
}CHookApp::CHookApp()
{}
CHookApp theapp;//鼠标钩子安装函数:  
BOOL InstallHook()
{hhk = ::SetWindowsHookEx(WH_MOUSE, MouseProc, hinst, 0);return true;
}//卸载鼠标钩子函数  
void UninstallHook()
{::UnhookWindowsHookEx(hhk);
}//在dll实例化中获得一些参数  
BOOL CHookApp::InitInstance()
{CWinApp::InitInstance();//获得dll 实例,进程句柄  hinst = ::AfxGetInstanceHandle();DWORD dwPid = ::GetCurrentProcessId();hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);//调用注射函数  Inject();return TRUE;
}


值得一提的是很多人都是改API开头的5个字节,但是现在很多杀毒软件用这样的方法检查API是否被HOOK,或其他病毒木马在你之后又改了前5个字节,这样就会互相覆盖,最后一个HOOK API的操作才是有效的。挂钩的方法很多,这里只是最基础的一种。希望大家多多补充。多多指正。
了解好以下的问题能够更好的提升自己的HOOK水平:
1.CPU指令长度问题,在32位系统里,一条JMP/CALL指令的长度是5个字节,因此你只有替换API里超过5个字节长度的机器码(或者替换几条指令长度加起来是5字节的指令),否则会影响被更改的小于5个字节的机器码后面的数条指令,甚至程序流程会被打乱,产生不可预料的后果;
2.参数问题,为了访问原API的参数,你要通过EBP或ESP来引用参数,因此你要非常清楚你的HOOK代码里此时的EBP/ESP的值是多少;
3.时机的问题,有些HOOK必须在API的开头,有些必须在API的尾部,比如HOOK CreateFilaA(),如果你在API尾部HOOK API,那么此时你就不能写文件,甚至不能访问文件;HOOK RECV(),如果你在API头HOOK,此时还没有收到数据,你就去查看RECV()的接收缓冲区,里面当然没有你想要的数据,必须等RECV()正常执行后,在RECV()的尾部HOOK,此时去查看RECV()的缓冲区,里面才有想要的数据;
4.上下文的问题,有些HOOK代码不能执行某些操作,否则会破坏原API的上下文,原API就失效了;
5.同步问题,在HOOK代码里尽量不使用全局变量,而使用局部变量,这样也是模块化程序的需要;
6.最后要注意的是,被替换的CPU指令的原有功能一定要在HOOK代码的某个地方模拟实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

access函数_ACCESS中的DLookUp函数是如何运算的?

​一、DLookUp函数介绍1. DLookUp函数的用途:可以用于从指定集合(一个域)中获取符合条件的特定字段的值。2. DLookUp函数的格式为:DLookUp( expr , domain , [criteria] )其中:expr 为字段名,或以字段名为基础的表达式字符串domai…

汇编语言之基础知识

1、机器语言 说到汇编语言的产生,首先要讲一下机器语言。机器语言是机器指令的集合。什么是机器指令?我们在使用CE时,常常见到。 请看下图: 图中所示的就是机器指令(或称机器码),这是十六进制的…

Entity Framework 简单增删改操作

前言 在 Entity Framework 简单查询操作 中主要是学习了在Entity Framework中的几种不同模式的查询操作,现在主要来学习一下简单的增加、删除、修改操作。 增加 在EF中添加操作一般有两种方式:一是直接创建对象,然后调用“DbSet”的”Add()”…

华为云服务器初探二(完结)

在上一篇《华为云服务器初探》 中介绍了在使用华为云服务器部署时的一些关键点,本篇继续,内容涉及如下:中间件的部署问题解决NAT 网关使用数据库服务的访问dotNET Core 程序的构建Redis首先更正上一篇中的一个错误,在运行参数中进行密码设置&…

for in for of区别_Python 第5课:for…in循环黄金搭档之列表

乐学趣学Py● 05:for…in循环黄金搭档之列表●Python趣味小百科Python程序中有一个有彩蛋,在IDLE Pythton模式下输入import this会出现一首(The Zen of Python, by Tim Peters)‘Pyton之禅’的小诗。这首小诗表明了用Python编写代码时遵循的原则&#xf…

开源虎墩同名电影《小虎墩大英雄》定档大年初一

文末有福利,记得看到最后哦~| 作者:虎虎生风的开源虎墩组| 编辑:刘雪洁| 责编:王玥敏开源虎墩诞生记:小源机器人2.0大家还记得 2020 疫情肆虐的时候开源社与来自全国各地的开源爱好者隔空合作,共同打造的疫…

HYDRAstor:可扩展的二级存储

为什么80%的码农都做不了架构师?>>> 原文:HYDRAstor: a Scalable Secondary Storage. HYDRAstor(官网)是NEC推出的二级存储系统,先后有多篇关于HYDRAstor的论文发表在FAST(包括后来9livesdata发表的论文&a…

汇编语言之寄存器(CPU工作原理)

1、介绍 一个典型的CPU,由运算器、控制器、寄存器等器件组成,对于游戏修改者来说,重点学习寄存器,其它不必管。 不同的CPU,寄存器的个数、结构是不相同的,8086CPU有14个寄存器,每个寄存器有一个…

学好英语网首页制作_没有美术基础的新手小白,如何学好淘宝美工?

没有美术基础的新手小白,如何学好淘宝美工?俗话说:万事开头难,没有任何基础、没有美术基础学淘宝美工,要怎么学习好呢?显然,作为一名没有美术基础的新手小白,一般开始学习的时候都是…

14岁上中科大,18岁攻读麻省博士,28岁成为哈佛最年轻副教授,“华人女天才”的开挂人生...

全世界只有3.14 % 的人关注了爆炸吧知识哈佛年纪最小的副教授是中国人1997年美国颁发的罗伯特里得奖获得者蔡天西,22岁获国际生物统计学协会颁发的极大成就"半参数随机"奖,28岁成为哈佛最年轻的女性副教授,香港、美国、著名学术刊物…

汇编语言之寄存器(内存访问)

1、内存中字的存储 2、ds和【address】 CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086CPU中,内存地址由段地址和偏移地址组成, 其中,段地址存放在段寄存器DS中,DS称作数据段寄存器。…

用sort()方法随机打乱数组

2019独角兽企业重金招聘Python工程师标准>>> 我们先看一下随机数组排序的示例,再来研究其中的原理。 随机数组排序示例 var numbers [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411]; numbers numbers.sort(function(…

Linux下部署Kubernetes+Kubesphere(一)Kubernetes基础

1.服务器规划为配置Kubesphere高可用集群,需要三台或三台以上机器作为Master节点,每台机器既可以作为Master也可以作为Worker节点。其中Master节点数量建议为单数。该示例中采用三台服务器,即作为Master节点也作为Worker节点,配置…

触发器及其应用实验报告总结_调机技巧总结: 先快后慢射胶法及其应用

戳我进入社区:注塑和模具人的网上家园先快后慢,也即先用一级快速射胶,射到某个设定的位置时,再起二级慢速射胶。这个二级起级点一般是设在射胶充型的后期,也即接近充型结束的位置。使用一级快速射胶,可以保…

使用 dotnet-monitor 在 Kubernetes 中收集 .NET metrics

使用 dotnet-monitor 在 Kubernetes 中收集 .NET metricsIntrodotnet-monitor 是微软推出的一个帮助我们诊断和监控 .NET 应用程序的工具,在 Kubernetes 中我们可以让 dotnet-monitor 作为 sidecar 运行,无侵入地监控 .NET 应用,今天我们就来…

arcgis 分区 属性值_ArcGIS制图之Maplex自动点抽稀

制图工作中,大量密集点显示是最常遇到的问题。其特点是分布可能不均匀、数据点比较密集,容易造成空间上的重叠,影响制图美观。那么,如果美观而详细的显示制图呢?主要原理Maplex中对标注有很好的显示控制,一…