当你从db$-msg移动edx,[msgLen]时,为什么写系统调用会打印一堆垃圾



由于某些原因[msgLen]和msgLen产生相同的结果。我知道我应该使用equ或其他什么,但我想知道为什么这不起作用。NASM是否忽略了取消引用?它打印了我的绳子和一堆垃圾,因为它认为绳子比它大,对吧?谢谢

请参阅下面的组件:

_start:
mov edx,[msgLen]
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
mov eax,1
int 0x80
section .data
msg db 'Zoom',0xA
msgLen db $-msg       

使用调试器在加载后查看EDX,或者使用strace ./a.out查看实际传递给系统调用的长度它会有所不同(地址与加载的值(。这两种方法都有不同的缺陷,恰好会产生大的值,所以效果只是碰巧相同。调试/跟踪工具会清楚地向你表明,NASM不是";忽略解引用";,虽然

mov edx, msgLen是地址,就在消息旁边。如果您使用ld -melf_i386 -o foo foo.o链接到一个标准的Linux非PIE静态可执行文件,它将类似于0x804a005

mov edx, [msgLen]从该地址(EDX的宽度(加载4个字节,在数据段中,您只将大小组装为1个字节。3个高字节来自映射到该内存页的文件部分的下一个字节。

当我只使用nasm -felf32 foo.asm组装并使用ld -melf_i386链接时,恰好是3个字节的零,所以我不会从dword加载版本中得到任何垃圾打印。(x86是little-endian(。

但你的非零字节似乎是调试信息。我发现NASM的调试信息与有用信息相反,有时会使GDB的反汇编变得混乱(layout reg/layout asm有时无法反汇编标签后的整个块(,所以我不使用它。

但如果我使用nasm -felf32 -Fdwarf,那么我确实会从经过.data部分末尾的dword加载中获得7173。这是一个不同的大数字,所以这在不同的方面是错误的,而不是同一个问题。71730x1c05,因此它对应于db 5, 0x1c, 0, 0。也就是说,你计算的长度5是低字节,但后面有一个0x1cyasm -gdwarf2给了我469762053=0x1c000005

如果使用db $-msg, 0,0,0dd $-msg,则可以加载整个dword。(要将字节加载到双字寄存器并进行零扩展,请使用movzx edx, byte [mem](


write()大长度行为

如果给它一个非常大的长度,write将一直到它到达一个不可读的页面,然后返回它实际写入的长度。(在开始copy_from_user之前,它不会检查整个缓冲区的可读性。如果在遇到不可读的页面之前写入的字节数为非零,它会返回。只有在立即遇到不可读页面时,才能获得-EFAULT,而不是在稍后传递包含一些未映射页面的巨大长度时。(

例如使用mov edx, msgLen(标签地址(

$ nasm -felf32 -Fdwarf foo.asm
$ ld -melf_i386 -o foo foo.o
$ strace -o foo.tr ./foo          # write trace to a file so it doesn't mix with terminal output
Zoom
foo.asmmsgmsgLen__bss_start_edata_end.symtab.strtab.shstrtab.text.data.debug_aranges.debug_info.debug_abbrev.debug_lin! '  6& 9B_ 
                                                                  $ cat foo.tr
execve("./foo", ["./foo"], 0x7fffdf062e20 /* 53 vars */) = 0
write(1, "Zoomn5342422041035"..., 134520837) = 4096
exit(1)                                 = ?
+++ exited with 1 +++

134520837是您传递的长度,0x804a005(msgLen的地址(。系统调用在到达未映射的页面并提前停止之前写入4096个字节,即1整页。**您传递的数字高于这个数字并不重要,因为在映射结束之前只有1页。(msg:显然就在那一页的开头。(

在终端上(打印为空(,您大多只看到可打印的字符;如果您想更好地查看二进制数据,请使用管道导入hexdump -C。它包括文件中的元数据,因为内核的ELF程序加载程序通过将文件mmaping到内存中来工作(对于该部分,使用MAP_PRIVATE读写无exec映射(。使用readelf -a并查看ELF程序头:它们告诉内核文件的哪些部分要映射到内存中,在哪里,使用什么权限。


有趣的事实:如果我重定向到/dev/null(strace ./foo > /dev/null(,内核的用于该特殊设备驱动程序的write处理程序甚至不会检查缓冲区的权限,所以write()实际上返回了134520837

write(1, "Zoomn5[...]220410"..., 134520837) = 134520837


正确使用EQU

是的,您应该使用equ,这样您就可以使用实际长度作为立即数。让汇编程序计算它,然后将该字节汇编到数据段中就不那么方便了。这样打印的大小正好合适,而且比使用绝对地址引用1个常量字节更有效。

mov   edx, msg.len         ; mov r32, imm32
...
section .rodata
msg:  db 'Zoom',0xA
.len  equ $-msg       

(使用NASM.本地标签与使用equ无关;我也展示了这一点,因为我喜欢它如何帮助组织全局命名空间。(


还与ld如何将部分布局为程序加载程序的ELF段半相关:最近的ld填充部分进行4k页面对齐或类似操作,以避免将数据映射到不需要的地方。特别是在可执行页面之外。

相关内容

  • 没有找到相关文章

最新更新