在筆者上一篇文章《驅動開發:內核MDL讀寫行程記憶體》
簡單介紹了如何通過MDL映射的方式實作行程讀寫操作,本章將通過如上案例實作遠程行程反匯編功能,此類功能也是ARK工具中最常見的功能之一,通常此類功能的實作分為兩部分,內核部分只負責讀寫位元組集,應用層部分則配合反匯編引擎對位元組集進行解碼,此處我們將運用capstone
引擎實作這個功能,
首先是實作驅動部分,驅動程式的實作是一成不變的,僅僅只是做一個讀寫功能即可,完整的代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <windef.h>
#define READ_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define WRITE_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define DEVICENAME L"\\Device\\ReadWriteDevice"
#define SYMBOLNAME L"\\??\\ReadWriteSymbolName"
typedef struct
{
DWORD pid; // 行程PID
UINT64 address; // 讀寫地址
DWORD size; // 讀寫長度
BYTE* data; // 讀寫資料集
}ProcessData;
// MDL讀取封裝
BOOLEAN ReadProcessMemory(ProcessData* ProcessData)
{
BOOLEAN bRet = TRUE;
PEPROCESS process = NULL;
// 將PID轉為EProcess
PsLookupProcessByProcessId(ProcessData->pid, &process);
if (process == NULL)
{
return FALSE;
}
BYTE* GetProcessData = https://www.cnblogs.com/LyShark/p/NULL;
__try
{
// 分配堆空間 NonPagedPool 非分頁記憶體
GetProcessData = ExAllocatePool(NonPagedPool, ProcessData->size);
}
__except (1)
{
return FALSE;
}
KAPC_STATE stack = { 0 };
// 附加到行程
KeStackAttachProcess(process, &stack);
__try
{
// 檢查行程記憶體是否可讀取
ProbeForRead(ProcessData->address, ProcessData->size, 1);
// 完成拷貝
RtlCopyMemory(GetProcessData, ProcessData->address, ProcessData->size);
}
__except (1)
{
bRet = FALSE;
}
// 關閉參考
ObDereferenceObject(process);
// 解除附加
KeUnstackDetachProcess(&stack);
// 拷貝資料
RtlCopyMemory(ProcessData->data, GetProcessData, ProcessData->size);
// 釋放堆
ExFreePool(GetProcessData);
return bRet;
}
// MDL寫入封裝
BOOLEAN WriteProcessMemory(ProcessData* ProcessData)
{
BOOLEAN bRet = TRUE;
PEPROCESS process = NULL;
// 將PID轉為EProcess
PsLookupProcessByProcessId(ProcessData->pid, &process);
if (process == NULL)
{
return FALSE;
}
BYTE* GetProcessData = NULL;
__try
{
// 分配堆
GetProcessData = ExAllocatePool(NonPagedPool, ProcessData->size);
}
__except (1)
{
return FALSE;
}
// 回圈寫出
for (int i = 0; i < ProcessData->size; i++)
{
GetProcessData[i] = ProcessData->data[i];
}
KAPC_STATE stack = { 0 };
// 附加行程
KeStackAttachProcess(process, &stack);
// 分配MDL物件
PMDL mdl = IoAllocateMdl(ProcessData->address, ProcessData->size, 0, 0, NULL);
if (mdl == NULL)
{
return FALSE;
}
MmBuildMdlForNonPagedPool(mdl);
BYTE* ChangeProcessData = NULL;
__try
{
// 鎖定地址
ChangeProcessData = MmMapLockedPages(mdl, KernelMode);
// 開始拷貝
RtlCopyMemory(ChangeProcessData, GetProcessData, ProcessData->size);
}
__except (1)
{
bRet = FALSE;
goto END;
}
// 結束釋放MDL關閉參考取消附加
END:
IoFreeMdl(mdl);
ExFreePool(GetProcessData);
KeUnstackDetachProcess(&stack);
ObDereferenceObject(process);
return bRet;
}
NTSTATUS DriverIrpCtl(PDEVICE_OBJECT device, PIRP pirp)
{
PIO_STACK_LOCATION stack;
stack = IoGetCurrentIrpStackLocation(pirp);
ProcessData* ProcessData;
switch (stack->MajorFunction)
{
case IRP_MJ_CREATE:
{
break;
}
case IRP_MJ_CLOSE:
{
break;
}
case IRP_MJ_DEVICE_CONTROL:
{
// 獲取應用層傳值
ProcessData = pirp->AssociatedIrp.SystemBuffer;
DbgPrint("行程ID: %d | 讀寫地址: %p | 讀寫長度: %d \n", ProcessData->pid, ProcessData->address, ProcessData->size);
switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
// 讀取函式
case READ_PROCESS_CODE:
{
ReadProcessMemory(ProcessData);
break;
}
// 寫入函式
case WRITE_PROCESS_CODE:
{
WriteProcessMemory(ProcessData);
break;
}
}
pirp->IoStatus.Information = sizeof(ProcessData);
break;
}
}
pirp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
if (driver->DeviceObject)
{
UNICODE_STRING SymbolName;
RtlInitUnicodeString(&SymbolName, SYMBOLNAME);
// 洗掉符號鏈接
IoDeleteSymbolicLink(&SymbolName);
IoDeleteDevice(driver->DeviceObject);
}
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT device = NULL;
UNICODE_STRING DeviceName;
DbgPrint("[LyShark] hello lyshark.com \n");
// 初始化設備名
RtlInitUnicodeString(&DeviceName, DEVICENAME);
// 創建設備
status = IoCreateDevice(Driver, sizeof(Driver->DriverExtension), &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);
if (status == STATUS_SUCCESS)
{
UNICODE_STRING SymbolName;
RtlInitUnicodeString(&SymbolName, SYMBOLNAME);
// 創建符號鏈接
status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
// 失敗則洗掉設備
if (status != STATUS_SUCCESS)
{
IoDeleteDevice(device);
}
}
// 派遣函式初始化
Driver->MajorFunction[IRP_MJ_CREATE] = DriverIrpCtl;
Driver->MajorFunction[IRP_MJ_CLOSE] = DriverIrpCtl;
Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIrpCtl;
// 卸載驅動
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
上方的驅動程式很簡單關鍵部分已經做好了備注,此類驅動換湯不換藥沒啥難度,接下來才是本節課的重點,讓我們開始了解一下Capstone
這款反匯編引擎吧,Capstone是一個輕量級的多平臺、多架構的反匯編框架,Capstone旨在成為安全社區中二進制分析和反匯編的終極反匯編引擎,該引擎支持多種平臺的反匯編,非常推薦使用,
- 反匯編引擎下載地址:https://cdn.lyshark.com/sdk/capstone_msvc12.zip
這款反匯編引擎如果你想要使用它則第一步就是呼叫cs_open()
官方對其的解釋是打開一個句柄,這個打開功能其中的引數如下所示;
- 引數1:指定模式 CS_ARCH_X86 表示為Windows平臺
- 引數2:執行位數 CS_MODE_32為32位模式,CS_MODE_64為64位
- 引數3:打開后保存的句柄&dasm_handle
第二步也是最重要的一步,呼叫cs_disasm()
反匯編函式,該函式的解釋如下所示;
- 引數1:指定dasm_handle反匯編句柄
- 引數2:指定你要反匯編的資料集或者是一個緩沖區
- 引數3:指定你要反匯編的長度 64
- 引數4:輸出的記憶體地址起始位置 0x401000
- 引數5:默認填充為0
- 引數6:用于輸出資料的一個指標
這兩個函式如果能搞明白,那么如下反匯編完整代碼也就可以理解了,
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
#include <inttypes.h>
#include <capstone/capstone.h>
#pragma comment(lib,"capstone64.lib")
#define READ_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define WRITE_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
typedef struct
{
DWORD pid;
UINT64 address;
DWORD size;
BYTE* data;
}ProcessData;
int main(int argc, char* argv[])
{
// 連接到驅動
HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ProcessData data;
DWORD dwSize = 0;
// 指定需要讀寫的行程
data.pid = 6932;
data.address = 0x401000;
data.size = 64;
// 讀取機器碼到BYTE位元組陣列
data.data = https://www.cnblogs.com/LyShark/p/new BYTE[data.size];
DeviceIoControl(handle, READ_PROCESS_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);
for (int i = 0; i < data.size; i++)
{
printf("0x%02X ", data.data[i]);
}
printf("\n");
// 開始反匯編
csh dasm_handle;
cs_insn *insn;
size_t count;
// 打開句柄
if (cs_open(CS_ARCH_X86, CS_MODE_32, &dasm_handle) != CS_ERR_OK)
{
return 0;
}
// 反匯編代碼
count = cs_disasm(dasm_handle, (unsigned char *)data.data, data.size, data.address, 0, &insn);
if (count > 0)
{
size_t index;
for (index = 0; index < count; index++)
{
/*
for (int x = 0; x < insn[index].size; x++)
{
printf("機器碼: %d -> %02X \n", x, insn[index].bytes[x]);
}
*/
printf("地址: 0x%"PRIx64" | 長度: %d 反匯編: %s %s \n", insn[index].address, insn[index].size, insn[index].mnemonic, insn[index].op_str);
}
cs_free(insn, count);
}
cs_close(&dasm_handle);
getchar();
CloseHandle(handle);
return 0;
}
通過驅動加載工具加載WinDDK.sys
然后在運行本程式,你會看到正確的輸出結果,反匯編當前位置處向下64
位元組,
說完了反匯編接著就需要講解如何對記憶體進行匯編操作了,匯編引擎這里采用了XEDParse
該引擎小巧簡潔,著名的x64dbg
就是在運用本引擎進行匯編替換的,本引擎的使用非常簡單,只需要向XEDParseAssemble()
函式傳入一個規范的結構體即可完成轉換,完整代碼如下所示,
- 匯編引擎下載地址:https://cdn.lyshark.com/sdk/XEDParse.zip
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
extern "C"
{
#include "D:/XEDParse/XEDParse.h"
#pragma comment(lib, "D:/XEDParse/XEDParse_x64.lib")
}
using namespace std;
#define READ_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define WRITE_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
typedef struct
{
DWORD pid;
UINT64 address;
DWORD size;
BYTE* data;
}ProcessData;
int main(int argc, char* argv[])
{
// 連接到驅動
HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ProcessData data;
DWORD dwSize = 0;
// 指定需要讀寫的行程
data.pid = 6932;
data.address = 0x401000;
data.size = 0;
XEDPARSE xed = { 0 };
xed.x64 = FALSE;
// 輸入一潭訓編指令并轉換
scanf_s("%llx", &xed.cip);
gets_s(xed.instr, XEDPARSE_MAXBUFSIZE);
if (XEDPARSE_OK != XEDParseAssemble(&xed))
{
printf("指令錯誤: %s\n", xed.error);
}
// 生成堆
data.data = https://www.cnblogs.com/LyShark/p/new BYTE[xed.dest_size];
// 設定長度
data.size = xed.dest_size;
for (size_t i = 0; i < xed.dest_size; i++)
{
// 替換到堆中
printf("%02X ", xed.dest[i]);
data.data[i] = xed.dest[i];
}
// 呼叫控制器,寫入到遠端記憶體
DeviceIoControl(handle, WRITE_PROCESS_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);
printf("[LyShark] 指令集已替換. \n");
getchar();
CloseHandle(handle);
return 0;
}
通過驅動加載工具加載WinDDK.sys
然后在運行本程式,你會看到正確的輸出結果,可打開反內核工具驗證是否改寫成功,
打開反內核工具,并切換到觀察是否寫入了一條mov eax,1
的指令集機器碼,如下圖已經完美寫入,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553185.html
標籤:C++
下一篇:返回列表