Assembly 英特尔X86汇编:如何判断宽多少位是一个参数?

Assembly 英特尔X86汇编:如何判断宽多少位是一个参数?,assembly,x86,int,intel,short,Assembly,X86,Int,Intel,Short,在以下组件中: mov dx, word ptr [ebp+arg_0] mov [ebp+var_8], dx 将其视为一个组合的C函数,(C函数的参数)arg_0宽多少位?(局部C变量)var_8宽多少位?也就是说,它是一个短的,一个int,等等 由此看来,var_8是16位的,因为dx是一个16位寄存器。但我不确定arg_0 如果部件也包含此行: ecx, [ebp+arg_0] 这是否意味着arg_0是一个32位的值?要解决这个问题,有三个原则需要理解 汇编程序必须能

在以下组件中:

mov     dx, word ptr [ebp+arg_0]
mov     [ebp+var_8], dx
将其视为一个组合的C函数,(C函数的参数)arg_0宽多少位?(局部C变量)var_8宽多少位?也就是说,它是一个短的,一个int,等等

由此看来,var_8是16位的,因为dx是一个16位寄存器。但我不确定arg_0

如果部件也包含此行:

ecx, [ebp+arg_0]

这是否意味着arg_0是一个32位的值?

要解决这个问题,有三个原则需要理解

  • 汇编程序必须能够推断正确的长度。
    虽然英特尔的语法没有使用像“汇编”那样的大小后缀,但汇编程序仍然需要一种方法来查找操作数的大小。

    如果存储的大小为32位(注意后缀
    l
    ),则不明确的指令
    mov[var],1
    在AT&T语法中写为
    movl$1,var
    ,因此很容易判断立即操作数的大小。
    接受英特尔语法的汇编程序需要一种方法来推断此大小,有四种广泛使用的选项:

    • 它是从另一个操作数推断出来的。
      例如,涉及寄存器时就是这种情况。
      例如,
      mov[var],dx
      是一个16位存储
    • 明确说明。
      mov-WORD[var],dx

      MASM语法汇编程序在大小之后需要一个
      PTR
      ,因为它们的大小说明符只允许在内存操作数上使用,而不允许在即时操作数或其他任何地方使用。
      这是我喜欢的格式,因为它清晰、突出,并且不太容易出错(
      mov-WORD[var],edx
      无效)
    • 它是根据上下文推断出来的

      MASM语法汇编程序可以推断,因为
      var
      是用
      db
      声明的,所以它的大小是8位,存储也是8位(默认情况下)。
      这是我不喜欢的形式,因为它使代码更难阅读(汇编的一个好处是指令语义的“局部性”),并且混合了类型之类的高级概念和存储大小之类的低级概念。这就是为什么

    • 在绝大多数情况下,只有一个正确的大小
      对于
      推送
      、分支和所有指令,它们的操作数大小取决于内存模型或代码大小。
      某些指令可以覆盖使用的实际大小,但默认值是一个明智的选择。(例如)

    简而言之,汇编器必须有一种方法来告诉大小,否则它将拒绝代码。(或者一些低质量的汇编程序,如emu8086,在不明确的情况下有默认的操作数大小。)

    如果您查看的是反汇编代码,反汇编程序通常会采取安全措施,并始终明确说明大小。
    如果没有,则必须手动检查操作码,如果反汇编程序无法显示操作码,则是时候更改操作码了。
    反汇编程序很容易找到操作数的大小,因为它要反汇编的二进制代码与CPU执行的相同,并且指令操作码对操作数大小进行编码。

  • C语言故意对C类型如何映射到位数的问题含糊其辞

    试图从反汇编中推断变量的类型并非徒劳,但也必须考虑平台,而不仅仅是架构。
    讨论了使用的主要模型:

    x86_64上的Windows使用LLP64。x86-64上的其他操作系统通常使用x86-64 System V ABI,一种LP64型号

  • 程序集没有类型,程序员可以利用它

    .

    在链接的情况下,
    bar
    类型的
    long
    (64位)变量与1进行OR运算,
    clang
    通过仅对低字节进行ORing来保留REX前缀。如果使用两个dword加载或一个qword立即重新加载变量,这会导致存储转发暂停,因此这可能不是一个好的选择,尤其是在32位模式下,
    或dword[bar],1
    大小相同,并且可能作为两个32位的一半重新加载。
    如果一个人不小心地查看反汇编代码,他们可能会推断
    bar
    是8位的。
    这种部分访问变量或对象的技巧很常见。

    为了正确猜测变量的大小,需要一些专业知识。
    例如,结构成员通常是填充的,因此它们之间存在未使用的空间,这可能会愚弄没有经验的用户,使其认为每个成员都比实际大。
    堆栈也有精确的对齐要求。

    经验法则是编译器通常更喜欢保持堆栈16字节对齐,并自然对齐所有变量。当通过堆栈传递函数参数时,每个参数都被填充为32或64位,但这不适用于堆栈上局部变量的布局

  • 最终回答您的问题

    是的,从第一段代码中可以假定
    arg\u 0
    的值为16位宽。
    请注意,由于它是在堆栈上传递的函数arg,因此它实际上是32位的,但不使用上面的16位

    如果
    mov ecx、[ebp+arg_0]
    出现在代码中的较晚时间,而您需要重新猜测
    arg_0
    值的大小,那么肯定至少是32位。
    它不太可能是64位的(64位类型在32位代码中很少见,我们可以打赌),所以我们可以断定它是32位的。
    显然,第一个代码片段是那些只使用部分变量的技巧之一

    这就是你如何处理一个var大小的反向工程,你做一个猜测,验证它与代码的其余部分一致,如果不一致,重新访问它,重复。
    随着时间的推移,你会做出几乎不需要修改的好猜测

    在那里
     var db 0
    
     mov [var], 1   ; MASM/TASM only.   associate sizes with labels 
    
    Datatype    LP64    ILP64   LLP64   ILP32   LP32
    char        8       8       8       8       8
    short       16      16      16      16      16
    _int32      32          
    int         32      64      32      32      16
    long        64      64      32      32      32
    long long                   64      [64]                    
    pointer     64      64      64      32      32