luajit中,利用ffi可以嵌入C,目前luajit的最新版是2.0.4,在这之前的版本我还不清楚这个扩展库具体怎么样,不过在2.04中,真的很爽。
既然是嵌入C代码,那么要说让lua支持面向对象,不如说是让C语言模拟面向对象编程,然后让luajit的ffi嵌入。
要文字彻底来描述清楚这个问题,我的表达能力很有限,说不清楚,所以直接用代码来说吧。
//C++
class foo_type{
public:
void foo1()
{
printf("%d", a + b);
}
void foo2(int n)
{
printf("%d", a + b + n);
}
int a;
int b;
};
foo_type obj;
obj.foo1();
obj.foo2(100);
//在C语言要做到同样的事,最简单的做法如下。
typedef struct{
int a;
int b;
}foo_type;
void foo1(foo_type *obj)
{
printf("%d", obj->a + obj->y);
}
void foo2(foo_type *obj, int n)
{
printf("%d", obj->a + obj->y + n);
}
foo_type obj;
foo1(&obj);
foo2(&obj, 100);
/*****************************************
//C++从汇编语言的角度看
obj.foo1();
lea ecx, obj
call foo1
-----------------------
obj.foo2(100);
push 100
lea ecx, obj
call foo1
-----------------------
//C语言从汇编语言的角度看
foo1(&obj);
lea eax, obj
push eax
call foo1
-----------------------
foo2(&obj, 100);
push 100
lea eax, obj
push eax
call foo2
那么就可以看到,C和C++在实现这种功能时的区别之处主要在于thiscall使用了ecx寄存器向下传递对象指针
而在C语言,只能把结构指针push下去,这就有点像微软的COM包装类的stdcall的类成员了。
*****************************************/
//从汇编看到问题所在之后,要解决这个问题,也就在可以考虑写一个shellcode,这个shellcode实现将向下传递上层传入的参数,并且直接将结构指针以push参数的方式最后入栈,然后call即可。
typedef struct{
int a;
int b;
}foo_type;
void foo1(foo_type *obj)
{
printf("%d", obj->a + obj->y);
}
void foo2(foo_type *obj, int n)
{
printf("%d", obj->a + obj->y + n);
}
typedef struct{
void (*foo1)();
void (*foo2)(int n);
}foo_class;
foo_type ft;
foo_class obj;
//与foo1对应的shellcode模板
BYTE scT1[] = {
0x68, 0, 0, 0, 0, //push CONST
0xE8, 0, 0, 0, 0, //call CONST
0x83, 0xC4, 0x04, //add esp, 4
0xC3 //ret
};
BYTE *psc = (BYTE*)VirtualAlloc(NULL, sizeof(scT1), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(psc, scT1, sizeof(scT1));
*(ULONG*)(psc + 1) = (ULONG)&ft;
*(ULONG*)(psc + 6) = (ULONG)&foo1 - (ULONG)(psc + 5) - 5;
*(ULONG*)&(obj.foo1) = psc;
//到这里之后,就已经能够这样用了
obj.foo1();
//与foo2对应的shellcode模板
BYTE scT2[] = {
0xFF, 0x74, 0x24, 0x04, //push dword ptr [esp + 4]
0x68, 0, 0, 0, 0, //call CONST
0xE8, 0, 0, 0, 0, //call CONST
0x83, 0xC4, 0x08, //add esp, 8
0xC3 //ret
};
BYTE *psc = (BYTE*)VirtualAlloc(NULL, sizeof(scT2), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(psc, scT2, sizeof(scT2));
*(ULONG*)(psc + 5) = (ULONG)&ft;
*(ULONG*)(psc + 10) = (ULONG)&foo2 - (ULONG)(psc + 5) - 5;
*(ULONG*)&(obj.foo2) = psc;
obj.foo2(100);
//这样就模拟了面向对象形式的调用,之后就需要考虑的就是一些如何整合管理资源之类的问题,还需要将代码尽可能的利用一些宏来简化,这些东西就不在这里说了,这个思路深入下去,相信大家都会知道该怎么做了。
下面是一套我封装的luajit使用ODBC访问数据库的代码,没有注释,将就着看吧。
//luaodbc.h**************************************************
#pragma once
extern "C"{
typedef struct{
void (*close)();
bool (*set)(int i, int type, char *data, int size);
bool (*get)(int i, int type);
void *(*data)();
bool (*next)();
char *(*getstr)(int i);
int (*getint)(int i);
char *(*getstr_fn)(const char *name);
char *(*getint_fn)(const char *name);
bool (*setstr)(int i, const char *str);
bool (*setint)(int i, int num);
bool (*setstr_fn)(const char *name, const char *str);
bool (*setint_fn)(const char *name, int num);
}luastmt_class;
typedef struct{
bool (*open)(const char*dsn);