【问题标题】:Is it possible to dereference something inside of a dereference in assembly?是否可以在程序集中的取消引用中取消引用某些内容?
【发布时间】:2024-04-13 16:25:02
【问题描述】:

考虑以下用值填充双字数组并接受 2 个参数的过程:EBP + 08h 是数组的大小,EBP + 0Ch 是给定数组的偏移量。 (即OFFSET myarray):

MyProc PROC
PUSH EBP
MOV EBP, ESP
SUB ESP, 04h
PUSH EDI
PUSH ESI
PUSH EBX
MOV EBX, [EBP + 08h] ;move the size of the array into EBX
MOV [EBP - 04h], 00h ;EBP - 04h will be the counter (or the index.)
MOV ESI, [EBP + 0Ch] ;move the offset of the array into ESI
MOV EDI, 01h
INC EBX
@@:


MOV [ESI + 04h * [EBP - 04h]], EDI ;How can I actually move EDI into
;the dword found at address ESI + 4 * the value found at address EBP - 4?


INC [EBP - 04h] ;increment the counter and the value to be stored.
INC EDI
CMP [EBP - 04h], EBX
JNE @B
POP EBX
POP ESI
POP EDI
MOV ESP, EBP
POP EBP
RET
MyProc ENDP

我尝试将EDI 移动到[ESI + 04h * [EBP - 04h]] 的位置是我正在尝试做的一个示例,因为地址EBP - 4 处的双字是数组的索引。
有没有办法将EDI 移动到地址ESI + 4 * the dword at address EBP - 4 的双字中?还是我看错了?

【问题讨论】:

    标签: assembly x86 masm dereference


    【解决方案1】:

    需要两条指令:

    MOV    EAX, [EBP - 04h]
    MOV   [ESI + 4*EAX], EDI
    

    您还可以考虑在函数的序言和尾声中保存/恢复 EAX。在大多数环境中,不需要保留 EAX。

    【讨论】:

    • 我可能是错的,但我认为您正在寻找的术语是“功能结语”。 :)
    • @DavidHoelzer:是的,当我徒步几个小时然后回来并立即尝试写一个连贯的答案时,就会发生这种情况。谢谢。
    • 我现在不能尝试,但你确定 [ESI + 4*[EAX]] 不应该是 [ESI + 4*EAX] 吗?这些术语是序言和尾声。
    • FWIW,必须在哪个 ABI 中保留 EAX?由于这是关于 MASM,我假设它适用于 Windows,并且我不知道必须保存 EAX 的任何 Windows 调用约定。
    • [ESI + 4 * [EAX]] 不是合法的 Intel 语法。 [ESI + 4 * EAX] 在 x86 的 32 和 64 保护模式下都可以。
    【解决方案2】:
    MOV [ESI + 04h * [EBP - 04h]], EDI ;How can I actually move EDI into
           ;the dword found at address ESI + 4 * the value found at address EBP - 4?
    INC [EBP - 04h] ;increment the counter and the value to be stored.
    

    [EBP-4] 处的值将在您的 dword 数组中保存一个递增索引。我看到这个小问题的 2 个解决方案:

    1. 您继续使用局部变量并分两步编写有问题的指令:

      mov eax, [ebp-4]
      mov [esi+eax*4], edi
      inc [ebp-4]
      
    2. 您根本不使用局部变量,并将索引保​​存在寄存器中:

      mov [esi+eax*4], edi
      inc eax
      

    需要考虑的错误:

    INC EBX
    

    这个inc会给你1次迭代太多!


    假设您想用比元素索引大 1 的递增值填充数组(a[0]=1, a[1]=2, a[2]=3,... ) 您可以通过预先增加索引并通过从地址中减去 4 来补偿此操作来编写更好的例程:

    MyProc PROC
    PUSH EBP
    MOV  EBP, ESP
    PUSH ESI
    
    xor  eax, eax         ;EAX will be the counter (or the index.)
    mov  esi, [ebp + 12]  ;move the offset of the array into ESI
    @@:
    inc  eax              ;increment the counter and the value to be stored.
    mov  [esi + eax * 4 - 4], eax
    cmp  eax, [ebp + 8]   ;Compare index to size of the array
    jb   @B
    
    POP ESI
    MOV ESP, EBP
    POP EBP
    RET
    MyProc ENDP
    

    使用的寄存器越少也意味着要保留的寄存器越少!

    【讨论】:

    • “变量”是一个高级概念。如果您将循环计数器保存在寄存器中并且不需要任何堆栈空间来溢出它,您仍然可以将循环计数器视为变量。
    【解决方案3】:

    您使此过程过于复杂。您需要做的就是:

     push  ebp
     mov   ebp, esp
    
     xor   eax, eax            ; Fill buffer with nulls
     mov   ecx, [ebp+8]        ; Number of dwords to fill
     push  edi
     mov   edi, [ebp+12]
     rep   stosd
     pop   edi
    
     leave
     ret   8                    ; Pop arguments passed by caller
    

    大多数 ABI 认为 EAX、ECX 和 EDX 是易变的,但如果您需要保留它们,请务必使用。

    【讨论】: