从基类中查找虚方法的地址

问题描述 投票:0回答:1
  • CPU:Mac M (AArch64)
  • 编译器:XCode(Clang)

我们有来自 UE5 的以下类

class IRHIComputeContext {}

class IRHICommandContext : public IRHIComputeContext
{
    // pure virtual method
    virtual void RHICopyTexture(FRHITexture* SourceTexture, FRHITexture* DestTexture, const FRHICopyTextureInfo& CopyInfo) = 0;
}

class FMetalRHICommandContext : public IRHICommandContext
{
    // overridden final virtual method
    virtual void RHICopyTexture(FRHITexture* SourceTextureRHI, FRHITexture* DestTextureRHI, const FRHICopyTextureInfo& CopyInfo) final override;
}

class FMetalRHIImmediateCommandContext : public FMetalRHICommandContext
{
}

如上面的代码所示,继承是这样进行的:

FMetalRHIImmediateCommandContext <-- FMetalRHICommandContext <-- IRHICommandContext <-- IRHIComputeContext

RHICopyTexture
中定义了一个
IRHICommandContext
方法,并在
FMetalRHICommandContext
中重写。

现在我有一个

FMetalRHIImmediateCommandContext*
指针,我想检索
RHICopyTexture
的地址进行挂钩。

    FMetalRHIImmediateCommandContext* Context = StaticCast<FMetalRHIImmediateCommandContext*>(GDynamicRHI->RHIGetDefaultContext());
    check(Context);
    FMetalDeviceContext& MetalDeviceContext = Context->GetInternalContext();
    
    void** const pCastedToHeader = reinterpret_cast<void**>(Context);
    for (size_t i = 0; i < sizeof(*Context) / sizeof(void*); i++)
    {
        UE_LOG(LogTemp, Warning, TEXT("Pointer[%04zu] = 0x%016X"), i, pCastedToHeader[i]);
    }
    
    UE_LOG(LogTemp, Warning, TEXT("&IRHICommandContext::RHICopyTexture = %d"), &IRHICommandContext::RHICopyTexture);
    UE_LOG(LogTemp, Warning, TEXT("&FMetalRHICommandContext::RHICopyTexture = %d"), &FMetalRHICommandContext::RHICopyTexture);
    UE_LOG(LogTemp, Warning, TEXT("&FMetalRHIImmediateCommandContext::RHICopyTexture = %d"), &FMetalRHIImmediateCommandContext::RHICopyTexture);
&IRHICommandContext::RHICopyTexture = 720
&FMetalRHICommandContext::RHICopyTexture = 720
&FMetalRHIImmediateCommandContext::RHICopyTexture = 720

RHICopyTexture
偏移量是
720(0x2D0)
,这与我从下面的
get_cxx_member_func_ptr
方法检索相同。但是
Pointer[720]
为零,超出了
sizeof(*Context)
的有效内存范围,即 512。

据我了解,每个类类型都有自己的独立 vtable,其中包含指向基表中方法的槽。我的代码适用于当前类中定义的其他虚拟方法,但不适用于此处继承的方法。如何找到

RHICopyTexture
的真实地址?

// https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#representation-of-pointer-to-member-function
template<typename Func>
inline void* get_cxx_member_func_ptr(void* BaseAddress, Func f) {
    // a pointer to member function is a pair of words <ptr, adj>
    verify(sizeof(f) == 16);
    union {
        Func Fcn;
        struct {
          // The generic C++ ABI [GCPPABI] specifies that the least significant bit of ptr discriminates between
          // (0) the address of a non-virtual member function and
          // (1) the offset in the class’s virtual table of the address of a virtual function.
          // However, this encoding cannot work for the AArch64 instruction set where the architecture reserves all bits of code addresses.
          // This ABI specifies that adj contains twice the this adjustment, plus 1 if the member function is virtual.
          // The least significant bit of adj then makes exactly the same discrimination as the least significant bit of ptr does for Itanium.
          // A pointer to the member function to call.  If the member function is
          // virtual, this will be a thunk that forwards to the appropriate vftable
          // slot.
          void *FunctionPointerOrVirtualThunk;
          int IsVirtual;
        };
    };
    Fcn = f;
    if (!IsVirtual){
        return FunctionPointerOrVirtualThunk;
    }
    else {
        uint64_t ThunkIndex = reinterpret_cast<uint64_t>(FunctionPointerOrVirtualThunk);
        void*** VTable = reinterpret_cast<void***>(BaseAddress);
        return *(VTable[ThunkIndex]);
    }
}

更新:

有两个虚拟桌子。

  • _ZTV23FMetalRHICommandContext
    FMetalRHICommandContext
    包含
    RHICopyTexture
  • 的定义
  • _ZTV32FMetalRHIImmediateCommandContext
    对于派生类
    FMetalRHIImmediateCommandContext

FMetalRHIImmediateCommandContext*
的指针指的是
vptr
地址。
vptr
地址比其
vtable
地址
_ZTV32FMetalRHIImmediateCommandContext
大 16 个字节 (0x10)。

问题是,

_ZTV32FMetalRHIImmediateCommandContext
中的槽对于继承的虚拟方法
RHICopyTexture
来说是零。

但它确实存在于基类的 vtable 中

_ZTV23FMetalRHICommandContext

c++ clang++ abi vtable
1个回答
0
投票

这里是在 ARM64(Clang) 中检索成员函数指针的代码


template<typename Func>
inline void* get_cxx_member_func_ptr(const gpointer Instance, Func f) {
    // a pointer to member function is a pair of words <ptr, adj>
    verify(sizeof(f) == 16);
    union {
        Func Fcn;
        struct {
          // A pointer to the member function to call.  If the member function is
          // virtual, this will be a thunk that forwards to the appropriate vftable
          // slot.
          void *FunctionPointerOrVirtualThunk;
          int IsVirtual;
        };
    };
    Fcn = f;
    if (!IsVirtual){
        return FunctionPointerOrVirtualThunk;
    }
    else {
        void** VPtr = *(reinterpret_cast<void***>(Instance));
        uint64_t ThunkIndex = reinterpret_cast<uint64_t>(FunctionPointerOrVirtualThunk);
        
        // vtable starts with offset_to_top and RTTI
        // hence vptr pointer is 16bytes behind vtable. e.g.
        // VTable = 0x0000000324E02708
        // VPtr   = 0x0000000324E02718
        // More details: https://taylorbowland.com/posts/virtual-tables/
        
        return VPtr[ThunkIndex/sizeof(void*) - 2];
    }
}

如果你不喜欢上面回复编译器实现的肮脏方法,也许你可以通过导出符号找到

vtable
地址

extern "C" {
    #include "frida-gum.h"
    #include <fcntl.h>
    #include <unistd.h>
}


static gboolean find_export_in_module(const GumModuleDetails * details, gpointer user_data)
{
    GumExportDetails* target = (GumExportDetails*)user_data;
    
    if (target) {
        target->address = gum_module_find_export_by_name( details->name, target->name);
    }
    
    return target->address == 0;
}


static gpointer find_vtable_address(const char* export_name) {
    gum_init_embedded();
    
    GumExportDetails target;
    target.name = export_name;
    target.address = GPOINTER_TO_SIZE(nullptr);
    target.type = GUM_EXPORT_VARIABLE;
    gum_process_enumerate_modules(find_export_in_module, &target);
    
    return GSIZE_TO_POINTER(target.address);
}


void** VTable = reinterpret_cast<void**>(find_vtable_address("_ZTV32FMetalRHIImmediateCommandContext"));
© www.soinside.com 2019 - 2024. All rights reserved.