引用多级指针的更好方法

Better ways to reference multi-level pointers?

本文关键字:更好 方法 指针 多级 引用      更新时间:2023-10-16

至于现在,我正在尝试为不存在这种模式的游戏制作一个基本的"飞行模式"。为此,我需要操作游戏内存,这意味着必须使用指针来访问内存(从 DLL 中(。

至于现在我使用此代码:

#define AddVar(Type,Name,Address) Type& Name = *reinterpret_cast<Type*>(Address)
AddVar(unsigned int,gameBaseAddress,0x0089CDA8);//base address
//*Reveal* pointer, dunno how to name this
#define RevPointer(addr,type) (*(type *)(addr))
void FrameTick(IDirect3DDevice9 * device, HWND wnd)
{
    DirectXFont::Access(0)->Print(0.0,0.0,0xFFFFFFFF,"{FFFF0000}N{FF00FF00}F{FF0000FF}S {FFFFFF00}MOD {FF00FFFF}1.0",true);
    if(gameBaseAddress)//check if the game has loaded <accessing the VALUE at gameBaseAddress)
    {
            //lvl 1 ptr
        unsigned int BaseAddr = RevPointer(gameBaseAddress+0x20,unsigned int);//base + pointer offset 1 (+0x20)
            //lvl2 ptr
        BaseAddr += 0x20;//(base + offset 1 (+0x20)) + pointer offset 2 (+0x20) - X pos
        unsigned int PosXAddr = RevPointer(&BaseAddr,unsigned int);//got this line of code by trial and error, don't know how this magicly works
        BaseAddr += 0x4;//y pos is 4 bytes further
        unsigned int PosYAddr = RevPointer(&BaseAddr,unsigned int);
        BaseAddr += 0x4;//z the same
        unsigned int PosZAddr = RevPointer(&BaseAddr,unsigned int);
        float *PosX = (float*)PosXAddr;
        float *PosY = (float*)PosYAddr;
        float *PosZ = (float*)PosZAddr;
        if(PosXAddr && PosXAddr < 0xAAAA0000)//check if pointer is valid
        {
            DirectXFont::Access(0)->Print(0.0,60.0,0xFFFFFFFF,string_format("%.2f %.2f %.2f",*PosX,*PosY,*PosZ).c_str(),true);//works
        }
    }
}

通过调试游戏,我发现所有指针都是..嗯..多级指针:

((base_adderss + 0x偏移量1( + 0x偏移量

2( + 0x偏移量3(...

但是,这样,管理所有这些地址和抵消将变得非常不方便。

我在此代码中所做的是访问游戏的基址,将第一个偏移量添加到指针,然后从该指针中输入 X Y Z 偏移量 (+0x20、+0x24、+0x28( 以从内存中获取所需对象的 XYZ 位置。

这段代码看起来已经很丑了。有没有更好的方法来完成我想做的事情?


谢谢大家的投入。如果有人想要我拥有的当前代码:

#define AddVar(Type,Name,Address) Type& Name = *reinterpret_cast<Type*>(Address)
AddVar(unsigned int,PositionBaseAddress,0x0089CDA8);//base address
struct Point { float x, y, z; };
void FrameTick(IDirect3DDevice9 * device, HWND wnd)
{
    DirectXFont::Access(0)->Print(0.0,0.0,0xFFFFFFFF,"{FFFF0000}N{FF00FF00}F{FF0000FF}S {FFFFFF00}MOD {FF00FFFF}1.0",true);
    if(PositionBaseAddress)
    {
        auto BaseAddr = *(unsigned int*)(PositionBaseAddress + 0x20);
        if(IsBadReadPtr(&BaseAddr,0x04) != 0)
            return;
        auto& p = *(Point*)(BaseAddr + 0x20);
        auto& v = *(Point*)(BaseAddr + 0x70);
        if(IsBadReadPtr(&p,0x04) != 0)
            return;
        DirectXFont::Access(0)->Print(0.0,45.0,0xFFFFFFFF,string_format("Position: %.2f %.2f %.2f",p.x,p.y,p.z).c_str(),true);
        DirectXFont::Access(0)->Print(0.0,60.0,0xFFFFFFFF,string_format("Velocity: %.2f %.2f %.2f",v.x,v.y,v.z).c_str(),true);
    }
}
/*
Position.X: (0x0089CDA8 + 0x20) + 0x20
Position.Y: (0x0089CDA8 + 0x20) + 0x24
Position.Z: (0x0089CDA8 + 0x20) + 0x28
Velocity.X: (0x0089CDA8 + 0x20) + 0x70
Velocity.Y: (0x0089CDA8 + 0x20) + 0x74
Velocity.Z: (0x0089CDA8 + 0x20) + 0x78
*/

然后我意识到我可以巧妙地利用这些结构。

我做了这个代码:

#define AddVar(Type,Name,Address) Type& Name = *reinterpret_cast<Type*>(Address)
AddVar(unsigned int,PositionBaseAddress,0x0089CDA8);//base address
struct Point { float x, y, z; };
struct VehicleInfo
{
    Point Pos;
    int unknown[0x11];
    Point Velocity;
};
void FrameTick(IDirect3DDevice9 * device, HWND wnd)
{
    DirectXFont::Access(0)->Print(0.0,0.0,0xFFFFFFFF,"{FFFF0000}N{FF00FF00}F{FF0000FF}S {FFFFFF00}MOD {FF00FFFF}1.0",true);
    if(PositionBaseAddress)
    {
        auto BaseAddr = *(unsigned int*)(PositionBaseAddress + 0x20);
        if(IsBadReadPtr(&BaseAddr,0x04) != 0)
            return;
        auto& info = *(VehicleInfo*)(BaseAddr + 0x20);
        if(IsBadReadPtr(&info,0x04) != 0)
            return;
        DirectXFont::Access(0)->Print(0.0,45.0,0xFFFFFFFF,string_format("Position: %.2f %.2f %.2f",info.Pos.x,info.Pos.y,info.Pos.z).c_str(),true);
        DirectXFont::Access(0)->Print(0.0,60.0,0xFFFFFFFF,string_format("Velocity: %.2f %.2f %.2f",info.Velocity.x,info.Velocity.y,info.Velocity.z).c_str(),true);
    }
}

让我们从简化算术开始:

unsigned int BaseAddr = *(unsigned int*)(gameBaseAddress + 0x20) + 0x20;
float* PosX = (float*)(BaseAddr);
float* PosY = (float*)(BaseAddr + 4);
float* PosZ = (float*)(BaseAddr + 8);
…string_format(…*PosX,*PosY,*PosZ)…

(请注意,这将跳过RevPointer(&BaseAddr,unsigned int)步骤。请参阅我对原始问题的评论以了解原因。

由此,我们可以进一步简化:

struct Point { float x, y, z; };
auto BaseAddr = *(unsigned int*)(gameBaseAddress + 0x20);
auto& p = *(Point*)(BaseAddr + 0x20);
…string_format(…p.x,p.y,p.z)…

我不会打扰AddVarRevPointer宏。它们只会混淆事情,海事组织。

对于遍历标头,我发现使用 auto 、自定义偏移量类型模板(携带结果指针的类型(和接受指针和所述偏移类型的operator+的组合很有用/简洁。

我认为这至少会比您当前的宏观计划有所改进

但我不确定是否真的有一种方法可以让代码干净。 也许在处理带有偏移的结构时必须接受一些脏污。


啊,我在文本文件中找到了我使用它的代码,用于早期的 SO 答案...... 无法找到该项目。但这是代码,展示了如何定义这种偏移类型和运算符,以及它在"实际"用法中的外观:

#include <assert.h>         // assert
#include <stddef.h>         // ptrdiff_t
#include <sstream>
using std::ostringstream;
#undef UNICODE
#define UNICODE
#include <windows.h>
template< class Result, class SomeType >
Result as( SomeType const p ) { return reinterpret_cast<Result>( p ); }
template< class Type >
class OffsetTo
{
private:
    ptrdiff_t offset_;
public:
    ptrdiff_t asInteger() const { return offset_; }
    explicit OffsetTo( ptrdiff_t const offset ): offset_( offset ) {}
};
template< class ResultPointee, class SourcePointee >
ResultPointee* operator+(
    SourcePointee* const            p,
    OffsetTo<ResultPointee> const   offset
    )
{
    return as<ResultPointee*>( as<char const*>( p ) + offset.asInteger() );
}
int main()
{
    auto const pImage =
        as<IMAGE_DOS_HEADER const*>( ::GetModuleHandle( nullptr ) );
    assert( pImage->e_magic == IMAGE_DOS_SIGNATURE );
    auto const pNTHeaders =
        pImage + OffsetTo<IMAGE_NT_HEADERS const>( pImage->e_lfanew );
    assert( pNTHeaders->Signature == IMAGE_NT_SIGNATURE );
    auto const& importDir =
        pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    auto const pImportDescriptors = pImage + OffsetTo<IMAGE_IMPORT_DESCRIPTOR const>(
        importDir.VirtualAddress //+ importSectionHeader.PointerToRawData
        );
    ostringstream stream;
    stream << "I'm loaded at " << pImage << ", and I'm using...n";
    for( int i = 0;  pImportDescriptors[i].Name != 0;  ++i )
    {
        auto const pModuleName = pImage + OffsetTo<char const>( pImportDescriptors[i].Name );
        DWORD const offsetNameTable = pImportDescriptors[i].OriginalFirstThunk;
        DWORD const offsetAddressTable = pImportDescriptors[i].FirstThunk;  // The module "IAT"
        auto const pNameTable = pImage + OffsetTo<IMAGE_THUNK_DATA const>( offsetNameTable );
        auto const pAddressTable = pImage + OffsetTo<IMAGE_THUNK_DATA const>( offsetAddressTable );
        stream << "n* '" << pModuleName << "'";
        stream << " with IAT at " << pAddressTable << "n";
        stream << "t";
        for( int j = 0; pNameTable[j].u1.AddressOfData != 0; ++j )
        {
            auto const pFuncName =
                pImage + OffsetTo<char const>( 2 + pNameTable[j].u1.AddressOfData );
            stream << pFuncName << " ";
        }
        stream << "n";
    }
    MessageBoxA(
        0,
        stream.str().c_str(),
        "FYI:",
        MB_ICONINFORMATION | MB_SETFOREGROUND
        );
}