如何通过HOOK改变windows的API函数(找到函数的相对偏移)
我们知道,系统函数都是以DLL封装起来的,应用程序应用到系统函数时,应首先把该DLL加载到当前的进程空间中,调用的系统函数的入口地址,可以通过GetProcAddress函数进行获取。当系统函数进行调用的时候,首先把所必要的信息保存下来(包括参数和返回地址,等一些别的信息),然后就跳转到函数的入口地址,继续执行。其实函数地址,就是系统函数“可执行代码”的开始地址。那么怎么才能让函数首先执行我们的函数呢?呵呵,应该明白了吧,把开始的那段可执行代码替换为我们自己定制的一小段可执行代码,这样系统函数调用时,不就按我们的意图乖乖行事了吗?其实,就这么简单。Very very简单。 :P.实际的说,就可以修改系统函数入口的地方,让他调转到我们的函数的入口点就行了。采用汇编代码就能简单的实现Jmp XXXX, 其中XXXX就是要跳转的相对地址。
我们的做法是:把系统函数的入口地方的内容替换为一条Jmp指令,目的就是跳到我们的函数进行执行。而Jmp后面要求的是相对偏移,也就是我们的函数入口地址到系统函数入口地址之间的差异,再减去我们这条指令的大小。用公式表达如下:
int nDelta = UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);
Jmp nDleta;
为了保持原程序的健壮性,我们的函数里做完必要的处理后,要回调原来的系统函数,然后返回。所以调用原来系统函数之前必须先把原来修改的系统函数入口地方给恢复,否则,系统函数地方被我们改成了Jmp XXXX就会又跳到我们的函数里,死循环了。
那么说一下程序执行的过程。
我们的dll“注射”入被hook的进程 -> 保存系统函数入口处的代码 -> 替换掉进程中的系统函数入口指向我们的函数 -> 当系统函数被调用,立即跳转到我们的函数 -> 我们函数进行处理 -> 恢复系统函数入口的代码 -> 调用原来的系统函数 -> 再修改系统函数入口指向我们的函数(为了下次hook)-> 返回。
于是,一次完整的Hook就完成了。
好,这个问题明白以后,讲一下下个问题,就是如何进行dll“注射”?即将我们的dll注射到要Hook的进程中去呢?
很简单哦,这里我们采用调用Windows提供给我们的一些现成的Hook来进行注射。举个例子,鼠标钩子,键盘钩子,大家都知道吧?我们可以给系统装一个鼠标钩子,然后所有响应到鼠标事件的进程,就会“自动”(其实是系统处理了)载入我们的dll然后设置相应的钩子函数。其实我们的目的只是需要让被注射进程载入我们的dll就可以了,我们可以再dll实例化的时候进行函数注射的,我们的这个鼠标钩子什么都不干的。
4简单的例子OneAddOne
讲了上面的原理,现在我们应该实战一下了。先不要考虑windows系统那些繁杂的函数,我们自己编写一个API函数来进行Hook与被Hook的练习吧,哈哈。
////////////////////
第一步,首先编写一个add.dll,很简单,这个dll只输出一个API函数,就是add啦。
新建一个win32 dll工程,
add.cpp的内容:
#i nclude "stdafx.h"
int WINAPI add(int a,int b){ file://千万别忘记声明WINAPI 否则调用的时候回产生声明错误哦!
return a+b;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
然后别忘了在add.def里面输出函数:
LIBRARY Add
DESCRIPTION "ADD LA"
EXPORTS
add @1;
编译,ok,我们获得了add.dll
////////////////////
第二步,编写1+1主程序
新建一个基于对话框的工程One,
在 OnOK()里面调用add函数:
ConeDlg.h里面加入一些变量的声明:
….
Public:
HINSTANCE hAddDll;
typedef int (WINAPI*AddProc)(int a,int b);
AddProc add;
…
ConeDlg.cpp里进行调用:
void COneDlg::OnOK()
{
// TODO: Add extra validation here
if (hAddDll==NULL)
hAddDll=::LoadLibrary("add.dll");
add=(AddProc)::GetProcAddress(hAddDll,"add");
int a=1;
int b=2;
int c=add(a,b);
CString temp;
temp.Format("%d+%d=%d",a,b,c);
AfxMessageBox(temp);
}
OK,编译运行,正确的话就会显示1+2=3咯
////////////////////
第3步,要动手Hook咯,爽阿
新建一个MFC的dll工程,Hook
在Hook.dll工程里:
添加一个鼠标Hook MouseProc,鼠标hook什么也不做
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
LRESULT RetVal= CallNextHookEx(hhk,nCode,wParam,lParam);
return RetVal;
}
添加鼠标钩子的安装和卸载函数:
BOOL InstallHook()
{
hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0);
….
return true;
}
void UninstallHook()
{
::UnhookWindowsHookEx(hhk);
}
再实例化中获得一些参数
BOOL CHookApp::InitInstance()
{
获得dll 实例,进程句柄
hinst=::AfxGetInstanceHandle();
DWORD dwPid=::GetCurrentProcessId();
hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
调用注射函数
Inject();
return CWinApp::InitInstance();
}
好,最重要的注射函数:
void Inject()
{
if (m_bInjected==false)
{ 保证只调用1次
m_bInjected=true;
获取add.dll中的add()函数
HMODULE hmod=::LoadLibrary("add.dll");
add=(AddProc)::GetProcAddress(hmod,"add");
pfadd=(FARPROC)add;
if (pfadd==NULL)
{
AfxMessageBox("cannot locate add()");
}
// 将add()中的入口代码保存入OldCode[]
_asm
{
lea edi,OldCode
mov esi,pfadd
cld
movsd
movsb
}
NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令
//获取Myadd()的相对地址
_asm
{
lea eax,Myadd
mov ebx,pfadd
sub eax,ebx
sub eax,5
mov dword ptr [NewCode+1],eax
}
//填充完毕,现在NewCode[]里的指令相当于Jmp Myadd
HookOn(); //可以开启钩子了
}
}
开启钩子的函数
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都加1
a=a+1;
b=b+1;
HookOff();//关掉Myadd()钩子防止死循环
int ret;
ret=add(a,b);
HookOn();//开启Myadd()钩子
return ret;
}
然后别忘记在hook.def里面输出
InstallHook
MouseProc
Myadd
UninstallHook
四个函数。
好到这里基本上大功告成咯
////////////////////
第4步,我们就可以修改前面的One的测试程序了
增加一个安装钩子的函数/按钮
void COneDlg::doHook()
{
hinst=LoadLibrary("hook.dll");
if(hinst==NULL)
{
AfxMessageBox("no hook.dll!");
return;
}
typedef BOOL (CALLBACK *inshook)();
inshook insthook;
insthook=::GetProcAddress(hinst,"InstallHook");
if(insthook==NULL)
{
AfxMessageBox("func not found!");
return;
}
DWORD pid=::GetCurrentProcessId();
BOOL ret=insthook();
}
别忘了退出时卸掉钩子
void COneDlg::OnCancel()
{
// TODO: Add extra cleanup here
typedef BOOL (CALLBACK *UnhookProc)();
UnhookProc UninstallHook;
UninstallHook=::GetProcAddress(hinst,"UninstallHook");
if(UninstallHook==NULL) UninstallHook();
if (hinst!=NULL)
{
::FreeLibrary(hinst);
}
if (hAddDll!=NULL)
{
::FreeLibrary(hAddDll);
}
CDialog::OnCancel();
}
http://blog.csdn.net/jiangxinyu/article/details/5385821
如何通过HOOK改变windows的API函数(找到函数的相对偏移)的更多相关文章
- mfc 调用Windows的API函数实现同步异步串口通信(源码)
在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信.串口通信方便易行,应用广泛. 一般情况下,工控机和各智能仪表通过RS485总线进行通信.RS485的通信方式是半 ...
- Windows API 编程----EnumWindows()函数的用法
1. 函数原型: BOOL WINAPI EnumWindows( _In_ WNDENUMPROC lpEnumFunc, _In_ LPARAM lParam); lpEnumFunc: 应用程序 ...
- DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)
函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...
- windows内核Api的学习
windows内核api就是ntoskrnl.exe导出的函数.我们能够跟调用应用层的api一样,调用内核api. 只是内核api须要注意的是.假设函数导出了.而且函数文档化(也就是能够直接在msdn ...
- [C#菜鸟]C# Hook (三) Windows常用消息大全
表A-1 Windows消息分布 消息范围 说 明 0 - WM_USER – 1 系统消息 WM_USER - 0x7FFF 自定义窗口类整数消息 WM_APP - 0xBFFF 应用程序自定义消 ...
- Windows Native API
http://en.wikipedia.org/wiki/Native_API Windows 的原生 API 函数通常在系统启动时(这里其他 Windows 组件还不可用).kernel32.dll ...
- Windows录音API学习笔记(转)
源:Windows录音API学习笔记 Windows录音API学习笔记 结构体和函数信息 结构体 WAVEINCAPS 该结构描述了一个波形音频输入设备的能力. typedef struct { W ...
- 【转】Windows 7 API Internet Connection Sharing(ICS) 与 Wireless Hosted Network构建本地AP
原文:http://hi.baidu.com/ritrachiao/item/bf7715e6bb8cb3a0c10d75be [此刻我要大大地记录一下!] 这个折腾了我好几天的Windows 7 A ...
- c运行时库,c标准库,Windows系统api的关系
原文地址:http://blog.csdn.net/seastars_sh/article/details/8233324 C运行库和C标准库的关系 C标准库,顾名思义既然是标准,就是由标准组织制定的 ...
随机推荐
- PreTranslateMessage作用和用法
PreTranslateMessage作用和用法 PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗体的消息都要通过这里,比較经常使用, ...
- iTunes 11.2更新下载:改善播客阅读
昨日,苹果公布了iTunes 11.2更新,新版别改善了播客阅读的方法,让用户能够在新的"未播映的"标签中疾速找到没有赏识的单集,其他功用包括主动删去现已播映的单集,并可在&quo ...
- Web站点架构设计考虑的因素
转自http://blog.csdn.net/moshengtan/article/details/8990052 1 Web负载均衡 1.1 - 使用商业硬件实现 最经常使用的F5 与citr ...
- 第一篇:K-近邻分类算法原理分析与代码实现
前言 本文介绍机器学习分类算法中的K-近邻算法并给出伪代码与Python代码实现. 算法原理 首先获取训练集中与目标对象距离最近的k个对象,然后再获取这k个对象的分类标签,求出其中出现频数最大的标签. ...
- 第一篇:数据工程师眼中的智能电网(Smart Grid)
前言 想必第一次接触到智能电网这个概念的人,尤其是互联网从业者,都会顾名思义的将之理解为"智能的电网". 然而智能电网中的"智能"是广义上的智能,它就是指更好的 ...
- 关于C++构造函数的FAQ
[1] 构造函数是用来干什么的? 构造函数构建类的对象,初始化类变量,分配资源(内存.文件.信号量.套接口等等) [2] List x; 和 List x();有什么不同? 前一个是定义List的一个 ...
- POJ 2007 Scrambled Polygon 凸包
Scrambled Polygon Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 7214 Accepted: 3445 ...
- Lucene全文检索
Lucene写入和更新操作: if (id.equals("")) { this.goodsService.save(goods); String goods_lucene_pat ...
- Android手机适配——UI图片适配
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/50727753 在Android项目当中,drawable文件夹都是用来放置图片资源 ...
- 转:Android中的Selector的用法
http://blog.csdn.net/shakespeare001/article/details/7788400