首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
编程技术
发新帖
3
6
[原创]实现简易ARK工具(5) x64信息快查
发表于: 2025-8-6 20:48
4588
[原创]实现简易ARK工具(5) x64信息快查
X66iaM
2025-8-6 20:48
4588
前言 开了这个坑,但前文都是x86的,感觉差点意思,发笔记凑活一下- - 写玩具的时候收集的一些api、结构体、遍历信息的方法,全靠看雪前辈们的文章啊-。-,帮助新同学省去检索信息的时间。能力有限,欢迎大家补充指正。 下文内容在win7 7601和win10 19h1经验证可行。  # 解析pdb R3:<a href="elink@25fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2N6$3q4F1M7%4V1&6z5q4)9J5c8V1g2S2M7%4W2b7k6r3u0Q4x3V1k6T1L8r3!0T1i4K6u0r3L8h3q4A6L8W2)9J5c8X3g2*7M7r3c8T1i4K6u0W2K9s2m8H3"><mark class="encrypted">912K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2N6$3q4F1M7%4V1&6z5q4)9J5c8V1g2S2M7%4W2b7k6r3t1`.</mark></a> R0:<a href="elink@bbfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6a6P5s2W2Y4k6h3^5I4j5e0q4Q4x3V1k6G2P5r3N6W2L8W2m8V1j5R3`.`."><mark class="encrypted">d33K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6a6P5s2W2Y4k6h3^5I4j5e0q4Q4x3V1k6G2P5r3N6W2L8W2m8V1j5R3`.`.</mark></a> 他用的ksocket库只能发http请求,所以我只用他解析pdb,让R3调UrlDownloadToFile下载。 也可以让ida下pdb 直接使用dia2dump解析即可 大家有什么在R0下载符号的方法吗 # GDT 用不了__asm了,用函数拿gdtr,联合编译asm也行 \#include <immintrin.h> extern "C" void _sgdt(void*); GDTR gdtr = { 0 }; _sgdt(&gdtr); 我在R3拿居然是错的 在x64下,TSS(任务状态段)和LDT(局部描述符表)等系统段描述符扩展为16字节,占用连续两个GDT项。 前8字节与32位一样,后8字节用于补充TSS/LDT的高32位base地址和保留字段。 <u>所以解析数据的时候遇到s=0且type为TSS/LDT类型(type=2,9,11),和下一项8字节的数据合并一起解析。</u> 平坦模式导致数据段和代码段的base/limit字段失效,只需关注系统段的base/limit。 ```cpp //32位的 typedef struct SegmentDescriptor { unsigned Limit1 : 16; // 界限低16位 unsigned Base1 : 16; // 基址低16位 unsigned Base2 : 8; // 基址中8位 unsigned type : 4; // 段类型 unsigned s : 1; // 系统段标志 unsigned dpl : 2; // 特权级 unsigned p : 1; // 存在位 unsigned Limit2 : 4; // 界限高4位 unsigned avl : 1; // 软件可用位 unsigned l : 1; // 64位代码段标志 以前的保留位 unsigned db : 1; // 操作数大小 以前代表段位数,64位代码段也就是l=1时db位必须为0 unsigned g : 1; // 粒度位 unsigned Base3 : 8; // 基址高8位 } SegmentDescriptor, *PSEGDESC; // 确保是8字节 // 64位系统段描述符(16字节) typedef struct SystemDescriptor64 { SegmentDescriptor low; // 低8字节 unsigned Base4 : 32; // 基址最高32位 unsigned reserved : 32; // 保留字段 } SystemDescriptor64; ``` 但是我观察许多ARK并没有合并显示 仍然保留了合并的表项 保留的 我没保留  虽然数值解析的一致 但是openark仍然显示了0x48 # IDT 与GDT一样 一个核心一张表 使用`KeSetSystemAffinityThreadEx`切核心+`__sidt(&idtr)`读 ```plain // 中断描述符结构 (x64) typedef struct InterruptDescriptor { USHORT OffsetLow; // 处理程序地址低16位 USHORT Selector; // 段选择子 USHORT IstIndex : 3; // IST索引 USHORT Reserved0 : 5; // 保留 USHORT Type : 4; // 门类型 USHORT Reserved1 : 1; // 保留 USHORT Dpl : 2; // 描述符特权级 USHORT Present : 1; // 存在位 USHORT OffsetMiddle; // 处理程序地址中16位 ULONG OffsetHigh; // 处理程序地址高32位 ULONG Reserved2; // 保留 } *PINTDESC; ```  # SSDT 在 64位Windows 中,SSDT表的结构与32位不同: + 32位:SSDT表直接存储函数地址 + 64位:SSDT表存储的是相对偏移量,需要计算得到真实地址 ```plain ULONG_PTR SSDT_GetPfnAddr(ULONG dwIndex, PULONG lpBase) { ULONG_PTR lpAddr = NULL; ULONG dwOffset = lpBase[dwIndex]; //按16位对齐省空间,所以>>4;负偏移有+-问题,所以|0xF00.. if (dwOffset & 0x80000000) dwOffset = (dwOffset >> 4) | 0xF0000000; else dwOffset >>= 4; lpAddr = (ULONG_PTR)((PUCHAR)lpBase + (LONG)dwOffset); return lpAddr; } ``` 1. 右移4位:Windows x64中,SSDT偏移量以16字节对齐,所以低4位总是0,可以节省空间 2. 符号扩展:处理负偏移(向前的地址),通过| 0xF0000000进行符号扩展 3. 相对地址计算:最终地址 = SSDT基址 + 计算出的偏移量 抄这篇[https://bbs.kanxue.com/thread-248117.htm](https://bbs.kanxue.com/thread-248117.htm) # ShadowSSDT 拿到KeServiceDescriptorTableShadow后找第二张表是Shadow,解析方式与SSDT一致。 ```plain NTSTATUS EnumShadowSSDT(PSSDT_INFO SsdtBuffer, PULONG SsdtCount) { INIT_PDB; PSYSTEM_SERVICE_DESCRIPTOR_TABLE ShadowTableArray = (PSYSTEM_SERVICE_DESCRIPTOR_TABLE)ntos.GetPointer("KeServiceDescriptorTableShadow"); Log("[XM] KeServiceDescriptorTableShadow Array: %p", ShadowTableArray); if (!ShadowTableArray) { Log("[XM] KeServiceDescriptorTableShadow == null"); return STATUS_UNSUCCESSFUL; } // 访问数组的第二个元素 [1] - 这才是真正的 ShadowSSDT PSYSTEM_SERVICE_DESCRIPTOR_TABLE ShadowTable = &ShadowTableArray[1]; Log("[XM] ShadowSSDT [0]: Base=%p, Count=%d", ShadowTableArray[0].Base, ShadowTableArray[0].NumberOfServices); Log("[XM] ShadowSSDT [1]: Base=%p, Count=%d", ShadowTable->Base, ShadowTable->NumberOfServices); if (!ShadowTable->Base || ShadowTable->NumberOfServices == 0) { Log("[XM] ShadowSSDT not available"); Log("[XM] ShadowSSDT [1] not available"); } ULONG nums = ShadowTable->NumberOfServices; PULONG shadowSsdt = ShadowTable->Base; *SsdtCount = nums; Log("[XM] ShadowSSDT found: %d services", nums); for (ULONG i = 0; i < nums; i++) { SsdtBuffer[i].Index = i + 0x1000; // ShadowSSDT的调用号从0x1000开始 ULONG_PTR pfnAddr = SSDT_GetPfnAddr(i, shadowSsdt); SsdtBuffer[i].FunctionAddress = (PVOID)pfnAddr; Log("[XM] ShadowSSDT[%d]: Raw=0x%X, Decoded=0x%p", i, shadowSsdt[i], pfnAddr); } return STATUS_SUCCESS; } ``` # 遍历进程 1.前文讲的遍历进程链表 PsInitialSystemProcess → EPROCESS.ActiveProcessLinks → 下一个EPROCESS → ... 2.用PsLookupProcessByProcessId 函数内部会查句柄表,相比链表更安全  由于pid是四的倍数 for(pid=0;pid<65535;pid+=4) → 句柄表查找 → 返回EPROCESS指针 3.内存特征EPROCESS 有什么表敌我都知道 最后还得暴力搜内存 _DISPATCHER_HEADER.type= 0x3 验证EPROCESS进程链表的后继的前驱是不是自己 页目录表是否有效 低12位是否为0(页对齐)等等方法 4.听闻还有一种从线程调度反推进程的方法 # 遍历模块 <a href="elink@b18K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3K9%4c8J5x3K6W2Q4x3V1k6H3i4K6u0r3x3K6b7&6y4o6R3%4y4g2)9J5k6h3S2@1L8h3H3`.">利用PsLoadModuleList 杖举驱动模块 - KTr - 博客园</a> 或者 <font style="color:rgb(22, 22, 22);">ZwQueryInformation</font> # 枚举常规回调 [https://bbs.kanxue.com/thread-148895.htm](https://bbs.kanxue.com/thread-148895.htm) **进程 线程 模块 **都用数组存放回调函数指针 ,数组里最多塞下64个回调指针: PspCreateProcessNotifyRoutine PspCreateThreadNotifyRoutine PspLoadImageNotifyRoutine ```javascript typedef struct _EX_CALLBACK_ROUTINE_BLOCK { EX_RUNDOWN_REF RundownProtect; // Rundown protection structure PEX_CALLBACK_FUNCTION Function; // 回调函数地址 PVOID Context; // 回调上下文参数 } EX_CALLBACK_ROUTINE_BLOCK, * PEX_CALLBACK_ROUTINE_BLOCK; ``` 通过索引/函数地址删除回调 PsSetCreateProcessNotifyRoutine(functionAddr, TRUE); **蓝屏 注册表 关机** 都用链表存放回调函数指针 : **注册表** <a href="elink@d6aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0L8r3!0#2k6q4)9J5k6i4c8W2L8X3y4W2L8Y4c8Q4x3X3g2U0L8$3#2Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3x3U0x3$3y4U0R3K6y4H3`.`."><mark class="encrypted">d3aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0L8r3!0#2k6q4)9J5k6i4c8W2L8X3y4W2L8Y4c8Q4x3X3g2U0L8$3#2Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3x3U0x3$3y4U0R3K6y4H3`.`.</mark></a> ```javascript //注册表回调结构 typedef struct _CM_NOTIFY_ENTRY { LIST_ENTRY ListEntryHead; //链表项 ULONG UnKnown1; ULONG UnKnown2; LARGE_INTEGER Cookie; //回调标识符 PVOID Context; //用户上下文 PVOID Function; //回调函数地址 } CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY; ``` 定位链表遍历就行  注册表回调得用cookie删除 ```javascript LARGE_INTEGER cookie; cookie.QuadPart = (LONGLONG)deleteKey; NTSTATUS status = CmUnRegisterCallback(cookie); ``` **蓝屏 ** 结构体Wdm.h都有 标准蓝屏回调 (KeBugCheckCallback) + 符号: KeBugCheckCallbackListHead + 结构: KBUGCHECK_CALLBACK_RECORD + 用途: 在蓝屏时保存数据、记录状态 KeBugCheckCallbackListHead ```javascript typedef struct _KBUGCHECK_CALLBACK_RECORD { LIST_ENTRY Entry; PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine; _Field_size_bytes_opt_(Length) PVOID Buffer; // 这个宏指 Buffer大小由Length字段指定 ULONG Length; // 指定Buffer的字节长度 PUCHAR Component; ULONG_PTR Checksum; UCHAR State; } KBUGCHECK_CALLBACK_RECORD, *PKBUGCHECK_CALLBACK_RECORD; ``` 蓝屏回调通过 结构指针 删除 KeDeregisterBugCheckCallback(recordPtr); 蓝屏原因回调 (KeRegisterBugCheckReasonCallback) + 符号: KeBugCheckReasonCallbackListHead + 结构: KBUGCHECK_REASON_CALLBACK_RECORD + 用途: 根据蓝屏原因执行特定处理 ```c typedef struct _KBUGCHECK_REASON_CALLBACK_RECORD { LIST_ENTRY Entry; // 链表项 PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine; // 回调函数 PUCHAR Component; // 组件名 ULONG_PTR Checksum; // 校验和 KBUGCHECK_CALLBACK_REASON Reason; // 回调原因 UCHAR State; // 状态 } KBUGCHECK_REASON_CALLBACK_RECORD, * PKBUGCHECK_REASON_CALLBACK_RECORD; ``` **关机 ** IopNotifyShutdownQueueHead ```javascript //关机回调结构 typedef struct _SHUTDOWN_PACKET { LIST_ENTRY ListEntry; // +0x00 链表项 PDEVICE_OBJECT DeviceObject; // +0x10 设备对象 PIRP Irp; // +0x18 IRP } SHUTDOWN_PACKET, *PSHUTDOWN_PACKET; ``` ```markdown 注册 → IoRegisterShutdownNotification → 创建SHUTDOWN_PACKET → 插入IopNotifyShutdownQueueHead 关机触发 → 遍历链表 → 向每个设备发送IRP_MJ_SHUTDOWN 删除 → IoUnregisterShutdownNotification → 从链表移除SHUTDOWN_PACKET ``` # 枚举对象回调 ObTypeIndexTable→ _OBJECT_TYPE → CallbackList → CALLBACK_BODY → Pre/PostCallbackRoutine ```markdown 1: kd> x nt!ObTypeIndexTable fffff806`25e1bd70 nt!ObTypeIndexTable = <no type information> ************************************************************************* 1: kd> dt nt!_OBJECT_TYPE +0x000 TypeList : _LIST_ENTRY +0x010 Name : _UNICODE_STRING +0x020 DefaultObject : Ptr64 Void +0x028 Index : UChar +0x02c TotalNumberOfObjects : Uint4B +0x030 TotalNumberOfHandles : Uint4B +0x034 HighWaterNumberOfObjects : Uint4B +0x038 HighWaterNumberOfHandles : Uint4B +0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x0b8 TypeLock : _EX_PUSH_LOCK +0x0c0 Key : Uint4B +0x0c8 CallbackList : _LIST_ENTRY ``` 测试1 获取ObTypeIndexTable中每个ObType并打印出Name ```plain void ForTest() { INIT_PDB; ULONG_PTR ObTypeIndexTable = ntos.GetPointer("ObTypeIndexTable"); Log("[XM] ObTypeIndexTable地址: %p", ObTypeIndexTable); //遍历 先打印出所有的对象名称看看 int maxType = 100; for (int i = 0; i < maxType; i++) { // 取出POBJECT_TYPE指针 ULONG_PTR objTypeAddr = *(ULONG_PTR*)(ObTypeIndexTable + i * sizeof(ULONG_PTR)); if (!objTypeAddr) continue; // OBJECT_TYPE结构体Name字段偏移 size_t nameoffset = ntos.GetOffset("_OBJECT_TYPE", "Name"); Log("[XM] nameoffset: %p", nameoffset); ULONG_PTR nameAddr = objTypeAddr + nameoffset; // 读取UNICODE_STRING UNICODE_STRING* pName = (UNICODE_STRING*)nameAddr; if (!MmIsAddressValid(pName) || !MmIsAddressValid(pName->Buffer)) continue; // 打印对象类型名 Log("[XM] 对象类型[%d] 地址: %p 名称: %ws", i, objTypeAddr, pName->Buffer); } } ```  CallbackList的结构体未公开 网上各种叫法 给我整蒙了 这篇有讲:[https://bbs.kanxue.com/thread-277238.htm](https://bbs.kanxue.com/thread-277238.htm) ```plain typedef struct _CALLBACK_NODE { USHORT Version; // 版本号,目前是0x100, 可通过ObGetFilterVersion获取该值 USHORT CallbackBodyCount; // 本节点上CallbackBody的数量 PVOID Context; // 注册回调时设定的0B_CALLBACK_REGISTRATION.RegistrationContext UNICODE_STRING Altitude; // 指向Altitude字符串 char CallbackBody[1]; // 原本是CALLBACK_BODY CallbackBody[1] -> CALLBACK_BODY数组, 其元素个数为CallbackCount // 我用不到 改成了char } CALLBACK_NODE, * PCALLBACK_NODE; typedef struct _CALLBACK_BODY { LIST_ENTRY ListEntry; /* 系统中同类型对象的的CALLBACK_NODE通过这个链表串在一起, 对应于_OBJECT_TYPE->TypeList */ OB_OPERATION Operations; /* 注册回调时设定的OB_OPERATION_REGISTRATION.Operations成员(OB_OPERATION_HANDLE_CREATE... ) */ ULONG Active; PCALLBACK_NODE CallbackNode; // 指向该CallbackBody POBJECT_TYPE ObjectType; POB_PRE_OPERATION_CALLBACK PreCallbackRoutine; POB_POST_OPERATION_CALLBACK PostCallbackRoutine; EX_RUNDOWN_REF RundownProtection; // Run-down Protection } CALLBACK_BODY, * PCALLBACK_BODY; ``` 遍历`ObTypeIndexTable`得到`_OBJECT_TYPE` 拿到`_OBJECT_TYPE->CallbackList`之后遍历链表 每个节点按`CALLBACK_BODY`解析。 `PreCallbackRoutine`和`PostCallbackRoutine`是对象回调函数数组。 卸载对象回调传`CALLBACK_BODY->CallbackNode`给`ObUnRegisterCallbacks`即可。 # IO派遣函数/设备栈 ```plain 3: kd> dt nt!_DRIVER_OBJECT -o Type : Int2B Size : Int2B DeviceObject : Ptr64 _DEVICE_OBJECT Flags : Uint4B DriverStart : Ptr64 Void DriverSize : Uint4B DriverSection : Ptr64 Void DriverExtension : Ptr64 _DRIVER_EXTENSION DriverName : _UNICODE_STRING HardwareDatabase : Ptr64 _UNICODE_STRING FastIoDispatch : Ptr64 _FAST_IO_DISPATCH DriverInit : Ptr64 long DriverStartIo : Ptr64 void DriverUnload : Ptr64 void MajorFunction : [28] Ptr64 long ``` 遍历驱动对象方法 有代码: [https://bbs.kanxue.com/thread-276245.htm](https://bbs.kanxue.com/thread-276245.htm) 得知道这一段连续的数据结构 _OBJECT_HEADER_NAME_INFO (size: 0x20) _OBJECT_HEADER (offset: 0x30) Driver Object  实测能跑 拿到驱动对象就可以拿设备对象 然后遍历attachdevice 和MajorFunction 查被过滤的设备 检测派遣函数指针是否在xx模块内 # 网络端口 R3有API 可以拿到端口pid 有示例代码 <a href="elink@637K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9k6h3q4J5L8W2)9J5k6h3#2A6j5%4u0G2M7$3!0X3N6q4)9J5k6h3y4G2L8g2)9J5c8Y4A6Z5i4K6u0V1j5$3&6Q4x3V1k6%4K9h3&6V1L8%4N6K6i4K6u0r3N6$3W2F1x3K6u0Q4x3V1k6S2M7r3W2Q4x3V1k6A6M7r3S2D9M7r3q4H3K9g2)9J5c8X3&6X3i4K6u0V1K9i4m8Z5L8s2m8S2M7r3W2Q4x3X3c8Y4k6i4c8@1j5%4m8@1j5h3u0D9k6b7`.`.">getTcpTable 函数 (iphlpapi.h) - Win32 apps</a> <a href="elink@6e6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9k6h3q4J5L8W2)9J5k6h3#2A6j5%4u0G2M7$3!0X3N6q4)9J5k6h3y4G2L8g2)9J5c8Y4A6Z5i4K6u0V1j5$3&6Q4x3V1k6%4K9h3&6V1L8%4N6K6i4K6u0r3N6$3W2F1x3K6u0Q4x3V1k6S2M7r3W2Q4x3V1k6A6M7r3S2D9M7r3q4H3K9g2)9J5c8X3&6X3i4K6u0V1K9i4m8Z5L8s2m8S2M7r3W2Q4x3X3c8Y4k6i4c8W2P5s2c8W2L8X3c8W2k6s2c8U0M7s2c8S2j5X3I4W2">getExtendedTcpTable 函数 (iphlpapi.h) - Win32 apps</a> openark的kernel api-network.cpp中也有R3实现 # 解除文件占用 强制关闭文件句柄 在目标进程上下文中调用 ZwClose() 关闭文件句柄 ```markdown R3获取文件路径 转换成设备路径 传给R0 ↓ ObQueryNameString(文件对象) 获取完整文件路径 ↓ RtlCompareUnicodeString(对象路径, 目标路径) 字符串匹配 ↓ PsLookupProcessByProcessId 获取EPROCESS对象 注意这个函数使用完要减少引用计数 ↓ KeStackAttachProcess 切换到目标进程上下文 ↓ ZwClose(句柄值) 在目标进程中关闭文件句柄 ↓ KeUnstackDetachProcess 恢复原始进程上下文 ``` ```plain NTSTATUS UnlockFile(WCHAR* filePath) { //遍历所有进程句柄表 NTSTATUS Status; PSYSTEM_HANDLE_INFORMATION_EX HandlesEx; PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleInfoEx; POBJECT_NAME_INFORMATION ObjectNameInfo; PVOID Buffer; ULONG BufferSize = 4096; ULONG ReturnLength; ULONG_PTR i; UNICODE_STRING ustrName; RtlInitUnicodeString(&ustrName, filePath); Log("[XM] UnlockFile ustr: %wZ", &ustrName); ObjectNameInfo = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, 4096, 'ULFL'); if (!ObjectNameInfo) { return STATUS_NO_MEMORY; } retry: Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, 'ULFL'); if (!Buffer) { ExFreePool(ObjectNameInfo); return STATUS_NO_MEMORY; } Status = ZwQuerySystemInformation(SystemExtendedHandleInformation, Buffer, BufferSize, &ReturnLength ); if (Status == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(Buffer); BufferSize = ReturnLength; goto retry; } if (NT_SUCCESS(Status)) { HandlesEx = (PSYSTEM_HANDLE_INFORMATION_EX)Buffer; Log("[XM] 开始遍历句柄,总数: %llu", HandlesEx->NumberOfHandles); for (i = 0; i < HandlesEx->NumberOfHandles; i++) { HandleInfoEx = &(HandlesEx->Handles[i]); Status = ObReferenceObjectByPointer(HandleInfoEx->Object, 0, *IoFileObjectType, KernelMode);//*IoFileObjectType if (NT_SUCCESS(Status)) { Status = ObQueryNameString(HandleInfoEx->Object, ObjectNameInfo, 4096, &ReturnLength); if (NT_SUCCESS(Status)) { if (RtlCompareUnicodeString(&ObjectNameInfo->Name, &ustrName, TRUE) == 0) { Log("[XM] 找到匹配句柄: PID=%llu, Handle=%llu, Path=%wZ", HandleInfoEx->UniqueProcessId, HandleInfoEx->HandleValue, &ObjectNameInfo->Name); //切换进程 PEPROCESS Process = NULL; Status = PsLookupProcessByProcessId((HANDLE)HandleInfoEx->UniqueProcessId, &Process); if (NT_SUCCESS(Status)) { KAPC_STATE ApcState; KeStackAttachProcess(Process, &ApcState); Status = ZwClose((HANDLE)HandleInfoEx->HandleValue); Log("[XM] unlock UniqueProcessId:%llu HandleValue:%llu Name:%wZ", HandleInfoEx->UniqueProcessId, HandleInfoEx->HandleValue, &ObjectNameInfo->Name); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); } } } ObDereferenceObject(HandleInfoEx->Object); } } } ExFreePool(ObjectNameInfo); ExFreePool(Buffer); return Status; } ``` # 文件粉碎 先解锁后删除 ZwDeleteFile(&ustrName); # 路径转换 1. C:\Users\XiaM\Desktop\1.txt - 名称: DOS路径 (DOS Path) / Win32路径 - 用途: 用户和应用程序使用的标准路径 2. ??\C:\Users\XiaM\Desktop\1.txt - 名称: NT路径 (NT Path) / 符号链接路径 - 用途: NT内核的路径表示,?? 是符号链接目录 3. \Device\HarddiskVolume1\Users\XiaM\Desktop\1.txt - 名称: 设备路径 (Device Path) / 物理设备路径 - 用途: 内核对象管理器中的真实设备路径 DOS路径 → NT路径 → 设备路径 C:\... → \??\C:\... → \Device\HarddiskVolume1\... 像UnlockFile功能 需要在R3获取文件路径 转换为设备路径 ```plain DOS路径到设备路径转换 std::wstring ConvertToDevicePath(const std::wstring& dosPath) { WCHAR driveLetter[3] = {dosPath[0], L':', L'\0'}; WCHAR deviceName[MAX_PATH]; // 查询DOS设备对应的真实设备 if (QueryDosDeviceW(driveLetter, deviceName, MAX_PATH)) { // 拼接:设备名 + 路径部分 std::wstring result = deviceName; // \Device\HarddiskVolume3 result += dosPath.substr(2); // + \Users\XiaM\Desktop\1.txt return result; } return L""; } ``` # 强制关闭进程 <font style="color:rgb(0, 0, 0);">PspTerminateProcess</font> <font style="color:rgb(0, 0, 0);">PspTerminateThreadByPointer</font> [https://bbs.kanxue.com/thread-270012.htm](https://bbs.kanxue.com/thread-270012.htm)
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2025-8-10 18:49 被X66iaM编辑 ,原因:
#基础知识
#系统内核
#驱动开发
收藏
・
3
点赞
・
6
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
wx_晨梦
期待更多优质内容的分享,论坛有你更精彩!
2026-7-16 10:48
马来
感谢你分享这么好的资源!
2026-5-7 19:50
AL10000
感谢你分享这么好的资源!
2025-8-10 00:04
只会逆一点点
这个讨论对我很有帮助,谢谢!
2025-8-9 06:52
TkBinary
感谢你的积极参与,期待更多精彩内容!
2025-8-7 09:49
huangyalei
你的帖子非常有用,感谢分享!
2025-8-6 22:15
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
3
)
AL10000
雪 币:
1272
活跃值:
(1820)
能力值:
( LV2,RANK:10 )
在线值:
发帖
3
回帖
56
粉丝
0
关注
私信
AL10000
2
楼
棒棒滴,继续加油,期待对ObjectHandle枚举的实现。
最后于
2025-8-10 00:44 被AL10000编辑 ,原因:
2025-8-10 00:26
0
mb_dmpagumk
雪 币:
205
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
6
粉丝
0
关注
私信
mb_dmpagumk
3
楼
谢谢分享
2025-12-24 18:53
0
ThanatosKer
雪 币:
170
能力值:
( LV1,RANK:0 )
在线值:
发帖
6
回帖
40
粉丝
2
关注
私信
ThanatosKer
4
楼
2026-2-4 01:06
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
X66iaM
8
发帖
33
回帖
30
RANK
关注
私信
他的文章
[原创]实现简易ARK工具(5) x64信息快查
4588
[原创]实现简易ARK工具(4) SSDT Hook
4057
[原创]实现简易ARK工具(3) 遍历进程和内核模块
9470
[原创]实现简易ARK工具(2) 切CR3进程读写
4208
[原创]实现简易ARK工具(1) GDT表
7100
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部