2.4.3 SpellDB - Object vs File question [Resolved] menu

User Tag List

Results 1 to 4 of 4
  1. #1
    DarkLinux's Avatar Former Staff
    CoreCoins Purchaser Authenticator enabled
    Reputation
    1584
    Join Date
    May 2010
    Posts
    1,829
    Thanks G/R
    188/531
    Trade Feedback
    16 (100%)
    Mentioned
    6 Post(s)
    Tagged
    0 Thread(s)

    2.4.3 SpellDB - Object vs File question [Resolved]

    I have been trying to reverse the db struct for the 2.4.3 spell class for the last 2 days. At first, I was thinking I was looking at the wrong list but after dumping the db names it looks to be the correct one.

    Code:
    DBFilesClient\Spell.dbc = 0x00BA0BE0
    The 1st thing that threw me off was the Spell header was a little different from all others. (from game memory)

    Spell
    Code:
    struct WoWClientDb2
    {
    	PVOID _vtable; 			//0x00
    	int IsLoaded; 			//0x04
    	int NumRows; 			//0x08
    	int MaxIndex; 			//0x0C
    	int MinIndex; 			//0x10
    	PVOID StringTable;		//0x14
    	PVOID FirstRow; 		//0x18
    	PVOID UnorderedRows; 		//0x1C
    	PVOID OrderedRows; 		//0x20
    };
    All others
    Code:
    struct WoWClientDb
    {
    	PVOID _vtable; 		//0x00
    	int IsLoaded; 		//0x04
    	int NumRows; 		//0x08
    	int MaxIndex; 		//0x0C
    	int MinIndex; 		//0x10
    	PVOID StringTable;	//0x14
    	PVOID FirstRow; 	//0x18
    	PVOID Rows; 		//0x1C
    };
    The next problem I ran into was the struct its self was much different than the 1.12.1 client. The struct was aligned on a byte boundary (from game memory). After looking over some emulator source code I concluded the file struct was different than how it was being saved into game memory, as mangos was reading on a 4 byte boundary. I started diffing the class with different spells and found some basic offsets. Soon after diffing more spells, I found the offsets stopped lining up.So I started to look into how everything was being loaded,

    0x005746E0, the start of the spelldb loading process

    Code:
    .text:005746E0 68 6B 01 00 00                                push    16Bh
    .text:005746E5 68 CC BE 8B 00                                push    offset a_Dbclient_cpp ; ".\\DBClient.cpp"
    .text:005746EA B9 E0 0B BA 00                                mov     ecx, offset off_BA0BE0 //WoWClientDb2* Spell
    .text:005746EF E8 5C B1 FF FF                                call    sub_56F850
    56F850 does some basic error checking (version check, file handling, and row count)

    After that it moves on to a virtual function that allocates and populates the WoWClientDb2 object,

    Code:
    SpellObjectSetup = 0x0057BA70
    Code:
    .text:0056F9A8 8B 16                                         mov     edx, [esi]
    .text:0056F9AA 8B 12                                         mov     edx, [edx]
    .text:0056F9AC 57                                            push    edi
    .text:0056F9AD 89 46 14                                      mov     [esi+14h], eax
    .text:0056F9B0 8B 45 FC                                      mov     eax, [ebp+var_4]
    .text:0056F9B3 53                                            push    ebx
    .text:0056F9B4 50                                            push    eax
    .text:0056F9B5 8B CE                                         mov     ecx, esi
    .text:0056F9B7 C7 46 0C 00 00 00 00                          mov     dword ptr [esi+0Ch], 0
    .text:0056F9BE C7 46 10 FF FF FF 0F                          mov     dword ptr [esi+10h], 0FFFFFFFh
    .text:0056F9C5 FF D2                                         call    edx //0x0057BA70

    A little into the function you will see a call to 76B370. 76B370 is a function that reads each row from the spell db (mapped in file) into the heap. ecx has the address of the write/out buffer. Looking at it after it's called I found that the spell struct looked normal, everything was alined like in 1.12.1, on a 4 byte boundary. So I could bootstrap the process, hook and create my own copy, try loading the table my self or understand what is going on. The odd thing is it looks to take up the same amount of memory for both versions (0x260). I should also note that at this point I can see its not just alined differntly, comparing the 2 objects they have different vlaues even if aliment was changed.

    Code:
    .text:0057BAC7 8B 46 14                                      mov     eax, [esi+14h]
    .text:0057BACA 8B 4D 08                                      mov     ecx, [ebp+arg_0]
    .text:0057BACD 50                                            push    eax             ; int
    .text:0057BACE 51                                            push    ecx             ; int
    .text:0057BACF 74 3F                                         jz      short loc_57BB10
    .text:0057BAD1 8D 8D 9C FD FF FF                             lea     ecx, [ebp+var_264] ; void * //ecx = out_buffer
    .text:0057BAD7 E8 94 F8 1E 00                                call    sub_76B370 //Read Spell Data

    The next function call is to 0x00792D80. This function must be the one that is re-alining the struct

    Code:
    .text:0057BADC 6A 00                                         push    0
    .text:0057BADE 8D 95 9C FD FF FF                             lea     edx, [ebp+var_264]
    .text:0057BAE4 68 60 02 00 00                                push    260h
    .text:0057BAE9 52                                            push    edx
    .text:0057BAEA E8 91 72 21 00                                call    sub_792D80
    Code:
    signed int __cdecl sub_792D80(_BYTE *pSpellObj, int size (0x260), _BYTE *pUnknown (NULL))
    {
      _BYTE *pUnknown2; // esi@1
      unsigned int v4; // edi@1
      signed int result; // eax@3
      _BYTE *i; // ecx@3
      char v7; // bl@6
      unsigned int j; // edx@7
    
      pUnknown2 = pUnknown;
      v4 = (unsigned int)&pSpellObj[size]; //Get end address
      if ( pUnknown ) // is NULL, skip
      {
        *pUnknown = *pSpellObj;
        pUnknown2 = pUnknown + 1;
      }
      result = 1;
      for ( i = pSpellObj + 1; (unsigned int)i < v4; ++i ) //loop over object
      {
        if ( pUnknown2 ) // is NULL, skip
          *pUnknown2++ = *i;
        v7 = *i;
        ++result;
        if ( *i == *(i - 1) )
        {
          ++i;
          for ( j = 0; (unsigned int)i < v4; ++j )
          {
            if ( *i != v7 )
              break;
            if ( j >= 0xFF )
              break;
            ++i;
          }
          if ( pUnknown2 )  // is NULL, skip
            *pUnknown2++ = j;
          ++result;
          if ( i == (_BYTE *)v4 )
            return result;
          if ( pUnknown2 ) // is NULL, skip
            *pUnknown2++ = *i;
          ++result;
        }
      }
      return result;
    }
    I can't really tell what it's doing, looks like its calculating blank space?

    I cant find where its getting copied over to from the temp object. I get hits from,

    All from, 792D80
    Code:
    .text:00792DC6 38 19                                         cmp     [ecx], bl
    ..
    .text:00792DB8 3A 59 FF                                      cmp     bl, [ecx-1]
    ..
    .text:00792DB3 8A 19                                         mov     bl, [ecx]
    Some mem copy function, called from ReadSpell (0x0076B370) -> ReadData (0x00654E00)
    Code:
    .text:0040B6CC 89 44 8F FC                                   mov     [edi+ecx*4-4], eax

    Another odd thing is when the game accesses the spell object list, it always makes a copy of it, and not just returning a pointer.

    Code:
    signed int __thiscall sub_466680(int this, int a2, void *a3)
    {
      int v3; // edx@1
      const void **v4; // eax@3
      signed int result; // eax@5
    
      v3 = *(_DWORD *)(this + 0x10);
      if ( a2 >= v3
        && a2 <= *(_DWORD *)(this + 0xC)
        && (v4 = (const void **)(*(_DWORD *)(this + 0x20) + 4 * (a2 - v3)), *v4) )
      {
        if ( byte_D2A084 )
        {
          call 004664C0(*v4, 0x260, a3); //<---
          result = 1;
        }
        else
        {
          qmemcpy(a3, *v4, 0x260u);
          result = 1;
        }
      }
      else
      {
        result = 0;
      }
      return result;
    }
    I must be missing something... For a quick fix I'm going to try calling it my self, and hooking the function. Hope someone can give me some insight. Thanks!

    Download: Spell.dbc dump from memory



    ----edit

    Temp fix works

    Code:
    PCHAR errorString = ".\\DBClient.cpp";
    WoWClientDb2* newSpellDBC = new WoWClientDb2;
    DWORD SpellDBCall = 0x56F850;
    
    memset(newSpellDBC, 0, sizeof(WoWClientDb2));
    newSpellDBC->_vtable = (PVOID)0x08BC6D0;
    newSpellDBC->MaxIndex = 0xFFFFFFFF;
    newSpellDBC->MinIndex = 0x0FFFFFFF;
    
    WoWClass::hook(0x0057BAD7, (DWORD)SpellCave);
    
    __asm
    {
    	push 0x16B;
    	push errorString;
    	mov ecx, newSpellDBC;
    	call SpellDBCall;
    };

    ----edit

    nvm. I'm just crazy...

    Code:
    typedef bool (__thiscall* dGetRow2)(PVOID _this, int index, PVOID buffer /*size = 0x260*/);
    bool GetRow(int index, PVOID buffer)
    {
    	dGetRow2 _GetRow = (dGetRow2)0x00466680;
    	return _GetRow(this, index, buffer);
    }
    found this the other day, noticed wow called it, I was like fuck that, I dont need a copy of the object... Just looked into it again, and it reverses what 466680 does with 4664C0... If only I trusted wow more... So 00466680 returns the real uncompressed object that we all know and love.
    Last edited by DarkLinux; 07-30-2017 at 02:54 AM.

    2.4.3 SpellDB - Object vs File question [Resolved]
  2. Thanks squiggy (1 members gave Thanks to DarkLinux for this useful post)
  3. #2
    tutrakan's Avatar Contributor
    Reputation
    134
    Join Date
    Feb 2013
    Posts
    175
    Thanks G/R
    124/52
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You nailed it!
    Code:
    signed int __thiscall ClientDb::GetLocalizedRow(WoWClientDB *this, int i, SpellRec *spellrec)
    {
      int v3; // edx@1
      const void **v4; // eax@3
      signed int result; // eax@5
    
      v3 = this->MinIndex;
      if ( i >= v3 && i <= this->MaxIndex && (v4 = (this->OrderedRows + 4 * (i - v3)), *v4) )
      {
        if ( g_isDBCCompressed )
        {
          ClientDB::DecompressRow(*v4, 0x260, spellrec);// the size of spellrec was picked up at compile time
          result = 1;
        }
        else
        {
          qmemcpy(spellrec, *v4, sizeof(SpellRec));
          result = 1;
        }
      }
      else
      {
        result = 0;
      }
      return result;
    }
    One thing i would like to learn from you: how you managed to dissect the WoWClientDb2 structure? By dumping the DB names?
    Code:
    struct WoWClientDb2
    {
    	PVOID _vtable; 			//0x00
    	int IsLoaded; 			//0x04
    	int NumRows; 			//0x08
    	int MaxIndex; 			//0x0C
    	int MinIndex; 			//0x10
    	PVOID StringTable;		//0x14
    	PVOID FirstRow; 		//0x18
    	PVOID UnorderedRows; 		//0x1C
    	PVOID OrderedRows; 		//0x20
    };
    Last edited by tutrakan; 07-30-2017 at 04:32 AM.

  4. #3
    Zazazu's Avatar Contributor
    Reputation
    191
    Join Date
    Jun 2016
    Posts
    390
    Thanks G/R
    5/143
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I dont know what same in 2.4.3, but in 7.2.5 x64 wow have same situation. In memory after pointer to indexes got pointer to direct "ordered" rows (in format for x64: spell_id (long) ptr_to_row(INT_PTR)) with count .

  5. #4
    tutrakan's Avatar Contributor
    Reputation
    134
    Join Date
    Feb 2013
    Posts
    175
    Thanks G/R
    124/52
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Zazazu View Post
    I dont know what same in 2.4.3, but in 7.2.5 x64 wow have same situation. In memory after pointer to indexes got pointer to direct "ordered" rows (in format for x64: spell_id (long) ptr_to_row(INT_PTR)) with count .
    I beg you pardon?
    Last edited by tutrakan; 08-06-2017 at 09:53 PM.

Similar Threads

  1. [Client] ADB file question
    By roc13x in forum WoW EMU Questions & Requests
    Replies: 3
    Last Post: 06-13-2011, 05:31 PM
  2. Go object view distance question
    By ghcghcghc in forum WoW EMU Questions & Requests
    Replies: 4
    Last Post: 03-22-2009, 11:33 AM
  3. Anthrax's 1-70 task file - Question about selling it
    By pixie12 in forum Community Chat
    Replies: 1
    Last Post: 05-25-2008, 07:40 AM
  4. Noob SQL file question.
    By foxfire in forum World of Warcraft Emulator Servers
    Replies: 2
    Last Post: 02-13-2008, 08:20 PM
  5. WDB file question
    By Kuth in forum Community Chat
    Replies: 0
    Last Post: 03-26-2007, 04:51 AM
All times are GMT -5. The time now is 10:46 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search