【问题标题】:Unclear usage of ESIESI的使用不清楚
【发布时间】:2016-04-28 12:28:36
【问题描述】:

我目前正在研究汇编和反汇编一些代码(x86),并尝试从中重新创建 C 代码。我至少知道寄存器和编程的基础知识。但是在以下情况下我无法理解 ESI 的用法:

mov esi, [esp+10h]
lea esi, [esi+0]

我不确定在这种情况下esp+10h 是什么,但我认为这并不重要,因为它应该只是一些随机的 32 位值,对吧?

据我所知,mov esi, [esp+10h] 将从地址esp+10h 复制数据(实际值)并将其存储在 ESI 中。所以esi 现在应该是某种数据。但随后lea esi, [esi+0] 出现并将esi+0(即数据)解释为地址并将其写入esi

在我读到的stackoverflow 上的另一个线程中,lea esi, [esi+0] 实际上是nop(What is the meaning of lea 0x0(%esi),%esi)。这是有道理的,因为它不应该修改地址。但是当esi 是使用数据时,这就令人困惑了。所以我的 C 代码看起来像这样:

int esi = *(esp + 0x10); //move data from esp+10h to esi
int* esi = &esi;         //write the address of esi+0 to esi which is
                         //actually an int and not int*

所以我的问题是,寄存器级别的数据和地址之间是否有任何区别,或者它是否只是因为看起来相同(32 位寄存器)而被解释为原样。我的另一个问题是这段代码现在实际上做了什么,最后 ESI 是什么? mov 是否总是复制数据? lea 总是复制地址不重要吗?

您现在可能已经猜到了,我很困惑,也许我想得太远了。请怜悯我:D

【问题讨论】:

  • mov esi, [esp+10h] 获取(假定)函数的第四个(DWORD/4 字节)参数并将其复制到ESIlea esi, [esi+0] 确实是 NOP,类似于 mov esi, esi
  • @zx485 :您假设堆栈在返回地址之后没有放置任何东西。也许代码有序言/尾声代码?问题中实际上没有足够的信息来说明esp+10h 的内容,除非 OP 用他们正在查看的其余代码更新了问题。
  • 第二条语句应为esi = esi + 0;。第一个有点不对,不是esp + 0x10而是esp + 0x10 / sizeof(*esp);也就是说,汇编程序的位移以字节/字符为单位,但在 C 中,位移/索引以元素的数量为单位。 lea 的含义是,如果给mov 操作码提供相同类型的操作数,它可以用来计算将使用的地址。
  • 这只是一个猜测,但通常像 lea esi, [esi+0] 这样的 nop 表示编译器可能会出于性能目的尝试将一段代码与边界对齐。通常,您会在循环顶部之前看到什么都不做指令,以将其对齐到特定边界(通常为 16 字节)。如果您向我们展示了您正在转换的所有代码,我们将能够更准确地确定代码实际在做什么。
  • 对于 CPU,在 ESI 中具有地址或值没有区别,两者都是整数。对于lea,需要注意的是,虽然指令本身是“加载有效地址”的缩写,但它实际上只是计算地址,而不是获取实际值。这就是为什么可以将其用作 NOP 的原因。如果它达到实际内存值,您将面临因非法地址访问而崩溃的风险(因为 ESI 的值未确定并且可能指向无效的内存区域)。

标签: assembly x86


【解决方案1】:

CPU 不像高级语言那样具有数据类型。对 CPU 来说,所有数据都只是位;将这些位解释为整数/位掩码/地址/浮点数只存在于程序员的脑海中。程序员有责任知道“这个 32 位值表示字符数组的地址,所以我应该只用它做在那个上下文中有意义的事情”。但如果你做一些不太明智的事情,比如转移地址,机器不会阻止你。

第一条指令获取esp 寄存器中的值(通常 包含堆栈顶部的地址),添加10h,从该地址获取一个 32 位值, 并将其加载到esi 寄存器中。这个地址的值可能是一个局部变量或函数参数,它被加载到esi 以准备进一步的计算。

第二条指令取esi寄存器中的值,加0,然后将那个值加载到esi中。 (请注意,尽管指令的“间接”外观,但它不会从内存中获取任何内容;这是 lea 指令的一个奇怪之处,它本质上计算了一个地址,但随后不取消引用它。你可以想到它有点像 C 的一元 &。)正如你所说,这是一个无操作;是的,esi 中有数据,但它自己被“替换”了。

请注意,机器中只有一个 esi 寄存器,因此将其声明为两个不同变量的代码具有误导性。将esi 视为一个全局变量。

由于 C 确实有数据类型,要在 C 中获得类似的效果,您必须在所有地方进行类型转换。我会这样表达代码:

int esi, esp;

esi = *(int *)(esp + 0x10);
esi = (int)&*(int *)esi; // does nothing

(你必须假设你可以将整数转换为指针并返回而不改变它们的值。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-17
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 2018-02-24
    • 2015-12-13
    • 1970-01-01
    • 2013-07-16
    相关资源
    最近更新 更多