大数跨境
0
0

如何用“程序入口点”玩进程注入

如何用“程序入口点”玩进程注入 Chris说出海
2025-10-16
2
导读:如何用“程序入口点”玩进程注入

 

如何用“程序入口点”玩进程注入

Introduction

进程注入是红队和对手常用的一招,用来绕过防御、拿更高权限,或者干点别的“有趣”的事。到我写这篇的时候,MITRE ATT&CK 里面和“远程”进程注入相关的子技术就有一大串(T1055 下面一堆)。当然还有更多变体和衍生玩法。

最近我在研究远程进程注入,想找一些“不太显眼”的办法:要么是没被系统整理过、要么是实现上要求很“极简”。传统那套“VirtualAllocEx → WriteProcessMemory → CreateRemoteThread”确实稳,但 EDR 盯得太紧,用这套“极简”起来基本不现实。

所以这篇就来聊两种围绕“程序入口点”的注入思路:尽量不显式分配内存,也不直接用创建线程或改线程上下文这类方法。

AddressOfEntryPoint Process Injection

有困难,先翻 Red Team Notes。这次我就看到 @spotheplanet 的这篇文章,讲怎么用 PE 的 AddressOfEntryPoint(入口点的相对虚拟地址)来做注入。

当一个 PE 被加载到内存里,AddressOfEntryPoint 表示“入口点相对镜像基址的地址”(微软文档是这么说的)。在一个 PE 可执行文件里,AddressOfEntryPoint 字段位于“可选头”(Optional Header):

顺手说一句,滥用 AddressOfEntryPoint 不是新点子。有些实现里(不一定都稳定),会直接把某个 PE 的这个字段“踩掉”换成 shellcode,这样程序一启动就先跑注入的代码(示例可以在社区文章里看到)。有意思的是,这个思路放到“远程进程”场景里也是能玩的。

当一个进程被创建时,最先被加载进内存的两个模块是“程序自身的镜像”和 ntdll.dll。如果你是用“挂起(suspended)”方式创建,那此时内存里就只有这俩:

也就是说,操作系统只做了最基础的引导,入口点还没被调用开始正式执行。于是问题来了——怎么在一个挂起的远程进程里,定位到它的 AddressOfEntryPoint 来做注入?

按照 Red Team Notes 的思路,大致流程可以这样理解(为安全起见,具体 API 名称在此做了泛化):

  • • 先拿到目标进程的 PEB 地址,从里头取到镜像基址(可用与“查询进程信息”相关的 API)。
  • • 读一下远程进程里该镜像基址处的 PE 头数据。
  • • 在可选头里定位到 AddressOfEntryPoint 字段对应的地址。
  • • 用跨进程写入的方式,把入口点处替换为你的自定义逻辑(原文示例为 shellcode,本文不提供可执行代码)。
  • • 恢复挂起的主线程,让程序自然流到入口点,从而触发你的逻辑。

按文中给的示例代码来跑的话,载荷会在远程进程里被拉起并执行(演示如下):

注:原文提到一个 64 位的演示实现项目,这里不再附链接。

‘ThreadQuery’ Process Injection

除了 NtQueryInformationProcess 之外,其实 ntdll 里还有一个名字很像的函数:NtQueryInformationThread:

我看微软文档的时候,在 ThreadInformationClass 参数介绍里有一句话挺扎眼:

“如果这个参数取 THREADINFOCLASS 枚举里的 ThreadQuerySetWin32StartAddress,函数会返回该线程的起始地址。”

——Microsoft Docs

虽然很有意思,但微软站点上对 THREADINFOCLASS 这个枚举的内容并不好找。Google 一下能翻到 ProcessHacker 的 GitHub 仓库里有相应定义:

从上面的图里你能看到,这个枚举能拿到的信息其实挺多。对我们来说,最关键的是拿到 ThreadQuerySetWin32StartAddress 的指针。如果结合“挂起创建进程”的背景——这时程序入口点还没被调用——那么当我们去查“主线程”的这个值时,它很大概率就是程序入口点的地址。下面就来验证这个猜想。

先得搞清楚怎么拿到“主线程”的句柄。好在我们就是自己用 CreateProcess 启动目标进程的(挂起方式),而这个函数会把新进程及其主线程的信息放到 PROCESS_INFORMATION 这个结构里,微软也写得很清楚:

PROCESS_INFORMATION 里包含新创建的进程以及它的主线程的信息,配合 CreateProcess / CreateProcessAsUser / CreateProcessWithLogonW / CreateProcessWithTokenW 使用。

——Microsoft Docs

于是,我们就去查询线程信息,目标是拿到 ThreadQuerySetWin32StartAddress(在那个枚举里它的数值也常被表示成 0x09)。

接下来,思路上就是把该地址处替换成我们想要运行的逻辑,然后恢复线程,让它启动时就会执行我们放进去的东西。

原文这里给了一个非常简洁的 C++ 演示程序(以 notepad.exe 为目标),并展示了运行后“看起来一切正常”的结果截图。出于安全考虑,本文不附上具体代码实现,也不逐步给出可复现的操作细节:

在宣布“成功”前,我们稍微改下代码做个验证。把恢复线程的那行先注释掉,重新编译运行,这样目标 notepad.exe 就会保持在挂起状态。等到需要时我们再手动恢复它。

程序里,查询 ThreadQuerySetWin32StartAddress 返回了一个地址,比如这样:

用 ProcessHacker 看一下挂起的进程,会看到只有一个线程,起始地址指到一个像 0x7ffdaf6a2680 这样的地方:

把 x64dbg 附上去,程序虽然“恢复”了,但这个唯一的线程还是挂起的。此时指令指针指到 ntdll:RtlUserThreadStart,这其实是进程启动初始化里的一个步骤:

为避免混淆,说明一下:这个被挂起的线程并不是程序真正的“主线程入口”;而 RtlUserThreadStart 是启动初始化流程的一部分。

接着我们手动恢复线程,让初始化继续往下走,然后在刚才拿到的 ThreadQuerySetWin32StartAddress 地址(比如 0x7ff6a0ff3f40)处下断。运行起来后,断点就打在了解析出来的“程序入口点”上:

继续单步往下,你会看到(在原始演示里)注入的逻辑被成功执行:

小注:覆盖入口点可能会让程序不稳定(比如你放的内容太大)。

Defensive Considerations

  • • 看线程栈的时候能看到一个挺有意思的调用:__report_securityfailure。这是 VTGuard 的一部分,它会“检测虚函数表异常,比如利用者想靠自己控制的 C++ 对象来接管执行流”的场景。

    这类栈上的事件如果能和系统/应用/安全缓解相关的日志错误串起来,可能会给检测带来一些启发(如果你有更多线索,欢迎交流)。

  • • 原文里还给了一个 PoC 级别的 Yara 规则,用来在静态层面筛出可能利用“入口点注入”的可疑 PE 文件:

   
    
   import "pe"

rule Identify_EntryPoint_Process_Injection
{
    meta:
        author = "@bohops"
        description = "Identify suspicious methods in PE files that may be used for entry point process injection"
    strings:
        $a = "CreateProcess"
        $b = "WriteProcessMemory"
        $c = "NtWriteVirtualMemory"
        $d = "ResumeThread"
        $e = "NtQueryInformationThread"
        $f = "NtQueryInformationProcess"

    condition:
        pe.is_pe and $a and ($b or $c) and $d and ($e or $f)
}

原文:No Alloc, No Problem: Leveraging Program Entry Points for Process Injection – bohops(2023-06-09)
链接:https://bohops.com/2023/06/09/no-alloc-no-problem-leveraging-program-entry-points-for-process-injection/






这是一个纯粹,开放,前沿的技术交流社区,成员主要有互联网大厂安全部门任职的成员,乙方红队专家,以及正在学习入门的小白等,目前主题主要以红队研发为主(有经验的都知道是什么意思),以及其他涉及到红队进攻侵入性技术,如果你想学习技术,认识不同的人或者寻求一个机会之类的,可以来看看👇👇👇


欢迎加入交流圈

扫码获取更多精彩


 

【声明】内容源于网络
0
0
Chris说出海
跨境分享角 | 每日更新跨境思考
内容 44155
粉丝 0
Chris说出海 跨境分享角 | 每日更新跨境思考
总阅读269.0k
粉丝0
内容44.2k