赞
踩
我们打开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 文件将使输出文件较大并降低加载速度。
- LIBRARY Add
- DESCRIPTION "ADD LA"
- EXPORTS
- add @1;
由此,一个简单的dll我们就完成了。
下面用MFC写一个程序来调用我们的dll。
还是新建一个工程,目录如下:
MFC的.Cpp中主要代码如下:
- void CMFCApplication5Dlg::OnBnClickedButton1()
- {
- // TODO: Add your control notification handler code here
-
- HINSTANCE 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);
-
- }
效果如图:出现这个说明你成功了:
一个调用dll中加法函数的MFC程序我们就完成了,下面就需要自己写一个dll,让程序执行我们的代码。
新建一个MFC的 dll工程,工程名为Hook,然后我们在Hook.cpp文件里面编写的代码如下:
- // 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 变量定义
- // CHookApp
-
- BEGIN_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, 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(); //可以开启钩子了
- }
- }
-
-
- 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;
- }
- ; Hook.def : Declares the module parameters for the DLL.
- LIBRARY "HOOK"
- EXPORTS
- InstallHook
- UninstallHook
值得一提的是很多人都是改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代码的某个地方模拟实现。
若有疏漏之处,欢迎各位大侠指正!
2014.10.27 6:30 pm
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。