什么 C++ 构造可以发出这样的 Watcom C++ 10.5 汇编程序列表?

逆向工程 部件 C++
2021-07-08 01:57:03

根据汇编程序列表,C++ 对象在堆栈上本地分配,而无需在调用例程中构造对象,被调用方将地址带到分配的堆栈空间并调用构造函数。这怎么可能使用 C++?使用的编译器是 1995 年的 Watcom C/C++ 10.5,早在 ISO C++98 出现之前。编译器使用 Watcom 的寄存器调用约定,因此第一个参数在 EAX 中传递,第二个在 EDX 中,返回值在 EAX 中传递回。EBP 用作堆栈帧指针。

调用函数是一个 A 类方法,它在堆栈帧上为 B 类本地分配的对象保留空间。尽管 B 类对象仅驻留在 dword 上,但它是一个复杂的对象,在调用构造函数时会在内部将大量内容分配给堆。它类似于指向智能对象的智能指针。我想强调的任何方式是 B 类远不是一个简单的结构。堆栈帧上的 ObjectA 是this传递给 EAX 中 Caller 方法的类 A 的指针。调用者为被调用者设置参数。Callee 的第二个参数是 ObjectB 堆栈空间的地址。

调用函数:

MethodCaller_ proc near

ObjectB = dword ptr -8
ObjectA = dword ptr -4

                push    32
                call    __CHK
                push    ebx
                push    ecx
                push    esi
                push    edi
                push    ebp
                mov     ebp, esp
                sub     esp, 8
                mov     [ebp+ObjectA], eax
                lea     edx, [ebp+ObjectB]
                mov     eax, [ebp+ObjectA]
                call    MethodCallee_
                ...

被调用函数也是 A 类方法,因此第一个参数是this指针。第二个参数是传递给类 B 的构造函数的内存空间的地址。构造不是基于赋值和复制构造函数,它是对类 B 的默认(隐式?)构造函数的调用,而不为它分配内存正在构造的对象。

调用函数:

MethodCallee_ proc near

ObjectA = dword ptr -8
ObjectB = dword ptr -4

                push    32
                call    __CHK
                push    ebx
                push    ecx
                push    esi
                push    edi
                push    ebp
                mov     ebp, esp
                sub     esp, 8
                mov     [ebp+ObjectA], eax
                mov     [ebp+ObjectB], edx
                mov     eax, [ebp+ObjectB]
                call    ObjectB_Ctor_
                mov     eax, [ebp+ObjectB]
                mov     esp, ebp
                pop     ebp
                pop     edi
                pop     esi
                pop     ecx
                pop     ebx
                retn
MethodCallee_ endp

程序集列表由 IDA 免费软件 7.0 生成。

可以作出以下声明:

  • 运算符 new 不用于将 B 类对象分配到堆栈上。
  • 在被调用函数中不使用放置 new 运算符来省略构造时的分配。那会生成完全不同的代码,并且会为放置新用例发出一个类似 operator new 的虚拟代码。
  • 1995 年没有 std::allocator 并且它也需要放置新的任何方式。
  • 我不认为原始作者只是简单地创建了一个 dword 并将其转换为我认为作为专业人士他们应该知道违反堆栈和对象对齐规则的危险,并且我不认为他们以某些邪恶的方式直接调用构造函数.
  • 我尝试了很多东西来复制 C++ 代码并在 MS-DOS 的 Watcom C/C++ 10.5 编译器中再次构建它以获得相同的反汇编列表或接近原始的反汇编列表,但完全失败了。

该构造在原始程序中的很多地方使用,重新设计代码库将非常困难。

任何新想法都将受到高度欢迎,在此先感谢您的帮助。

1个回答

您描述的模式听起来像是按值返回对象的标准模式。所以你正在寻找

class A {
    B callee()
    {
        return B();
    }
    void caller()
    {
        B b = callee();
    }
}