Assembly 如果函数没有明确使用';ret';
我有以下计划:Assembly 如果函数没有明确使用';ret';,assembly,x86,Assembly,X86,我有以下计划: SECTION .text main: mov ebx, 10 mov ecx, 50 repeat: inc ebx loop repeat mov eax, ebx ret 当这个程序运行时,它会按预期返回60。但是,如果删除最后的ret语句,程序运行正常,但随后返回0。为什么会这样?当您关闭“ret”时,计算机将执行最后一个“移动eax,ebx”,然后执行计算机内存中下一个发生的任何操作 我很惊讶你没有得到非
SECTION .text
main:
mov ebx, 10
mov ecx, 50
repeat:
inc ebx
loop repeat
mov eax, ebx
ret
当这个程序运行时,它会按预期返回60。但是,如果删除最后的ret
语句,程序运行正常,但随后返回0。为什么会这样?当您关闭“ret”时,计算机将执行最后一个“移动eax,ebx”,然后执行计算机内存中下一个发生的任何操作
我很惊讶你没有得到非法的指令/访问;这将是最常见的反应。不知何故,垃圾指令在破坏寄存器后就像一个返回
这也有点不清楚你所说的“回报60”是什么意思。您的意思是作为命令提示的值?很明显,您的程序没有针对非法指令陷阱的防御措施。
我不清楚Windows在没有防御的情况下会做什么;根据我的经验,我知道当我这样做时,Windows倾向于终止我的进程,我会得到一些随机退出状态。“0”可能就是这种状态
尝试添加:
mov byte ptr[eax], 0
在“ret”指令之前;这将导致非法内存引用。你报告你的状态。如果您在本例中得到零状态结果,我也不会感到惊讶。因为它会失败并运行链接器在它之后放置的下一个函数 请参阅我对Ira答案的评论,了解为什么您的代码没有崩溃。如果您没有链接到C运行时库启动代码(即,您只是使用了
\u start
而不是main
),则执行将命中一些非代码,或者是非法指令出错,或者是尝试访问未映射的内存。见下文
分解最后一个二进制文件,看看发生了什么。当我尝试这一点时,我发现链接器将main
放在标准的C运行时启动函数frame\u dummy
和\uu libc\u csu\u init
之间。它
00000000004004f6 <main>:
4004f6: b8 0a 00 00 00 mov $0xa,%eax
4004fb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000400500 <__libc_csu_init>:
400500: 41 57 push %r15
400502: 41 56 push %r14
400504: 41 89 ff mov %edi,%r15d
400507: 41 55 push %r13
... a bunch more code that eventually returns.
内存中代码后面的00
字节也在ELF可执行文件中。文件的内存映射只发生在页面粒度上,因此它最终都映射为可执行指令。(机器代码不会从可执行文件的磁盘缓存中复制出来;内存只是使用读取+执行权限映射到execve(2)
s二进制文件的进程中。)
剥离二进制文件仍然会使其segfault,但使用不同的指令。耶
# b _start would be b *0x4000d4 without symbols.
(gdb) r
...
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000d9 in ?? ()
(gdb) disassemble /r $rip-5, $rip +15
Dump of assembler code from 0x4000d4 to 0x4000e8:
0x00000000004000d4: b8 0a 00 00 00 mov $0xa,%eax
=> 0x00000000004000d9: 00 2e add %ch,(%rsi)
0x00000000004000db: 73 68 jae 0x400145
0x00000000004000dd: 73 74 jae 0x400153
0x00000000004000df: 72 74 jb 0x400155
0x00000000004000e1: 61 (bad)
0x00000000004000e2: 62 (bad)
0x00000000004000e3: 00 2e add %ch,(%rsi)
0x00000000004000e5: 6e outsb %ds:(%rsi),(%dx)
0x00000000004000e6: 6f outsl %ds:(%rsi),(%dx)
0x00000000004000e7: 74 65 je 0x40014e
$ hexdump -C a.out
...
000000d0 11 67 99 58 b8 0a 00 00 00 00 2e 73 68 73 74 72 |.g.X.......shstr|
000000e0 74 61 62 00 2e 6e 6f 74 65 2e 67 6e 75 2e 62 75 |tab..note.gnu.bu|
000000f0 69 6c 64 2d 69 64 00 2e 74 65 78 74 00 00 00 00 |ild-id..text....|
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
我们的mov
指令是hextump中第一行I中的b8 0a 00 00
。我认为下面的002e…
是一个ELF数据结构,可能是一个节索引或其他内容。作为x86指令,它是一个add%ch,(%rsi)
,由于%rsi
没有指向可写内存,所以会发生故障。(ABI说进程条目上没有定义堆栈指针以外的寄存器,但Linux选择在ELF加载程序中将它们归零,以避免内核数据泄漏。%rsi
没有指向可写内存,进程可能也没有。)
那么,如果你在这里加了一个报税表呢?不,没有什么可以回去的。堆栈包含指向进程args环境变量的指针。您必须进行
退出
系统调用
.section .text
.globl _start
_start:
xor %edi, %edi
mov $231, %eax # exit(0)
syscall
# movl $1, %eax # The 32bit ABI works even for processes in long mode, BTW.
# int $0x80 # exit(edx)
你怎么知道它“运行良好”?OP显然很幸运,链接器在
main
之后放置的任何函数都返回零main
进入代码(.text
)部分,以及通过与标准运行时链接获得的运行时样板启动函数。我希望后面会有另一个函数,而不是数据。记住,这是main
,而不是\u start
。很明显,他的意思是作为main的返回值,其低位8位作为Unix进程退出状态结束。根据您的要求,与SIGSEGV
一起终止的Unix进程的退出状态为139。(分段冲突=非法内存访问)。SIGSEGV=信号11,139=128+11。希望这有助于解释OP让main崩溃时看到了什么。哇,我写这篇文章的时间比我想的要长得多。>。然后执行会碰到“碰到什么?”@迈克尔:谢谢。我只是结束了我的漫无边际,并没有真正校对后,意识到我已经写了大概一个多小时,在一些愚蠢的老问题。
# b _start would be b *0x4000d4 without symbols.
(gdb) r
...
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000d9 in ?? ()
(gdb) disassemble /r $rip-5, $rip +15
Dump of assembler code from 0x4000d4 to 0x4000e8:
0x00000000004000d4: b8 0a 00 00 00 mov $0xa,%eax
=> 0x00000000004000d9: 00 2e add %ch,(%rsi)
0x00000000004000db: 73 68 jae 0x400145
0x00000000004000dd: 73 74 jae 0x400153
0x00000000004000df: 72 74 jb 0x400155
0x00000000004000e1: 61 (bad)
0x00000000004000e2: 62 (bad)
0x00000000004000e3: 00 2e add %ch,(%rsi)
0x00000000004000e5: 6e outsb %ds:(%rsi),(%dx)
0x00000000004000e6: 6f outsl %ds:(%rsi),(%dx)
0x00000000004000e7: 74 65 je 0x40014e
$ hexdump -C a.out
...
000000d0 11 67 99 58 b8 0a 00 00 00 00 2e 73 68 73 74 72 |.g.X.......shstr|
000000e0 74 61 62 00 2e 6e 6f 74 65 2e 67 6e 75 2e 62 75 |tab..note.gnu.bu|
000000f0 69 6c 64 2d 69 64 00 2e 74 65 78 74 00 00 00 00 |ild-id..text....|
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
.section .text
.globl _start
_start:
xor %edi, %edi
mov $231, %eax # exit(0)
syscall
# movl $1, %eax # The 32bit ABI works even for processes in long mode, BTW.
# int $0x80 # exit(edx)