【问题标题】:I got segmentation fault in c inline assembly when I called jmp当我调用 jmp 时,我在 c 内联汇编中遇到分段错误
【发布时间】:2017-02-02 18:33:27
【问题描述】:

我在使用 jmp 时遇到了分段错误。

第一次使用jmp 0x30,出现segmentation fault。

我用gdb调试了我的程序,看到jmp被调用后,跳转到了一个绝对地址。

(gdb) b main
Breakpoint 1 at 0x80483b7: file f.c, line 3.
(gdb) r
Starting program: /root/work/f

Breakpoint 1, main () at f.c:3
3       __asm__("jmp 0x30\n"
(gdb) n
0x00000030 in ?? ()
(gdb)

我还以为可能是相对地址,所以我把jmp的参数从disassemble main修改为call的地址。 就是这样,

#include<stdio.h>
int main(){
__asm__("jmp 0x080483e6\n"
"popl %esi\n"
"movl %esi,0x8(%esi)\n"
"movb $0x0,0x7(%esi)\n"
"movl $0x0,0xc(%esi)\n"
"movl $0xb,%eax\n"
"movl %esi,%ebx\n"
"leal 0x8(%esi),%ecx\n"
"leal 0xc(%esi),%edx\n"
"int $0x80\n"
"movl $0x1, %eax\n"
"movl $0x0, %ebx\n"
"int $0x80\n"
"call 0x2a\n"
".string \"/bin/sh\"\n");
return 0;
}

但我得到了这个

Breakpoint 1, main () at f.c:3
3       __asm__("jmp 0x080483e6\n"
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x0000002a in ?? ()
(gdb)

我发现了这个相关的问题confusing with JMP instruction,我像这样修改了我的代码。

#include<stdio.h>
int main(){
__asm__("jmp L\n"
"sub:\n"
"popl %esi\n"
"movl %esi,0x8(%esi)\n"
"movb $0x0,0x7(%esi)\n"
"movl $0x0,0xc(%esi)\n"
"movl $0xb,%eax\n"
"movl %esi,%ebx\n"
"leal 0x8(%esi),%ecx\n"
"leal 0xc(%esi),%edx\n"
"int $0x80\n"
"movl $0x1, %eax\n"
"movl $0x0, %ebx\n"
"int $0x80\n"
"jmp exit\n"
"L:\n"
"call sub\n"
".string \"/bin/sh\"\n"
"exit:\n");
return 0;
}

但是不适合我,jmp被调用后,指令地址还是jmp这一行

(gdb) b main
Breakpoint 1 at 0x80483b7: file f.c, line 3.
(gdb) r
Starting program: /root/work/f

Breakpoint 1, main () at f.c:3
3       __asm__("jmp L\n"
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x080483ba in main () at f.c:3
3       __asm__("jmp L\n"
(gdb) n

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb)

我不知道问题出在哪里,非常感谢您的帮助!

【问题讨论】:

  • 我不确定您要在这里完成什么。如果您的目标是学习 c,那么这是一个不好的开始方式。如果目标是学习 asm,这是最复杂的开始方式。也就是说,您正在修改 asm 中的(一堆)寄存器,而不通知编译器。当您的 asm() 退出时,这会造成混乱。如果您必须这样做,请考虑使用extended asm,它允许您破坏寄存器。

标签: c assembly x86 segmentation-fault inline-assembly


【解决方案1】:

我不认为分段错误是由jmp L 指令引起的。

看看我在这里做了什么:

(gdb) b main
Breakpoint 1 at 0x80483be: file test.c, line 3.
(gdb) run
Starting program: /home/cad/a.out 

Breakpoint 1, main () at test.c:3
3       __asm__("jmp L\n"
(gdb) display/i $pc
1: x/i $pc
=> 0x80483be <main+3>:  jmp    0x80483ec <main+49>
(gdb) si
0x080483ec      3       __asm__("jmp L\n"
1: x/i $pc
=> 0x80483ec <main+49>: call   0x80483c0 <main+5>
(gdb) si
0x080483c0      3       __asm__("jmp L\n"
1: x/i $pc
=> 0x80483c0 <main+5>:  pop    %esi
(gdb) si
0x080483c1      3       __asm__("jmp L\n"
1: x/i $pc
=> 0x80483c1 <main+6>:  mov    %esi,0x8(%esi)
(gdb) si

Program received signal SIGSEGV, Segmentation fault.
0x080483c1 in main () at test.c:3
3       __asm__("jmp L\n"
1: x/i $pc
=> 0x80483c1 <main+6>:  mov    %esi,0x8(%esi)
(gdb)

如您所见,我在main 处设置了一个断点,并启用了对执行的每条机器指令的反汇编 (display/i $pc)。然后我逐步完成了机器指令 (si)。原来错误指令是mov %esi,0x8(%esi)0x80483c1

据我所知,问题在于 gdb 只显示它执行的下一个完整的语句。由于一条语句以分号结尾,所以整个__asm__("...") 都算作一条语句,gdb 只打印它的第一行,即__asm__("jmp L\n",只要调试器逐步执行__asm__ 语句。


所以我们已经弄清楚了,现在让我们找出导致分段错误的原因。

当你跳转到L时,call sub会被执行。这会将 32 位返回地址压入堆栈。 sub 中的第一条指令pop %esi 用返回地址填充%esi 并将其从堆栈中移除。
当您现在执行mov %esi,0x8(%esi) 时,CPU 会尝试将%esi 移动到返回地址指向的0x8 字节后面,即代码段内。而且,看起来,代码在你的操作系统上是只读的,所以程序出错了。

【讨论】:

    【解决方案2】:

    有更多时间看这个:

    我认为您正在尝试执行 sys_write 但一切似乎都通过 esi 寄存器进行初始化,该寄存器被“初始化”为堆栈上的最高值。我猜程序员正在假设 GNU 调用标准所以 for main(argc, argv): 但你不需要这样做; 32 位系统上的 esi 将具有 argv 参数。但是为什么流行呢?为什么不使用参数显式声明 main 。我认为这就是混乱的来源。

    【讨论】:

      【解决方案3】:

      在不使用额外标志的情况下执行 jmp

      jmp . + 42
      

      42 是字节数。它也可以用十六进制写0x2c。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多