前言
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。(本文仅用于交流学习)
这是国外老哥2020年提出的一种蛮有意思的思路。
我们先来看看大致的思路是什么样子的,然后来看看一些需要学习的点。
-
首先我们需要获得调式权限(SeDebugPrivilege)
-
然后我们使用NtQuerySystemInformation生成所有进程打开的所有句柄
-
利用OpenProcess打开句柄,赋予PROCESS_DUP_HANDLE权限
-
NtDuplicateObject将获取远程进程句柄的副本到我们的进程
-
利用NtQueryObject函数判断句柄是进程句柄还是其他一些东西
-
如果是进程句柄,则使用该句柄的副本调用QueryFullProcessImageName函数,它将显示进程可执行路径,以此判断是不是我们需要的那个进程
获得系统的调试权限就不多提了,这个利用RtlAdjustPrivilege函数即可轻松的获取到权限(不过需要在管理员权限下运行),我们先看看这个几函数以及其参数
NtQuerySystemInformation
__kernel_entry NTSTATUS NtQuerySystemInformation( [in] SYSTEM_INFORMATION_CLASS SystemInformationClass, [in, out] PVOID SystemInformation, [in] ULONG SystemInformationLength, [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
这个函数是复制句柄,其原型如下,其可以对照ZwDuplicateObject
NTSYSCALLAPI NTSTATUS NTAPI NtDuplicateObject( _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
函数原型如下
NTSYSCALLAPI NTSTATUS NTAPI NtQueryObject( _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_STRING
typedef 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 <iostream> #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); }
代码可能写的有点磕碜,读者可以把参考一下:Enumerating opened handles from a process | blez's : blog
但是我们可以看到Name中有些进程并不是我们想要的,我们可以看看lsass.exe中Type为Process的Name,有很多我们并不需要的
因此我们需要对Name进行筛选,这时候就需要用到QueryFullProcessImageName
QueryFullProcessImageName
函数原型
WINBASEAPI BOOL WINAPI QueryFullProcessImageNameW( _In_ HANDLE hProcess, _In_ DWORD dwFlags, _Out_writes_to_(*lpdwSize, *lpdwSize) LPWSTR lpExeName, _Inout_ PDWORD lpdwSize );
根据其句柄获得其文件的路径,我们可以利用其去判断是否是我们需要的文件
#include <windows.h> #include <stdio.h> #include <iostream> #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,我们可以通过进程快照或者复制所有的进程句柄,到最后判断那再进行筛选来自动获得其进程。
题外话
初入计算机行业的人或者大学计算机相关专业毕业生,很多因缺少实战经验,就业处处碰壁。下面我们来看两组数据:
- 2023届全国高校毕业生预计达到1158万人,就业形势严峻;
- 国家网络安全宣传周公布的数据显示,到2027年我国网络安全人员缺口将达327万。
一方面是每年应届毕业生就业形势严峻,一方面是网络安全人才百万缺口。
6月9日,麦可思研究2023年版就业蓝皮书(包括《2023年中国本科生就业报告》《2023年中国高职生就业报告》)正式发布。
2022届大学毕业生月收入较高的前10个专业
本科计算机类、高职自动化类专业月收入较高。2022届本科计算机类、高职自动化类专业月收入分别为6863元、5339元。其中,本科计算机类专业起薪与2021届基本持平,高职自动化类月收入增长明显,2022届反超铁道运输类专业(5295元)排在第一位。
具体看专业,2022届本科月收入较高的专业是信息安全(7579元)。对比2018届,电子科学与技术、自动化等与人工智能相关的本科专业表现不俗,较五年前起薪涨幅均达到了19%。数据科学与大数据技术虽是近年新增专业但表现亮眼,已跻身2022届本科毕业生毕业半年后月收入较高专业前三。五年前唯一进入本科高薪榜前10的人文社科类专业——法语已退出前10之列。
“没有网络安全就没有国家安全”。当前,网络安全已被提升到国家战略的高度,成为影响国家安全、社会稳定至关重要的因素之一。
网络安全行业特点
1、就业薪资非常高,涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万!
2、人才缺口大,就业机会多
2019年9月18日《中华人民共和国中央人民政府》官方网站发表:我国网络空间安全人才 需求140万人,而全国各大学校每年培养的人员不到1.5W人。猎聘网《2021年上半年网络安全报告》预测2027年网安人才需求300W,现在从事网络安全行业的从业人员只有10W人。
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图 ![](https://img-blog.csdnimg.cn/direct/424e38ca2a4f4d9f8309ef122fbcf07a.png)
攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去就业和接私活完全没有问题。
2.视频教程
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。
内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等,都是网络安全入门必知必会的学习内容。
(都打包成一块的了,不能一一展开,总共300多集)
因篇幅有限,仅展示部分资料,需要保存下方图片,微信扫码即可前往获取
3.技术文档和电子书
技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本,由于内容的敏感性,我就不一一展示了。
因篇幅有限,仅展示部分资料,需要保存下方图片,微信扫码即可前往获取
4.工具包、面试题和源码
“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。
还有我视频里讲的案例源码和对应的工具包,需要的话也可以拿走。
因篇幅有限,仅展示部分资料,需要保存下方图片,微信扫码即可前往获取
最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。
这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。
参考解析:深信服官网、奇安信官网、Freebuf、csdn等
内容特点:条理清晰,含图像化表示更加易懂。
内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…
因篇幅有限,仅展示部分资料,需要保存下方图片,微信扫码即可前往获取