read函数头文件 window_Windows系统调用中API的3环部分(依据分析重写ReadProcessMemory函数)...

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

3 #include

4 #include

5

6 intmain() {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, &a[i], &t, sizeof(int), NULL);20 printf("%d\n", 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

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

3. 在 API-MS-Win-Core-Memory-L1-1-0.ReadProcessMemo 中调用 KernelBa.ReadProcessMemory 函数

761F20CD  - FF25 0C191F7>jmp dword ptr ds:[; KernelBa.ReadProcessMemory

4. 在KernelBa.ReadProcessMemory 调用 函数

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.ZwReadVirtualMemory

5. 在 中调用 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函数重写的实现(重点看汇编代码)

该代码是使用快速调用所编写的,如果想使用中断实现调用内核函数,请移步这里:Windows系统调用中API从三环到零环(下)

(执行结果)

1 #include "pch.h"

2 #include

3 #include

4 #include

5 void ReadMemory(HANDLE hProcess, PVOID pAddr, PVOID pBuffer, DWORD dwSize, DWORD *dwSizeRet)6 {7

8 _asm9 {10 lea eax, [ebp + 0x14]11 push eax12 push[ebp + 0x14]13 push[ebp + 0x10]14 push[ebp + 0xc]15 push[ebp + 8]16 sub esp, 4

17 mov eax, 0x115

18 mov edx, 0X7FFE0300 //sysenter不能直接调用,我间接call的

19 CALL DWORD PTR[EDX]20 add esp, 24

21

22 }23 }24 intmain()25 {26 HANDLE hProcess = 0;27 int t = 123;28 DWORD pBuffer;29 //hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0,a);

30 ReadMemory((HANDLE)-1, (PVOID)&t, &pBuffer, sizeof(int), 0);31 printf("%X\n", pBuffer);32 ReadProcessMemory((HANDLE)-1, &t, &pBuffer, sizeof(int), 0);33 printf("%X\n", pBuffer);34

35 getchar();36 return 0;37 }

原文出处:https://www.cnblogs.com/onetrainee/p/11704626.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值