吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5379|回复: 29
收起左侧

[原创] 试谈一下获取进程句柄的那些事

  [复制链接]
忆魂丶天雷 发表于 2023-3-30 16:24
本帖最后由 忆魂丶天雷 于 2023-3-30 16:57 编辑

前言

本文由笔者(论坛UID: 666926)首发于52破解论坛,如需转载请保留完整的作者信息以及出处。

本文仅作为记录之用,所写内容皆源自于笔者所接触、了解、总结以及网络有关知识,因为碍于自身知识储备有限,所以实在是不敢也没有能力展开深入讨论,内容可能比较肤浅,如有错误还望各位大牛教正。

正文


什么是进程句柄

以下引用bing搜索给出的答案

句柄实际是一个指针,他指向一块包含具体信息数据的内存,可以当做索引 ,所以进程句柄是当你要访问该进程时取得的,使用完毕必须释放。
进程的句柄则是基于特定进程的,对于同一个进程对象,在不同的进程中可能有不同的句柄值。因为句柄实际上是进程空间中的句柄表的偏移,由于在不同的进程空间中句柄表是不同的,针对相同的对象在句柄表的偏移也就不一样了!
一个进程在不同的调用时间中有可能句柄的值是不一样的,但是ID只有也只能有一个
在Windows有一张指针表(就是指针数组),这些指针指向Windows内的各种对象(Windows概念的对象),其中就包含进行对象。句柄就就是指针数组的编号。
进程句柄,每次打开这个进程(OpenProcess),返回给你的句柄是变化的

综上所述,我们可以对进程句柄有一个简单的了解。

进程句柄有什么用?

在Ring3环境中,我们对进程的操作基本上都离不开进程句柄,例如通过GetProcessId使用进程句柄获取进程的PID,又例如对目标进程内存的读写,获取目标进程的一些信息,挂起或者终止目标进程,这等等一大串操作都需要进程句柄。

什么是进程句柄的权限?

每一个句柄都有属于自己的权限,只有当拥有对应的权限才能做对应的事情,如果拿一个QQ群举例,有权限那么你就可能是权限🐶管理,或者是权限🐶群主,在群内可以为所欲为,因为一切敢反抗你的人你就可以权限它,比如禁言或者移除。

当然权限也不是固定的,比如你可以由普通群员通过PY交易努力成为管理,甚至成为群主。

那么对于进程句柄来说,你想获取对应进程的信息、挂起或者终止一个进程,那么你也就需要一个拥有对应权限的进程句柄,当然同理,进程句柄也有提权和降权一说。

如果你想详细的知道有哪些权限,可以阅读一下微软的文档。

https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights

如何获取进程句柄?

既然进程句柄有这么多作用,那么我们怎么去得到它?这也是本文的主要内容。

通过GetCurrentProcess获取自进程的进程句柄

求人不易,求己不难。对于自己已经有的东西,自当求己,同理,在笔者看来,获取自进程的进程句柄也是所有方法中最为简单的一个。只需要一个APIGetCurrentProcess即可。

根据微软官方给出的文档,它的使用方法也是非常的简单,不需要任何参数,直接调用,它的返回值即是自进程的进程句柄。

HANDLE GetCurrentProcess();

甚至,如果你想偷懒,可以连这仅需的一个API都不用调用,在需要使用到自身进程句柄的地方直接填写-1即可。

但是,值得注意的是,-1是一个伪句柄,即当前进程句柄,它拥有PROCESS_ALL_ACCESS访问权限,它不会被子进程继承,也不能直接被其他进程使用。

相关文档:

https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess

通过OpenProcess获取进程句柄

上面已经说过了如何获取自进程的进程句柄,那么如何获取其他进程的进程句柄呢?伟大的微软同样也提供了一个APIOpenProcess

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);

OpenProcess,相比GetCurrentProcess使用起来就复杂一些,它需要提供3个参数。

[in] dwDesiredAccess你想要获取的句柄权限,具体权限可以参考前文,但是一般都是PROCESS_ALL_ACCESS访问权限,即获取全部权限。虽然,严格说不太规范,但是是真的香。
[in] bInheritHandle是否允许被继承,如果此值为 TRUE,则此进程创建的进程将继承句柄。否则,进程不会继承此句柄。
[in] dwProcessId目标进程的PID

当你填充以上3个参数,就可以尝试通过OpenProcess去获取你想获取的目标进程的进程句柄了。如果没发生什么意外情况,那么一般来说你都可以如愿以偿的得到。

相关文档:

https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess

OpenProcess失效的一些情况

人生不如意事常八九,在实际运用OpenProcess时其实经常会遇见它失效的情况。在抛开一些因为语法错误导致的失效后,其实更多的时候是你想获取的目标进程存在保护。

虽然的确值得反思,为什么大家要尝试对一个有保护的进程存在一些想法,但是作为一篇水贴笔记来说,的确有必要记录一些处理这种异常情况的方法,但是由于逐渐进入深水区因为我也不会,接下来的内容可能不会有详细的文档,甚至只会提供一种解决思路。

OpenProcess/ZwOpenProcess被HOOK

举个简单的例子,你对一个心爱的男生写了一封情书放在他课桌里,但是你的情敌早有准备,对你无时无刻进行了监视,趁你不注意直接拿走了情书,或者对里面的内容进行了修改,那么这个过程就可以被称为被情敌HOOK了。

在十多年前XP系统流行的年代,这种直接HOOK SSDT表中的OpenProcess达到保护进程的目的手段还是很常见的,虽然过时,但是也还是存在(以另外一种方式......)。

为了解决这个问题,我们首先得了解一下OpenProcess的实现过程

以一个32位程序在64位系统上运行为例,通过CE我们可以发现,OpenProcess本质上对ZwOpenProcess的封装

KERNELBASE.OpenProcess - 8B FF                 - mov edi,edi
KERNELBASE.OpenProcess+2- 55                    - push ebp
KERNELBASE.OpenProcess+3- 8B EC                 - mov ebp,esp
KERNELBASE.OpenProcess+5- 83 EC 24              - sub esp,24 { 36 }
KERNELBASE.OpenProcess+8- 8B 45 10              - mov eax,[ebp+10]
KERNELBASE.OpenProcess+B- 33 C9                 - xor ecx,ecx
KERNELBASE.OpenProcess+D- 89 45 F4              - mov [ebp-0C],eax
KERNELBASE.OpenProcess+10- 8B 45 0C              - mov eax,[ebp+0C]
KERNELBASE.OpenProcess+13- F7 D8                 - neg eax
KERNELBASE.OpenProcess+15- 89 4D F8              - mov [ebp-08],ecx
KERNELBASE.OpenProcess+18- C7 45 DC 18000000     - mov [ebp-24],00000018 { 24 }
KERNELBASE.OpenProcess+1F- 1B C0                 - sbb eax,eax
KERNELBASE.OpenProcess+21- 89 4D E0              - mov [ebp-20],ecx
KERNELBASE.OpenProcess+24- 83 E0 02              - and eax,02 { 2 }
KERNELBASE.OpenProcess+27- 89 4D E4              - mov [ebp-1C],ecx
KERNELBASE.OpenProcess+2A- 89 45 E8              - mov [ebp-18],eax
KERNELBASE.OpenProcess+2D- 8D 45 F4              - lea eax,[ebp-0C]
KERNELBASE.OpenProcess+30- 50                    - push eax
KERNELBASE.OpenProcess+31- 8D 45 DC              - lea eax,[ebp-24]
KERNELBASE.OpenProcess+34- 89 4D EC              - mov [ebp-14],ecx
KERNELBASE.OpenProcess+37- 50                    - push eax
KERNELBASE.OpenProcess+38- FF 75 08              - push [ebp+08]
KERNELBASE.OpenProcess+3B- 8D 45 FC              - lea eax,[ebp-04]
KERNELBASE.OpenProcess+3E- 89 4D F0              - mov [ebp-10],ecx
KERNELBASE.OpenProcess+41- 50                    - push eax
KERNELBASE.OpenProcess+42- FF 15 8C38BD77        - call dword ptr [KERNELBASE.WakeConditionVariable+5A11] { ->ntdll.ZwOpenProcess }
......
ntdll.ZwOpenProcess - B8 26000000           - mov eax,00000026 { 38 }
ntdll.NtOpenProcess+5- BA 408BD577           - mov edx,ntdll.RtlInterlockedCompareExchange64+170 { (-1842862593) }
ntdll.NtOpenProcess+A- FF D2                 - call edx
ntdll.NtOpenProcess+C- C2 1000               - ret 0010 { 16 }
ntdll.NtOpenProcess+F- 90                    - nop 

所以如果他的上层OpenProcess被HOOK那么我们直接调用ZwOpenProcess就可以绕过HOOK了

当然,这只是一个非常理想的情况,实际上,也不可能HOOK这么浅层,可能直接就HOOK了ZwOpenProcess那么你就需要再主动深入一层(32位应用程序在64位系统上运行会存在更多的HOOK点,比如被称为天堂之门的WOW64,或者更进一步直接到达64位的Ntdll)或者手动重写OpenProcess。

天堂之门相关资料:

https://bbs.kanxue.com/thread-270153.htm

重写ZwOpenProcess相关资料:

https://blog.csdn.net/weixin_44286745/article/details/104585713

当然,需要说明的是上面采取的是相对温和的战略,实际上我们也可以直接想办法恢复HOOK然后正常调用,不过这可能就会触发一些其他保护,比如CRC检测。

那么,我们来总结一下,在OpenProcess/ZwOpenProcess被HOOK的情况下,我们可以尝试以下思路解决。

1.直接调用比被HOOK点的更深入一层的API函数
2.重写ZwOpenProcess的实现流程
3.32位应用在64位系统的运行环境下可以考虑尝试直接调用X64Ntdll中的NtOpenProcess,在Ring3环境中这是相对而言比较底层的了,比它更底层的就是直接用syscall调用了。
4.直接恢复目标进程的HOOK,这里我们可以考虑使用一些工具,比如YDark,PCHunter,火绒剑。

相关工具:
YDark:

https://github.com/ClownQq/YDArk

PCHunter:

https://www.anxinsec.com/

火绒剑:

https://www.huorong.cn/

另辟蹊径

天下同归而殊途,一致而百剃糠提虑。

前文所说,基本上都是对于OpenProcessAPI的应用和拓展。但是不要忘了我们求取的始终是进程句柄,从另外一些道路,虽有曲折但我们依旧可以得到它。

通过DuplicateObject拷贝句柄

你配吗?咳咳,我是说配钥匙,有一些孩子打小就丢三落四,把钥匙丢了,这个时候就需要,大喊:“妈,我钥匙丢了”。这个时候就会带着你去配一把一模一样的钥匙。

同理,我们可以通过DuplicateObject去拷贝/复制一份句柄给自己使用。

NTSYSAPI NTSTATUS ZwDuplicateObject(
  [in]            HANDLE      SourceProcessHandle,
  [in]            HANDLE      SourceHandle,
  [in, optional]  HANDLE      TargetProcessHandle,
  [out, optional] PHANDLE     TargetHandle,
  [in]            ACCESS_MASK DesiredAccess,
  [in]            ULONG       HandleAttributes,
  [in]            ULONG       Options
);

因为这个函数参数比较多,建议读者直接阅读微软的官方文档

相关文档:

https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwduplicateobject

可能有读者想到了,既然要配钥匙拷贝句柄,那么肯定得原本就有一把。

哦,对了,我们没有,那告辞。

是的,使用DuplicateObject的前提就是已有一份才能拷贝,那么面对被保护的进程我们如何去拥有一个句柄呢?

还记得前文使说的伪句柄吗,我们可以结合一些其他技术,比如dll劫持,那么我们就可以在dll中拷贝一份目标进程的-1伪句柄出来。

当然,不可否认的是这个方法是很鸡肋的,都已经注入dll进被保护的进程了,重新拷贝一份就显得多余,但这也是一种思路,有一些环境下的确也需要这种多此一举的操作。毕竟你可能不能把所有的运行逻辑写入一个dll中,而暴露一个句柄出来就可以更方便。

通过CreateProcess创建一个进程得到进程句柄

众所周知,进程关系中存在父子进程的关系,那么儿子的东西,就是老子的东西应该合乎情理吧。

BOOL CreateProcessA(
  [in, optional]      LPCSTR                lpApplicationName,
  [in, out, optional] LPSTR                 lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCSTR                lpCurrentDirectory,
  [in]                LPSTARTUPINFOA        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);

CreateProcessAPI中的最后一个参数LPPROCESS_INFORMATION结构中就包含了子进程的进程句柄

结构如下

typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

那么这就好办了,既然打不过你,我就当你爸爸。

面对一个被保护的严严实实的进程,我们可以尝试一下这个办法,比如自己去调用CreateProcess创建它,这样作为父级,我们就可以直接得到子进程的进程句柄。

相关文档:

https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information

关于本文中涉及方法的可用性

上述所有方法,笔者均有进行亲测,但是我依旧不能保证,因系统差异、目标进程状态等各种不可控的情况下,各位读者进行尝试是否能与我得到一致的结果。

另外,面对具有保护的进程,希望各位读者三思而后行,如果有成功利用本文所记录的方案获取目标进程句柄,或者因尝试使用本文所记录的方案过程中导致的其他损失,以及其他可能发生的一切已知或未知后果,均应由各位读者自身承担。

后记

本文,到这里就结束了,仓促编辑或有疏落,可能存在错字,别字;或者是因笔者自身认知有限,某些方法可能存在错误、不足、使用不规范的情况,望各位读者海涵。

最后,再次强调一下版权问题

本文由笔者(论坛UID: 666926)首发于52破解论坛,如需转载请保留完整的作者信息以及出处。

免费评分

参与人数 16威望 +2 吾爱币 +115 热心值 +14 收起 理由
D3M1 + 1 + 1 谢谢@Thanks!
Issacclark1 + 1 谢谢@Thanks!
hjf571x + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
家里有个肉团纸 + 1 + 1 我很赞同!
mtkppqq + 2 + 1 我很赞同!
枫叶丶丿 + 1 + 1 谢谢@Thanks!
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wtflxk + 1 + 1 打不过你我就当你爸爸哈哈哈哈
w759003376 + 1 热心回复!
BananaLi + 1 + 1 我很赞同!
HillBoom + 1 + 1 用心讨论,共获提升!
leommm + 1 热心回复!
TomsLi + 1 + 1 用心讨论,共获提升!
gunxsword + 1 + 1 热心回复!
空竹 + 1 + 1 热心回复!
sifeng + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

我就是那寒冬 发表于 2023-7-31 15:55
大佬,OpenProcess这个函数,如果在java或者C#这种面向对象的语言里,没有办法直接处理window内核函数,有其他获取句柄内存的途径吗
 楼主| 忆魂丶天雷 发表于 2023-3-30 16:25
hehengfa 发表于 2023-3-30 17:06
leommm 发表于 2023-3-30 23:25
感谢分享,很有帮助!
dadaliya 发表于 2023-3-30 23:40
感谢分享,有用的知识
balley 发表于 2023-3-31 13:02
学到了,谢谢分享
BananaLi 发表于 2023-3-31 16:28
感谢分享,学到了
guangzisam 发表于 2023-3-31 21:07
为小白科普。有一定的用处
lizy169 发表于 2023-4-1 06:53
感谢分享,收藏一下
rocdeng 发表于 2023-4-1 07:57
非常感谢,很早就用过OpenProcess了,没想到有这么多门道在里面。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 00:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表