在 Win32 上挂钩虚拟方法(返回大于 ptr 崩溃的对象)?



所以试图挂接一个OpenVR方法"ITrackedDeviceServerDriver::GetPose",但是当我的自定义挂钩方法返回时,我遇到了访问冲突(我认为调用约定有问题,但对x64 asm的期望足够了)。

我在"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.RegionSize, mbi.Protect, &mbi.Protect);// lock
}

那么,为什么"GetPose"是唯一在返回后失败的方法呢? 如何修复"GetPose"上的调用约定以正确使用寄存器?

如注释中所述

__thiscall__fastcall不是同一个调用约定

即使对于x64。 对于x86,这很明显 -__thiscall- 通过Ecx寄存器传递第一个参数(this),通过堆栈传递下一个参数(我现在跳过浮点/双精度参数大小写)。__fastcall- 1 个第一个通过 Ecx,2 秒通过Edx下一个通过堆栈

x64的情况下(再次假设没有浮点/双精度参数) - 前 4 个参数通过Rcx、Rdx、R8、R9和堆栈中的下一个参数。 所以看起来x64具有单一和通用的调用约定(__stdcall、__fastcall、__cdecl、__thiscall之间没有区别)

但存在关于隐式参数的问题。 说在__thiscall的情况下 - 第一个参数 -这是隐式参数,始终通过Ecx/Rcx作为第一个参数传递

现在函数尝试"返回"对象的情况。 实际上,在任何平台上,函数只能通过CPU寄存器返回一些东西。 它是有限的计数,并且只有其中的几个可用于返回值存储。

比如在x86(x64) 上 - 对于返回值可以使用: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);

编译器(当然这已经实现了细节,不同的编译器可以用不同的方式做到这一点!)可以将其转换为

void GetPose(DriverPose_t *ppos, ITrackedDeviceServerDriver* thisptr)
{
DriverPose_t pos = ...;
*ppos = pos;
}

并致电

DriverPose_t pos;
GetPose(&pos, thisptr);

所以这里的指针指向作为第一个,隐藏/隐式参数传递给函数的对象。 但是在成员函数的情况下,仍然存在另一个隐藏/隐式参数 -这个,它总是作为第一个传递,结果隐藏/隐式,返回值的参数移动到第二位!

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);

所以函数的真实签名,如果"发现">这个参数是

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", __FUNCTION__, 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", __FUNCTION__, this);
}
virtual ~DemoObject()
{
DbgPrint("%s<%p>n", __FUNCTION__, this);
}
};
class Hook_DemoInterface : public IDemoInterface
{
IDemoInterface* pItf;
virtual DriverPose_t GetPose()
{
DriverPose_t pos = pItf->GetPose();
DbgPrint("%s<%p>=<%d, %d, %d>n", __FUNCTION__, this, pos.x, pos.y, pos.z);
return pos;
}
virtual ULONG GetComponent( const char * pchComponentNameAndVersion )
{
ULONG v = pItf->GetComponent(pchComponentNameAndVersion);
DbgPrint("%s<%p>(%s)=%un", __FUNCTION__, this, pchComponentNameAndVersion);
return v;
}
virtual void Delete()
{
delete this;
}
public:

Hook_DemoInterface(IDemoInterface* pItf) : pItf(pItf) 
{
DbgPrint("%s<%p>n", __FUNCTION__, this);
}
~Hook_DemoInterface() 
{ 
pItf->Delete(); 
DbgPrint("%s<%p>n", __FUNCTION__, 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, %d, %d>n", v, pos.x, pos.y, 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, 2, 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, 2, 3>
v=1, pos<-1, 2, 3>
DemoObject::~DemoObject<0000012C31E08F70>
Hook_DemoInterface::~Hook_DemoInterface<0000012C31DFAA10>

相关内容

  • 没有找到相关文章

最新更新