本文为原创,汇编写吐了,点个赞吧
众所周知,C++调用函数的实质就是将参数从右往左一个个push,接着Call,返回值存储在eax中,这样的一个汇编实现过程。
知道原理就很简单了,由于我们需要动态传参,所以我们需要建一个结构体以方便我们读取参数
#include <Windows.h>
struct MyArg {
size_t size; // 数据长度
void* bytes; // 开头指针
DWORD32 ToPtr; // 是否传递指针
};
我定义了一个名为MyArg的结构体,它是怎么工作的?(在内联汇编中,我是使用结构体指针+N的形式以读取各个数据的)
- size (Ptr+0):保留参数,由于在我的实际开发中没有用到,我才意识到这其实是一个无用的参数,但是内存地址已经确定,不方便修改,所以没有删除。
- bytes(Ptr+4):数据指针参数,它是这样使用的
就像这样,他不一定一定是一个字符串的指针,他甚至可以是一个int变量的指针const char a[] = "Hello"; bytes = &a; // 获取a[0]的指针存储到bytes中
- ToPtr(Ptr+8):这其实是一个Bool变量。当它为1时,他将会传递bytes变量的值;当他为0时,它会传递*byte的值
至此,参数结构体已经处理完毕,接下来就是如何调用函数了
bool TyPfunBegin(FARPROC pFun, MyArg* args, DWORD32 len, void* pret) {
if (IsBadCodePtr(pFun)) {
return false;
}
DWORD32 NOWARG = (DWORD32)args + (len>0?len-1 : 0) * sizeof(MyArg);
DWORD32 MAXARG = (DWORD32)args - (len>0? 1 : 0) * sizeof(MyArg);
DWORD32 SIZEARG = sizeof(MyArg);
DWORD32 DW32PRET = (DWORD32)pret;
__asm {
mov eax, MAXARG;
mov ebx, NOWARG;
table_forbegin:
cmp eax, ebx;
je table_beginbegin;
cmp [ebx + 8], 0;
jne table_truetop;
mov ecx, dword ptr[ebx + 4];
mov ecx, dword ptr[ecx]
push ecx;
jmp table_nortop;
table_truetop:
mov ecx, dword ptr[ebx + 4];
push ecx;
table_nortop:
sub ebx, SIZEARG;
jmp table_forbegin;
table_ret:
mov ecx, DW32PRET;
mov dword ptr[ecx], eax;
jmp table_end;
table_beginbegin:
call pFun;
cmp DW32PRET, 0;
jne table_ret;
}
table_end:
return true;
}
我写出了一个函数,他可以调用其他任意一个函数,无论参数数量(似乎参数数量只能在8及以下?我并没有测试过)
- pFun 函数地址,比如这样
void add(int a, int b) { std::cout << a + b << endl; } pFun = &add;
-
args 参数列表,比如MyArg a[25]; args = &a;
-
len 参数数量
-
pret 返回值接收位置,可为NULL(也就是不获取返回值),这会把返回值存储到*pret
代码原理非常简单,就是使用汇编进行遍历MyArgs,然后读取数据,push,最后call,写入返回值 ,汇编不方便解释,自行查看