这个反汇编代码发生了什么,它在C中会是什么样子?

时间:2015-11-05 04:26:18

标签: c x86 disassembly ida

我已经拆解了这个c代码(使用ida),然后遇到了这段代码。我相信第二行是一个数组,以及第五行,但我不确定为什么它使用符号扩展或零扩展。

我需要将代码转换为C,并且我不确定为什么使用符号/零扩展,或者C代码会导致什么。

mov     ecx, [ebp+var_58]
mov     dl, byte ptr [ebp+ecx*2+var_28]
mov     [ebp+var_59], dl
mov     eax, [ebp+var_58]
movsx   ecx, [ebp+eax*2+var_20]
movzx   edx, [ebp+var_59]
or      edx, ecx
mov     [ebp+var_59], dl

1 个答案:

答案 0 :(得分:2)

unsigned整数类型将进行零扩展,而签名类型将进行符号扩展。

我有点想将此事视为过于微不足道。这并不像指令参考手册没有涉及的任何内容。我想这与要求解释一个非常简单的C程序有所不同,因为这里的技巧是理解为什么人们可能将这一系列指令串在一起,而不是每个人单独做什么。熟悉非优化编译器使用的习惯用法(在每个语句之后从RAM存储和重新加载)会有所帮助。

我猜这是一个来自制作堆栈帧的函数内部的片段,因此ebp的正偏移是当局部变量不在寄存器中时溢出的地方。

mov     ecx, [ebp+var_58]     ; load var58 into ecx
mov     dl, byte ptr [ebp+ecx*2+var_28]   ; load a byte from var28[2*var58]
mov     [ebp+var_59], dl      ; store it to var59
mov     eax, [ebp+var_58]     ; load var58 again for some reason?  can var59 alias var58?
;  otherwise we still have the value in ecx, right?
;  Or is this non-optimizing compiler output that's really annoying to read?
movsx   ecx, [ebp+eax*2+var_20]   ; load var20[var58*2]
movzx   edx, [ebp+var_59]         ; load var59 again
or      edx, ecx                  ; edx = var59|var20[var58*2]
mov     [ebp+var_59], dl          ; spill var59 back to memory

我想movsx / movzx的默认操作数大小是字节到双字。 word-to-dword也存在,我很惊讶你的反汇编程序没有消除内存操作数上byte ptr的歧义。我推断它是一个字节加载,因为该地址的前一个存储是字节宽的。

在加载小于32b的签名数据时使用movsx。 C的整数提升规则规定小于int的整数类型的操作会自动提升为int(如果unsigned int不能代表所有值,则为int。例如{ {1}}和unsigned short大小相同。)

8位或32位操作数大小可用,没有操作数大小前缀字节。有些只有Intel P6 / SnB系列CPU跟踪部分寄存器的依赖关系,在加载时符号扩展到完整的寄存器宽度可以实现更快的代码(避免对AMD和Silvermont上寄存器的先前内容的错误依赖)。因此,对负载进行符号或零扩展(适用于数据类型)通常是处理窄内存位置的最佳方法。

查看非优化编译器的输出通常不值得打扰。

如果代码是由适当的优化编译器生成的,那么它可能更像是

unsigned int

更容易阅读,IMO,没有不断存储/重新加载的噪音。您可以查看何时多次使用某个值,而不必注意到加载来自刚刚存储到的地址。

如果对eax的高24位的错误依赖导致问题,我们可以将mov ecx, [ebp+var_58] ; var58 is live in ecx mov al, byte ptr [ebp+ecx*2+var_28] ; var59 = var28[2*var58] or al, [ebp+ecx*2+var_20] ; var59 |= var20[var58*2] mov [ebp+var_59], al ; spill var59 to memory movzx加载到两个寄存器中,并像原始版本一样执行movsx,但是仍然存储低8位。(使用32位或内存操作数将执行4B加载,而不是1B加载,这可能会跨越缓存行甚至页面和段错误。)