1 c++ assembly structure visual-studio
我正在尝试获取隐藏在结构后面的函数地址.不幸的是,void*基本的C++转换不起作用,所以我使用了C++ template.
1.基本C++转换不适用于结构内部的函数,为什么?void*
void * lpfunction;
lpfunction = scanf; //OK
lpfunction = MessageBoxA; //OK
Run Code Online (Sandbox Code Playgroud)
我做了一个简单的结构:
struct FOO{
void PRINT(void){printf("bla bla bla");}
void SETA(int){} //nothing you can see
void SETB(int){} //nothing you can see
int GETA(void){} //nothing you can see
int GETB(void){} //nothing you can see
};
///////////////////////////////////////////
void *lpFunction = FOO::PRINT;
Run Code Online (Sandbox Code Playgroud)
编译错误:
error C2440: 'initializing' :
cannot convert from 'void (__thiscall FOO::*)(void)' to 'void *'
Run Code Online (Sandbox Code Playgroud)
2.获取功能成员地址是不可能的?
然后,我创建了一个模板函数,它能够将函数成员转换为地址.然后我将通过汇编调用它.它应该是这样的:
template <class F,void (F::*Function)()>
void * GetFunctionAddress() {
union ADDRESS
{
void (F::*func)();
void * lpdata;
}address_data;
address_data.func = Function;
return address_data.lpdata; //Address found!!!
}
Run Code Online (Sandbox Code Playgroud)
以下是代码:
int main()
{
void * address = GetFunctionAddress<FOO,&FOO::PRINT>();
FOO number;
number.PRINT(); //Template call
void * lpdata = &number;
Run Code Online (Sandbox Code Playgroud)
__asm mov ecx, lpdata //Attach "number" structure address
__asm call address //Call FOO::PRINT with assembly using __thiscall
Run Code Online (Sandbox Code Playgroud)
printf("Done.\n");
system("pause");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,我认为这是非常具体的.它看起来像LOCK - KEY,我必须为每组参数类型创建一个新模板.
原件(OK):
void PRINT(); //void FOO::PRINT();
Run Code Online (Sandbox Code Playgroud)
修改一下:
void PRINT(int); //void FOO::PRINT(int);
Run Code Online (Sandbox Code Playgroud)
编译器立即使用旧模板代码显示:
//void (F::*func)();
//address_data.func = Function;
Run Code Online (Sandbox Code Playgroud)
error C2440: '=' : cannot convert from
'void (__thiscall FOO::*)(int)' to 'void (__thiscall FOO::*)(void)'
Run Code Online (Sandbox Code Playgroud)
为什么?他们只是地址.
69: address_data.func = Function;
00420328 mov dword ptr [ebp-4],offset @ILT+2940(FOO::PRINT) (00401b81)
Run Code Online (Sandbox Code Playgroud)
...
编辑3:我知道更好的解决方案:
void(NUMBER::*address_PRINT)(void) = FOO::PRINT;
int(NUMBER::*address_GETA)(void) = FOO::GETA;
int(NUMBER::*address_GETB)(void) = FOO::GETB;
void(NUMBER::*address_SETA)(int) = FOO::SETA;
void(NUMBER::*address_SETA)(int) = FOO::SETB;
Run Code Online (Sandbox Code Playgroud)
它比模板好得多.顺便说一句,我想实现这个目标:
<special_definition> lpfunction;
lpfunction = FOO::PRINT; //OK
lpfunction = FOO::GETA; //OK
lpfunction = FOO::GETB; //OK
lpfunction = FOO::SETA; //OK
lpfunction = FOO::SETB; //OK
Run Code Online (Sandbox Code Playgroud)
这可能吗?
指向成员函数的指针与指向全局函数或静态成员函数的指针完全不同.这有很多原因,但我不知道你对C++的工作原理有多了解,所以我不确定哪些理由是有意义的.
我知道你在装配中尝试的东西根本不适用于一般情况.看起来你对成员函数和函数指针的目的有一个基本的误解.
问题是,你正在做一些你通常不会在C++中做的事情.您通常不会在C++中构建函数指针表,因为您将使用的那些东西是virtual函数的用途.
如果你决定使用这种方法,我建议你根本不使用C++,只使用C.
要证明这些指针类型完全不兼容,这里有一个程序:
#include <cstdio>
struct Foo {
int a;
int b;
int addThem() { return a + b; }
};
struct Bar {
int c;
int d;
int addThemAll() { return c + d; }
};
struct Qux : public Foo, public Bar {
int e;
int addAllTheThings() { return Foo::addThem() + Bar::addThemAll() + e; }
};
int addThemGlobal(Foo *foo)
{
return foo->a + foo->b;
}
int main()
{
int (Qux::*func)();
func = &Bar::addThemAll;
printf("sizeof(Foo::addThem) == %u\n", sizeof(&Foo::addThem));
printf("sizeof(Bar::addThemAll) == %u\n", sizeof(&Bar::addThemAll));
printf("sizeof(Qux::addAllTheThings) == %u\n", sizeof(&Qux::addAllTheThings));
printf("sizeof(func) == %u\n", sizeof(func));
printf("sizeof(addThemGlobal) == %u\n", sizeof(&addThemGlobal));
printf("sizeof(void *) == %u\n", sizeof(void *));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在我的系统上,该程序产生以下结果:
$ /tmp/a.out
sizeof(Foo::addThem) == 16
sizeof(Bar::addThemAll) == 16
sizeof(Qux::addAllTheThings) == 16
sizeof(func) == 16
sizeof(addThemGlobal) == 8
sizeof(void *) == 8
Run Code Online (Sandbox Code Playgroud)
注意成员函数指针的长度是16个字节.它不适合void *.它不是正常意义上的指针.你的代码和union工作完全是偶然的.
这样做的原因是成员函数指针经常需要存储在其中的额外数据,这些数据与修复它传递的对象指针有关,以便对被调用的函数是正确的.在我的例子中,当调用Bar::addThemAll一个Qux对象(由于继承而完全有效)时,Qux需要调整指向该对象的指针,以便在调用Bar该函数之前指向该子对象.因此Qux::*,成员函数必须在其中编码此调整.毕竟,说func = &Qux::addAllTheThings完全有效,如果调用该函数,则不需要指针调整.因此指针调整是函数指针值的一部分.
这只是一个例子.允许编译器以他们认为合适的任何方式实现成员函数指针(在某些约束内).许多编译器(比如我正在使用的64位平台上的GNU C++编译器)将以不允许任何成员函数指针被视为完全等同于普通函数指针的方式实现它们.
有办法解决这个问题.处理成员函数指针的瑞士军刀是::std::functionC++ 11或C++ TR1中的模板.
一个例子:
#include <functional>
// .... inside main
::std::function<int(Qux *)> funcob = func;
Run Code Online (Sandbox Code Playgroud)
funcob可以指向任何可以像函数一样调用的东西,需要一个Qux *.成员函数,全局函数,静态成员函数,仿函数...... funcob都可以指出它.
该示例仅适用于C++ 11编译器.但是如果您的编译器是合理的最新版本,但仍然不是C++ 11编译器,则可能会改为:
#include <tr1/functional>
// .... inside main
::std::tr1::function<int(Qux *)> funcob = func;
Run Code Online (Sandbox Code Playgroud)
如果情况变得更糟,你可以使用Boost库,这是整个概念的来源.
但我会重新考虑你的设计.我怀疑你会得到更多的milage,因为你有一个经过深思熟虑的继承层次结构和使用virtual函数,而不是你现在正在做的任何事情.使用解释器,我将有一个顶级抽象'表达式'类,它是可以评估的任何东西的抽象类.我会给它一个虚拟evaluate方法.然后,您可以派生不同语法元素的类,例如加法表达式变量或常量.他们每个人都会为其特定情况重载评估方法.然后你可以建立表达式树.
虽然不知道细节,但这只是对你的设计的一个模糊的建议.