25-shellcode执行总结

本节介绍shellcode注入三部曲的最后一步,也是至关重要的一步。不管你前面已经完成了注入的步骤,比如分配内存、写入shellcode,只要你不触碰到AV/EDR的逆鳞——执行shellcode,AV/EDR都不会管你,只会标记为可疑状态。

可是,我们的最终目标不就是让shellcode执行起来吗,我们先不管执行shellcode会不会触发检测,也不用考虑防御规避的事情,动起来才是第一要义,下面我会介绍9种执行shellcode的方法,它们之间的优劣主要看使用场景。

⚠注意

  1. 在shellcode注入中一定要分清是注入本进程还是远程进程。

  2. 这里并不讨论使用Nt或Zw类型的API,也不讨论系统调用。

一、基于CreateThread

使用 CreateThread 执行shellcode是最基础、最经典、最出名的一种执行方式。其原理就是创建在调用进程的虚拟地址空间内执行的线程,在shellcode注入中就是为shellcode创建一个线程。

  • 原理CreateThread

  • 优势:Win32标准API,系统版本兼容性强

  • 劣势:EDR必监控点,原始形态存活率趋近于0

  • 场景:对抗基础AV

详细代码,请看创建线程注入(CreateThread Injection)

二、基于CreateFiber

  • 原理ConvertThreadToFiber + CreateFiber + SwitchToFiber 将当前线程转换为纤程→创建指向Shellcode的新纤程→通过纤程调度切换执行

  • 优势

    • 无传统线程创建事件(CreateThread等敏感API)

    • 纤程对象存储在用户态内存,规避部分内核回调检测

    • 可与合法纤程池混合伪装(如IIS等使用纤程的宿主进程)

  • 劣势

    • 主线程必须先行转换为纤程(ConvertThreadToFiber触发HOOK风险)

    • 部分EDR开始监控FiberLocalStorage异常修改

    • 无法绕过用户态堆栈内存扫描(如CarbonBlack的栈回溯检测)

  • 场景

    • 对抗依赖线程创建行为分析的EDR(如CrowdStrike Falcon)

    • 注入.NET等托管进程(CLR本身使用纤程机制)

详细代码,请看创建纤程注入(CreateFiber Injection)

三、基于CreateRemoteThread

  • 原理CreateRemoteThread + WriteProcessMemory

  • 优势:Win32标准API,系统版本兼容性强

  • 劣势:EDR必监控点,原始形态存活率趋近于0

  • 场景:对抗基础AV

详细代码,请看 创建远程线程注入(CreateRemoteThread Injection)

四、基于APC

  • 原理:利用QueueUserAPC向目标线程插入异步调用

  • 优势:可针对已存在线程操作,无新线程创建日志

  • 劣势:需目标线程进入Alertable状态,执行时机不可控

  • 场景:针对GUI程序(如explorer.exe常驻消息循环)

详细代码,请看 APC注入(APC Injection)

五、基于函数指针

  • 原理:函数指针强制类型转换

  • 优势

    • 无显式线程/纤程操作

    • 代码极度简洁

    • 伪装成合法回调函数(如TLS回调、COM对象方法)

  • 劣势:直接调用导致堆栈回溯异常,极易受AV/EDR拦截

  • 场景:需要快速验证Shellcode有效性的红队测试,实战用的比较少

理解 (*(void (* )()) lpBaseAddress)()

  1. void (*)() 是一个无参数、无返回类型的函数指针。

  2. (void (*)())lpBaseAddress 是将lpBaseAddress强转为函数指针类型。

  3. (*(void (*)()) lpBaseAddress)() 就是通过函数指针(相当于这个格式(*函数指针)())进行函数调用。

示例代码

六、基于内联汇编

  • 原理:直接嵌入机器码到C/C++代码中

  • 优势

    • 无任何外部API调用

    • 代码段内嵌Shellcode,规避字符串特征扫描

  • 劣势

    • 平台依赖性极强(x86/x64/ARM需分别编写)

    • MSVC x64禁用内联汇编

只适用于x86,示例代码

七、基于Windows的API提供的回调函数调用

什么是回调函数:回调函数是一个被作为参数传递的函数

  • 原理:利用Windows API的回调机制(如 EnumWindowsEnumChildWindows 等)将Shellcode伪装成合法回调函数,由系统API主动触发执行

  • 优势:最大的优势就是不使用 CreateThreadCreateRemoteThread

  • 劣势:需手动处理 stdcall / cdecl 等调用约定,若Shellcode未适配参数压栈顺序会导致栈崩溃(暂时未遇到相关问题)

Windows的API上有很多都提供回调函数的调用,我就根据网上公开的信息,尝试几个并给出示例代码。

7.1 EnumSystemLocalesA

官方文档: EnumSystemLocalesA 函数 (winnls.h) - Win32 apps | Microsoft Learnarrow-up-right

我对这个API的使用,最早可以追溯到 第一章-转换 那一小节,详细的代码示例可以去那一小节参考一下。

7.2 CreateTimerQueueTimer

官方文档:createTimerQueueTimer 函数 (threadpoollegacyapiset.h) - Win32 apps | Microsoft Learnarrow-up-right

CreateTimerQueueTimer:创建计时器队列计时器。 此计时器在指定的到期时间过期,然后在每个指定的时间段后过期。 计时器过期时,将调用回调函数。

语法

重点关注第三个参数Callback,以为着我们可以将shellcode作为回调函数。

示例代码

7.3 EnumChildWindows

EnumChildWindows:通过将每个子窗口的句柄依次传递给应用程序定义的回调函数,枚举属于指定父窗口的子窗口。 EnumChildWindows 会一直持续到枚举最后一个子窗口或回调函数返回 FALSE

语法

重点关注第二个参数lpEnumFunc即可。

示例代码

7.4 EnumUILanguagesW

EnumUILanguagesW:枚举操作系统上可用的用户界面语言,并使用列表中的每种语言调用回调函数。

语法

重点关注第一个参数lpUILanguageEnumProc

示例代码

7.5 EnumFontsW

官方文档:EnumFontsW 函数 (wingdi.h) - Win32 apps | Microsoft Learnarrow-up-right

EnumFontsW:枚举指定设备上可用的字体。

7.6 总结

由于Windows有非常多的API都提供回调函数的调用,在这里我就不一一尝试了,感兴趣的读取可以去微软的官方文档中寻找。在这里补充一下我认为可行的API:

八、基于TLS回调机制

TLS(Thread Local Storage) 是Windows中用于管理线程局部存储的机制,允许每个线程拥有独立的数据副本。TLS回调函数 是程序初始化或线程启动/退出时自动触发的函数,其执行时机 早于程序入口点(如mainWinMain,常用于初始化线程相关资源。

攻击者通过 篡改TLS回调函数,将Shellcode的执行嵌入到TLS回调中。由于TLS回调在程序主入口点之前执行,这种技术可以:

  • 绕过部分检测:某些安全工具(如调试器、行为监控)可能未监控TLS初始化阶段。

  • 隐蔽性增强:执行时机更早,规避基于入口点的分析。

示例代码

⚠注意:不知道什么原因,该代码只能用clang-cl编译,用MSVC不会触发回调函数。

九、尾语

当然执行shellcode的手段应该不只这些,继续探究下去还有VEH异常机制执行shellcode,感兴趣的读者可以自行研究,其本质就是VEH机制为攻击者提供了一种在异常处理流程中执行任意代码的方法。通过注册处理函数触发异常并修改执行上下文,攻击者可以绕过部分安全机制执行shellcode。

看到这里你应该对执行shellcode有一个大致的认识了,即执行shellcode无外乎创建线程、函数指针、APC队列、内联汇编、回调机制,其中回调机制是比较复杂也比较隐秘的一种方法,Windows系统上有很多功能都要通过回调机制来完成,这也就给了攻击者触发执行shellcode的机会。

行文至此,有些感慨,不知不觉就写完了 第二章-执行与注入技术,见识了这么多注入技术,总算是准备进入到 问鼎免杀之路 的核心篇章 第三章-防御规避

防御规避从来不是简单的技术堆砌。当我们在第二章将代码注入玩转得如同庖丁解牛般娴熟时,真正的考验才悄然降临。就像攀岩者征服了九十度的绝壁,抬头却看见云雾中若隐若现的倒悬冰川——AV的主动防御体系正在云端编织天罗地网,EDR探针像夜枭般蛰伏在每个API调用的阴影里。

从内存加密到伪装欺骗AV/EDR的扫描,从动态获取API函数到底层系统调用,每个技术节点都暗藏认知边疆的暴风雪。但胸腔里燃烧的究竟是什么?是发现Hook点被绕过时的战栗,是看见AMSI Bypass的会心一笑又或者是ETW Bypass欣喜若狂,更是亲手构建的免杀木马在真实攻防演练中存活72小时的璀璨黎明。

相信防御规避绝对会给每个探索者带来认知风暴,这注定是一场优雅的攻击方的暴力美学。