副标题[/!--empirenews.page--]
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
Windows系统调用中API的3环部分
?
一、R3环API分析的重要性
- Windows所提供给R3环的API,实质就是对操作系统接口的封装,其实现部分都是在R0实现的。
- 很多恶意程序会利用钩子来钩取这些API,从而达到截取内容,修改数据的意图。
- 现在我们使用olldbg对ReadProcessMemory进行跟踪分析,查看其在R3的实现,并根据我们的分析来重写一个ReadProcessMemory。
- 重写ReadProcessMemory之后,这就会加大恶意代码截获的难度。
- 当然,对于自己来说也有很多弊端,比如只能在指定的操作系统中运行(32位与64位操作系统,其运行ReadProcessMemory的执行动作是不一样的,在64位运行32位程序,其中间会调用wow64cpu.dll来进行转换)
?
二、调试代码
1 #include "pch.h"
2 #include <iostream>
3 #include <algorithm>
4 #include <Windows.h>
5
6 int main() { 7 getchar(); 8 getchar(); 9 int a[4],t; 10 printf("hello world!"); 11 getchar(); 12 getchar(); 13 // 依次往 p 指针中写入数据,再用ReadProcessMemory读取数据
14 for (int i = 0; i < 4; i++) { 15 WriteProcessMemory(INVALID_HANDLE_VALUE,&a[i],&i,sizeof(int),NULL); 16
17 } 18 for (int i = 0; i < 4; i++) { 19 ReadProcessMemory(INVALID_HANDLE_VALUE,&t,NULL); 20 printf("%dn",t); 21 } 22 getchar(); 23 getchar(); 24
25 }
?
三、调试中的关键汇编代码(系统环境:在Windows7 32位操作系统 / 调试器:olldbg)

?
?
?
?
1. 在exe 中 调用 kernel32.ReadProcessMemroy函数 01314E3E??? 8BF4???????? mov esi,esp 01314E40??? 6A 00??????? push 0x0 01314E42??? 6A 04??????? push 0x4 01314E44??? 8D45 DC????? lea eax,dword ptr ss:[ebp-0x24] 01314E47??? 50?????????? push eax 01314E48??? 8B4D C4????? mov ecx,dword ptr ss:[ebp-0x3C] 01314E4B??? 8D548D E8??? lea edx,dword ptr ss:[ebp+ecx*4-0x18] 01314E4F??? 52?????????? push edx 01314E50??? 6A FF??????? push -0x1 01314E52??? FF15 64B0310>call dword ptr ds:[<&KERNEL32.ReadProcessMemory>]; kernel32.ReadProcessMemory 01314E58??? 3BF4???????? cmp esi,esp
2. 在 kernel32.ReadProcessMemroy函数 中调用 jmp.&API-MS-Win-Core-Memory-L1-1-0.ReadProcessMemory> 函数 // 该函数相当于什么也没做... 7622C1CE >? 8BFF???????????? mov edi,edi 7622C1D0??? 55?????????????? push ebp 7622C1D1??? 8BEC???????????? mov ebp,esp 7622C1D3??? 5D?????????????? pop ebp?????????????????????????????????????????????????????????? ; 7622C1D4? ^ E9 F45EFCFF????? jmp <jmp.&API-MS-Win-Core-Memory-L1-1-0.ReadProcessMemory>
3. 在 API-MS-Win-Core-Memory-L1-1-0.ReadProcessMemo 中调用 KernelBa.ReadProcessMemory 函数 761F20CD? - FF25 0C191F7>jmp dword ptr ds:[<&API-MS-Win-Core-Memory-L1-1-0.ReadProcessMemo>; KernelBa.ReadProcessMemory
4. 在KernelBa.ReadProcessMemory 调用 <&ntdll.NtReadVirtualMemory> 函数 75DA9A0A >? 8BFF???????? mov edi,edi // 这两部分在编写函数时就会使用 75DA9A0C??? 55?????????? push ebp 75DA9A0D??? 8BEC???????? mov ebp,esp 75DA9A0F??? 8D45 14????? lea eax,dword ptr ss:[ebp+0x14] 75DA9A12??? 50?????????? push eax 75DA9A13??? FF75 14????? push dword ptr ss:[ebp+0x14] 75DA9A16??? FF75 10????? push dword ptr ss:[ebp+0x10] 75DA9A19??? FF75 0C????? push dword ptr ss:[ebp+0xC] 75DA9A1C??? FF75 08????? push dword ptr ss:[ebp+0x8] 75DA9A1F??? FF15 C411DA7>call dword ptr ds:[<&ntdll.NtReadVirtualMemory>] ; ntdll.ZwReadVirtualMemory
5. 在 <&ntdll.NtReadVirtualMemory> 中调用 ntdll.KiFastSystemCall 函数 77A162F8 >? B8 15010000? mov eax,0x115? // 对应操作系统内核中某一函数的编号。 77A162FD??? BA 0003FE7F? mov edx,0x7FFE0300? // 该地方是一个函数,该函数决定了什么方式进零环。 77A16302??? FF12???????? call dword ptr ds:[edx]? ; ntdll.KiFastSystemCall
6. 在 ntdll.KiFastSystemCall 中 调用sysenter 77A170B0 >? 8BD4???????? mov edx,esp 77A170B2??? 0F34???????? sysenter 77A170B4 >? C3?????????? retn
?
四、汇编代码分析解读(根据三中的序号依次解读)
- 这部分是我们程序中调用ReadProcessMemory后编译器直接编译后的汇编代码,传入参数与API调用
- 在kenel32.dll中,mov edi,edi 是用于热补丁技术所保留的(函数开始处的MOV EDI,EDI的作用),这段代码仔细看其实除了jmp什么也没干。
- 转到kernelBase.dll中实现ReadProcessMemory。
- 这段汇编代码,将ReadProcessMemory中传入的参数再次入栈,调用ntdll.ZwReadVirtualMemory函数。
- 这段汇编代码看注释,eax中存放了一个编号,其就是在内核中的ReadProcessMemory实现;在 0x7FFE0300 处存放了一个函数指针,该函数指针决定了以什么方式进入0环(中断/快速调用)。
- 在ntdll.KiFastSystemCall调用sysenter。
五、重写ReadProcessMemory函数的思路
我们所看到的汇编代码,本质就是Windows所执行的步骤,我们依据上面的分析,完全可以重新写一个该函数,只需要关键部分。
1) 退而求其次
我们希望可以在自己的代码中直接使用 "sysenter",但经过编写发现其并没有提供这种指令。
因此在"sysenter"无法直接使用的情况下,只能退而求其次,调用ntdll.KiFastSystemCall函数。
2)传递参数,模拟call指令
ntdll.KiFastSystemCall函数需要借助ntdll.NtReadVirtualMemory传递过来的参数,然后执行call指令。
我们并不希望执行call指令执行,因为执行call指令意味着又上了一层。(多一层被钩取的风险)
我们希望自己的代码中直接传递参数,并且直接调用调用ntdll.KiFastSystemCall函数。
因此我们需要模拟call指令。call指令的本质就是将返回地址入栈,并跳转。我们不需要跳转,只需要将返回地址入栈(四个字节 使用 sub esp,4 模拟)
3)手动实现栈平衡
我们内嵌汇编代码后,需要手动平衡栈,我们只需要分析esp改变了多少(push、pop以及直接对esp的计算)。
经过分析共减少了24字节,所以代码最后应该有 add esp,24 来平衡栈。
?
六、ReadProcessMemory函数重写的实现(重点看汇编代码)
(编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|