为什么此"Hello world"汇编程序中缺少RDI寄存器?



我发现了这个"你好"(外壳代码(组装程序:

SECTION .data
SECTION .text
global main
main:
mov rax, 1
mov rsi, 0x6f6c6c6548 ; "Hello" is stored in reverse order "olleH"
push rsi
mov rsi, rsp
mov rdx, 5
syscall
mov rax, 60
syscall

我发现mov rdi, 1不见了。在其他";你好世界";程序的指令出现,所以我想了解为什么会发生这种情况。

我本来打算说,使用argc作为文件描述符来保存代码字节是一种有意的技巧或破解。(如果在没有额外命令行参数的情况下从shell运行1(。main(int argc, char**argv)分别在EDI和RSI中获取其参数,这是Linux上使用的x86-64 SysV调用约定。

但考虑到其他选择,比如mov rax, 1而不是mov eax, edi,这可能只是一个因为代码碰巧工作而被忽略的错误

对于代码注入攻击,不能在真实的shell代码中工作,在这种情况下,执行可能会使用EDI中的0、1或2以外的垃圾到达该代码。您链接的教程中的shell代码测试程序调用机器代码的const char[]作为main中唯一的东西,它通常会编译为不涉及RDI的asm。

此代码也不适用于基于strcpy或其他C字符串溢出的代码注入攻击,因为机器代码包含00字节作为mov eax, 1mov edx, 5和该字符串末尾的一部分。

此外,现代链接器不会将.rodata链接到可执行段中,并且-zexecstack只影响实际堆栈,而不是所有可读内存。所以shell代码测试不会起作用,尽管我预计它在编写时会起作用。请参阅如何获取c代码来执行十六进制机器代码?用于工作方式,如使用本地数组和使用-zexecstack进行编译。


这个教程总体上不太好,可能是这个家伙在学习时写的。(但根据这个错误和Kali的使用,并没有我预期的那么糟糕;它至少写得很好,只是缺少了一些技巧。(

由于您使用的是NASM,因此不需要手动浪费时间查找ASCII代码并获得正确的字节顺序。与某些汇编程序不同,mov rsi, "Hello"/push rsi会导致这些字节按源顺序在内存中。

您也不需要空的.data部分,尤其是在制作shell代码时,shell代码只是一个独立的机器代码片段,不能引用任何外部内容。

写入32位寄存器隐式零扩展到64位。NASM将mov rax,1优化为mov eax,1(如您在objdump -dAT&D反汇编中所见;objdump -drwC -Mintel使用类似于NASM的Intel语法反汇编。(

以下内容应该有效:

global main
main:
mov   rax, `Hellon  `  ; non-zero padding to fill 8 bytes
push  rax
mov rsi, rsp
push   1                ; push imm8
pop    rax              ; __NR_write
mov    edi, eax         ; STDOUT_FD is also 1
lea    edx, [rax-1 + 6]    ; EDX = 6;  using 3 bytes with no zeros
syscall
mov    al, 60    ; assuming write success, RAX = 5, zero outside the low byte
;lea    eax, [rdi-1 + 60]    ; the safe way that works even with ./hello >&-  to return -EBADF
syscall

这比原来的机器代码字节数少,并且避免了strcpy会停止的x00字节。我将字符串改为以换行符结尾,使用NASM反标记来支持C风格的转义序列,如n作为0x0a字节。

正常运行(我将它链接到一个没有CRT的静态可执行文件中,尽管它被称为main而不是_startld foo.o -o foo(:

$ strace ./foo > /dev/null
execve("./foo", ["./foo"], 0x7ffecdc70a20 /* 54 vars */) = 0
write(1, "Hellon", 6)                  = 6
exit(1)                                 = ?

在stdout关闭的情况下运行以打破mov al, 60__NR_exit破解:

$ strace ./foo >&-
execve("./foo", ["./foo"], 0x7ffe3d24a240 /* 54 vars */) = 0
write(1, "Hellon", 6)                  = -1 EBADF (Bad file descriptor)
syscall_0xffffffffffffff3c(0x1, 0x7ffd0b37a988, 0x6, 0, 0, 0) = -1 ENOSYS (Function not implemented)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xffffffffffffffda} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)

要仍然干净地退出,请使用lea eax, [rdi-1 + 60](3个字节(而不是mov al, 60(2个字节(根据未修改的EDI设置RAX,而不是依赖于RAX的高位字节为零(在返回错误后它们不是零(。

另请参阅https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code

最新更新