如何通过反汇编从C++函数获取"lea"指令?

How can I get the "lea" instruction from a C++ function by disassembly?

本文关键字:lea 指令 获取 函数 何通过 反汇编 C++      更新时间:2023-10-16

我正在努力学习逆向工程,但我被这件小事卡住了。我有这样的代码:

.text:10003478                 mov     eax, HWHandle
.text:1000347D                 lea     ecx, [eax+1829B8h] <------
.text:10003483                 mov     dword_1000FA64, ecx
.text:10003489                 lea     esi, [eax+166A98h]<------
.text:1000348F                 lea     edx, [eax+11FE320h]
.text:10003495                 mov     dword_1000FCA0, esi

我想知道,它在C或C++中是什么样子的?尤其是用箭头标记的两条说明。CCD_ 1是保存从CCD_ 2函数返回的a值的变量。更有趣的是,在这个指令下面的几行,dword_1000FCA0被用作一个函数:

.text:1000353C                 mov     eax, dword_1000FCA0
.text:10003541                 mov     ecx, [eax+0A0h]
.text:10003547                 push    offset asc_1000C9E4 ; "rn========================rn"
.text:1000354C                 call    ecx

这将在我的游戏机中绘制此文本。伙计们,你们有什么想法吗?

LEA只不过是一个算术运算:在这种情况下,ECX只填充EAX+偏移量(正是地址,而不是指向的内容)。如果HWHandle指向一个(非常大的)结构,ECX将只是其成员之一。

这可能是一个相关的源代码:

extern A* HWHandle;                 // mov     eax, HWHandle
B* ECX = HWHandle->someStructure;   // lea     ecx, [eax+1829B8h]

然后B的一个成员被用作函数。

*(ECX->ptrFunction(someArg))        // mov     ecx, [eax+0A0h]
                                    // call    ecx

由于HWHandle是一个模块句柄,它只是DLL的基地址,因此添加到其中的常量似乎是DLL内函数或静态数据的偏移量。代码计算这些函数或数据项的地址,并将它们存储起来以备将来使用。

由于这通常是动态链接器的工作,我不确定这个程序集代码是否对应于实际的C++代码。知道你在什么环境下工作会很有帮助——既然你指的是游戏机,这是Xbox代码吗?不幸的是,我不知道动态链接在Xbox上到底是如何工作的,但看起来这可能就是这里发生的事情。

dword_1000FCA0的特定情况下,看起来好像这是DLL中跳转表(即,本质上是函数指针列表)的位置。您的第二个代码片段是从该表内的偏移量0xA获取一个函数指针,然后调用它——显然,被调用的函数会向屏幕输出字符串。(指向要输出的字符串的指针被推送到堆栈,这是通常的x86调用约定。)与此相对应的C++代码类似于

my_print_function("rn========================rn");

编辑:

如果您想自己调用DLL中的函数,获取函数指针的规范方法是使用GetProcAddress():

FARPROC func=GetProcAddress(HWHandle, "MyFunction");

然而,你发布的代码本身就是在计算偏移量,如果你真的想这样做,你可以使用这样的东西:

DWORD func=(DWORD)HWHandle + myOffset;

myOffset是你想要使用的偏移量——当然,你需要有一些方法来确定这个偏移量,每次重新编译DLL时,这个偏移量都会改变,所以我不建议使用这种技术——但这毕竟是你想要的。

不管你用这两种方法中的哪一种来获取函数的地址,你都需要调用它。要做到这一点,你需要声明一个函数指针——要做到这,你需要知道函数的签名(它的参数和返回类型)。例如:

typedef void (*print_func_type)(const char *);
print_func_type my_func_pointer=(print_func_type)func;
my_func_pointer("rn========================rn");

小心——如果函数的地址或签名错误,代码很可能会崩溃。所有这些都是这种低级工作的乐趣所在。

看起来HWHandle是某个结构(一个大结构)的顶点。lea指令是从该结构中读取地址,例如:

mov eax, HWHandle
lea ecx, [eax+1829B8h]
mov dword_1000FA64, ecx

表示:

  1. HWHandle0读取地址并将其放入ecx
  2. 将该地址(来自ecx)放入某个(全局)变量dword_1000FA64

其余的看起来更相似。

在C++中,你几乎可以在任何地方获得它,但你真的无法预测它在哪里(取决于编译器和优化),例如:

int x;
int* pX = &X;

第二行可以生成CCD_ 14。

另一个例子:

struct s
{
   int x;
   int y;
};
my_s s;
int Y = s.y; //here: probably lea <something> , [address(my_s) + 0x4]

希望能有所帮助。

在C++中,这大致相当于

char* ecx, eax, esi;
ecx = eax+0x1829B8   // lea ecx, [eax+1829B8h]
esi = eax+0x166A98   // lea esi, [eax+166A98h]

假设eax、esi和ecx实际上持有指向内存位置的指针。当然,lea指令也可以用于简单的算术,事实上,它经常被编译器用于加法。与简单的add相比的优点是:它最多可以有三个输入操作数和一个不同的目的地。

例如,foo = &bar->baz与(简化的)foo = (char *)bar + offsetof(typeof(*bar), baz)相同,后者可以转换为lea foo, [bar+offsetofbaz]

它确实依赖于编译器和优化,但如果IIRC,lea可以仅用于添加。。。。因此GetModuleHandle()0可以理解为ecx = eax + 0x1829B8