编译器在设置调用堆栈时生成了意外的“IN AL,DX”(操作码`EC`)

时间:2017-02-17 21:58:49

标签: assembly x86

我正在查看一些编译器输出,当调用一个函数时,它通常会开始设置调用堆栈,如下所示:

private func randomProduct() -> String { // Return String, not Int
    let myArray1 = ["Nintendo Classic Mini NES", "Microsoft Office 2016 Home and Business", "Rubie's Deluxe Muscle Chest Batman Child", "Giro Dime", "Varta Powersports AGM 12V 12Ah 512014010A514", "Pohl-Boskamp Gelomyrtol Forte", "Panasonic ES-LV9N-S803", "Traxxas X-Maxx (77076-4)", "GoPro HERO 4", "Bose Lifestyle 650", "WMF Function 4 Kochtopf-Set 4-teilig", "Microsoft Surface Book", "Hewlett-Packard HP Color LaserJet Pro M252dw (B4A22A)", "Apple iPhone 7", "X-lite X-403GT Elegance", "Denon AVR-X3300W", "Hasbro Spiel des Lebens Banking", "Avent SCD 630/26", "Ray-Ban Aviator Metal RB3025"]
    return myArray1[Int(arc4random_uniform(UInt32(myArray1.count)))] // Remove the Int()
}

因此,我们将调用例程的基指针保存在堆栈中,向上移动我们自己的基指针,然后将几个寄存器的内容存储在堆栈中。然后在例程结束时将它们恢复为原始值,如下所示:

PUSH EBP
MOV EBP, ESP
PUSH EDI
PUSH ESI
PUSH EBX

到目前为止,这么好。但是,我注意到在一个例程中,设置调用堆栈的代码看起来有点不同。事实上,它看起来像这样:

LEA ESP, [EBP-0Ch]
POP EBX
POP ESI
POP EDI
POP EBP
RET

由于多种原因,这令人困惑。首先,方法结束代码与上面引用的另一种方法相同,特别是期望在堆栈上可以获得保存的EBP副本。

另一方面,如果我理解正确,IN AL, DX PUSH EDI PUSH ESI PUSH EBX 命令会读入IN AL, DX寄存器,该寄存器与AL寄存器相同,因此下一个命令就在这里是

EAX

因为程序想要将它在堆栈上分配的一些东西归零。

问题:我想知道我在这里发生了什么,我不明白。被翻译为XOR EAX, EAX 的机器代码是单字节IN AL, DX,而是一对指令     推动EBP     MOV EBP,ESP 将对应于三个字节EC。反汇编程序是否以某种方式误读了这个?或者是依赖于我不理解的副作用的东西?

如果有人好奇,这个机器代码是由CLR的JIT编译器生成的,我用Visual Studio调试器查看它。这是C#中的最小复制:

55 88 EC

然而,请注意,这似乎是不确定的;有时我似乎得到class C { string s = ""; public void f(string s) { this.s = s; } } 版本,而有时候IN AL, DX后跟PUSH EBP

编辑:我开始强烈怀疑反汇编程序错误 - 我只是在另一种情况下显示MOV EBP, ESP(操作码IN AL, DX)和两个内存中的前面字节是EC。因此,反汇编程序可能只是对方法的切入点感到困惑。 (虽然我仍然希望了解 发生的原因!)

2 个答案:

答案 0 :(得分:7)

听起来像是在使用VS2015。你的结论是正确的,它的调试引擎有一个很多的错误。是的,地址不对。不是唯一的问题,它不能正确恢复断点,你很容易在代码中看到INT3指令。当抖动重新生成代码并替换存根调用时,它无法正确刷新反汇编。你不能相信你看到的任何东西。

我建议您使用工具>选项>调试>常规并勾选“使用托管兼容模式”复选框。这迫使调试器使用较旧的调试引擎VS2010 vintage。它更稳定。

您将丢失此引擎的某些功能,例如返回值检查和64位编辑+继续。进行这种调试时不会错过。然而,您将看到假代码地址,这在以前一直很常见,因此所有CALL地址都是错误的,您无法轻松识别到CLR的调用。反复翻转引擎是一种解决方法,但当然是一个很大的烦恼。

这也没有奏效,我看到更新中没有任何改进。但毫无疑问,它们有一个很大的错误列表可以解决,VS2015在它完成之前就已经发布了。希望VS2017更好,我们很快就会发现。

答案 1 :(得分:2)

正如 Hans 的回答,这是 Visual Studio 中的一个错误。
为了确认这一点,我使用 IDA 6.5 和 Visual Studio 2019 反汇编了一个二进制文件。这是屏幕截图:
enter image description here

Visual Studio 2019 在考虑 0x55 0x8B 的开头时错过了 2 个字节 (main)。

注意:Hans 提到的“使用托管兼容模式”没有解决 VS2019 中的问题。