如何通过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标准库,顾名思义既然是标准,就是由标准组织制定的 ...
随机推荐
- [Unity-24] Unity的四种载入场景的方法
Unity官方提供了4种载入场景(scene)的方法.各自是: 1. Application.LoadLevel():同步载入 2. Application.LoadLevelAsync():异步载入 ...
- CSRF——攻击与防御
CSRF——攻击与防御 author: lake2 0x01 什么是CSRF攻击 CSRF是Cross Site Request Forgery的缩写(也缩写为XSRF),直译过来就是跨站请求伪造的意 ...
- 改写URL的查询字符串QUERY_STRING(转)
查询字符串是指URL请求中“问号”后面的部分.比如,http://www.nowamagic.net/?foo=bar中粗体部分就是查询字符串,其中变量名是foo,值是bar. 1. 利用QSA转换查 ...
- Linux cpuinfo 详解
在Linux系统中,如何详细了解CPU的信息呢? 当然是通过cat /proc/cpuinfo来检查了,但是比如几个物理CPU/几核/几线程,这些问题怎么确定呢? 经过查看,我的开发机器是1个物理C ...
- How To Cluster Rabbit-MQ--reference
Foreword This explanation of clustering Rabbit-MQ assumes that you’ve had some experience with Rabbi ...
- 线程、委托、lambda运算符的简单示例
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Linux防火墙配置
防火墙命令 service iptables stop --停止 service iptables start --启动文件 /etc/sysconfig/iptables # Firewall c ...
- vijos P1375 大整数(高精不熟的一定要做!)
/* 我尼玛这题不想说啥了 亏了高精写的熟..... 加减乘除max都写了 高精二分 */ #include<iostream> #include<cstdio> #inclu ...
- jQuery的选择器中的通配符[id^='code']或[name^='code']
这两天在做一个专题的时候遇到了一个通配符的问题 //弹层操作$(function(){ //视频播放 $("a[href^='#video']").each(function(in ...
- CSS实现背景透明而背景上的文字不透明完美解决
在我们设计制作一些网页的时候可能会用到半透明的效果,首先我们可能会想到用PNG图片处理,当然这是一个不错的办法,唯一的兼容性问题就是ie6 下的BUG,但这也不困难,加上一段js处理就行了.但假如我们 ...