HOOk流程 HOOK程序,本质上是修改程序的执行流程(思路类似于pwn中的构造ROP链),通过修改程序的执行流程,可以将程序中的某些代码指令替换成自己想要的指令。
1 2 3 4 5 6 7 8 9 #include <stdio.h> 1.(define n 0&&1) //1.处进行代码添加 int main(){ 2.(不添加代码)//2.处进行代码添加 printf("%d\n",n==n); return 0; } //c++中==运算符的优先级高于&&运算符(类似于SQL注入操作),0&&1==0&&1中1和0先进行==的判断,后从左到右进行&&的操作。 //如果说要在上段代码中的1.2.处添加代码,使得程序可以输出0,也就是n!=n。原视频可以去看b站老哥的视频。https://www.bilibili.com/video/BV1fw411G741
上述问题中的思路是,对程序中原有的代码不进行操作,而是通过添加恶意代码对程序的逻辑漏洞进行利用。对于HOOK一个程序,我们的思路是修改程序的执行流程,首先执行原有代码部分,后执行添加的恶意代码,返回到原有执行流,继续实现后续的代码。 弹窗通过windows.h库的Messagebox函数进行实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include<windows.h> int main(){ MessageBoxA(0,"HOOK","Tip",0); return 0; /* int MessageBoxA( [in, optional] HWND hWnd, //创建窗口的所属窗口的信息,可以为NULL即程序没有夫句柄窗口 [in, optional] LPCSTR lpText,//要显示的文本信息 [in, optional] LPCSTR lpCaption,//窗口的标题信息 [in] UINT uType//窗口的按钮 ); */ }
对MessageBoxA函数进行反编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 .text:0041179C call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)//VS所写的代码,仅调试所写的代码 //保存栈 将esi赋值到当前栈顶 .text:004117A1 mov esi, esp //从右向左依次向栈内压入MessageBoxA的四个参数 .text:004117A3 push 0 ; uType .text:004117A5 push offset Caption ; "Tips" .text:004117AA push offset Text ; "HOOK" .text:004117AF push 0 ; hWnd //调用导入表中所存放的函数地址 .text:004117B1 call ds:MessageBoxA //比较esi和esp的位置 .text:004117B7 cmp esi, esp //检测栈顶指针 .text:004117B9 call j___RTC_CheckEsp
j_RTC_CheckEsp 函数IDA步入可以看到
1 2 3 4 5 .text:00411A00 ; void __cdecl _RTC_CheckEsp() .text:00411A00 __RTC_CheckEsp proc near ; CODE XREF: j___RTC_CheckEsp↑j//调用程序在同一代码段内使用near 不在同一代码段内使用far .text:00411A00 jnz short $esperror$4//jz为0跳转 jnz非零跳转 // .text:00411A02 retn
跳转到$esperror$4处,主要用于执行_RTC_Failure函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .text:00411A03 $esperror$4: ; CODE XREF: __RTC_CheckEsp↑j .text:00411A03 push ebp .text:00411A04 mov ebp, esp .text:00411A06 sub esp, 0 .text:00411A09 push eax .text:00411A0A push edx .text:00411A0B push ebx .text:00411A0C push esi .text:00411A0D push edi .text:00411A0E mov eax, [ebp+4] .text:00411A11 push 0 ; int .text:00411A13 push eax ; void * .text:00411A14 call j_?_RTC_Failure@@YAXPAXH@Z ; _RTC_Failure(void *,int) .text:00411A19 add esp, 8 .text:00411A1C pop edi .text:00411A1D pop esi .text:00411A1E pop ebx .text:00411A1F pop edx .text:00411A20 pop eax .text:00411A21 mov esp, ebp .text:00411A23 pop ebp .text:00411A24 retn .text:00411A24 __RTC_CheckEsp endp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 j_?_RTC_Failure@@YAXPAXH@Z 函数调用后会调用failwithmessage text:00412140 push ebp .text:00412141 mov ebp, esp .text:00412143 mov eax, [ebp+arg_4] .text:00412146 cmp eax, 4 .text:00412149 ja short loc_41216E .text:0041214B mov ecx, dword_41A000[eax*4] .text:00412152 mov edx, ds:lpMultiByteStr[eax*4] .text:00412159 cmp ecx, 0FFFFFFFFh .text:0041215C jz short loc_41218C .text:0041215E push edx ; lpMultiByteStr .text:0041215F push eax ; int .text:00412160 push ecx ; int .text:00412161 push [ebp+arg_0] ; void * .text:00412164 call ?failwithmessage@@YAXPAXHHPBD@Z ; failwithmessage(void *,int,int,char const *) .text:00412169 add esp, 10h .text:0041216C pop ebp .text:0041216D retn
_RTC_Failure 函数返回的字符串在内存中偏移
1 2 3 4 5 6 7 .rdata:00417B40 lpMultiByteStr dd offset aTheValueOfEspW .rdata:00417B40 ; DATA XREF: _RTC_Failure(void *,int)+12↑r .rdata:00417B40 ; "The value of ESP was not properly saved"... .rdata:00417B44 dd offset aACastToASmalle ; "A cast to a smaller data type has cause"... .rdata:00417B48 dd offset aStackMemoryWas ; "Stack memory was corrupted\r\n" .rdata:00417B4C dd offset aALocalVariable ; "A local variable was used before it was"... .rdata:00417B50 dd offset aStackMemoryAro ; "Stack memory around _alloca was corrupt"...
注意___security_cookie 这个函数常用于检测栈溢出,VS引进为了防止栈溢出。windows还有类似的检测函数CheckStackVars 检测堆栈 CheckEsp 检测栈顶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .text:00412340 push ebp .text:00412341 mov ebp, esp .text:00412343 sub esp, 0E3Ch .text:00412349 mov eax, ___security_cookie .text:0041234E xor eax, ebp .text:00412350 mov [ebp+var_4], eax .text:00412353 mov eax, [ebp+arg_8] .text:00412356 push ebx .text:00412357 mov ebx, [ebp+lpMultiByteStr] .text:0041235A push esi .text:0041235B mov esi, [ebp+arg_0] .text:0041235E push edi .text:0041235F push esi .text:00412360 mov [ebp+var_E30], eax .text:00412366 mov [ebp+var_E3C], ebx .text:0041236C mov [ebp+var_E34], 0 .text:00412376 call sub_41137F .text:0041237B mov edi, eax .text:0041237D add esp, 4 .text:00412380 test edi, edi .text:00412382 jnz short loc_412393 .text:00412384 push esi .text:00412385 call sub_411082 .text:0041238A add esp, 4 .text:0041238D mov [ebp+var_E34], eax
为了给程序添加弹窗,我们需要将反汇编对应得硬编码写入PE文件中。二进制文件中push立即数(6A)和push地址(68)有所区分,立即数的范围是0~0x7f 地址则是大于立即数的最大范围的值。 call(E8) 内存地址 硬编码不是直接写对应的函数的地址,而是call对应函数地址-当前内存地址-当前指令的总长度 由于文件的文件对齐,我们可以在未运行PE文件中找到空白区,我们可以在空白区中写入我们想要执行的代码,修改PE文件的入口点(文件拓展头中的AddressOfEntryPoint),在需要添加的恶意代码后加入一个代码的返回原有入口点的jmp语句。jmp(E9)语句的跳转规则 偏移=跳转的内存地址-当前内存地址-当前的指令长度
寻找空白区 在EverEdit这个三十二位程序的副本中,偏移为2B0位置发现空白区,并且可以发现空白区是在节表和节块之间的位置。或者是选择第二张图的第二个空白区,这个空白区位于文件的尾部。(空白区的产生主要是由于文件对齐,在PE文件未运行时,程序需要根据文件对齐FileAlignment成员的整数倍数) 我们选择第二个空白区位于文件的节块末位,所以我们要对FOA进行转换为VA,在重定位表块的尾部。 257770是FOA,FOA=差值+所在节的文件偏移 VA=差值+所在节的内存偏移(RVA) 最终可以算出RVA为296B70 VA=696B70
参数设置和函数引用 为了执行MessageBoxA函数,我们需要User32.dll导出表的MessageBoxA的地址(也可以使用一个图形化窗口的程序)直接使用OD附加,打开导出表,查找MessageBoxA函数的地址。我直接打开了编译过的上文使用的弹窗exe。 (OD打开导出表的快捷键是alt+e,ctrl+n可以查看区段内的函数的名称)可以发现MessageBoxA函数的地址为75E2A000,现在我们就可以call调用MessageBoxA函数。 push立即数的字节码时为6A 0是第一个参数和第四个参数为0 第二个参数是HOOK字符串 第三个参数是Tips字符串根据函数参数调用约定,从右向左进行参数压栈。顺序可以直接查看反编译的结果。
1 2 3 4 5 6 7 MessageBoxA(0, "HOOK", "Tips", 0); 8B F4 mov esi, esp 6A 00 push 0 ; 68 30 7B 41 00 push offset Caption ; 68 38 7B 41 00 push offset Text ; 6A 00 push 0 ; FF 15 98 B0 41 00 call ds:MessageBoxA
查看反汇编的机械码发现call指令对应的指令不是E8而是FF 15 (https://stackoverflow.com/questions/15209993/what-does-opcode-ff350e204000-do?rq=1),FF开头的指令中的第二个指令为CallN指令,第二个字节码15以二进制形式表示为0001 0101 从右向左最右的二进制表示0位,最左的二进制位表示7,规定查看二进制位的3~5位,010表示为2,在INC DEC CALLN CALLF JMPN JMPF PUSH ModR/M PUSH Ev 所以指令指的是CALLN。 获得压入参数的硬编码为6A 00 68 00 00 00 00 68 00 00 00 00 6A 00 调用函数的硬编码 E8 7C 34 79 75 当前地址296b7F 修改程序的入口点为 296B70(入口点使用RVA) 跳转回原入口点硬编码为 E9 86 43 ED FF FF FF FF FF 当前地址296b83