Search code examples
pointersoffsetcheat-engine

Finding pointer with 'find out what writes to this address' strange offset


I'm trying to find a base pointer for UrbanTerror42. My setup is as followed, I have a server with 2 players. cheat-engine runs on client a. I climb a ladder with client b and then scan for incease/decrease. When I have found the values, I use find out what writes to this address. But the offset are very high and point to empty memory. I don't really know how to proceed

For the sake of clarity, I have looked up several other values and they have the same problem

I've already looked at a number of tutorials and forums, but that's always about values where the offsets are between 0 and 100 and not 80614.

I would really appreciate it if someone could tell me why this happened and what I have to do/learn to proceed.

enter image description here

enter image description here

thanks in advance


Solution

  • Urban Terror uses the Quake Engine. Early versions of this engine use the Quake Virtual Machine and the game logic is implemented as bytecode which is compiled into assembly by the Quake Virtual Machine. Custom allocation routines are used to load these modules into memory, relative and hardcoded offsets/addresses are created at runtime to accommodate these relocations and do not use the normal relocation table method of the portable executable file format. This is why you see these seemingly strange numbers that change every time you run the game.

    The Quake Virtual Machines are file format .qvm and these qvms in memory are tracked in the QVM table. You must find the QVM table to uncover this mystery. Once you find the 2-3 QVMs and record their addresses, finding the table is easy, as you're simply doing a scan for pointers that point to these addresses and narrowing down your results by finding those which are close in memory to each other.

    The QVM is defined like:

    struct vmTable_t
    {
        vm_t vm[3];
    };
    
    struct vm_s {
        // DO NOT MOVE OR CHANGE THESE WITHOUT CHANGING THE VM_OFFSET_* DEFINES
        // USED BY THE ASM CODE
        int         programStack;       // the vm may be recursively entered
        intptr_t(*systemCall)(intptr_t *parms);
    
        //------------------------------------
    
        char        name[MAX_QPATH];
    
        // for dynamic linked modules
        void        *dllHandle;
        intptr_t     entryPoint;           //(QDECL *entryPoint)(int callNum, ...);
        void(*destroy)(vm_s* self);
    
        // for interpreted modules
        qboolean    currentlyInterpreting;
    
        qboolean    compiled;
        byte        *codeBase;
        int         codeLength;
    
        int         *instructionPointers;
        int         instructionCount;
    
        byte        *dataBase;
        int         dataMask;
    
        int         stackBottom;        // if programStack < stackBottom, error
    
        int         numSymbols;
        struct vmSymbol_s   *symbols;
    
        int         callLevel;      // counts recursive VM_Call
        int         breakFunction;      // increment breakCount on function entry to this
        int         breakCount;
    
        BYTE        *jumpTableTargets;
        int         numJumpTableTargets;
    };
    typedef struct vm_s vm_t;
    

    The value in EAX in your original screenshot should be the same as either the codeBase or dataBase member variable of the QVM structure. The offsets are just relative to these addresses. Similarly to how you deal with ASLR, you must calculate the addresses at runtime.

    Here is a truncated version of my code that does exactly this and additionally grabs important structures from memory, as an example:

    void OA_t::GetVM()
    {
        cg = nullptr;
        cgs = nullptr;
        cgents = nullptr;
        bLocalGame = false;
        cgame = nullptr;
    
        for (auto &vm : vmTable->vm)
        {
            if (strstr(vm.name, "qagame")) { bLocalGame = true; continue; }
    
            if (strstr(vm.name, "cgame"))
            {
                cgame = &vm;
                gamestatus = GSTAT_GAME;
                //char* gamestring = Cvar_VariableString("fs_game");
    
                switch (cgame->instructionCount)
                {
                case 136054: //version 88
                    cgents = (cg_entities*)(cgame->dataBase + 0x1649c);
                    cg = (cg_t*)(cgame->dataBase + 0xCC49C);
                    cgs = (cgs_t*)(cgame->dataBase + 0xf2720);
                    return;
    

    Full source code for reference available at OpenArena Aimbot Source Code, it even includes a video overview of the code.

    Full disclosure: that is a link to my website and the only viable resource I know of that covers this topic.