当我调用 jmp 时,我在 c 内联汇编中遇到了分段错误
I got segmentation fault in c inline assembly when I called jmp
我在使用 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)
我以为它可能是一个相对地址,too.So我把jmp
的参数修改为disassemble main
的call
的地址。
就是这样,
#include<stdio.h>
int main(){
__asm__("jmp 0x080483e6\n"
"popl %esi\n"
"movl %esi,0x8(%esi)\n"
"movb [=11=]x0,0x7(%esi)\n"
"movl [=11=]x0,0xc(%esi)\n"
"movl [=11=]xb,%eax\n"
"movl %esi,%ebx\n"
"leal 0x8(%esi),%ecx\n"
"leal 0xc(%esi),%edx\n"
"int [=11=]x80\n"
"movl [=11=]x1, %eax\n"
"movl [=11=]x0, %ebx\n"
"int [=11=]x80\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 [=13=]x0,0x7(%esi)\n"
"movl [=13=]x0,0xc(%esi)\n"
"movl [=13=]xb,%eax\n"
"movl %esi,%ebx\n"
"leal 0x8(%esi),%ecx\n"
"leal 0xc(%esi),%edx\n"
"int [=13=]x80\n"
"movl [=13=]x1, %eax\n"
"movl [=13=]x0, %ebx\n"
"int [=13=]x80\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)
我不知道问题出在哪里,非常感谢您的帮助!
我认为段错误不是由 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)
at 0x80483c1
.
据我所知,问题是 gdb 只显示它执行的下一个完整 语句。由于语句以分号结尾,所以整个 __asm__("...")
算作一个语句,gdb 只打印它的第一行,即 __asm__("jmp L\n"
,只要调试器逐步执行 __asm__
]声明。
所以我们弄清楚了,现在让我们找出导致分段错误的原因。
当你跳到L
时,call sub
被执行。这会将 32 位 return 地址压入堆栈。 sub
中的第一条指令,pop %esi
,用return地址填充%esi
,并将其从堆栈中删除。
当您现在执行 mov %esi,0x8(%esi)
时,CPU 会尝试将 %esi
移动到 return 地址指向的后面 0x8
个字节,即在代码段内。而且,看起来,代码在您的 OS 上是只读的,所以程序出错了。
有更多时间看这个:
我认为您正在尝试执行 sys_write 但似乎所有内容都通过 esi 寄存器初始化,该寄存器 "initialised" 作为堆栈上的最高值。我猜程序员假设 GNU 调用标准所以 for main(argc, argv): 但你不需要这样做; 32 位系统上的 esi 将具有 argv 参数。但为什么流行?为什么不显式声明 main 以及参数。我认为这就是混乱的来源。
要在不使用额外标志的情况下执行 jmp,请使用
jmp . + 42
42是字节数。它也可以用十六进制写成 0x2c。
我在使用 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)
我以为它可能是一个相对地址,too.So我把jmp
的参数修改为disassemble main
的call
的地址。
就是这样,
#include<stdio.h>
int main(){
__asm__("jmp 0x080483e6\n"
"popl %esi\n"
"movl %esi,0x8(%esi)\n"
"movb [=11=]x0,0x7(%esi)\n"
"movl [=11=]x0,0xc(%esi)\n"
"movl [=11=]xb,%eax\n"
"movl %esi,%ebx\n"
"leal 0x8(%esi),%ecx\n"
"leal 0xc(%esi),%edx\n"
"int [=11=]x80\n"
"movl [=11=]x1, %eax\n"
"movl [=11=]x0, %ebx\n"
"int [=11=]x80\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 [=13=]x0,0x7(%esi)\n"
"movl [=13=]x0,0xc(%esi)\n"
"movl [=13=]xb,%eax\n"
"movl %esi,%ebx\n"
"leal 0x8(%esi),%ecx\n"
"leal 0xc(%esi),%edx\n"
"int [=13=]x80\n"
"movl [=13=]x1, %eax\n"
"movl [=13=]x0, %ebx\n"
"int [=13=]x80\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)
我不知道问题出在哪里,非常感谢您的帮助!
我认为段错误不是由 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)
at 0x80483c1
.
据我所知,问题是 gdb 只显示它执行的下一个完整 语句。由于语句以分号结尾,所以整个 __asm__("...")
算作一个语句,gdb 只打印它的第一行,即 __asm__("jmp L\n"
,只要调试器逐步执行 __asm__
]声明。
所以我们弄清楚了,现在让我们找出导致分段错误的原因。
当你跳到L
时,call sub
被执行。这会将 32 位 return 地址压入堆栈。 sub
中的第一条指令,pop %esi
,用return地址填充%esi
,并将其从堆栈中删除。
当您现在执行 mov %esi,0x8(%esi)
时,CPU 会尝试将 %esi
移动到 return 地址指向的后面 0x8
个字节,即在代码段内。而且,看起来,代码在您的 OS 上是只读的,所以程序出错了。
有更多时间看这个:
我认为您正在尝试执行 sys_write 但似乎所有内容都通过 esi 寄存器初始化,该寄存器 "initialised" 作为堆栈上的最高值。我猜程序员假设 GNU 调用标准所以 for main(argc, argv): 但你不需要这样做; 32 位系统上的 esi 将具有 argv 参数。但为什么流行?为什么不显式声明 main 以及参数。我认为这就是混乱的来源。
要在不使用额外标志的情况下执行 jmp,请使用
jmp . + 42
42是字节数。它也可以用十六进制写成 0x2c。