1

我正在通过将简单的 C 代码转换为汇编代码来练习我在汇编中学到的东西。

这是C中的代码

int square_me (int val)

{

return (val* val)

}

这是我的代码转换为程序集(我声明了 val 并将其初始化为 4)

    val dw 4    ; declaration and initialization of the val variable (in main)

    push val    ; push val onto the stack so that you still have a copy of the original value of val incase i'll be needing it in some methods or functions (in main)

    call square_me  ; calling the function square_me, (in main)

    push EIP   ; pushing the value of EIP onto the stack so the code knows where to go back after the function

    push EBP    ; creating the stack frame for the function

    mov EBP, ESP    ; same with the one above

    push val     ; save the value of val so that ill have a copy of the original value of val in case I made some changes to it

    mul val, val   ; multiply to val to itself, and save it to val

    mov eax, val  ; move the value of val to eax

    pop val    ; pop the original value of val from the stack

    mov ESP, EBP   ; to restore the stack frame

    pop EBP    ; same with the one above

    leave     

    ret     ; return to the caller

但是当我看文档中写的答案时,它与我的相差甚远,这是他如何将其转换为汇编的

Push EBP

mov EBP, ESP

mov EAX, DWORD PTR [EBP + 8]

XOR EDX, EDX

mov EBX, EAX

MUL EBX

MOV ESP, EBP

POP EBP

Ret

问题 1:我是否正确地将上面看到的 C 代码转换为汇编?

问题2:这是为了什么

mov EAX, DWORD PTR [EBP + 8]

问题3:他为什么需要这样做?在该声明之后没有使用 EDX,那有什么意义呢?

异或 EDX,E

任何的想法?

谢谢!

4

2 回答 2

3

您的代码有两个部分刚刚在您的示例中被卡在一起。希望这只是一个格式错误。否则代码没有意义。

首先,调用函数的部分。假设这将是某个较大程序的片段,该程序对结果执行某些操作并最终退出。

val dw 4
push val
call square_me
push EIP

这里的问题是:

  1. push EIP不是一个有效的指令。你不能推送 EIP 寄存器,即使你可以,你为什么要这么做?
  2. 对于这种调用格式,清理堆栈是调用者的责任,因此您需要在该调用之后使用诸如add ESP,4清理您压入堆栈的val之类的东西。

然后对于函数本身:

push EBP
mov EBP, ESP

那一点很好 - 你正在设置堆栈框架。但是下一行的目的是什么?

push val

首先,您访问的是全局变量val而不是传递给函数的参数。如果你想通过名称访问参数,我相信你可以将它设置为PROCMASM 中指令的一部分,否则你需要使用它[ebp+8]来获取堆栈上的第一个参数。

但是,无论您如何访问它,都无需在此处将值保存在堆栈中。它已经在记忆中——你不会失去它。

至于乘法,这不是一个有效的指令:

mul val, val

您不能将两个内存引用相乘 - 您只能将某个值与 EAX 寄存器相乘。因此,您需要先将值加载到 eax 中。像这样的东西:

mov EAX,[EBP+8]
mul EAX,EAX

此时结果将在 EAX(和 EDX - 但我们不关心高位字)中,这是您希望函数返回答案的结果。但是你正在这样做:

mov eax, val

这只会覆盖您刚刚计算的乘法结果。

pop val

假定此流行音乐与初始的 一起使用push val,并且同样可以被删除。

mov ESP, EBP
pop EBP

那些很好 - 只是清理堆栈框架。但是你不需要这个:

leave

leave指令与mov esp,ebp / pop ebp. 你不需要他们两个。

简而言之,您的函数应该看起来更像这样:

push EBP
mov EBP, ESP
mov EAX, [EBP + 8]
mul EAX, EAX
mov ESP, EBP
pop EBP
ret

至于文档中给出的答案,也不是很好。没有理由将 EDX 设置为零。正如 ady 已经说过的那样,该mul指令无论如何都会覆盖 EDX。

我可以理解可能在mul指令之后将其设置为零,因为鉴于您在有符号整数上使用无符号乘法,结果在技术上是不正确的。没关系,因为无论如何您都将丢弃高位词(EDX)。在我看来,使用它会更清楚imul

我也不明白为什么他们觉得有必要将参数移动到 EBX 和 EAX 中,而他们本可以将 EAX 与自身相乘。也许这样做有一些性能原因,但我对此表示怀疑。

关于使用[EBP+8]访问函数参数,您需要了解您的堆栈在函数内部的样子。

调用函数时,首先推送val,然后调用将其返回地址推送到堆栈上,然后在函数内部推送 EBP。因此,那时,您的堆栈将如下所示:

           val             // push val
           return address  // call square_me 
STACK TOP: EBP             // push ebp

现在,当您这样做时,mov EBP,ESP您将设置 EBP 指向堆栈的顶部。因此,在[EBP+0]您有 EBP 的保存副本,在[EBP+4]您有调用者的返回地址,在[EBP+8]您有在调用中传递的val参数。

于 2013-05-20T12:03:45.110 回答
0

异或 EDX,EDX

为什么他需要这样做?在该声明之后没有使用 EDX,那有什么意义呢?


它被清零

在更大的程序中 edx 可能 >0 并且可能有有价值的数据

如果数字足够大,mul 无论如何都会覆盖它,但是通过输入“xor dx,dx”,程序员向您发送了一个烟雾信号,dx 在此例程中有时会吐司,因此您需要先推送或存储 dx

==============

mov EAX, DWORD PTR [EBP + 8]

MOV EAX, DWORD PTR SS:[EBP+8h] 是什么意思,如何翻译成 AT&T 格式?

在这种情况下,OllyDbg 和 Assembler 中的 EBP+8 是什么意思?

于 2013-05-20T10:05:59.947 回答