Win32 程序的API内联与Hook
概述
我们有一个程序比较简单界面如下:
我们点击中间的按钮会弹出一个MessageBox
如下图所示
为方便学习这个原始程序我这里也用汇编编写
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitiveinclude dlgApp.inc
.datag_szTitle db "myTitle",0.codestart:invoke GetModuleHandle,NULLmov hInstance,eaxinvoke InitCommonControlsinvoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULLinvoke ExitProcess,0;########################################################################DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAMmov eax,uMsg.if eax==WM_INITDIALOG.elseif eax==WM_COMMANDmov ebx,wParam.if bx == IDC_BTN1invoke MessageBox,NULL,offset g_szTitle,offset g_szTitle,MB_OK.endif.elseif eax==WM_CLOSEinvoke EndDialog,hWin,0.elsemov eax,FALSEret.endifmov eax,TRUEretDlgProc endpend start
我们假设想实现一个功能注入一个dll到程序后,会修改程序所有弹出的MessageBox
的标题加上注入
前缀
思路如下
Win32程序
弹出窗口是调用系统的MessageBox
实现的,而其实现位于user32.dll
,我们修改程序内的user32.dll
的MessageBox
函数内部即可。
但是请注意修改程序内user32.dll
只会修改当前程序MessageBox
函数,而不会影响其他程序的MessageBox
。不管是win32程序还是linux程序加载共享库机制都差不多,都是公用内存中同一份dll
或者so
,当某一程序修改内存中的共享库时会将此内存区域做拷贝后做修改并不会影响其他加载此so的程序。修改后的共享库的那块内存区域我们一般称为Shared_Dirty
,此块区域修改程序独享
在linux中你可以通过 /proc/${pid}/smaps
更加深刻的去理解:
如下图所示:
win32
的dll加载后会调用DllMain
函数,我们在此函数中调用我们的内存修改函数修改MessageBox
其函数实现。
;HookMessageBox.asm
DllMain proc hInst:HMODULE,fdReason:DWORD,pReserve:LPVOID;此dll被附加到程序中.if fdReason == DLL_PROCESS_ATTACH;调用hook函数修改MessageBox实现invoke InstallHook.endifmov eax,TRUEretDllMain endp
在查看上面InstallHook
函数前我们首先查看原始messagebox
函数的实现:
首先打开OD
附加对应的程序,选中菜单上的View->Excutable module
我们首先对这个函数进行断点查看栈区传参结构
从上面我们可以进行如下操作,MessageBox
前5个字节指令我们可以替换为jmp xxx
(这个指令正好5个字节)然后再xxx
地址编写相关逻辑再跳转回原来的地址。
我们画一个图来描述下
我们首先在汇编代码中定义我们需要的变量
;HookMessageBox.asm
.datag_szUser32 db "user32",0g_szMessageBox db "MessageBoxA",0g_dwMsgBoxAddr dd 0g_szBuff db 256 dup(0)g_szFmt db "注入 %s",0
接着我们需要编写InstallHook
函数,当然你需要知道MessgeBox函数的地址,并且由于MessgeBox内存地址由于是代码段
具有只读的特性因此我们需要修改为可读写
InstallHook procLOCAL @hUser32:HMODULELOCAL @dwOldProtect:DWORD;获取user32.dll模块invoke GetModuleHandle,offset g_szUser32;存储模块句柄mov @hUser32,eax;取user32.dll模块MessageBoxA内存地址invoke GetProcAddress,@hUser32,offset g_szMessageBox;保存函数地址mov g_dwMsgBoxAddr,eax;修改地址为可读可写invoke VirtualProtect,g_dwMsgBoxAddr,1,PAGE_EXECUTE_READWRITE,addr @dwOldProtect;修改代码mov eax,g_dwMsgBoxAddr; e9是jmp远跳的机器码mov byte ptr[eax] ,0e9h;偏移5个字节码,jmp指令的格式:e9+下一个指令到跳转地址的偏移量 注意e9是远跳mov eax,g_dwMsgBoxAddradd eax,5;HOOKCODE是一个jmp的具体地址mov ebx,offset HOOKCODE;计算偏移后sub ebx,eax;放入偏移量信息到g_dwMsgBoxAddr函数中mov eax,g_dwMsgBoxAddr;inc是自增1字节,因为eax存储了e9inc eax;将偏移地址写入e9之后mov dword ptr[eax],ebx;将函数的内存只读性质还原invoke VirtualProtect,g_dwMsgBoxAddr,1,@dwOldProtect,addr @dwOldProtectxor eax,eaxretInstallHook endp
上面看到了offset HOOKCODE
这个就是我们将要执行具体的资源替换函数实现
;InstallHook proc
HOOKCODE proc far;将通用寄存器的上下文保存到栈中pushad;将标志寄存器的上下文保存到栈中pushfd;esp+0ch是原来调用MsgBox函数传入的字符串invoke wsprintf,offset g_szBuff,offset g_szFmt,dword ptr[esp+08h+24h]mov dword ptr[esp+08h+24h],offset g_szBuff;恢复标志寄存器popfd;恢复通用寄存器的popad;跳转原来的Msg函数的下一行mov eax,g_dwMsgBoxAddradd eax,5;被替换jmp之前的指令mov edi,edipush ebpmov ebp,espjmp eax HOOKCODE endp
上面便是我们较为核心的细节。
但是我们忽律了一种情况便是重入,假设在你的HOOKCODE 中存在自己也调用messageBox
的情况那么便会出现死循环。
于是我们修改下上面的代码:
.data;...忽律其他代码g_bIsSelfCall db FALSE ;是否在调用自己的API.code HOOKCODE proc far;将通用寄存器的上下文保存到栈中pushad;将标志寄存器的上下文保存到栈中pushfd.if g_bIsSelfCall==FALSE;esp+0ch是原来调用MsgBox函数传入的字符串invoke wsprintf,offset g_szBuff,offset g_szFmt,dword ptr[esp+08h+24h]mov dword ptr[esp+08h+24h],offset g_szBuffmov g_bIsSelfCall,TRUE;自己弹出MessageBoxinvoke MessageBox,NULL,offset g_szBuff,offset g_szBuff,MB_OKmov g_bIsSelfCall,FALSE.endif;恢复标志寄存器popfd;恢复通用寄存器的popad;跳转原来的Msg函数的下一行mov eax,g_dwMsgBoxAddradd eax,5mov edi,edipush ebpmov ebp,espjmp eax HOOKCODE endp
完整代码实现
.586
.model flat,stdcall
option casemap:noneinclude windows.incinclude user32.incinclude kernel32.incinclude msvcrt.incincludelib kernel32.libincludelib user32.libincludelib msvcrt.lib.datag_szUser32 db "user32",0g_szMessageBox db "MessageBoxA",0g_dwMsgBoxAddr dd 0g_szBuff db 256 dup(0)g_szFmt db "注入 %s",0g_bIsSelfCall db FALSE ;是否在调用自己的API.code HOOKCODE proc far;将通用寄存器的上下文保存到栈中pushad;将标志寄存器的上下文保存到栈中pushfd.if g_bIsSelfCall==FALSE;esp+0ch是原来调用MsgBox函数传入的字符串invoke wsprintf,offset g_szBuff,offset g_szFmt,dword ptr[esp+08h+24h]mov dword ptr[esp+08h+24h],offset g_szBuffmov g_bIsSelfCall,TRUE;自己弹出MessageBoxinvoke MessageBox,NULL,offset g_szBuff,offset g_szBuff,MB_OKmov g_bIsSelfCall,FALSE.endif;恢复标志寄存器popfd;恢复通用寄存器的popad;跳转原来的Msg函数的下一行mov eax,g_dwMsgBoxAddradd eax,5mov edi,edipush ebpmov ebp,espjmp eax HOOKCODE endpInstallHook procLOCAL @hUser32:HMODULELOCAL @dwOldProtect:DWORD;获取user32模块invoke GetModuleHandle,offset g_szUser32;将模块句柄放入首地址mov @hUser32,eax;获取MessageBoxA内存地址invoke GetProcAddress,@hUser32,offset g_szMessageBox;存储box函数地址mov g_dwMsgBoxAddr,eax;修改地址为可读可写invoke VirtualProtect,g_dwMsgBoxAddr,1,PAGE_EXECUTE_READWRITE,addr @dwOldProtect;修改代码mov eax,g_dwMsgBoxAddr; e9是jmp远跳的机器码mov byte ptr[eax] ,0e9h;偏移5个字节码,jmp指令的格式:e9+下一个指令到跳转地址的偏移量 注意e9是远跳mov eax,g_dwMsgBoxAddradd eax,5mov ebx,offset HOOKCODEsub ebx,eax;放入偏移量信息到g_dwMsgBoxAddr函数中mov eax,g_dwMsgBoxAddrinc eaxmov dword ptr[eax],ebxinvoke VirtualProtect,g_dwMsgBoxAddr,1,@dwOldProtect,addr @dwOldProtectxor eax,eaxretInstallHook endp DllMain proc hInst:HMODULE,fdReason:DWORD,pReserve:LPVOID.if fdReason == DLL_PROCESS_ATTACHinvoke InstallHook.endifmov eax,TRUEretDllMain endp end
Win32 程序的API内联与Hook相关推荐
- C++ Primer 5th笔记(chap 18 大型程序工具)内联命名空间 (inline namespace)
1. inline必须出现在命名空间第一次出现的地方 inline namespace FifthEd {//... }//后续再打开命名空间的时候可以写inline也可以不写 namespace F ...
- 在Visual C++ 中使用内联汇编
一. 优点 使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤.在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工 ...
- 用VC写Assembly代码(7)--在Visual C++中使用内联汇编
在Visual C++中使用内联汇编 一. 优点 使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤.在 Visual C++ 中,内联汇编是内置的编译器,因此不需 ...
- 在 Visual C++ 中使用内联汇编
在 Visual C++ 中使用内联汇编 一. 优点 使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤.在 Visual C++ 中,内联汇编是内置的编译器,因此 ...
- C++内联函数学习总结
C++中的内联函数inline总结 http://blog.csdn.net/coder_xia/article/details/6723387 突然看到C++Primer中讲到,对于vector的一 ...
- C++内联(inline)函数
内联函数 内联函数是一种特殊类型的函数,内联函数在定义或声明时前面加上"inline"关键字.比如: inline int max(int a,int b) { return (a ...
- c++内联函数解析(inline)
一.基本定义 inline是C++语言中的一个关键字,可以用于程序中定义内联函数,inline的引进使内联函数的定义更加简单.说到内联函数,这里给出比较常见的定义,内联函数是C++中的一种特殊函数,它 ...
- ARM_NEON_CNN编程 SIMD单指令多数据流 intrinsics指令 内联汇编 CNN卷积网络优化 深度学习优化
ARM_NEON_CNN编程 SIMD单指令多数据流 intrinsics指令 内联汇编 CNN卷积网络优化 深度学习优化 博文末尾支持二维码赞赏哦 _ 本文github 神经网络arm neon加速 ...
- 【C++】内联函数是什么?内联和宏有什么区别?
目录 什么是内联函数? 什么时候使用内联函数? 内联函数和常规函数的区别 如何使用内联函数? 注意: 代码示例 运行结果: 内联与宏有什么区别 什么是内联函数? 内联函数是C++为了提高程序运算速度所 ...
- 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)
正文 最近北京房价蹭蹭猛涨,买了房子的人心花怒放,没买的人心惊肉跳,咬牙切齿,楼主作为北漂无房一族,着实又亚历山大了一把,这些天晚上睡觉总是很难入睡,即使入睡,也是浮梦连篇,即使亚历山大,对C++的热 ...
最新文章
- leetcode 343. Integer Break | 343. 整数拆分(Java)
- 《剑指offer》栈的压入、弹出序列
- 实现option上下移动_js: 实现Select的option上下移动 | 学步园
- PLSQL安装教程,无需oracle客户端(解决本地需要安装oracle客户端的烦恼)
- c语言内容逆置程序设计,C语言程序设计练习题含程序及参考答案.docx
- 查找重复文件_重复文件查找和磁盘整理工具:Tidy Up
- 第45届国际大学生程序设计竞赛(ICPC)银川站太原理工大学收获4枚奖牌
- 千载新论:只能指望员工做完工作,要做好依靠主管
- 湖北省软件行业协会会员单位全名录(2014年的信息)
- WEBService-SOAP协议
- 无刷直流电机驱动系统:组成结构及其控制原理
- leetcode 927. 三等分
- 学习记录514@react使用antd选择器设置下拉菜单宽度
- Unity基础案例讲解:创建小型太空射击游戏(三)
- PLC-Recorder常用授权功能详解
- 3D技术一些回答以及前景
- 6000字总结动态内存管理
- 中国用于先天性代谢错误的医用食品市场深度研究分析报告
- K均值(kmeans)分类
- ICLR2020推荐阅读论文50篇