缙云企业网站建设优化设计三年级上册答案
web/
2025/10/3 3:48:04/
文章来源:
缙云企业网站建设,优化设计三年级上册答案,青岛市南区城市建设局网站,2018做网站站长在笔者上一篇文章《内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作#xff0c;本章将通过如上案例实现远程进程反汇编功能#xff0c;此类功能也是ARK工具中最常见的功能之一#xff0c;通常此类功能的实现分为两部分#xff0c;内核部分只负责读写…在笔者上一篇文章《内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作本章将通过如上案例实现远程进程反汇编功能此类功能也是ARK工具中最常见的功能之一通常此类功能的实现分为两部分内核部分只负责读写字节集应用层部分则配合反汇编引擎对字节集进行解码此处我们将运用capstone引擎实现这个功能。 首先是实现驱动部分驱动程序的实现是一成不变的仅仅只是做一个读写功能即可完整的代码如下所示
#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\\??\\ReadWriteSymbolNametypedef struct
{DWORD pid; // 进程PIDUINT64 address; // 读写地址DWORD size; // 读写长度BYTE* data; // 读写数据集
}ProcessData;// MDL读取封装
BOOLEAN ReadProcessMemory(ProcessData* ProcessData)
{BOOLEAN bRet TRUE;PEPROCESS process NULL;// 将PID转为EProcessPsLookupProcessByProcessId(ProcessData-pid, process);if (process NULL){return FALSE;}BYTE* GetProcessData 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转为EProcessPsLookupProcessByProcessId(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这款反汇编引擎吧
3.6.1 内存反汇编的应用
Capstone 是一款轻量级、多平台、多架构的反汇编引擎旨在成为二进制分析和反汇编的终极工具。它支持多种平台和架构的反汇编包括x86、ARM、MIPS等并且可以轻松地集成到各种二进制分析工具中。Capstone的主要优点是它易于使用和快速的反汇编速度而且由于其开源和活跃的社区支持可以很容易地更新和维护。因此Capstone被广泛用于二进制分析、安全研究和反汇编工作中。
反汇编引擎GitHub地址https://github.com/capstone-engine
这款反汇编引擎如果你想要使用它则第一步就是调用cs_open()打开一个句柄这个打开功能的函数原型如下所示
cs_err cs_open(cs_arch arch,cs_mode mode,csh *handle
);参数 arch指定架构类型例如 CS_ARCH_X86 表示为 x86 架构。参数 mode指定模式例如 CS_MODE_32 表示为 32 位模式。参数 handle打开的句柄用于后续对引擎的调用。由于其是传递指针的方式因此需要先分配好该指针的内存。函数执行成功后该句柄将被填充可以用于后续的反汇编操作。
函数cs_open()是Capstone反汇编引擎提供的它用于初始化Capstone库并打开一个句柄以便进行后续的反汇编操作。该函数有三个参数分别是架构类型、执行模式和指向句柄的指针。
具体地说第一个参数CS_ARCH_X86指定了反汇编的架构类型这里表示为Windows平台第二个参数CS_MODE_32或CS_MODE_64则指定了反汇编的执行模式即32位模式或64位模式第三个参数则是指向一个Capstone库句柄的指针通过该指针可以进行后续的反汇编操作。
打开句柄后我们可以使用其他的Capstone函数进行反汇编操作比如cs_disasm()函数用于对二进制代码进行反汇编反汇编后的结果可以用于分析和理解程序的行为。最后我们还需要使用cs_close()函数关闭打开的句柄以释放资源。
第二步也是最重要的一步调用cs_disasm()反汇编函数函数返回实际反汇编的指令数或者如果发生错误则返回0。该函数的原型如下所示
size_t cs_disasm(csh handle, const uint8_t *code, size_t code_size, uint64_t address, size_t count, cs_insn *insn);其中各参数的含义为
参数 handle要使用的Capstone引擎的句柄指定dasm_handle反汇编句柄参数 code要反汇编的二进制代码的指针定你要反汇编的数据集或者是一个缓冲区参数 code_size要反汇编的二进制代码的大小以字节为单位指定你要反汇编的长度64参数 address要反汇编的二进制代码在内存中的地址用于计算跳转目标地址输出的内存地址起始位置 0x401000参数 count要反汇编的指令数量限制。如果设置为0则表示没有数量限制将会反汇编所有有效的指令参数 insn用于存储反汇编结果的结构体数组。它是一个输出参数由调用者分配内存。用于输出数据的一个指针
如上所示的cs_open()以及cs_disasm()两个函数如果能搞明白那么反汇编完整代码即可写出来了根据如下流程实现
创建一个句柄 handle用于连接到驱动程序。定义 ProcessData 结构体包含需要读取的进程 ID、起始地址、读取的字节数以及存储读取结果的 BYTE 数组。使用 DeviceIoControl() 函数从指定进程读取机器码将结果存储到 data 结构体的 data 字段中。使用 cs_open() 函数打开 Capstone 引擎的句柄 dasm_handle指定了架构为 x86 平台模式为 32 位。使用 cs_disasm() 函数将 data 结构体中的机器码进行反汇编将结果存储到 insn 数组中同时返回反汇编指令的数量 count。循环遍历 insn 数组将每个反汇编指令的地址、长度、助记符和操作数打印出来。使用 cs_free() 函数释放 insn 数组占用的内存。使用 cs_close() 函数关闭 Capstone 引擎的句柄 dasm_handle。关闭连接到驱动程序的句柄 handle。
根据如上实现流程我们可以写出如下代码片段
#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 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字节。 3.6.2 内存汇编的应用
实现了反汇编接着就需要讲解如何对内存进行汇编操作汇编引擎这里采用了XEDParse该引擎小巧简洁著名的x64dbg就是在运用本引擎进行汇编替换的XEDParse 是一个开源的汇编引擎用于将汇编代码转换为二进制指令。它基于Intel的XED库并提供了一些易于使用的接口。
汇编引擎GitHub地址https://github.com/x64dbg/XEDParse
一般而言再进行汇编转换之前需要做如下几个步骤的工作
1.定义xed_state_t结构体该结构体包含有关目标平台的信息例如处理器架构和指令集。可以使用xed_state_zero()函数来初始化该结构体。
xed_state_t state;
xed_state_zero(state);
state.mmode XED_MACHINE_MODE_LONG_64;
state.stack_addr_width XED_ADDRESS_WIDTH_64b;2.定义xed_error_enum_t类型的变量来接收转换过程中可能出现的错误信息。
xed_error_enum_t error XED_ERROR_NONE;3.定义xed_encoder_request_t结构体该结构体包含要转换的汇编指令的信息例如操作码和操作数。
xed_encoder_request_t request;
xed_encoder_request_zero_set_mode(request, state);
request.iclass XED_ICLASS_MOV;
request.operand_order[0] 0;
request.operand_order[1] 1;
request.operands[0].name XED_REG_RAX;
request.operands[1].name XED_REG_RBX;4.使用XEDParseAssemble()函数将汇编代码转换为二进制指令并将结果存储在xed_uint8_t类型的数组中。此函数返回转换后的指令长度。
xed_uint8_t binary[15];
xed_uint_t length XEDParseAssemble(request, binary, sizeof(binary), error);
if (error ! XED_ERROR_NONE) {// handle error
}5.使用转换后的二进制指令进行后续操作。
typedef int (*func_t)(void);
func_t func (func_t)binary;
int result func();在本次转换流程中我们只需要向XEDParseAssemble()函数传入一个规范的结构体即可完成转换通过向XEDPARSE结构传入需要转换的指令并自动转换为机器码放入到data.data堆中实现核心代码如下所示
#define _CRT_SECURE_NO_WARNINGS
#include Windows.h
#include iostreamextern 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 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的指令集机器码如下图已经完美写入。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/86000.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!