游客发表
shellcode编码,说白了就是给机器指令“化妆”。攻击者写好了能弹shell的机器码,但直接扔给程序往往不行——因为输入过滤、字符限制、安全软件查杀,都会让裸奔的shellcode当场毙命。编码的目的,就是让这些指令以“合法”的样子混进目标进程,到运行时再原形毕露。
最常见的坑是坏字符。很多漏洞利用场景里,像\x00、\x0a这类字节会被程序截断或当作终止符。比如你精心构造的shellcode里有个\x00,strcpy就只复制到那儿为止,后面的代码全被丢弃。解决办法简单粗暴:用异或。把每个字节和一个固定key做异或,生成不含坏字符的新序列,解码器放在开头,运行时先逐字节还原,再跳转到真正的代码。我见过一个真实案例,某CTF题目里过滤了所有ASCII可打印字符之外的字节,选手硬是把整个shellcode用0x40异或了一遍,解码器只有几条指令,干净利落。
编码不只是躲过滤器。有些场景要求shellcode全部由可见字符组成,比如PHP的preg_replace加上/[\x00-\x20]/过滤。这时候得用更聪明的办法——字母数字shellcode。用push、pop、add这类指令的机器码恰好落在0x30-0x39(数字)和0x41-0x5a(大写字母)区间内,写出来的代码全长得像普通字符串。我第一次调试这种shellcode时头皮发麻,看着反汇编里一串push rax; pop rbx; add eax, 0x41414141,感觉像在解谜。但正是这种扭曲感,让它能穿透WAF的字符白名单检查。
还有一层编码是防“特征码”查杀。杀毒软件会扫描固定字节模式,比如经典的\x31\xc0\x50\x68...开头的execve shellcode。编码后特征完全变了,但解码器本身也可能被杀软盯上。于是有人搞出多态编码——每次生成都用不同的key、不同的解码逻辑,甚至插入垃圾指令来混淆。这就像给同一个锁配了无数把形态各异的钥匙,但锁芯始终没变。
写编码器最烦人的是处理相对跳转。shellcode在内存里的位置不确定,所有call和jmp都得用相对偏移。你编码的时候,解码器执行完要跳转到还原后的代码,这个偏移量得算准。我踩过一次坑:解码器末尾用jmp rsi跳转,结果rsi指向的是缓冲区开头,但解码后代码长度变了,偏移没同步更新,直接跳进垃圾数据里,程序当场崩溃。调试到凌晨三点才意识到,解码器自身长度也得算进偏移里。
编码不是炫技,是生存需求。现实中很多漏洞利用链都靠这层“化妆”绕过缓解措施。但别指望编码能解决一切——如果目标开启了DEP(数据执行保护),你解码后的代码在栈上照样跑不了,还得配合ROP或其它手段。编码只是把“能不能执行”的问题,暂时变成了“怎么执行得更隐蔽”。
友情链接