通過修改物理內存實現跨進程內存讀寫

習一下利用修改物理內存來跨進程內存讀寫

系統:win10 21h1 x64

編譯環境: vs2022 詳情見附錄

基礎

虛擬地址轉物理地址

虛擬地址也稱線性地址,一個線性地址+進程的DirBase地址可以轉換成物理地址。先來看線性地址的含義

在x64體系中只實現了48位的virtual address,高16位被用作符號擴展,這高16位要么全是0,要么全是1。
不同于x86體系結構,每級頁表尋址長度變成9位,由于在x64體系結構中,普通頁大小仍為4KB,然而數據卻表示64位長,因此一個4KB頁在x64體系結構下只能包含512項內容,所以為了保證頁對齊和以頁為單位的頁表內容換入換出,在x64下每級頁表尋址部分長度定位9位。

?

從Page Map Level 4(PML4)開始到最后的物理地址,每一個都可以理解成一層頁表的索引,索引值就是線性地址上不同的部分,分別縮寫是PML4, PDPE, PDE,PTE。?

?

使用windbg可以先查看進程對應的DirBase地址,然后再使用!vtop Dirbase地址 虛擬地址查看虛擬地址對應的物理地址,如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

3: kd> !process 258c 0

Searching for Process with Cid == 258c

PROCESS ffffc40d2ab48340

????SessionId: 1? Cid: 258c??? Peb: a6e35cd000? ParentCid: 1250

????DirBase: 235ae6000? ObjectTable: ffff998138d4ee00? HandleCount:? 38.

????Image: test.exe

3: kd> !vtop 235ae6000 0000A6E334FB00

Amd64VtoP: Virt 000000a6e334fb00, pagedir 0000000235ae6000

Amd64VtoP: PML4E 0000000235ae6008

Amd64VtoP: PDPE 00000001087fb4d8

Amd64VtoP: PDE 000000010f7fc8c8

Amd64VtoP: PTE 00000000ad207a78

Amd64VtoP: Mapped phys 000000011b10cb00

Virtual address a6e334fb00 translates to physical address 11b10cb00.

上面得到DirBase的值是235ae6000,然后需要查看物理地址的虛擬地址是0x0000A6E334FB00,就使用命令

1

!vtop 235ae6000 0000A6E334FB00

得到最后對應的物理地址是0x11b10cb00。

簡單例子代碼如下:

1

2

3

4

5

6

7

8

9

10

11

#include <stdio.h>

#include <stdlib.h>

int main() {

????char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};

????printf("value addr: %p", flag);

????getchar();

????return 0;

}

運行后可以打印出來字符串的虛擬地址0000A6E334FB00,然后通過上述步驟得到物理地址。

我們嘗試看看物理內存中的字符串,現在已經確定物理內存的地址是0xD0000147,使用!db 0xD0000147來查看物理內存,記住要!,沒有感嘆號的是查看虛擬內存的

1

2

3

4

5

6

7

8

9

3: kd> !db 0x11b10cb00

#11b10cb00 66 6c 61 67 7b 62 37 32-38 35 64 37 34 38 64 64 flag{b7285d748dd

#11b10cb10 30 34 32 61 34 39 32 39-64 33 64 62 65 63 37 37 042a4929d3dbec77

#11b10cb20 38 65 36 33 37 7d 00 00-f8 82 20 82 f7 7f 00 00 8e637}.... .....

#11b10cb30 00 00 00 00 00 00 00 00-20 13 1f 82 f7 7f 00 00 ........ .......

#11b10cb40 00 00 00 00 00 00 00 00-99 13 1f 82 f7 7f 00 00 ................

#11b10cb50 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

#11b10cb60 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

#11b10cb70 00 00 00 00 00 00 00 00-44 73 d3 08 fe 7f 00 00 ........Ds......

可以看到物理內存上的字符串內容。

DirBase地址獲取

DirBase地址除了通過上述windbg直接得到這個值以外,還可以通過EPROCESS來得到,這個是代碼比較需要的

1

2

3

4

5

6

7

8

9

10

11

12

13

3: kd> dt _eprocess ffffc40d2ab48340

nt!_EPROCESS

???+0x000 Pcb????????????? : _KPROCESS

???+0x438 ProcessLock????? : _EX_PUSH_LOCK

???+0x440 UniqueProcessId? : 0x00000000`0000258c Void

???+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffffc40d`2cb43788 - 0xffffc40d`2cd444c8 ]

???+0x458 RundownProtect?? : _EX_RUNDOWN_REF

???.....

3: kd> dx -id 0,0,ffffc40d23c95040 -r1 (*((ntkrnlmp!_KPROCESS *)0xffffc40d2ab48340))

(*((ntkrnlmp!_KPROCESS *)0xffffc40d2ab48340))???????????????? [Type: _KPROCESS]

????[+0x000] Header?????????? [Type: _DISPATCHER_HEADER]

????[+0x018] ProfileListHead? [Type: _LIST_ENTRY]

????[+0x028] DirectoryTableBase : 0x235ae6000 [Type: unsigned __int64]

DirectoryTableBase的值就是DirBase地址了,實際上就是EPROCESS + 0x28的偏移

還可以通過獲取CR3寄存器的值,CR3寄存器中的值就是頁目錄表的物理地址,也就是DirBase

思路

目的:進程B可以通過修改物理內存的內容來修改進程A內存中的數據

實驗設置:進程A泄露一個變量地址,然后等待進程B修改,修改后再回復執行,打印變量值看是否修改成功

內核部分思路:

  • 將R3的虛擬地址轉換為物理地址
  • 使用MmCopyMemory復制物理地址內容
  • 修改內容
  • 使用mmMapIoSpaceEx將修改后的內容映射回物理地址

代碼實現

被修改進程代碼

這里寫一個例子來充當被攻擊(修改內存)的進程。主要就是打印變量內容和地址,然后暫停程序等待一段時間(等待被驅動修改),然后再打印變量內容,看看是否被驅動修改內存成功。

1

2

3

4

5

6

7

8

9

10

11

12

13

#include <stdio.h>

#include <stdlib.h>

int main() {

????char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};

????printf("value addr: %p\r\n", flag);

????printf("flag data: %s\r\n", flag);

????getchar();

????printf("flag data Now: %s\r\n", flag);

????return 0;

}

驅動代碼

這里就是主要邏輯,通過驅動代碼取修改目標進程的內存內容,做到跨進程內存讀取,修改。

定義一個讀取物理內存函數

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// @brief 讀取物理地址的內存內容

/// @param address 物理地址

/// @param buffer 復制內存地址到buffer

/// @param size 復制大小

/// @param BytesTransferred 讀取的字節數

/// @return

NTSTATUS ReadPhysicalAddress(IN PVOID64 address, OUT PVOID64 buffer,

?????????????????????????????IN SIZE_T size, OUT SIZE_T* BytesTransferred)

{

????MM_COPY_ADDRESS Read????????? = {0};

????Read.PhysicalAddress.QuadPart = (LONG64)address;

????return MmCopyMemory(

????????buffer, Read, size, MM_COPY_MEMORY_PHYSICAL, BytesTransferred);

}

再定義一個寫入物理內存的函數

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

26

27

28

29

/// @brief 寫入指定內容到物理內存中

/// @param address 被寫入的物理地址

/// @param buffer 需要寫入的緩沖區指針

/// @param size 需要寫入的大小

/// @param BytesTransferred 寫入成功后的大小

/// @return

NTSTATUS WritePhysicalAddress(IN PVOID64 address, IN PVOID64 buffer,

??????????????????????????????IN SIZE_T size, OUT SIZE_T* BytesTransferred)

{

????PVOID??????????? map;

????PHYSICAL_ADDRESS Write = {0};

????if (!address) {

????????kprintf("Address value error. \r\n");

????????return STATUS_UNSUCCESSFUL;

????}

????Write.QuadPart = (LONG64)address;

????map??????????? = MmMapIoSpaceEx(Write, size, PAGE_READWRITE);

????if (!map) {

????????kprintf("Write Memory faild.\r\n");

????????return STATUS_UNSUCCESSFUL;

????}

????RtlCopyMemory(map, buffer, size);

????*BytesTransferred = size;

????MmUnmapIoSpace(map, size);

????return STATUS_SUCCESS;

}

我們需要將虛擬地址轉換成物理地址,那么首先需要線性地址+DirBase地址,DirBase地址獲取是通過PEPROCESS+0x28偏移讀取的

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

/// @brief 通過EPROCESS獲取DirBase值

/// @param pid 進程PID

/// @param pDirbase 一個UINT64指針,獲取成功后返回值

/// @return

NTSTATUS GetDirBaseByEprocess(IN UINT64 pid, OUT PUINT64 pDirbase)

{

????PEPROCESS pEprocess;

????NTSTATUS? status;

????status = PsLookupProcessByProcessId((HANDLE)pid, &pEprocess);

????if (!NT_SUCCESS(status)) {

????????kprintf("[!] Get Pid=%d _EPROCESS failed!", pid);

????????return STATUS_UNSUCCESSFUL;

????}

????*pDirbase =

????????*(PUINT64)((PUCHAR)pEprocess + WIN10_21H1_EPROCESS2DIRBASE_OFFSET);

????kprintf("[+] uDirBase ==> %llx\r\n", *pDirbase);

????return STATUS_SUCCESS;

}

得到DirBase后,就可以虛擬地址轉換物理地址。

傳入虛擬地址后,取后48bit,然后將這48bit分成4個9bit和最后12bit,分別是PML4,PDPE,PDE,PTE和頁內偏移offset。需要注意的是DirBase就已經是物理內存了,所以讀取DirBase內容并且一層一層讀取都要用自定義函數ReadPhysicalAddress

每一層都是基地址+8*偏移,讀取的內容,取12-35bit就是下一層的基地址

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

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

/// @brief 傳入DirBase值和虛擬地址后,回轉化成一個物理地址返回

/// @param DirBase DirBase地址,傳入一個UINT64值

/// @param addr 傳入一個指向虛擬地址的指針,轉化成物理地址后會修改這個指針的值

/// @return

NTSTATUS TranslateAddress(IN UINT64 DirBase, _Inout_ PUINT64 addr)

{

????UINT16?? PML4, PDPE, PDE, PTE, offset;

????UINT64?? mask = 0x7fffff000;

????UINT64?? uTmp;

????SIZE_T?? BytesTransferred;

????NTSTATUS status;

????offset = *addr & 0xfff;

????PTE??? = (*addr >> 12) & 0x1ff;

????PDE??? = (*addr >> (12 + 9)) & 0x1ff;

????PDPE?? = (*addr >> (9 * 2 + 12)) & 0x1ff;

????PML4?? = (*addr >> (9 * 3 + 12)) & 0x1ff;

????status = ReadPhysicalAddress(

????????(PVOID64)(DirBase + PML4 * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

????uTmp &= mask;

????kprintf("[+] PML4(%x) ==> %llx\r\n", PML4, uTmp);

????status = ReadPhysicalAddress(

????????(PVOID64)(uTmp + PDPE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

????uTmp &= mask;

????kprintf("[+] PDPE(%x) ==> %llx\r\n", PDPE, uTmp);

????status = ReadPhysicalAddress(

????????(PVOID64)(uTmp + PDE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

????uTmp &= mask;

????kprintf("[+] PDE(%x) ==> %llx\r\n", PDE, uTmp);

????status = ReadPhysicalAddress(

????????(PVOID64)(uTmp + PTE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

????uTmp &= mask;

????kprintf("[+] PTE(%x) ==> %llx\r\n", PTE, uTmp);

????*addr = uTmp + offset;

????kprintf("[+] physical address: %llx\r\n", *addr);

????return STATUS_SUCCESS;

}

最后再主函數中定義一下邏輯。這里直接手動指定進程號和目標進程打印出來的變量地址,然后將虛擬地址轉化成物理地址,讀取物理地址上的內容并打印出來看看是否正確。再修改物理地址上的內容。

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

26

27

28

29

30

31

32

33

34

35

36

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING path)

{

????NTSTATUS status;

????UINT64?? pid, uAddr, uDirBase;

????SIZE_T?? BytesTransferred;

????UCHAR??? charArry[40] = {0};

????UCHAR??? example[40] = {"Yes I change memory by physical"};

????pid?? = 10276;

????uAddr = 0x3629FAFB80;

????pDriver->DriverUnload = DriverUnload;

????// 手動指定進程號

????status = GetDirBaseByEprocess(pid, &uDirBase);

????if (!NT_SUCCESS(status)) {

????????kprintf("[!] Get DirBase address failed!\r\n");

????????return STATUS_UNSUCCESSFUL;

????}

????// 將虛擬地址轉化成物理地址

????status = TranslateAddress(uDirBase, &uAddr);

????if (!NT_SUCCESS(status)) {

????????kprintf("[!] Translate address failed!\r\n");

????????return STATUS_UNSUCCESSFUL;

????}

????// 讀取物理地址內容, 然后修改內容

????ReadPhysicalAddress((PVOID64)uAddr, charArry, 40, &BytesTransferred);

????kprintf("[+] data is %s\r\n", charArry);

????// 將example字符串寫入物理內存

????WritePhysicalAddress((PVOID64)uAddr, example, 40, &BytesTransferred);

????kprintf("[+] Write end\r\n");

????return STATUS_SUCCESS;

}

結果

目標進程

?

可以看到目標進程的指定內存被修改,同時驅動也跨進程讀取,修改內存成功?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/14385.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14385.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14385.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

刀片式服務器的主要組件有哪些?

刀片式服務器是專門為特殊應用行業跟高密度計算環境設計的&#xff0c;是高可用高密度低成本的服務器平臺&#xff0c;刀片服務器這個名稱主要是根據產品的形狀&#xff0c;刀片式服務器的組成單元外形扁平&#xff0c;就像刀片一樣&#xff0c;整體設計的比較緊湊&#xff0c;…

C#和C++有什么區別?

C#和C都是廣泛使用的編程語言&#xff0c;但它們在設計理念、應用場景和語法上有許多顯著的區別。以下是一些關鍵區別的詳細介紹&#xff1a; 1. 設計理念和目的 C&#xff1a; 設計目的&#xff1a;C是一種面向系統編程和應用程序開發的語言&#xff0c;具有高效性和靈活性…

電工杯b題

雖然這次休息不打&#xff0c;但是看了一下&#xff0c;我比較傾向于機器學習灌水&#xff08;doge

刷題之從前序遍歷與中序遍歷序列構造二叉樹(leetcode)

從前序遍歷與中序遍歷序列構造二叉樹 前序遍歷&#xff1a;中左右 中序遍歷&#xff1a;左中右 前序遍歷的第一個數必定為根節點&#xff0c;再到中序遍歷中找到該數&#xff0c;數的左邊是左子樹&#xff0c;右邊是右子樹&#xff0c;進行遞歸即可。 #include<vector>…

Juniper查看并調整策略順序

1.查看安全策略 >show security policies 順序就是按照顯示出來的順序&#xff0c;與Index無關&#xff0c;從上到下匹配 2. 調整防火墻策略 #insert security policies from-zone CAMERAS to-zone INTERNET policy CAMERAS-to-NTP before policy CAMERAS-to-INTERNET …

操作系統3_作業與處理機調度

操作系統3_作業與處理機調度 文章目錄 操作系統3_作業與處理機調度1. 作業的概念與組成2. 作業的建立及狀態3. 處理機調度相關概念3.1 調度級別3.2 調度隊列模型3.3 選擇準則4. 作業調度與進程調度5. 典型處理機調度算法5.1 先來先服務算法FCFS5.2 短作業優先算法SJF5.3 優先級…

【力扣一輪】字符串異位 數組并集

先驗知識記錄&#xff1a; 遇到哈希問題&#xff0c;想到三種數據結構&#xff1a; ①數組&#xff1a;適用于哈希值比較小&#xff0c;范圍較小&#xff0c; ②set&#xff1a;適用于哈希值較大。 ③map&#xff1a;如果需要用到鍵值對&#xff0c;則用之。 242.有效的字母…

撥云見日,ATFX七場研討會揭秘投資先機

財經先機&#xff0c;一手掌握。近期&#xff0c;隨著國際金價持續走高&#xff0c;避險情緒高漲&#xff0c;由此激發新一輪投資熱潮。作為業界領先的金融創新品牌&#xff0c;ATFX深受投資者認可和信賴&#xff0c;為助力廣大投資者了解市場運行規律&#xff0c;捕捉財經脈絡…

C++通過讀取二進制流的方式來解析PE(靜態文件讀取法)

步驟解讀 先選擇文件讀取文件二進制流從二進制流讀取DOS頭&#xff08;DOS_HEADER&#xff09;&#xff0c;長度64字節讀取DOS殼&#xff08;DOS_STUB&#xff09;&#xff0c;DOS頭開始&#xff0c;長度至到dosHeader->e_lfanew偏移量讀取PE標識&#xff08;Signature&…

520節日特別篇:構建浪漫互動網站實戰技巧

520節日特別篇&#xff1a;構建浪漫互動網站實戰技巧 一、非零分積分資源概覽二、基礎概念與作用說明HTML5 Canvas & SVGCSS3 動畫與過渡JavaScript 動態交互 三、實戰代碼示例&#xff1a;打造浪漫愛心雨HTML 結構CSS 樣式JavaScript 邏輯 四、實際開發應用思路1. 個性化祝…

怎么畫思維導圖?方法介紹

怎么畫思維導圖&#xff1f;在數字化時代&#xff0c;思維導圖已成為我們工作、學習和生活中的得力助手。它不僅能幫助我們更好地組織和表達思想&#xff0c;還能提升我們的思維能力和創造力。那么&#xff0c;哪些軟件可以畫思維導圖呢&#xff1f;本文將為你揭秘幾款功能強大…

Linux 應用入門(一)

1. 交叉編譯 概念&#xff1a;在當前編譯平臺下&#xff0c;編譯出來的程序能運行在體系結構不同的另一種目標平臺上&#xff0c;但是編譯平臺本身卻不能運行該程序。 為什么需要交叉編譯&#xff1f; 速度&#xff1a;目標平臺得運行速度比主機往往慢得多&#xff0c;因為許多…

Docker+nginx部署SpringBoot+vue前后端分離項目(保姆及入門指南)

前后分離項目部署 項目回顧工具上線準備1、win1.1、前端1.2、后端 2、linux環境2.1、安裝docker2.2、安裝docker compose2.3、編寫Dockerfile文件2.4、編寫docker-compose.yml文件2.5、修改application-pro.yml2.6、準備好nginx的掛載目錄和配置2.7、部署后端服務 項目回顧 書…

數據挖掘實戰-基于內容協同過濾算法的電影推薦系統

&#x1f935;?♂? 個人主頁&#xff1a;艾派森的個人主頁 ?&#x1f3fb;作者簡介&#xff1a;Python學習者 &#x1f40b; 希望大家多多支持&#xff0c;我們一起進步&#xff01;&#x1f604; 如果文章對你有幫助的話&#xff0c; 歡迎評論 &#x1f4ac;點贊&#x1f4…

【從C++到Java一周速成】章節9:構造器

章節9&#xff1a;構造器 對于一個類來說&#xff0c;一般有三種常見的成員&#xff1a;屬性、方法、構造器。 這三種成員都可以定義零個或多個。 構造方法也叫構造器&#xff0c;是一個創建對象時被自動調用的特殊方法&#xff0c;用于對象的初始化。 Java通過new關鍵字來調用…

OpenHarmony集成OCR三方庫實現文字提取

1. 簡介 Tesseract(Apache 2.0 License)是一個可以進行圖像OCR識別的C庫&#xff0c;可以跨平臺運行 。本樣例基于Tesseract庫進行適配&#xff0c;使其可以運行在OpenAtom OpenHarmony&#xff08;以下簡稱“OpenHarmony”&#xff09;上&#xff0c;并新增N-API接口供上層應…

.Net Core學習筆記 框架特性(注入、配置)

注&#xff1a;直接學習的.Net Core 6&#xff0c;此版本有沒有startup.cs相關的內容 項目Program.cs文件中 是定義項目加載 啟動的地方 //通過builder對項目進行配置、服務的加載 var builder WebApplication.CreateBuilder(args); builder.Services.AddControllers();//將…

Ubuntu服務器運行Subspace節點和Farm

提供Subspace 節點部署&性能優化&機房托管&運維監控等服務。myto88 磁盤格式化 將插入的磁盤格式化。 sudo mkfs.ext4 -m 0 -T largefile4 /dev/sd*磁盤掛載 此處為語雀內容卡片&#xff0c;點擊鏈接查看&#xff1a;https://www.yuque.com/u25096009/lvoxa…

企商在線榮登甲子光年“2024中國AI算力層創新企業”榜單

5月15日&#xff0c;「AI創生時代——2024甲子引力X科技產業新風向」大會在北京順利舉辦&#xff0c;大會發布2024【星辰100】創新企業榜。企商在線憑借全棧式一體化AI算力能力&#xff0c;與超聚變、寒武紀等企業共同入選“2024中國AI算力層創新企業”榜單。 本次大會由中國科…

AJAX(JQuery版本)

目錄 前言 一.load方法 1.1load()簡介 1.2load()方法示例 1.3load()方法回調函數的參數 二.$.get()方法 2.1$.get()方法介紹 2.2詳細說明 2.3一些例子 2.3.1請求test.php網頁并傳送兩個參數 2.3.2顯示test返回值 三.$.post()方法 3.1$.post()方法介紹 3.2詳細說明 …