我在 ollydbg 中找到了这几行汇编:
MOV ECX,DWORD PTR DS:[xxxxxxxx] ; xxxxxxxx is an address
MOV EDX,DWORD PTR DS:[ECX]
MOV EAX,DWORD PTR DS:[EDX+116]
CALL EAX
有人可以过来告诉我这里发生了什么吗?
这是对存储在结构中的函数指针的调用。
第一行获取存储在 address 的指针DS:xxxxxxxx
。方括号表示地址的取消引用,就像*
在 C 中一样。来自内存的值即将用作指针;它被放入ecx
寄存器。
MOV ECX,DWORD PTR DS:[xxxxxxxx] ; xxxxxxxx is an address
第二行取消引用上面获得的指针。该值ecx
现在用作地址,该地址已被取消引用。在内存中找到的值是另一个指针。第二个指针被放入edx
寄存器中。
MOV EDX,DWORD PTR DS:[ECX]
第三行再次取消引用内存;这一次,访问发生在与上面获得的指针相距0x116字节的地址偏移处。这不能被 4 整除,所以这个函数指针似乎不是来自 C++ vtable。这次从内存中获得的值存储在寄存器中eax
。
MOV EAX,DWORD PTR DS:[EDX+116]
最后,执行指向的函数eax
。这只是通过函数指针调用函数。该函数似乎接受零参数,但我有一个关于修改我的答案的问题:PUSH
这个片段之前是否有指令?这些将是函数参数。问号表示这个函数可能会返回一个值,从我们的角度来看我们无法判断。
CALL EAX
总的来说,代码片段看起来像是从插件库调用 OllyDbg 的扩展函数。OllyDbg ABI 指定了struct
包含一些函数指针的各种 s。还有函数指针数组,但双间接到达edx
-held 指针(也是未对齐的偶数倍偏移量)让我认为这是一个struct
而不是函数指针数组或 C++类的虚表。
换句话说,xxxxxxxx
是一个指向一个struct
包含函数指针的指针。
在 OllyDbg 源文件 PlugIn.h 中有一些候选struct
定义。这是一个例子:
typedef struct t_sorted { // Descriptor of sorted table
char name[MAX_PATH]; // Name of table, as appears in error
int n; // Actual number of entries
int nmax; // Maximal number of entries
int selected; // Index of selected entry or -1
ulong seladdr; // Base address of selected entry
int itemsize; // Size of single entry
ulong version; // Unique version of table
void *data; // Entries, sorted by address
SORTFUNC *sortfunc; // Function which sorts data or NULL
DESTFUNC *destfunc; // Destructor function or NULL
int sort; // Sorting criterium (column)
int sorted; // Whether indexes are sorted
int *index; // Indexes, sorted by criterium
int suppresserr; // Suppress multiple overflow errors
} t_sorted;
这些示例是允许的NULL
,并且您的 asm 代码段不会检查NULL
函数指针中的指针。因此,它必须DRAWFUNC
来自t_table
或SPECFUNC
。t_dump
您可以创建一个包含头文件并使用的小项目,printf()
并offsetof()
确定其中任何一个的偏移量是否为 0x116。
否则,我想 OllyDbg 的内部都是用同样的风格编写的。因此,struct
在 OllyDbg 中可能有用于各种目的的私有定义(未在 Plugin.h 文件中发布)。
我想补充一下,我认为 OllyDbg 资源不可用是一种耻辱。我的印象是它包含的静态链接反汇编程序是在某种 ?GPL 许可下,但我没有任何运气将源代码提供给 OllyDbg。
从地址 xxxxxxx 中取出 32 位数字,放入 ECX 寄存器,然后将此值作为地址,读取该值并放入 EDX 寄存器,最后将该数字加 116,将该地址的值读入 EAX。然后它开始在现在保存在 EAX 中的地址处执行代码。当该代码遇到返回操作码时,将在调用指令之后继续执行。
这是非常基本的组装。这让我想知道您正在使用调试器做什么以及您的任务何时到期;-)
自从我做 ASM(1997 年)以来已经有一段时间了,即使那时我也只做 i386 ASM,所以如果我的回答不是那么有帮助,请原谅我......
不幸的是,这 4 行代码并没有告诉我太多。它主要只是将东西加载到 CPU 寄存器中并调用一个函数。
具体来说,它看起来像是数据或指针正在从该地址加载到您的 CX 寄存器中。然后将该值从 CX 复制到 DX。所以你有位于 DX 中的 CX 指针的值。然后 DX 中的值加上 116 的偏移量被复制到 AX 寄存器(你的累加器?)
然后将执行位于复制到 AX 中的该地址处的任何函数。
考虑到有关编译器是 MSVC 的评论,我 99% 确定这是一个虚拟方法调用。
MOV ECX,DWORD PTR DS:[xxxxxxxx]
指向类实例的指针从全局变量加载到 ECX 中。(注意:默认的 __thiscall 调用约定使用 ECX 传递实例指针,也就是this指针)。
MOV EDX,DWORD PTR DS:[ECX]
vftable(虚函数表)指针通常是类布局中的第一项。这里指针被加载到 EDX 中。
MOV EAX,DWORD PTR DS:[EDX+116]
将表中偏移量 116 (0x74) 处的方法指针加载到 EAX 中。由于每个指针是 4 个字节,这是该类的第 30 个虚拟方法 (116/4 + 1)。
CALL EAX
该方法被调用。
在原始 C++ 中,它看起来像这样:
g_pObject1->method30();
要了解更多关于 MSVC 对 C++ 类的实现,包括虚方法,请参阅我的文章。