跨進程的本質是"進程掛靠”正常情況下, A進程的線程只能訪問A進程的地址空間,如果A進程的線程想訪問B進程的地址空間,就要修改當前的Cr3的值爲B進程的頁目錄表基值(KPROCESS.DirectoryTableBase)。
即: mov cr3,B.DirectoryTableBase
跨進程操作
A進制中的線程代碼:
mov cr3,B.DirectoryTableBase //切換Cr3的值爲B進程
mov eax,dword ptr ds:[0x12345678] //將進程B Ox12345678的值存的eax中
mov dword ptr ds[0x00401234],eax //將數據存儲到0x00401234中
mov cr3,A.DirectoryTableBase //切換回Cr3的值
//此時0x00401234中的數據還有嗎?
//如何將數據傳遞給A進程的變量呢?
NtReadVirtualMemory流程解析:
- 切換Cr3
- 將數據讀複製到高2G
- 切換Cr3
- 從高2G複製到目標位置
NtWriteVirtualMemory流程解析:
- 將數據從目標位置複製到2高2G地址
- 切換Cr3
- 從高2G複製到目標位置
- 切換Cr3
代碼
3環
#include<stdio.h>
int main() {
char *ch = "Hello world";
printf("%c,%x", ch, ch);
getchar();
printf("%c,%x", ch, ch);
getchar();
}
0環
0環
#include <ntifs.h>
VOID DriverUnload(PDRIVER_OBJECT pDriver);
// 根據PID返回進程EPROCESS,失敗返回NULL
PEPROCESS LookupProcess(HANDLE hPid)
{
PEPROCESS pEProcess = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId(hPid, &pEProcess)))
return pEProcess;
return NULL;
}
VOID ChangeData(ULONG uId)
{
KAPC_STATE ks;
//1 根據ID獲得進程內核對象
PEPROCESS pEprocess = LookupProcess((HANDLE)uId);
//2 掛靠到此進程上去
//需要注意:不能掛靠之後,將內存中的數據往用戶層地址存儲,是不對的。
//因爲當掛靠到目標進程之後,用戶層地址就是目標進程的了,也就存儲到
//目標進程中,而且目標進程的那個地址不一定有效,可能造成崩潰。
KeStackAttachProcess(pEprocess,&ks);
//3 修改內存
char * p = (char *)0x1dfaf0;
p[5] = 'h';
p[6] = 'a';
p[7] = 'h';
p[8] = 'a';
p[9] = '\0';
//4 解除掛靠
KeUnstackDetachProcess(&ks);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath)
{
UNREFERENCED_PARAMETER(pPath);
//DbgBreakPoint();
ChangeData(1716);
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNREFERENCED_PARAMETER(pDriver);
}