C 数据段之前的未初始化、可写数据
我正在编写一个简单的程序,将代码转换为x86_64汇编。其中一部分涉及在程序开始时创建一个大型零初始化数组。因此,每个编译的程序都以以下汇编代码开始:C 数据段之前的未初始化、可写数据,c,assembly,compilation,brainfuck,C,Assembly,Compilation,Brainfuck,我正在编写一个简单的程序,将代码转换为x86_64汇编。其中一部分涉及在程序开始时创建一个大型零初始化数组。因此,每个编译的程序都以以下汇编代码开始: .data ARR: .space 32430 .text .globl _start .type _start, @function _start: ... #code as compiled from the brainfuck program ... 从那里编译的程序应该能够访问该数组的任何部分,但是如果它
.data
ARR:
.space 32430
.text
.globl _start
.type _start, @function
_start:
... #code as compiled from the brainfuck program
...
从那里编译的程序应该能够访问该数组的任何部分,但是如果它试图在它之前或之后访问内存,它应该会出错
因为数组后面紧跟着一个.text部分,据我所知,该部分是只读的,而且因为它是程序的第一部分,所以我希望我所期望的行为会自然发生。不幸的是,情况并非如此:编译程序能够访问数组开头左侧(即地址低于)的非零初始化数据
为什么会出现这种情况?在汇编代码中是否有任何东西可以阻止这种情况?这当然是高度依赖于系统的,但由于您的观察结果适合典型的Linux/GNU系统,因此我将提及这样的系统 我假设链接器没有把我的片段放在我想的地方 诚然,链接器并不是按照它们在代码段中出现的顺序放置这些段,而是将
.text
放在第一位,.data
放在第二位。我们可以看到这个e。G与
> objdump -h ARR
ARR: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000042 08048074 08048074 00000074 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00007eae 080490b8 080490b8 000000b8 2**2
CONTENTS, ALLOC, LOAD, DATA
解决此问题的一种方法是简单地计算
+
和-
符号,以查看程序是否超出指定的大小-只需在超出允许的大小时引发分段错误即可.text
应为只读,是否尝试使用gdb
或类似工具对结果进行迭代?它可能不会覆盖.text
您是对的,只有左边界被违反。事实上,最初的数组大小是30000,但程序能够写入超过数组开始的32430字节的位置;因此,我的带助解决方案简单地增加了初始化数组大小。对方的问题更难解决,;我假设链接器没有把我的片段放在我想的地方。我可以检查数据指针的每次移动是否有效,但这比我希望的开销要大;我更喜欢这样一种解决方案,即放置我的段时,坏的数组访问会导致segfault。在x86上,您总是被授予访问页面最后一个字节的权限(至少在现代操作系统中是这样)。查看生成的二进制文件(因为32430不是页面大小的倍数)并考虑分配粒度将非常有用。特别是,防护页面可能会有所帮助。@BrandonSides可能是ASLR导致的,试试看。你有没有尝试过写作,也远高于32340?不过,我认为我的解决方案可能更好——期望操作系统警告用户可能是一个非常糟糕的主意。同样,如果您只是转换为汇编,因此没有理由关心程序本身的行为,那么x86中的内存保护不是以字节粒度工作,而是按页工作。默认的.data
段不太可能位于两个保护页之间(无效访问),即使如此,该段的大小也将是mem页面大小的倍数。我想我看到了两个比较优雅的选择。1) 检查OS API,如果您可以详细设置内存,在32+kiB缓冲区周围指定两个保护页,然后将缓冲区用作ARR->write/read+-4kiB outside=>crash。2) 创建64kiB ARR,并将所有ARR索引计算写入无符号16位,然后ARR[zero_extended_16b_index]将环绕。您应该使用.bss
,而不是.data
。还有,那个奇怪的asm是从哪里来的?为什么会有这么多16位的东西?您需要根据CX的高位字节进行调零,以便movb
而不是movzbl
工作,因此为什么不依赖于整个ECX和jecxz
等等。(或者更好,使用movzbl
删除循环中的假依赖项)。或者最好避免使用jecxz
,因为它效率不高。此外,您可以乘以10并使用2条LEA指令进行累加,或者您可以使用imul$10、%eax、%eax
而不是dx
的笨重东西。此外,每个人都写int$0x80
,而不是$128
。此外,每个人都写int$0x80,而不是$128。-我已经驳斥了那个说法。嘿,说得对。其他人都像正常人一样写int$0x80
。这只是风格问题;其他点导致不同(效率较低)的机器代码。
.data
.align 4096
ARR:
.space 30000 # we'll actually get 32768
.text
.globl _start
.type _start, @function
_start:
mov (%esp),%ebx # argc
cmp $1,%ebx
jbe 9f
mov $0,%ax
mov $1,%ebx # sign 1
mov 8(%esp),%esi # argv[1]
0: movb (%esi),%cl # convert argument string to integer
jcxz 1f
sub $'0',%cl
js 2f
mov $10,%dx
mul %dx
add %cx,%ax
jmp 3f
2: neg %ebx # change sign
3: add $1,%esi
jmp 0b
1: mul %ebx # multiply with sign 1 or -1
movzx ARR(%eax),%ebx# load ARR[atoi(argv[1])]
9: mov $1,%eax
int $128 # _exit(ebx);