【问题标题】:Assembly 32-bit addressing size instead of 64-bit in 64-bit Mode汇编 32 位寻址大小而不是 64 位模式下的 64 位
【发布时间】:2019-12-05 07:41:02
【问题描述】:

我对 64 位模式下的 32 位寻址大小而不是 64 位寻址大小有疑问

func:
    movzx    eax, al  ; instead of movzx rax, al
    mov      eax, DWORD [4 * eax + .data]  ; instead of mov rax, QWORD [8 * rax + .data]
    ret

    .data:
         DD .DATA1     ; instead of DQ
         DD .DATA2     ; instead of DQ
         DD .DATA3     ; instead of DQ
         DD .DATA4     ; instead of DQ

.DATA1     DB 'HEY1', 0x00
.DATA2     DB 'HEY2', 0x00
.DATA3     DB 'HEY3', 0x00
.DATA4     DB 'HEY4', 0x00

64 位 中是否安全?因为我认为在 64 位和这样的寻址中,没有问题! (我这样做是因为 .data)

我认为 .data 如果程序大小(可执行文件)小于约 100 Mb,则每个项目地址都适合 32 位寄存器 strong> 永远是!

【问题讨论】:

  • 您正在从 .data 中读取数据,它只是一个指向内存的指针。之后的事情是你自己的选择。此外,您在阅读时必须知道是否需要将数据读取为 32 位或 64 位值(甚至是字节,因为内容似乎是 ascii 字符串)。
  • 您可以安全地作弊,通过使用从 64 位基地址偏移的 32 位偏移量,例如“myTable: dd .DATA1- myTable, DATA2-myTable, ...”和“movzx ecx, al,mov ecx,dword [myTable + ecx*4],mov rax,myTable,@987654326 @"。

标签: assembly x86-64 addressing-mode


【解决方案1】:

这在 x86-64 MacOS 或 Linux PIE 可执行文件中是不安全的。程序大小不是唯一的因素,因为它不是从虚拟地址0 开始加载的。你的程序的第一个字节可能是0x555555555000,所以无论你的程序有多小,将地址截断为 32 位都会破坏你的代码。

(不过,在这种情况下,您会因为使用[.data + rax*4] 而只是使用.data 作为绝对disp3232-bit absolute addresses no longer allowed in x86-64 Linux? 得到一个无效的重定位链接器错误)。但是,如果您在 RDI 中使用了带有有效指针的 [edi + eax*4],您可以编写可以在 PIE 可执行文件或 MacOS 可执行文件中汇编但会崩溃的代码。)

但是,是的,默认的非 PIE Linux 代码模型将所有代码和静态数据放在低 2GiB 的虚拟地址空间中,因此 32 位绝对符号或零扩展数字可以表示地址。


无论您如何寻址,您在内存中的数据大小都是相同的,因此您的替代方案是

 movzx    eax, al
 mov      eax, DWORD [4 * eax + table_of_32bit_pointers]  ; pointless
 mov      eax, DWORD [4 * rax + table_of_32bit_pointers]  ; good

 ; RAX holds a zero-extended pointer.

mov rax, QWORD [8 * rax + .data] 将从不同位置加载 8 个字节。您仍在混淆地址大小和操作数大小。

在内存中使用紧凑的 32 位指针并不意味着在加载它们时必须使用 32 位地址大小。

Like I explained in your previous question 没有理由在使用 movzx eax, al 将索引零扩展为 64 位之后使用 32 位地址大小。(顺便说一句,更喜欢 movzx ecx, al;mov-elimination只适用于不同的寄存器。)

顺便说一句,如果您的字符串长度相同,或者您可以廉价地将它们填充到固定长度,则不需要指针表。您可以改为从第一个字符串的开头 + 缩放索引开始计算地址。例如p = .DATA1 + idx*5 在这种情况下,您的字符串每个长度为 5 个字节。

lea  eax, [.DATA1 + RAX + RAX*4]    ; 4+1 = 5
; eax points at the selected 5-byte string buffer

另外,不要使用.data 作为符号名称。这是一个部分的名称,所以会让人感到困惑。

【讨论】:

  • 谢谢...所以你告诉我使用'DQ'而不是'DD'是一种安全的方式,最好使用我在源代码注释中写的'instread'? (mov rax, QWORD [8 * rax + .data])
  • @ELHASKSERVERS:不,我的意思是,如果您正在制作一个可以安全使用[data + rax*4] 的可执行文件,您可以将静态地址存储在 4 字节数组元素中并以零加载它扩展而不必执行 64 位加载。
  • 喜欢这个 eax, DWORD [4 * rax + table_of_32bit_pointers] ?但它仍然是 MacOS 或 PIE 的问题......真的吗?
  • @ELHASKSERVERS:两者都是。你在Linux上,对吧?您可以与ld -piegcc -pie -nostdlib(甚至gcc -static-pie -nostdlib)链接并自己尝试。 [disp32 + rax*4] 有效但 [disp32 + eax*4] 无效的一种情况是高半内核,其中您的静态地址位于地址空间的顶部 2GiB 中,因此 32 位符号扩展有效,但零扩展无效。 (32 位地址大小从零扩展到 64。)当然,在这种情况下,您必须使用 movsxd rax, [table + rax*4] 加载指针以显式地对内存中的数据进行符号扩展。
  • 我正在为我自己创建一个小型库,它可以是 linux 或 win,也可以是 PIE 或 ELF 或 MZ COFF 或 ...!所以我选择了这种方式: movzx rax, al mov rax, QWORD [8 * rax + .DATA] 我的表是 DQ .DATA0 .....现在好吗?
猜你喜欢
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 1970-01-01
  • 2016-07-13
  • 1970-01-01
相关资源
最近更新 更多