背景
shellcode 中要避免 null byte(\x00)这个是个通用的概念(windows,linux 都是一样),因为栈溢出的数据作为字符串写入到栈上,\x00 会作为字符串终止符,毁掉整个 shellcode。
这就是为什么无论什么类型的栈溢出,无论用到什么技巧,在开发过程中都要千方百计避免 null byte 的出现。当然,还有应用自身的坏字符检测,这个我们以后再讨论,自己写 shellcode 的好处就是能够规避已知的坏字符。在某些特殊情况下,还能节省很多的栈空间(如果栈空间并不大的话,这就是好消息)。
今天我们讨论下 null byte 的成因,以及几个自己写 shellcode 的时候避免 null byte 的小技巧。
Null Byte 成因
开发的过程中,产生 Null Byte 的原因基本上有三个。
写入寄存器的值过小
例如我们需要寻找一个栈上的地址作为 Win32 API 的返回值写入地址,这个地址我们选择在 esp-0x260 的位置上。如果直接在作为减数的寄存器中写入 0x260,就会产生 null byte。
mov eax, esp
mov ecx, x260
sub eax, ecx
指令本身产生的 Null Byte
例如 mov eax, [eax]
指令,就会产生 null byte。
说完了两个最基本的成因,我们分享一下避免 null byte 的小技巧。
call 和 jmp 指令产生的 Null Byte
这个部分有机会写到自定义 shellcode 话再深入说。由于汇编也是从上到下执行的,这就涉及到代码逻辑的调整以及 call 指令的地址性质,得花点篇幅好好说。
避免 Null Byte(前两个成因)
解决小数产生的 Null Byte
方法1
针对第一种成因,我们可以选择使用负数的方式来解决。
拿上面的例子来说,我们可以取 0x260 的负数值,得到
fffffda0
然后使用加法而不是减法,得到相同的结果。
所有的数值过小的成因,都可以用这种方法解决。
方法2
或者,也可以使用 neg
指令,更加方便。
; 先将 0x260 的负数值写入 ecx,然后取负
mov ecx, 0xfffffda0
neg ecx
可以看到 opcode 里面没有 null byte。
同时运行起来,寄存器中也是我们想要的值。
解决指令本身产生的 Null Byte
针对第二种成因,很简单了,就是换个寄存器 😄
总结
第一种避免 null byte 的方式其实很简单,开发过程中注意就行,有小数都换成大数加负数或者负数取负即可。
第二种避免 null byte 的方式得结合实际情况,看当时哪个寄存器可用。可以看到除了特定的 mov eax, [eax]
会产生 null byte 外,使用其他寄存器都是安全的。
call 和 jmp 指令产生的 Null Byte,得从头开始讲了。不随便挖坑,还有坑没填完 😄
KEEP CALM AND HACK AWAY!