Windows内核——直接调用0环函数实现ReadProcessMemory&WriteProcessMemory函数
直接调用0环函数实现ReadProcessMemory&WriteProcessMemory函数
工具
IDA
VC++6.0
步骤
一、实现ring0调用ReadProcessMemory
1、ALT+T 找到ReadProcessMemory
2、找到NtReadVirtualMemory(ntdll.dll)函数
去导入表里面查看这个函数在哪个dll里
发现这个函数也只是间接调用的一个内核函数的地址而已
二、实现ring0调用WriteProcessMemory
1、ALT+T 找到 WriteProcessMemory
2、找到NtProtectVirtualMemory(ntdll.dll)函数
去导入表里面查看这个函数在哪个dll里
发现这个函数也只是间接调用的一个内核函数的地址而已
从上面NtReadVirtualMemory、NtProtectVirtualMemory中的具体实现可以知道,这里调用的知识内核函数的一个地址,换个系统可能这个地址就会不一样,所以这样的函数兼容性差。
为啥要直接调用ring0实现函数功能?
-
Windows所提供给Ring3的API,实质就是对操作系统接口的封装,其实现部分都是在Ring0实现的。
观察上面图中分析函数就可以很清楚的知道了。
-
防止恶意程序会利用钩子来钩取这些API,从而达到截取内容,修改数据的意图。
二、实现ring0调用ReadProcessMemory
1、汇编实现
lea eax, [ebp + 0x14]
push eax
push[ebp + 0x14]
push[ebp + 0x10]
push[ebp + 0xc]
push[ebp + 8]
sub esp, 4 //调用NtReadVirtualMemory的是call xxx,实质是 将返回地址压栈&JMP,所以这里sub 4个byte实现返回地址的压栈
mov eax, 0x0BA
mov edx, 0X7FFE0300 //不能直接调用内核,间接call函数地址来实现
CALL DWORD PTR[EDX]
add esp, 24 //堆栈平衡 24=6*4
2、测试
#ifdef debug
int main()
{
// HANDLE hProcess = 0;
int t = 0x12345678;
DWORD pBuffer;
// DWORD dwChromeID = GetCurrentProcessId();
//1、不调用dll实现ReadProcessMemory
ReadMemory((HANDLE)-1, (PVOID)&t, &pBuffer, sizeof(int), 0);
printf("%X\n", pBuffer);
//2、调用api实现
ReadProcessMemory((HANDLE)-1, &t, &pBuffer, sizeof(int), 0);
printf("%X\n", pBuffer);
//结果:1 2的结果一致
getchar();
return 0;
}
#endif
第二种方法:
#include<windows.h>
#include<stdio.h>
void __declspec(naked) read(HANDLE hProcess,LPVOID addr,LPVOID buffer,DWORD len)
{
_asm
{
mov eax, 0BAh
mov edx, 7FFE0300h
call dword ptr[edx]
ret
}
}
#ifndef debug
int main() {
int t;
int a[8];
// 依次往 p 指针中写入数据,再用ReadProcessMemory读取数据
for (int i = 0; i < 8; i++) {
WriteProcessMemory(INVALID_HANDLE_VALUE, &a[i], &i, sizeof(int),NULL);
}
for (int j = 0; j < 8; j++) {
read(INVALID_HANDLE_VALUE, &a[j], &t, sizeof(int));
printf("%d\n", t);
}
getchar();
return 0;
}
#endif
三、实现ring0调用WriteProcessMemory
1、汇编实现
void WriteMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{
_asm{
push ecx ;Buffer
push ecx
mov eax, [ebp+0x0C] ;lpBaseAddress 2
push ebx
mov ebx, [ebp+0x14] ;OldProtect???
push edi
mov edi, [ebp+8] ;ProcessHandle 1
mov [ebp-8], eax ;BaseAddress
lea eax, [ebp+0x14]
push eax ; OldProtect
push 0x40 ; NewProtect
lea eax, [ebp-4]
push eax ; ProtectSize
lea eax, [ebp-8]
push eax ; BaseAddress
push edi ; ProcessHandle
mov [ebp-4], ebx
sub esp, 4 ;call esi ; NtProtectVirtualMemory
mov eax, 0x89
mov edx, 0x7FFE0300
call dword ptr [edx]
add esp, 0x28
}
}
批注:这个还有些问题没想明白:这里ecx为什么要push两次???
2、测试
#ifndef debug
int main() {
int t;
int a[8];
// 依次往 p 指针中写入数据,再用ReadProcessMemory读取数据
for (int i = 0; i < 8; i++) {
WriteMemory(INVALID_HANDLE_VALUE, &a[i], &i, sizeof(int),NULL);
}
for (int j = 0; j < 8; j++) {
ReadProcessMemory(INVALID_HANDLE_VALUE, &a[j], &t, sizeof(int), NULL);
printf("%d\n", t);
}
getchar();
return 0;
}
#endif