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)