c - 为什么 GDB 和不同的汇编程序给出不同的错误 jmp 地址?

标签 c assembly x86 gdb

我想在 C 代码(下方)中运行此程序集 jmp 0x8048540 以运行位于内存地址 0x8048540 的函数。但是我遇到了段错误。我决定看看我哪里出错了......

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

#define AMOUNT_OF_STUFF 10

//TODO: Ask IT why this is here
void win(){
    system("/bin/cat ./flag.txt");    
}


void vuln(){
    char * stuff = (char *)mmap(NULL, AMOUNT_OF_STUFF, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if(stuff == MAP_FAILED){
        printf("Failed to get space. Please talk to admin\n");
        exit(0);
    }
    printf("Give me %d bytes:\n", AMOUNT_OF_STUFF);
    fflush(stdout);
    int len = read(STDIN_FILENO, stuff, AMOUNT_OF_STUFF);
    if(len == 0){
        printf("You didn't give me anything :(");
        exit(0);
    }
    void (*func)() = (void (*)())stuff;
    func();      
}

int main(int argc, char*argv[]){
    printf("My mother told me to never accept things from strangers\n");
    printf("How bad could running a couple bytes be though?\n");
    fflush(stdout);
    vuln();
    return 0;
}

这是地址处的函数:

Dump of assembler code for function win:
   0x08048540 <+0>: push   %ebp
   0x08048541 <+1>: mov    %esp,%ebp
   0x08048543 <+3>: sub    $0x8,%esp
   0x08048546 <+6>: sub    $0xc,%esp
   0x08048549 <+9>: push   $0x8048700
   0x0804854e <+14>:    call   0x80483f0 <system@plt>
   0x08048553 <+19>:    add    $0x10,%esp
   0x08048556 <+22>:    leave  
   0x08048557 <+23>:    ret    
End of assembler dump.

我注意到我的汇编程序给我的操作码不一致。他们给我的跳转地址也与预期地址 0x8048540 不同。

根据 x86 的 defuse.ca,我的字符串文字是 \xE9\x3C\x85\x04\x08。我看到的地址是0x804853C

但是,根据 x86 的 rasm2,我的字符串文字是 \xe9\x3b\x85\x04\x08。我看到的地址是0x804853B

第一个问题:为什么地址与我的预期地址不同,而且彼此之间差异如此之大?他们都应该为 x86 提供操作码。

尽管如此,我还是决定使用 rasm2 的操作码。 然后,我注意到 GDB 中有些奇怪的东西。 (注意:read()命令读取10个字节到内存地址0xf7fd3000。

(gdb) x/8x 0xf7fd3000
0xf7fd3000: 0xe9    0x3b    0x85    0x04    0x08    0x00    0x00    0x00

目前看来一切都很好。内存地址中的值与 rasm2 给出的字符串文字相匹配。 然后我决定按照指令看内存:

(gdb) x/2i 0xf7fd3000
   0xf7fd3000:  jmp    0x1b540
   0xf7fd3005:  add    BYTE PTR [eax],al

哇哦。为什么要跳转到地址 0x1b540??会不会只是视觉错误? 所以我运行了它。

但是 GDB 真的跳到了那个地址!

(gdb) si
0x0001b540 in ?? ()
=> 0x0001b540:  Cannot access memory at address 0x1b540

我想也许我犯了一个错误。也许 jmp 0x8048540 是非法的。但是,根据 this source , jmp 接受 32 位指针。

第二个问题:为什么 GDB 给我这么可笑的地址?

有人能告诉我不同​​地址背后的原因吗?我只想跳转到 0x8048540。 defuse.ca 给了我 0x804853C,rasm2 给了我 0x804853B,GDB 给了我 0x1b540。 T.T

谢谢。

仅供引用,这是来自 PicoCTF 2017 中的 Shells 挑战。

最佳答案

The machine code for "jmp 0x8048540" is the input.

这是错误的:

在 x86 CPU 上有不同种类的 jmp 指令(比如 jmp ecxecx 寄存器获取目标地址)。

跳转指令(jmp, call, je, jae ...)但是值是 PC 相关的:

跳转的目的地址由公式计算:

argument of "jmp" + address of the next instruction

所以下面的代码:

0x12340000 E9 00 00 01 00

反汇编为:

0x12340000 jmp 0x12350005

这是按以下方式计算的:

  • jmp 指令长 5 个字节,位于地址 0x12340000。所以下一条指令(jmp 之后的指令)位于 0x12340005。

  • jmp 的参数是 0x10000 和 0x12340005 + 0x10000 = 0x12350005。

当然:该指令不仅会这样反汇编,还会跳转到 0x12350005。

关于c - 为什么 GDB 和不同的汇编程序给出不同的错误 jmp 地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47206807/

相关文章:

c - C中的简单数组操作

assembly - 对于 asm 语句中的临时寄存器,我应该使用 clobber 还是虚拟输出?

c++ - 与C++和汇编相关,什么是ebp+8?

汇编保留寄存器值?

c - 副作用、序列点和未定义的行为

c - 使用预处理器在 C 中进行模板化

c - 什么在 ARM 上更快? MUL 或 (SHIFT + SUB)?

memory - 如何实现/设置数据断点?

将8.24定点,0.000000000000000到1.000000000000000范围转换为C中的uint32_t

汇编中的 C++ 类函数