编写execve系统调用Shellcode时首条MOV指令触发SIGSEGV问题求助
嘿,碰到shellcode里MOV指令触发段错误的问题太常见了,结合你说的execve调用和给出的反汇编开头,我给你梳理几个大概率的原因和解决办法:
首先先把你给出的反汇编内容贴出来方便分析:
execve2: file format elf32-i386
Disassembly of section .text:
08048080 <_start>:
8048080: eb 1a jmp 804809c <GotoCall&...
常见问题点及解决方法
内存权限不匹配
shellcode经常需要在可写可执行的内存区域运行,如果你把shellcode放在默认的只读.text段,而MOV指令又需要往这个区域写入数据(比如构造字符串、参数数组),必然触发段错误。
解决办法:可以用execstack -s execve2命令给程序开启栈可执行权限,或者在编译/链接时加上-z execstack选项,让栈区域具备可执行能力——毕竟shellcode通常是注入到栈上执行的。AT&T格式操作数搞反了
AT&T汇编的操作数顺序是源在前,目标在后,和Intel格式完全相反。比如你要是想把立即数存入寄存器,应该写movl $0x0, %eax,如果写成movl %eax, $0x0,就会试图往只读的立即数地址0x0写入,直接触发段错误。
你得仔细检查那条出错的MOV指令,确认操作数顺序有没有搞反,有没有试图往不可写的地址(比如代码段地址、常量地址)写入数据。没有做到位置无关(PIC)
shellcode需要能在任意内存地址执行,如果你在MOV指令里用了固定的绝对地址(比如直接引用.data段的字符串地址),当程序加载到不同内存位置时,这个地址可能无效或者不可访问。
正确的做法是用jmp-call-pop技巧来获取字符串地址(就像你反汇编开头的jmp指令那样),然后在栈上构造execve需要的参数,比如把"/bin/sh"字符串通过call指令压栈,再用pop获取地址,所有操作都基于寄存器和栈偏移,避免硬编码地址。execve参数构造非法
execve系统调用需要三个参数:路径字符串地址、argv数组地址、envp数组地址。如果你的MOV指令在构造这些参数时访问了非法内存(比如数组地址指向了未分配的区域),也会触发段错误。
给你一个标准的32位execve("/bin/sh")的AT&T汇编示例,参考它的参数构造方式:_start: jmp short GotoCall ShellCode: popl %ebx ; 将"/bin/sh"的地址弹出到ebx xorl %eax, %eax ; 清空eax(置0) movb %al, 0x7(%ebx); 将字符串最后一个字节设为0(字符串终止符) movl %eax, 0x8(%ebx); argv数组的第二个元素设为0(结束标记) movl %ebx, 0x4(%ebx); argv数组的第一个元素指向"/bin/sh" movl $0xb, %eax ; 32位Linux下execve的系统调用号是11(0xb) leal 0x4(%ebx), %ecx; ecx指向argv数组的起始地址 xorl %edx, %edx ; envp设为0(没有环境变量) int $0x80 ; 触发系统调用 GotoCall: call ShellCode ; call指令会把下一条指令的地址压栈,也就是字符串的地址 .ascii "/bin/sh" ; 没有终止符,后面用MOV来补
这个示例里所有操作都在栈上完成,没有访问非法内存,也保证了位置无关性,能有效避免段错误。
内容的提问来源于stack exchange,提问作者BeastOnly1 LastOne

