25-shellcode执行总结
本节介绍shellcode注入三部曲的最后一步,也是至关重要的一步。不管你前面已经完成了注入的步骤,比如分配内存、写入shellcode,只要你不触碰到AV/EDR的逆鳞——执行shellcode,AV/EDR都不会管你,只会标记为可疑状态。
可是,我们的最终目标不就是让shellcode执行起来吗,我们先不管执行shellcode会不会触发检测,也不用考虑防御规避的事情,动起来才是第一要义,下面我会介绍9种执行shellcode的方法,它们之间的优劣主要看使用场景。
⚠注意:
在shellcode注入中一定要分清是注入本进程还是远程进程。
这里并不讨论使用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)()
void (*)()是一个无参数、无返回类型的函数指针。(void (*)())lpBaseAddress是将lpBaseAddress强转为函数指针类型。(*(void (*)()) lpBaseAddress)()就是通过函数指针(相当于这个格式(*函数指针)())进行函数调用。
示例代码

六、基于内联汇编
原理:直接嵌入机器码到C/C++代码中
优势
无任何外部API调用
代码段内嵌Shellcode,规避字符串特征扫描
劣势:
平台依赖性极强(x86/x64/ARM需分别编写)
MSVC x64禁用内联汇编
只适用于x86,示例代码

七、基于Windows的API提供的回调函数调用
什么是回调函数:回调函数是一个被作为参数传递的函数
原理:利用Windows API的回调机制(如
EnumWindows、EnumChildWindows等)将Shellcode伪装成合法回调函数,由系统API主动触发执行优势:最大的优势就是不使用
CreateThread或CreateRemoteThread劣势:需手动处理
stdcall/cdecl等调用约定,若Shellcode未适配参数压栈顺序会导致栈崩溃(暂时未遇到相关问题)
Windows的API上有很多都提供回调函数的调用,我就根据网上公开的信息,尝试几个并给出示例代码。
7.1 EnumSystemLocalesA
官方文档: EnumSystemLocalesA 函数 (winnls.h) - Win32 apps | Microsoft Learn
我对这个API的使用,最早可以追溯到 第一章-转换 那一小节,详细的代码示例可以去那一小节参考一下。
7.2 CreateTimerQueueTimer
官方文档:createTimerQueueTimer 函数 (threadpoollegacyapiset.h) - Win32 apps | Microsoft Learn
CreateTimerQueueTimer:创建计时器队列计时器。 此计时器在指定的到期时间过期,然后在每个指定的时间段后过期。 计时器过期时,将调用回调函数。
语法
重点关注第三个参数Callback,以为着我们可以将shellcode作为回调函数。
示例代码

7.3 EnumChildWindows
EnumChildWindows:通过将每个子窗口的句柄依次传递给应用程序定义的回调函数,枚举属于指定父窗口的子窗口。 EnumChildWindows 会一直持续到枚举最后一个子窗口或回调函数返回 FALSE。
语法
重点关注第二个参数lpEnumFunc即可。
示例代码

7.4 EnumUILanguagesW
EnumUILanguagesW:枚举操作系统上可用的用户界面语言,并使用列表中的每种语言调用回调函数。
语法
重点关注第一个参数lpUILanguageEnumProc
示例代码

7.5 EnumFontsW
官方文档:EnumFontsW 函数 (wingdi.h) - Win32 apps | Microsoft Learn
EnumFontsW:枚举指定设备上可用的字体。
7.6 总结
由于Windows有非常多的API都提供回调函数的调用,在这里我就不一一尝试了,感兴趣的读取可以去微软的官方文档中寻找。在这里补充一下我认为可行的API:
八、基于TLS回调机制
TLS(Thread Local Storage) 是Windows中用于管理线程局部存储的机制,允许每个线程拥有独立的数据副本。TLS回调函数 是程序初始化或线程启动/退出时自动触发的函数,其执行时机 早于程序入口点(如main或WinMain),常用于初始化线程相关资源。
攻击者通过 篡改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小时的璀璨黎明。
相信防御规避绝对会给每个探索者带来认知风暴,这注定是一场优雅的攻击方的暴力美学。