驅動開發:內核中實現Dump進程轉儲

来源:https://www.cnblogs.com/LyShark/archive/2022/10/11/16779358.html
-Advertisement-
Play Games

多數ARK反內核工具中都存在驅動級別的記憶體轉存功能,該功能可以將應用層中運行進程的記憶體鏡像轉存到特定目錄下,記憶體轉存功能在應對加殼程式的分析尤為重要,當進程在記憶體中解碼後,我們可以很容易的將記憶體鏡像導出,從而更好的對樣本進行分析,當然某些加密殼可能無效但絕大多數情況下是可以被轉存的。 ...


多數ARK反內核工具中都存在驅動級別的記憶體轉存功能,該功能可以將應用層中運行進程的記憶體鏡像轉存到特定目錄下,記憶體轉存功能在應對加殼程式的分析尤為重要,當進程在記憶體中解碼後,我們可以很容易的將記憶體鏡像導出,從而更好的對樣本進行分析,當然某些加密殼可能無效但絕大多數情況下是可以被轉存的。

在上一篇文章《驅動開發:內核R3與R0記憶體映射拷貝》介紹了一種方式SafeCopyMemory_R3_to_R0可以將應用層進程的記憶體空間映射到內核中,要實現記憶體轉儲功能我們還是需要使用這個映射函數,只是需要在此函數上增加一些功能而已。

在實現轉存之前,需要得到兩個東西,進程內模塊基地址以及模塊長度這兩個參數是必不可少的,至於內核中如何得到指定進程的模塊數據,在很早之前的文章《驅動開發:內核中枚舉進線程與模塊》中有詳細的參考方法,這裡就在此基礎之上實現一個簡單的進程模塊遍歷功能。

如下代碼中使用的就是枚舉進程PEB結構得到更多參數的具體實現,如果不懂得可以研讀《驅動開發:內核通過PEB得到進程參數》這篇文章此處不再贅述。

#include <ntddk.h>
#include <windef.h>

// 聲明結構體
typedef struct _KAPC_STATE
{
	LIST_ENTRY ApcListHead[2];
	PKPROCESS Process;
	UCHAR KernelApcInProgress;
	UCHAR KernelApcPending;
	UCHAR UserApcPending;
} KAPC_STATE, *PKAPC_STATE;

typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY64	InLoadOrderLinks;
	LIST_ENTRY64	InMemoryOrderLinks;
	LIST_ENTRY64	InInitializationOrderLinks;
	PVOID			DllBase;
	PVOID			EntryPoint;
	ULONG			SizeOfImage;
	UNICODE_STRING	FullDllName;
	UNICODE_STRING 	BaseDllName;
	ULONG			Flags;
	USHORT			LoadCount;
	USHORT			TlsIndex;
	PVOID			SectionPointer;
	ULONG			CheckSum;
	PVOID			LoadedImports;
	PVOID			EntryPointActivationContext;
	PVOID			PatchInformation;
	LIST_ENTRY64	ForwarderLinks;
	LIST_ENTRY64	ServiceTagLinks;
	LIST_ENTRY64	StaticLinks;
	PVOID			ContextInformation;
	ULONG64			OriginalBase;
	LARGE_INTEGER	LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

// 偏移地址
ULONG64 LdrInPebOffset = 0x018;		//peb.ldr
ULONG64 ModListInPebOffset = 0x010;	//peb.ldr.InLoadOrderModuleList

// 聲明API
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
NTKERNELAPI PPEB PsGetProcessPeb(PEPROCESS Process);
NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);

// 根據進程ID返回進程EPROCESS,失敗返回NULL
PEPROCESS LookupProcess(HANDLE Pid)
{
	PEPROCESS eprocess = NULL;
	if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))
		return eprocess;
	else
		return NULL;
}

// 枚舉指定進程的模塊
// By: LyShark.com
VOID EnumModule(PEPROCESS Process)
{
	SIZE_T Peb = 0;
	SIZE_T Ldr = 0;
	PLIST_ENTRY ModListHead = 0;
	PLIST_ENTRY Module = 0;
	ANSI_STRING AnsiString;
	KAPC_STATE ks;
	
	// EPROCESS地址無效則退出
	if (!MmIsAddressValid(Process))
		return;
	
	// 獲取PEB地址
	Peb = (SIZE_T)PsGetProcessPeb(Process);
	
	// PEB地址無效則退出
	if (!Peb)
		return;
	
	// 依附進程
	KeStackAttachProcess(Process, &ks);
	__try
	{
		// 獲得LDR地址
		Ldr = Peb + (SIZE_T)LdrInPebOffset;
		// 測試是否可讀,不可讀則拋出異常退出
		ProbeForRead((CONST PVOID)Ldr, 8, 8);
		// 獲得鏈表頭
		ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + ModListInPebOffset);
		// 再次測試可讀性
		ProbeForRead((CONST PVOID)ModListHead, 8, 8);
		// 獲得第一個模塊的信息
		Module = ModListHead->Flink;
		
		while (ModListHead != Module)
		{
			//列印信息:基址、大小、DLL路徑
			DbgPrint("模塊基址 = %p | 大小 = %ld | 模塊名 = %wZ | 完整路徑= %wZ \n",
				(PVOID)(((PLDR_DATA_TABLE_ENTRY)Module)->DllBase),
				(ULONG)(((PLDR_DATA_TABLE_ENTRY)Module)->SizeOfImage),
				&(((PLDR_DATA_TABLE_ENTRY)Module)->BaseDllName),
				&(((PLDR_DATA_TABLE_ENTRY)Module)->FullDllName)
				);
			Module = Module->Flink;
			
			// 測試下一個模塊信息的可讀性
			ProbeForRead((CONST PVOID)Module, 80, 8);
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER){ ; }
	
	// 取消依附進程
	KeUnstackDetachProcess(&ks);
}

VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	ULONG i = 0;
	PEPROCESS eproc = NULL;
	for (i = 4; i<100000000; i = i + 4)
	{
		eproc = LookupProcess((HANDLE)i);
		if (eproc != NULL)
		{
			ObDereferenceObject(eproc);
			if (strstr(PsGetProcessImageFileName(eproc), "lyshark.exe") != NULL)
			{
				EnumModule(eproc);
			}
		}
	}

	DriverObject->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

如上我們指定獲取應用層lyshark.exe進程的模塊信息,並可得到以下輸出效果:

上篇文章中的代碼就不再啰嗦了,這裡只給出記憶體轉存的核心代碼,如下代碼:

  • RtlInitUnicodeString 用於初始化轉存後的名字字元串
  • ZwCreateFile 內核中創建文件到應用層
  • ZwWriteFile 將文件寫出到文件
  • ZwClose 最後是關閉文件並釋放堆空間

很簡單隻是利用了SafeCopyMemory_R3_to_R0將進程記憶體讀取到緩衝區內,並將緩衝區寫出到C盤目錄下。

// 進程記憶體拷貝函數
// By: LyShark.com
NTSTATUS ProcessDumps(PEPROCESS pEprocess, ULONG_PTR nBase, ULONG nSize)
{
	BOOLEAN bAttach = FALSE;
	KAPC_STATE ks = { 0 };
	PVOID pBuffer = NULL;
	NTSTATUS status = STATUS_UNSUCCESSFUL;

	if (nSize == 0 || pEprocess == NULL)
	{
		return status;
	}

	pBuffer = ExAllocatePoolWithTag(PagedPool, nSize, 'lysh');
	if (!pBuffer)
	{
		return status;
	}

	memset(pBuffer, 0, nSize);

	if (pEprocess != IoGetCurrentProcess())
	{
		KeStackAttachProcess(pEprocess, &ks);
		bAttach = TRUE;
	}

	status = SafeCopyMemory_R3_to_R0(nBase, (ULONG_PTR)pBuffer, nSize);

	if (bAttach)
	{
		KeUnstackDetachProcess(&ks);
		bAttach = FALSE;
	}

	OBJECT_ATTRIBUTES object;
	IO_STATUS_BLOCK io;
	HANDLE hFile;
	UNICODE_STRING log;

	// 導出文件名稱
	RtlInitUnicodeString(&log, L"\\??\\C:\\lyshark_dumps.exe");
	InitializeObjectAttributes(&object, &log, OBJ_CASE_INSENSITIVE, NULL, NULL);

	status = ZwCreateFile(&hFile,
		GENERIC_WRITE,
		&object,
		&io,
		NULL,
		FILE_ATTRIBUTE_NORMAL,
		FILE_SHARE_WRITE,
		FILE_OPEN_IF,
		FILE_SYNCHRONOUS_IO_NONALERT,
		NULL,
		0);

	if (!NT_SUCCESS(status))
	{
		DbgPrint("打開文件錯誤 \n");
		return STATUS_SUCCESS;
	}

	ZwWriteFile(hFile, NULL, NULL, NULL, &io, pBuffer, nSize, NULL, NULL);
	DbgPrint("寫出位元組數: %d \n", io.Information);
	DbgPrint("[*] LyShark.exe 已轉存");
	ZwClose(hFile);

	if (pBuffer)
	{
		ExFreePoolWithTag(pBuffer, 'lysh');
		pBuffer = NULL;
	}

	return status;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint(("Uninstall Driver Is OK \n"));
}

// lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	NTSTATUS ntStatus;
	PEPROCESS pCurProcess = NULL;

	__try
	{
		ntStatus = PsLookupProcessByProcessId((HANDLE)272, &pCurProcess);
		if (NT_SUCCESS(ntStatus))
		{
			// 設置基地址以及長度
			ntStatus = ProcessDumps(pCurProcess, 0x140000000, 1024);
			ObDereferenceObject(pCurProcess);
		}
	}
	__except (1)
	{
		ntStatus = GetExceptionCode();
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

轉存後效果如下所示:

至於導出的進程無法運行只是沒有修複而已(後期會講),可以打開看看是沒錯的。

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/16779358.html
版權聲明:本博客文章與代碼均為學習時整理的筆記,文章 [均為原創] 作品,轉載請 [添加出處] ,您添加出處是我創作的動力!

轉載文章請遵守《中華人民共和國著作權法》相關法律規定或遵守《署名CC BY-ND 4.0國際》規範,合理合規攜帶原創出處轉載!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • LinkedBlockingDeque介紹 【1】LinkedBlockingDeque是一個基於鏈表實現的雙向阻塞隊列,預設情況下,該阻塞隊列的大小為Integer.MAX_VALUE,可以看做無界隊列,但也可以設置容量限制,作為有界隊列。 【2】相比於其他阻塞隊列,LinkedBlockingD ...
  • 羅馬數字包含以下七種字元: I, V, X, L,C,D 和 M。 字元 數值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 羅馬數字 2 寫做 II ,即為兩個併列的 1 。12 寫做 XII ,即為 X + II 。 27 寫做 XXVII, 即為 XX + ...
  • LinkedBlockingQueue介紹 【1】LinkedBlockingQueue是一個基於鏈表實現的阻塞隊列,預設情況下,該阻塞隊列的大小為Integer.MAX_VALUE,由於這個數值特別大,所以 LinkedBlockingQueue 也被稱作無界隊列,代表它幾乎沒有界限,隊列可以隨著 ...
  • 演算法步驟 遍歷整個數組,找到最小(大)的元素,放到數組的起始位置。 再遍歷剩下的數組,找到剩下元素中的最小(大)元素,放到數組的第二個位置。 重覆以上步驟,直到排序完成。 一共需要遍曆數組元素個數-1次,當找到第二大(小)的元素時,可以停止。這時最後一個元素必是最大(小)元素。 代碼 import ...
  • ArrayBlockingQueue介紹 ArrayBlockingQueue是最典型的有界阻塞隊列,其內部是用數組存儲元素的,初始化時需要指定容量大小,利用 ReentrantLock 實現線程安全。 在生產者-消費者模型中使用時,如果生產速度和消費速度基本匹配的情況下,使用ArrayBlocki ...
  • springboot結合redis做緩存,在@Cacheable中使用如下SpEL時報錯。 @Cacheable(cacheNames = ENTERPRISE_CACHE_KEY, key = "#{T(com.emax.common.RestApiSignUtil).foo(#root.args ...
  • MyBatisPlus 快速入門 1.創建資料庫mybatisplus 2.創建user表並插入數據 DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主鍵ID', name VARCHAR( ...
  • 前言 嗨嘍,大家好呀~這裡是愛看美女的茜茜吶 又到了學Python時刻~ 所以特地給大家獻上如何用Python來開發一款看視頻不需要VIP的軟體~ 如果想發給朋友用的話,咱們在打包成exe可執行軟體,這樣小伙伴也能一起用了~ 效果展示 這是本次要寫的界面 優、騰、愛三個主流的都可以,其它的我沒試,直 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...