问题描述
因此,尝试挂接OpenVR方法“ ITrackedDeviceServerDriver :: GetPose”,但是自定义的挂接方法返回时,我遇到访问冲突(我认为调用约定有问题,但对x64 asm的期望却不高。) / p>
我挂在“ ITrackedDeviceServerDriver”中的所有其他方法都可以正常工作。只有“ GetPose”失败。
被钩住的类如下:
class ITrackedDeviceServerDriver
{
public:
virtual EVRInitError Activate( uint32_t unObjectId ) = 0;
virtual void Deactivate() = 0;
virtual void EnterStandby() = 0;
virtual void *GetComponent( const char *pchComponentNameAndVersion ) = 0;
virtual void DebugRequest( const char *pchRequest,char *pchResponseBuffer,uint32_t unResponseBufferSize ) = 0;
virtual DriverPose_t GetPose() = 0;
};
大于ptr的对象并从崩溃的方法中返回
struct DriverPose_t
{
double poseTimeOffset;
vr::HmdQuaternion_t qWorldFromDriverRotation;
double vecWorldFromDriverTranslation[ 3 ];
vr::HmdQuaternion_t qDriverFromHeadRotation;
double vecDriverFromHeadTranslation[ 3 ];
double vecPosition[ 3 ];
double vecVelocity[ 3 ];
double vecAcceleration[ 3 ];
vr::HmdQuaternion_t qRotation;
double vecAngularVelocity[ 3 ];
double vecAngularAcceleration[ 3 ];
ETrackingResult result;
bool poseIsValid;
bool willDriftInYaw;
bool shouldApplyHeadModel;
bool deviceIsConnected;
};
如何挂接虚拟方法的示例:
typedef DriverPose_t(__thiscall* GetPose_Org)(ITrackedDeviceServerDriver* thisptr);
GetPose_Org GetPose_Ptr = nullptr;
DriverPose_t __fastcall GetPose_Hook(ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t result = GetPose_Ptr(thisptr);// works
//result.deviceIsConnected = true;
return result;// after return I get access violation crash
}
void TestHook(ITrackedDeviceServerDriver *pDriver)
{
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi,sizeof(MEMORY_BASIC_INFORMATION));
void** vTable = *(void***)(pDriver);
VirtualQuery((LPCVOID)vTable,&mbi,sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&mbi.Protect);// unlock
Activate_Ptr = (Activate_Org)vTable[0];
vTable[0] = &Activate_Hook;// Hook!
Deactivate_Ptr = (Deactivate_Org)vTable[1];
vTable[1] = &Deactivate_Hook;// Hook!
EnterStandby_Ptr = (EnterStandby_Org)vTable[2];
vTable[2] = &EnterStandby_Hook;// Hook!
GetPose_Ptr = (GetPose_Org)vTable[5];
vTable[5] = &GetPose_Hook;// Hook!
VirtualProtect(mbi.BaseAddress,mbi.Protect,&mbi.Protect);// lock
}
那么,为什么“ GetPose”是返回后唯一失败的方法? 如何固定“ GetPose”上的调用约定以正确使用寄存器?
解决方法
已在评论中指出
__thiscall
和__fastcall
是不同的调用约定
即使是 x64 。对于 x86 ,这很明显-__thiscall
-通过 Ecx 寄存器传递第一个参数( this ),并通过堆栈传递下一个参数(i立即跳过float / double参数的情况)。 __fastcall
-首先通过 Ecx ,第二次通过 Edx ,然后通过堆栈
在 x64 情况下(再次假设没有浮点/双精度参数)-通过 Rcx,Rdx,R8,R9 的前4个参数,然后是堆栈中的下一个。所以看起来 x64 具有单一和通用的调用约定( __ stdcall,__fastcall,__cdecl和__thiscall 之间没有区别)
但存在关于隐式参数的问题。例如说 __ thiscall -第一个参数- this 是隐式参数,始终作为第一个参数通过 Ecx / Rcx 传递>
和现在的情况下,当函数尝试“返回”对象时。实际上,任何平台上的函数只能通过cpu寄存器返回某些内容。它是有限计数,只有其中几个可以用于返回值存储。
在 x86(x64)上的say-可以使用返回值: AL,AX,EAX,RAX,DAX:EAX,RDX:RAX 因此函数只能返回大小为1、2、4、8(对于 x64 为16)的东西
如果函数尝试“返回”另一个大小的对象-这是不可能直接执行的,则编译器需要转换您的代码。例如,如果您编写函数:
DriverPose_t GetPose(ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t pos = ...;
return pos;
}
并致电
DriverPose_t pos = GetPose(thisptr);
compiler(当然,这已经是实现的细节了,不同的编译器可以用不同的方式来实现!! )可以将其转换为
void GetPose(DriverPose_t *ppos,ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t pos = ...;
*ppos = pos;
}
并致电
DriverPose_t pos;
GetPose(&pos,thisptr);
因此这里指向对象的指针传递给功能,该功能作为 first (隐藏/隐式)参数。但是在成员函数的情况下,仍然存在另一个隐式/隐式参数- this ,该参数始终作为第一个传递,结果隐式/隐式地将返回值的参数移至 second 地方!
struct ITrackedDeviceServerDriver
{
DriverPose_t GetPose()
{
DriverPose_t pos = ...;
return pos;
}
};
并致电
ITrackedDeviceServerDriver obj;
DriverPose_t pos = obj.GetPose();
可以转换为
struct ITrackedDeviceServerDriver
{
void GetPose(DriverPose_t *ppos)
{
DriverPose_t pos = ...;
*ppos = pos;
}
};
并致电
ITrackedDeviceServerDriver obj;
DriverPose_t pos;
obj.GetPose(&pos);
是函数的真实签名,如果“ uncover” 这个参数是
void ITrackedDeviceServerDriver::GetPose(ITrackedDeviceServerDriver* this,DriverPose_t *ppos)
{
DriverPose_t pos = ...;
*ppos = pos;
}
并致电
ITrackedDeviceServerDriver obj;
DriverPose_t pos;
GetPose(&obj,&pos);
所以比较
void ITrackedDeviceServerDriver::GetPose(ITrackedDeviceServerDriver* this,DriverPose_t *ppos);
和
void GetPose(DriverPose_t *ppos,ITrackedDeviceServerDriver* thisptr);
您实现
DriverPose_t GetPose_Hook(ITrackedDeviceServerDriver* thisptr);
它将被编译器转换为
void GetPose_Hook(DriverPose_t*,ITrackedDeviceServerDriver* thisptr);
但实际上您需要实现下一个钩子:
void GetPose_Hook(ITrackedDeviceServerDriver* thisptr,DriverPose_t* );
您混淆了第一参数和第二参数。但是我认为根本不需要更改对象vtable,而是需要将代理对象返回给客户端。 COM 接口挂钩的可能且非常通用的实现,但在此处过长。您的情况可以在下一个钩子示例中完成:
struct DriverPose_t
{
int x,y,z;
};
struct __declspec(novtable) IDemoInterface
{
virtual DriverPose_t GetPose() = 0;
virtual ULONG GetComponent( const char * pchComponentNameAndVersion ) = 0;
virtual void Delete() = 0;
};
class DemoObject : public IDemoInterface
{
ULONG v;
DriverPose_t pos;
virtual DriverPose_t GetPose()
{
DbgPrint("%s<%p>\n",__FUNCTION__,this);
return pos;
}
virtual ULONG GetComponent( const char * pchComponentNameAndVersion )
{
DbgPrint("%s<%p>(%s)\n",this,pchComponentNameAndVersion);
return v;
}
virtual void Delete()
{
delete this;
}
public:
DemoObject(ULONG v,int x,int y,int z) : v(v)
{
pos.x = x,pos.y = y,pos.z = z;
DbgPrint("%s<%p>\n",this);
}
virtual ~DemoObject()
{
DbgPrint("%s<%p>\n",this);
}
};
class Hook_DemoInterface : public IDemoInterface
{
IDemoInterface* pItf;
virtual DriverPose_t GetPose()
{
DriverPose_t pos = pItf->GetPose();
DbgPrint("%s<%p>=<%d,%d,%d>\n",pos.x,pos.y,pos.z);
return pos;
}
virtual ULONG GetComponent( const char * pchComponentNameAndVersion )
{
ULONG v = pItf->GetComponent(pchComponentNameAndVersion);
DbgPrint("%s<%p>(%s)=%u\n",pchComponentNameAndVersion);
return v;
}
virtual void Delete()
{
delete this;
}
public:
Hook_DemoInterface(IDemoInterface* pItf) : pItf(pItf)
{
DbgPrint("%s<%p>\n",this);
}
~Hook_DemoInterface()
{
pItf->Delete();
DbgPrint("%s<%p>\n",this);
}
};
BOOL CreateDemoIface(IDemoInterface** ppItf)
{
if (DemoObject* pObj = new DemoObject (1,-1,2,3))
{
*ppItf = pObj;
return TRUE;
}
return FALSE;
}
BOOL Hook_CreateDemoIface(IDemoInterface** ppItf)
{
IDemoInterface* pItf;
if (CreateDemoIface(&pItf))
{
if (Hook_DemoInterface* pObj = new Hook_DemoInterface(pItf))
{
*ppItf = pObj;
return TRUE;
}
*ppItf = pItf;
return TRUE;
}
return FALSE;
}
void UseIface(IDemoInterface* pItf)
{
ULONG v = pItf->GetComponent("some string");
DriverPose_t pos = pItf->GetPose();
DbgPrint("v=%u,pos<%d,v,pos.z);
}
void test()
{
IDemoInterface* pItf;
if (CreateDemoIface(&pItf))
{
UseIface(pItf);
pItf->Delete();
}
if (Hook_CreateDemoIface(&pItf))
{
UseIface(pItf);
pItf->Delete();
}
}
代替钩子对象 vtable -钩子(如果可能)创建对象CreateDemoIface
-将其替换为返回代理对象的自身Hook_CreateDemoIface
。
调试输出
DemoObject::DemoObject<0000012C31E08F70>
DemoObject::GetComponent<0000012C31E08F70>(some string)
DemoObject::GetPose<0000012C31E08F70>
v=1,pos<-1,3>
DemoObject::~DemoObject<0000012C31E08F70>
DemoObject::DemoObject<0000012C31E08F70>
Hook_DemoInterface::Hook_DemoInterface<0000012C31DFAA10>
DemoObject::GetComponent<0000012C31E08F70>(some string)
Hook_DemoInterface::GetComponent<0000012C31DFAA10>(some string)=1
DemoObject::GetPose<0000012C31E08F70>
Hook_DemoInterface::GetPose<0000012C31DFAA10>=<-1,3>
v=1,3>
DemoObject::~DemoObject<0000012C31E08F70>
Hook_DemoInterface::~Hook_DemoInterface<0000012C31DFAA10>