__dest = (byte *)(**(code **)(*plVar5 + 0x10))(plVar5,(ulonglong)(numBytes + 1));
memcpy(__dest,param_2,numBytes + 1);
有人可以解释一下第一行的步骤吗?我知道它正在为 memcpy 函数准备一个字节数组,但我对其余部分感到困惑,尤其是“代码”类型。那是操作码还是什么?
__dest = (byte *)(**(code **)(*plVar5 + 0x10))(plVar5,(ulonglong)(numBytes + 1));
memcpy(__dest,param_2,numBytes + 1);
有人可以解释一下第一行的步骤吗?我知道它正在为 memcpy 函数准备一个字节数组,但我对其余部分感到困惑,尤其是“代码”类型。那是操作码还是什么?
code 确实意味着某些东西被解释为要执行的代码(很可能是一个函数)
但是,除了执行某些内容之外,还可以从该代码段中恢复更多内容:
(**(code **)(*plVar5 + 0x10))这很可能是 C++ vtable 调用。
plVar5应该是一些包含 C++ 对象的变量,或者更确切地说是一个应该被解释为 C++ 对象的指针。在偏移量 0(刚刚写入*plVar5)处是指向对象 vtable 的指针。vtable 中偏移量 0x10 处的元素是某个函数。如果这是第 3 个 (vtable[2]) 或第 5 个 (vtable[4]) 取决于指针大小,但让我们假设这是 64 位以进行解释)。这意味着这是第三个条目,它通常是 vtable[0] 和 vtable[1] 处的构造函数和析构函数之后的第一个真正的 vtable 函数。让我们调用这个函数prepare_buffer。
所以更容易理解的翻译是
plVar5->vtable->prepare_buffer
(byte *)plVar5->vtable->prepare_buffer(plVar5,(ulonglong)(numBytes + 1))将我们之前的结果代入后 (byte *)(**(code **)(*plVar5 + 0x10))(plVar5,(ulonglong)(numBytes + 1))
因为这是对象的 C++ 函数,所以第一个参数self是非静态函数必须存在的参数。所以唯一正确的论点是(ulonglong)(numBytes + 1)。结果被分配给一个类型的变量 byte *并进行转换。
memcpy(__dest,param_2,numBytes + 1);这确实只是对前一个函数返回的缓冲区的 memcopy。因为前一个函数调用的唯一真正参数是复制的字节数,所以我假设它正在设置这个缓冲区(并调用它prepare_buffer)。如果你能找到变量的类,plVar5你可以找到这个类的vtable,然后找到这里调用的实际函数来确认这一点。
如果您想了解更多相关信息,我建议https://alschwalm.com/blog/static/2016/12/17/reversing-c-virtual-functions/(我略读以解释这一点)并查看 Ghidra Advanced https://ghidra.re/online-courses/上的课程,其中包括“虚拟函数表”一章,其中涵盖了针对此类情况获取正确反编译器输出的 Ghidra 细节。
我相当确定:
(**(code **)(*plVar5 + 0x10))
是一个函数指针。因此,该行看起来更像:
__dest = (byte*) somefunction(plVar5,(ulonglong)(numBytes + 1));
使用所有这些类型转换和指针算法,反编译有时会变得有点麻烦。如果有很多代码使用plVar5加上一些偏移量,您可能会使用上下文线索定义一个自定义数据类型,使其更易于阅读。