进程动态拦截注入API HOOK
最近工作中遇到一个问题,需要通过程序界面进行判断程序的运行状态,刚开始认为很简单,不就是一个窗体控件获取,获取Button的状态和Text。刚好去年干过该事情,就没太在意,就把优先级排到后面了,随着项目交付时间的临近,就准备开始解决问题,一下懵逼了,这次软件作者也聪明了,居然换了花样。

从图中可以发现,此处的按钮上的文字等信息不是通过Button信息进行操作。静想猜测是通过Windows API直接将信息进行写入,那么哪个API可以进行写入呢,想想不难发现,其实就是窗口程序中在窗口中写入文字的方法DrawText,同时隐约记得SetWindowsText也具备该功能。既然有猜想了那就实践看看,验证下猜想。
开干之前,还需要理理思路:
- 既然程序会调用DrawText方法,那我要获取文本,就必须截获到该方法
- 截获方法不就是使用Windows上大名鼎鼎的钩子(Hook)函数。
- 说到钩子函数,这又分钩窗口消息和钩API函数。对于本文来说当然就是后者了。实现API HOOK主要有两个重要环节:
- 如何把代码注入到目标地址空间
- 如何让自己的代码被调用
- 稍稍查询下资料,发现钩窗口函数貌似就复杂了,如果要研究细节请参考该文
- 进一步资料查询,我发现以牛逼函数库(居然还是微软自己开发的):Detours(当然我会提供下载链接)Detours它用于实现拦截Win32二进制代码中的API函数。它使用一个JMP指令替换了目标函数的前面几个字节,使得控制直接调用实现的Detours函数。并通过一个trampoline函数保留了原来函数的功能调用。
- 到目前为止就是对该库的使用了。从文档上来看应该没什么问题了。但是文档上是通过创建进程进行注入DetourCreateProcessWithDll,对目前我的应用场景就不太匹配了。客户程序一直处于运行状态,我需要获取动态注入,那么问题来了,如何解决了?
BOOL WINAPI DetourCreateProcessWithDll(LPCSTR lpApplicationName,
LPSTR lpCommandLine,
...);
- 能提出问题,基本上问题就解决了一般。果不然,通过百度和Google的不懈努力,终于发现原来Detours1.5版本中有个方法DetourContinueProcessWithDll该方法就是进行动态注入的,这下就可以开干了(至于Detours的原理本文就不再赘述,请大家自行查询资料,本文以解决实际问题问题主)
BOOL WINAPI DetourContinueProcessWithDll(HANDLE hProcess, LPCSTR lpDllName);
经过上面几个步骤下来,已经有了完整的思路,下文主要结合实践,进行代码实践。本文主要从如下几个方面进行时间:
- 首先,居然是要截取函数DrawText和SetWindowText那么首先的先实现自己的函数(通过DLL封装)
- 然后,就是动态注入我们的DLL文件指定进程
- 最后,拿出来溜溜(本文为了简便,仅将相关信息打印到DebugView中)
实现HookWindowTextDll
首先,按照Detours的编程规范,需要在加载HookWindowTextDll时通过方法DetourFunctionWithTrampoline进行注册。在卸载的时候通过DetourRemove卸载。
该处主要分以下几步:
- 需要Hook的方法声明和实现
- 安装和卸载注入方法
Hook方法声明和实现
首先需要声明我们的方法
BOOL WINAPI MySetWindowTextA( HWND hWnd, LPCTSTR lpString );
BOOL WINAPI MySetWindowTextW( HWND hWnd, LPCWSTR lpString );
int WINAPI MyDrawTextA( HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat );
int WINAPI MyDrawTextW( HDC hDC, LPCWSTR lpString, int nCount, LPRECT lpRect, UINT uFormat );
//该方法主要是用Real_SetWindowTextA保存原来函数SetWindowTextA地址,方便后面调用
DETOUR_TRAMPOLINE( BOOL WINAPI Real_SetWindowTextA( HWND a0, LPCTSTR a1 ), SetWindowTextA );
DETOUR_TRAMPOLINE( BOOL WINAPI Real_SetWindowTextW( HWND a0, LPCWSTR a1 ), SetWindowTextW );
DETOUR_TRAMPOLINE( int WINAPI Real_DrawTextA( HDC a0, LPCTSTR a1, int a2, LPRECT a3, UINT a4 ), DrawTextA );
DETOUR_TRAMPOLINE( int WINAPI Real_DrawTextW( HDC a0, LPCWSTR a1, int a2, LPRECT a3, UINT a4 ), DrawTextW );
//代码实现,限于篇幅,仅列出MySetWindowTextA
BOOL WINAPI MySetWindowTextA( HWND hWnd, LPCTSTR lpString )
{
#ifdef _DEBUG
char strMsg[ 1024 ]={0};
wsprintf( strMsg, "SetWindowTextA : %s. size = %ld\n", lpString, strlen(lpString) );
OutputDebugString( strMsg );
#endif
return Real_SetWindowTextA( hWnd, lpString );
}
Hook方法的安装和卸载
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
if( DLL_PROCESS_ATTACH == ul_reason_for_call )//dll加载
{
InstallProbes();
}
else if( DLL_PROCESS_DETACH == ul_reason_for_call )//dll卸载
{
UninstallProbes();
}
else;
return TRUE;
}
拦截注入通过方法DetourFunctionWithTrampoline进行,该方法原型如下:
BOOL WINAPI DetourFunctionWithTrampoline(PBYTE pbTrampoline,
PBYTE pbDetour);
这个函数有两个参数,pbTrampoline和一个指向pbDetour函数的指针。目标函数Target之所以没有作
为一个参数,是因为它已经编码到pbTrampoline函数之中(上文中进行编码DETOUR_TRAMPOLINE)。
BOOL InstallProbes()
{
DetourFunctionWithTrampoline( (PBYTE)Real_SetWindowTextA, (PBYTE)MySetWindowTextA );
DetourFunctionWithTrampoline( (PBYTE)Real_SetWindowTextW, (PBYTE)MySetWindowTextW );
DetourFunctionWithTrampoline( (PBYTE)Real_DrawTextA, (PBYTE)MyDrawTextA );
DetourFunctionWithTrampoline( (PBYTE)Real_DrawTextW, (PBYTE)MyDrawTextW );
OutputDebugString("InstallProbesA ok.\n");
return TRUE;
}
BOOL UninstallProbes()
{
DetourRemove( (PBYTE)Real_SetWindowTextA, (PBYTE)MySetWindowTextA );
DetourRemove( (PBYTE)Real_SetWindowTextW, (PBYTE)MySetWindowTextW );
OutputDebugString("UNInstallProbesB ok.\n");
return TRUE;
}
至此拦截注入的方法就完成。下一小节就是如何进行动态注入了。
动态注入HookWindowTextDll
既然要动态注入,那么就先要看看动态注入方法DetourContinueProcessWithDll方法的使用方法
//把一个动态链接库注入到一个新的进程中
BOOL WINAPI DetourContinueProcessWithDllA(HANDLE hProcess, LPCSTR lpDllName)
该方法有两个参数,一看看就明白了
- hProcess:需要注入的原进程句柄
- lpDllName:需要注入的Dll路径,本文即HookWindowTextDll.dll
那么此时应该先获取进程句柄,获取进程句柄通过如下方法即可:
通过进程名称获取进程ID
DWORD GetProcessIdFromProcessName(std::string processname)
{
DWORD dwRet = 0;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap != INVALID_HANDLE_VALUE)
{
BOOL bMore = ::Process32First(hProcessSnap, &pe32);
while (bMore)
{
if (boost::iequals(pe32.szExeFile, processname))
{
dwRet = pe32.th32ProcessID;
break;
}
bMore = ::Process32Next(hProcessSnap, &pe32);
}
::CloseHandle(hProcessSnap);
}
return dwRet;
}
调用测试:
std::string str1 = "WireCut.EXE";
DWORD dwProcessId = GetProcessIdFromProcessName(str1);
std::cout << dwProcessId << std::endl; //
获取到了进程,那就进入下一节,获取句柄。
通过进程ID获取进程句柄
OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄
HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);
参数:
- dwDesiredAccess:想拥有的该进程访问权限
- PROCESS_ALL_ACCESS:所有能获得的权限
- PROCESS_CREATE_PROCESS:需要创建一个进程
- PROCESS_CREATE_THREAD:需要创建一个线程
- PROCESS_DUP_HANDLE:重复使用DuplicateHandle句柄
- PROCESS_QUERY_INFORMATION:获得进程信息的权限,如它的退出代码、优先级
- PROCESS_SET_INFORMATION :设置某些信息的权限,如进程优先级
- PROCESS_SET_QUOTA :设置内存限制的权限,使用SetProcessWorkingSetSize
- PROCESS_SUSPEND_RESUME :暂停或恢复进程的权限
- PROCES_TERMINATE :终止一个进程的权限,使用TerminateProcess
- PROCESS_VM_OPERATION :操作进程内存空间的权限(可用VirtualProtectEx和WriteProcessMemory)
- PROCESS_VM_READ :读取进程内存空间的权限,可使用ReadProcessMemory
- PROCESS_VM_WRITE :读取进程内存空间的权限,可使用WriteProcessMemory
- SYNCHRONIZE :等待进程终止
- bInheritHandle:表示所得到的进程句柄是否可以被继承
- dwProcessId:被打开进程的PID
返回类型:
- 如成功,返回值为指定进程的句柄。
- 如失败,返回值为NULL,可调用GetLastError()获得错误代码。
HANDLE GetProcessHandle(DWORD nID)
{
//PROCESS_ALL_ACCESS 所有能获得的权限
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, nID);
}
DWORD dwProcessId = GetProcessIdFromProcessName("WireCut.EXE");
if (dwProcessId != 0)
{
bRet = DetourContinueProcessWithDllW(GetProcessHandle(dwProcessId), szDllFilePath);
}

到此就完成了所有工作,后面提供项目代码和库文件
链接:https://pan.baidu.com/s/1c09LWg9zo5NIVwR2htJYZA
提取码:f0kt
欢迎关注交流共同进步

博客地址:wqliceman.top
进程动态拦截注入API HOOK的更多相关文章
- Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术
catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...
- EasyHook远程进程注入并hook api的实现
EasyHook远程进程注入并hook api的实现 http://blog.csdn.net/v6543210/article/details/44276155
- API Hook基本原理和实现
API Hook基本原理和实现 2009-03-14 20:09 windows系统下的编程,消息message的传递是贯穿其始终的.这个消息我们可以简单理解为一个有特定意义的整数,正如我们看过的老故 ...
- 程序破解之 API HOOK技术 z
API HOOK,就是截获API调用的技术,在程序对一个API调用之前先执行你的函数,然后根据你的需要可以执行缺省的API调用或者进行其他处理,假设如果想截获一个进程对网络的访问,一般是几个socke ...
- API Hook完全手册
文章来源: http://blog.csdn.net/atfield 原文作者: ATField 整理日期: 2008-07-16 发表评论 字体大小: 小 中 大 注:本文是根据我两年前写的一个 ...
- Linux Debugging(六): 动态库注入、ltrace、strace、Valgrind
实际上,Linux的调试方法非常多,针对不同的问题,不同的场景,不同的应用,都有不同的方法.很难去概括.本篇文章主要涉及本专栏还没有涵盖,但是的确有很重要的方法.本文主要包括动态库注入调试:使用ltr ...
- API HOOK
API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...
- API HOOK技术
API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...
- Windows API Hook
原文地址:http://blog.sina.com.cn/s/blog_628821950100xmuc.html 原文对我的帮助极大,正是由于看了原文.我才学会了HOOK.鉴于原文的排版不是非常好, ...
随机推荐
- 理解CSS中的BFC(块级可视化上下文)[译]
开篇 一些元素,如float元素,如position为absolute,inline-block,table-cell或table-caption的元素,以及overflow属性不为visible的元 ...
- spring test---測试SpringMvc初识
如今越来越多人使用SpringMvc来开发系统,在开发中可定须要对后台url地址请求測试,而且返回预期的结果! Spring提供的測试类MockMvc来进行url地址请求測试,使用方方式: packa ...
- cocos2d-x(vs2012)环境搭建(第一篇)[版本号:cocos2d-x-3.1.1]
1.下载资源 下载cocos2d-x包V3.1.1,下载戳这里: http://www.cocos2d-x.org/download vs2012下载戳这里: http://www.xiazaiba. ...
- poj 1180 Batch Scheduling (斜率优化)
Batch Scheduling \(solution:\) 这应该是斜率优化中最经典的一道题目,虽然之前已经写过一道 \(catstransport\) 的题解了,但还是来回顾一下吧,这道题其实较那 ...
- android 提示
1.Toast: Toast toast=new Toast(context); Toast.makeText(context, text, duration);//返回值为Toast toast.s ...
- Velocity模板引擎笔记
模板引擎中判断对象是否为空: #if(!${jsonObj.data.buyerName} || ${jsonObj.data.buyerName} == '') <p>采 ...
- 哈希表---线性探测再散列(hash)
//哈希表---线性探测再散列 #include <iostream> #include <string> #include <stdio.h> #include ...
- YTU 2802: 判断字符串是否为回文
2802: 判断字符串是否为回文 时间限制: 1 Sec 内存限制: 128 MB 提交: 348 解决: 246 题目描述 编写程序,判断输入的一个字符串是否为回文.若是则输出"Yes ...
- Eos的Wasm智能合约的局限性
官方只支持用C++写智能合约 用C++写智能合约门槛过高,会把许多开发者挡在门外,C++的复杂性也会让智能合约的设计变得困难. Wasm智能合约的效率并不是最优 由于C++最终也是编译成wasm字节码 ...
- iOS——多线程编程详细解析
基本定义: 程序:由代码生成的可执行应用.(例如QQ.app) 进程:一个正在运行的程序可以看做是一个进程. (例如:正在运行的QQ 就是一个进程),进程拥有独立运行所需要的全部资源. 线程: 程序中 ...