我想写一个汇编程序,通过EXECVE(syscall#0x3C)执行程序/bin/ls和开关-al。
手册页(man 2 execve)指出该调用需要三个值:int execve(const char *filename, char *const argv[], char *const envp[]);
我不太明白如何建立这三个论点。据我所知,第一个参数进入RDI
,第二个进入RSI
,第三个进入RDX
。我相信,建立第一个,这就足够了
push 0x736c2f2f ;sl//
push 0x6e69622f ;nib/
mov rdi, rsp
对于第三个,事情很简单:
xor r11, r11
mov rdx, r11
我的问题是我不知道如何构建第二个参数,它应该是一个包含['/bin//ls', '-aal']
的数组
我需要为x86-64编写它,所以请不要int 0x80
建议。
3条答案
按热度按时间ua4mk5z41#
您可以将
argv
数组放到堆栈上,并将其地址加载到rsi
中。argv
的第一个成员是指向程序名的指针,因此我们可以使用加载到rdi
中的相同地址。这也纠正了问题中代码的其他几个问题。由于x86-64不支持4字节的push,因此它使用8字节的push作为文件名,并确保文件名具有空终止符。
这段代码确实使用了一个64位的push和一个4字节的立即数来push“-aal”,因为字符串适合4个字节。这也使得它以null结尾,而不需要在代码中使用null字节。
我在问题中使用了双字符串,以避免代码中的空字节,但我的首选是这样的:
请注意,文件名字符串通过移位得到一个空终止符,避免了额外的推送。这种模式适用于双精度字符不起作用的字符串,也可以用于较短的字符串。
91zkwejq2#
您可以在NASM中写入
push '/bin'
,以按该顺序将字节放入内存。(用4个字节的零填充,总宽度为qword;在64位模式下,dword推送是不可能的。)无需手动编码ASCII字符;不像某些汇编器,NASM不需要多字符文字,可以让你的生活更轻松。您可以使用use
mov dword [rsp+4], '//ls'
来存储高半部分。(或者使用mov r/m64, sign_extended_imm32
将其设置为qword
存储,以便在此基础上再写入4个字节的零。)或者如果您希望存储8个字节,则在执行mov rsi, '/bin//ls'
/push rsi
之前使用较早的push将其零终止。或
mov eax, '//ls'
;shr eax, 8
以在寄存器中获得EAX="/ls\0"
,准备存储以形成8字节0终止的字符串。或者使用相同的技巧,在
mov r64, imm64
之后移出一个字节(就像@prl的答案一样),而不是单独的push / mov。或者NOT你的文字数据,所以你做mov rax, imm64
/not rax
/push rax
,在你的寄存器中产生零,而机器码中没有零。例如:如果你想让尾部字节保持隐式,而不是显式的
\0
,你可以写mov rsi, ~'/bin/ls'
,它组装成相同的mov rsi, 0xff8c93d091969dd0
。NASM语法中的反引号处理C风格的转义序列,与单引号或双引号不同。我建议使用\0
来提醒自己为什么要麻烦地使用这个NOT和~
按位求反汇编时运算符。(在NASM中,多字符文字用作整数常量。)我相信,建立第一个,这就足够了
否,
push 0x736c2f2f
是8字节的推送,该值的符号扩展为64位。所以你按了'/bin\0\0\0\0//ls\0\0\0\0'
。可能您是从32位代码中复制的,其中
push 0x736c2f2f
是4字节的推送,但64位代码是不同的。x86-64不能编码4字节的
push
,只有2或8字节的操作数大小。标准技术是一次推送8个字节:如果您有奇数个4字节块,第一个可以是
push imm32
,然后使用8字节对。如果它不是4的倍数,并且您不能填充像/
,mov dword [mem], imm32
这样的冗余字符,* 部分 * 重叠可能会有所帮助,或者将值放入寄存器并移位以引入零字节。看
4bbkushb3#
将下面的C示例加载到Godbolt编译器资源管理器中(如果需要,请修改),您可以看到各种编译器通常如何为AMD64(或其他)架构上的
execve
调用生成汇编。