如何通过反汇编从C++函数获取"lea"指令?
How can I get the "lea" instruction from a C++ function by disassembly?
我正在努力学习逆向工程,但我被这件小事卡住了。我有这样的代码:
.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
表示:
- 从
HWHandle
0读取地址并将其放入ecx
- 将该地址(来自
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
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 无法编译 rtmidi 测试 cmidiin.cpp 文件, 非法指令
- C++:对不存在的命名空间使用命名空间指令
- 函数名是c中该函数的第一条指令的地址吗
- 错误:无效的预处理指令 #i 的意思是 #if?
- 组装指令中乘法的下部和上部是什么
- OpenMP 与有序和关键指令并行
- C++中的移动分配出现问题.非法指令: 4.
- 嵌套命名空间的"using"指令,但需要命名内部命名空间
- C++CMake编译指令与
- 使用宏扩展的泛型:为什么指令缓存使用不当?
- 如何在 c++ 中确定一条指令(以字节为单位)在哪里结束,另一条指令从哪里开始?
- AVX 指令中寄存器和指针之间的客观差异
- while 循环 c++ 中的非法指令
- 如何在编译时定义C++预处理器指令的值?
- 存储指令是否会阻止缓存未命中的后续指令?
- 保证编译器指令在C++中重新排序
- VS2008中的AVX-512指令库
- LEA指令"=> 0xb48daed9 <+3479>: lea -0xc(%ebp),%esp"的意义是什么?
- 如何通过反汇编从C++函数获取"lea"指令?