Results 1 to 6 of 6

Thread: GW1 Trouble understanding the Item handling in memory

  1. #1
    Junior Member
    Join Date
    Jul 2009
    Posts
    18

    GW1 Trouble understanding the Item handling in memory

    Hello Guys!

    Find a static pointer to the bagArray:
    I start with the address long numberOfItems in the main bag. From there I count down 0x10 for the beginning of the bag struct (looked this up in an old GwAPI source) and search for the value. I get numberOfItems in bag + 3 results. These represent the Bag-pointer in the corresponding items itemStruct (value changes if moved to another bag) + 3 of unknown functionality to me. So I reckon to be at the right spot. So if the mainBags ID is 18 and the Bagstruct address is eg. 0C138B48, I would substract 4*18 (=0x48) from 0C138B48 und search for 0C138B00 in order to find a static pointer to the BagArray (similar to Agentstruct - 4*AgentId...; you get what I mean). Unfortunately, no results.

    Other approach: numOfItems + 4 is a bag pointer to a bag pointer to the BagArray. So if (Bag** bagarray) 0C138B5C contains 077C5318, I search for 077C5318 in order to find the "master" pointer. I get roundabout 12 results. I searched for all of these addresses - again in order to find a static pointer - but again no results.

    Can you guys please be so kind a help me out a bit with the concept of GW's item handling? I don't quite know where I am wrong.

    Best Regards

  2. #2
    Member reduf's Avatar
    Join Date
    Aug 2013
    Location
    university basement
    Posts
    77
    The second approch seem right, though you won't get a static pointer in this case. A lot of gw stuff will be access through the thread local storage accessor which is the function here: 005B3440
    When you are there you are prety much done as you can use MemoryReadPtr($mBasePointer, offsets found).

  3. #3
    Junior Member
    Join Date
    Jul 2009
    Posts
    18
    Edit: I finally managed to find a static pointer chain to the NumberOfItems in a Bag with NumberOfItems and "what accesses this address" and "what accesses this pointer".
    ( 0xA2AF70 / 0xA2B17C / 0xA35298 ) + 0x18 + 0x40 + 0xF8 + 0x4 + 0x10 (I was glad cheat engine is over 18 though). So I do recognize the Offsets from the GWApi source. As my project is much more about understanding the matter than functionality, I would want to ask you, what I have just found. I really don't get the concept of this whole basePointer thing.



    Unfortunately I need to ask you to elaborate further. I am not exactly familiar with the whole architecture but as far as I have understood, the BasePointer is a pointer to the currently executed function: "By using a base pointer the return address will always be at ebp+4, the first parameter will always be at ebp+8, and the first local variable will always be at ebp-4. Even as the stack size grows and shrinks those offsets do not change." (https://practicalmalwareanalysis.com...all-about-ebp/)
    Sounds reasonable. In the GWApi Source, there is this definition:
    Code:
    dword BasePointer(){
    		return *(dword*)(BasePointerLocation);
    	}
    Meaning, by finding the location with the corresponding pattern, at this (static?) location is the pointer to the BasePointer. With the offsets 0x18 (agent related?), 0x40 for - I guess - inventory stuff and 0xF8 for bag/chest stuff I finally find the BagArray.
    But I do not get how the GWApi gets into the current context of a function, e.g. SellItem (when I have the explanation above in mind).
    Code:
    void SellItem(long itemId){
    	long itemValue = (MyItemManager->GetItemPtr(itemId)->quantity * GetItemValue(itemId));
    	long* itemIdPtr = &itemId;
    
    	_asm {
    		PUSH 0
    		PUSH 0
    		PUSH 0
    		PUSH itemValue
    		PUSH 0
    		PUSH itemIdPtr
    		PUSH 1
    		MOV ECX,0xB
    		MOV EDX,0
    		CALL SellItemFunction
    	}
    }
    So the basepointer is accessed (MyItemManager->GetItemPtr...) before the instruction jumps into the sell function. So I guess the basepointer would point to a totally random function that was called before, meaning the offsets 0x18, 0x40 and 0xF8 would get you somewhere but not the bagArray, as these offsets are only valid within an item related function that needs these item related parameters.

    If you could find the time to explain the matter a bit more detailed and for less experienced people, it would be very very much appreciated.

    Best regards
    Last edited by void; 05-12-2016 at 09:46 PM.

  4. #4
    Member reduf's Avatar
    Join Date
    Aug 2013
    Location
    university basement
    Posts
    77
    There is few points that i'll try to elaborate on.

    1. Parameters / Stack:
    Generally, ebp represent the queue of the stack (in the current function) and esp the head of it.
    Quite often you will see at the start of a function:
    Code:
    push ebp
    mov ebp, esp
    Furthermore, when a function is called the return address (so caller address +5/6, depend on which kind of call) is at the top of the stack. So at the call [esp] = ret addr. we push ebp => esp -= 4 => [exp + 4] = ret addr.
    Finally, mov ebp, esp => [ebp + 4] = retn addr. We use ebp since esp will keep changing through the function. Indeed, everytime we push a value esp -= 4.

    Also, there is few standard way to pass arguments to an other function. It's the __stdcall, __fastcall, __thiscall, ... It's documented here: https://msdn.microsoft.com/en-us/library/984x0h58.aspx
    By default, most application use __stdcall, so the arguments are push on the stack, right to left, before calling the function.
    Code:
    int__stdcall add(DWORD a, DWORD b, DWORD c)
    {
        return a + b + c;
    }
    
    __asm ; How to call in assembly.
    {
    push 10 ; c = 10
    push 5 ; b = 5
    push 0 ; a = 0
    call add
    
    ;in eax you have the result because return value are in eax.
    }
    In this case, after the push ebp & mov ebp, esp, we will have:
    [ebp + 4] = retn address
    [ebp + 8] = a = 0
    [ebp + C] = b = 0
    [ebp + 10] = c = 0

    Though, in Guild Wars the most common calling convention is __fastcall. Hence, the first argument will be in ecx & the second argument will be in edx then others will be on the stack.
    To find the calling convention is fairly easy. You can use the fact that ecx & edx doesn't have to be save through the call. Indeed, only esi, edi, esp, ebp, ebx are save through the call. So, if ecx/edx aren't mov in one of this (won't be ebp & exp) or on the stack before a call you are not in a fastcall scenario. To check how many arguments you have on the stack, you can either check the caller or how far [ebp + x] goes.

    Finally, when you are inside a function and you declare local variable you don't have a guarantee that the variable is on the stack, especially after the optimisation. Indeed, a variable that is use everywhere in the function will quite often be in esi/edi. So you have the be carefull with that.

    2. The base pointer
    Generally you will fall after all offset on a static variable that doesn't change when you restart the game. There is a lot of this in Guild Wars (i.e. Camera, ChatBuffer, ..., ...). This is a base offset and will be necessary, with the offset, to find the other values. But, in Guild Wars there is something quite special. They use the thread local storage. This is special because the instruction mov register, fs:[some offset].
    This instruction will mov into the reg a value that is in the teb_addr + offset. The offset 0x2c is the thread local storage address. See: https://en.wikipedia.org/wiki/Win32_...ormation_Block

    Hence, if you execute the exact same code in two differents thread it won't do the same as the storage won't be at the same address. In Guild Wars the function that return this address is currently @ 005B3440 and it's.
    Code:
    mov eax,dword ptr ds:[D57710]
    mov ecx,dword ptr fs:[2C] ; fs because it's for thread information block access.
    mov edx,dword ptr ds:[ecx+eax*4]
    mov eax,dword ptr ds:[edx+4]
    ret
    If you did some offset you probably at somepoint (if you also check the disasembly) fall on a call of this function. If you label this function you will see the call everywhere.
    After the call of this function you will have in eax the base pointer. The GameContext will be at *(GameContext**)((*(DWORD*)basepointer) + 0x18).
    See here: https://bitbucket.org/0x4D1/gwca/src.../GameContext.h

    After that you follow all offset & you will find what you were searching.
    Indeed if you look at structure & the offsets you talked about we already have the +x18, then +x40 is ItemContext, +xF8 is inventory, +x4 is inventory.bag[1] aka Belt Pouch, +x10 ItemsCount.
    See structure here: https://bitbucket.org/0x4D1/gwca/src...GWStructures.h

    Though, it turns out that the base_pointer can also be found in a static way, through a scan & mov reg, [base_pointer]. So, this is the way that we can it.

    End:
    When you call SellItem with an itemId (assuming that no other args where past in). It will do something like that.
    Code:
    GameContext *game = *(GameContext**)(*(BYTE*)GetThreadContext() + 0x18);  // We hardcore offset 0x18 as we don't have much information about the structure of the context.
    Item *item = game->itemCtx->itemarray.get(itemId);
    The gw_array as it is named in GWCA++ are obviously object, this is why i assume get as a method of it to get the itemId'n item. Otherwise, hardcored you will have to add offset 0 to get into the array.

    Indeed the gw_array format is:
    Code:
    struct gw_array<T>
    {
        T *container;
        DWORD maxSize;
        DWORD numberOfElements;
        DWORD defaultSize; // Not sure for this one
    }
    Anyway, i hope my english wasn't too bad & you could understand. If you have other questions i'll be happy to help you.
    Last edited by reduf; 05-15-2016 at 05:51 AM.

  5. #5
    Junior Member
    Join Date
    Jul 2009
    Posts
    18
    Thanks for the effort!

    I finally managed to code myself an ident function and a sell function. Was a bit tricky though.
    ident @ 7FC220 with ECX for IdentKitId and EDX for ItemToIdentId. GetItemValue @ 56EB10 with ECX for ItemID. And SellItem @ 88A770.

    I manage the items with a GetItem(bag, slot) to get a certain item into context.

    Code:
    void IdentItem(int idKit, int idToIdent)
    {
    	DWORD IdentItemFunc = 0x7FC220;
    
    	_asm
    	{
    		MOV ECX, idKit
    		MOV EDX, idToIdent
    		CALL IdentItemFunc
    	}
    }
    
    void GetItemValue(int itemID)
    {
    	DWORD GetItemValueFunc = 0x56EB10;
    
    	long itemValue = 0;
    
    	_asm 
    	{
    		PUSHAD
    		MOV ECX, itemID
    		CALL GetItemValueFunc
    		MOV itemValue, EAX
    		POPAD
    	}
    
    	SendMessageToMasterParam(AM_RECEIVEITEMVALUE, itemValue, itemID);
    }
    
    void SellItem(int value, int id)
    {
    	DWORD SellItemFunc = 0x88A770;
    
    	int itemValue = value;
    	int* itemIdPtr = &id;
    
    	_asm
    	{
    		PUSH 0
    		PUSH 0
    		PUSH 0
    		PUSH itemValue
    		PUSH 0
    		PUSH itemIdPtr
    		PUSH 1
    		MOV ECX, 0xB
    		MOV EDX, 0
    		CALL SellItemFunc
    	}
    
    	SendMessageToMaster(AM_SELLCOMPLETE);
    }
    
            public int[] GetItem(int bag, int slot)
            {
                int[] itemStats = { 0, 0, 0, 0 }; //Id, rarity, quantity, modelId
    
                int[] itemArrayOffsets = { 0x18, 0x40, 0xF8, bag, 0x18 };
                int itemArray = ReadIntPtrChain(baseAddress, itemArrayOffsets);
                int itemOfInterest = ReadInt(itemArray + (slot * 4));
    
                itemStats[0] = ReadInt(itemOfInterest);
    
                int itemOfInterestRarity = ReadInt(itemOfInterest + 0x3C);
                itemStats[1] = ReadByte(itemOfInterestRarity);
    
                itemStats[2] = ReadByte(itemOfInterest + 0x4B);
                itemStats[3] = ReadInt(itemOfInterest + 0x2C);
    
                return itemStats;
            }
    I copied some ASM from the API source.

  6. #6
    Member reduf's Avatar
    Join Date
    Aug 2013
    Location
    university basement
    Posts
    77
    Well done.
    You can improve it a little by using the typedef.
    Code:
    typedef void(__fastcall *Identify_t)(DWORD kitId, DWORD itemId);
    Identify_t Identify = (Identify_t)(0x7FC220);
    
    // ex
    Identify(kitId, itemId);
    
    typedef DWORD(__fastcall *GetItemValue_t)(DWORD itemId);
    GetItemValue_t GetItemValue = (GetItemValue_t)(0x56EB10);
    
    // ex:
    DWORD value = GetItemValue(itemId);
    Also, if you do it in C/C++ you may want to just hardcore the structure. Then you don't have to care anymore about the whole offsets thing.
    Last edited by reduf; 05-18-2016 at 09:52 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •