+ Reply to Thread
Results 1 to 8 of 8

  Click here to go to the first staff post in this thread.   Thread: [WoW] Client databases

Hybrid View

  1. #1
    Join Date
    Dec 2005
    Posts
    1,295
    Thanks
    1
    Thanked 11 Times in 3 Posts
    Rep Power
    101

    [WoW] Client databases

    Hello,

    the following code snippets allow you to access the WoW client databases which are loaded into memory by the game client. This was done by kynox before, but it only rarely worked for me.

    Just create a CWoWClientDB class instance and the databases are available to you!
    Code:
    class CClientDB
    {
    public:
    	class CRow
    	{
    	public:
    		DWORD & GetField( DWORD dwIndex )
    		{
    			return *( DWORD * )( this + 4 + dwIndex * 4 );
    		}
    	};
    
    	CRow * GetRow( DWORD dwIndex )
    	{
    		_asm mov ecx, this
    		return ( ( CRow * ( __stdcall * )( DWORD ) )0x0047AA20 )( dwIndex );
    	}
    };
    Please note that you can also write to these DBC fields, this would allow you to edit certain models for example.
    ( see ItemDisplayInfo.dbc in MyWarcraftStudio )
    
    class CWoWClientDB
    {
    	std::map<DWORD, CClientDB *> m_mDatabases;
    public:
    	CWoWClientDB()
    	{
    		for( DWORD dwStart = 0x56E470; *(BYTE*)( dwStart ) != 0xC3; dwStart += 0x14 )
    			m_mDatabases[ *(DWORD*)( dwStart + 1 ) ] = *(CClientDB**)( dwStart + 11 );
    	}
    
    	CClientDB * GetDatabase( DWORD dwIndex )
    	{
    		return m_mDatabases[ dwIndex ];
    	}
    };
    I attached a list of all loaded databases ( along with their IDs ).
    The list was logged in the same way to the CWoWClientDB constructor gets the databases:
    Code:
    for( DWORD dwStart = 0x56E470; *(BYTE*)( dwStart ) != 0xC3; dwStart += 0x14 )
    		{
    			DWORD dwIndex = *(DWORD*)( dwStart + 1 );
    			CClientDB * pDatabase = *(CClientDB**)( dwStart + 11 );
    
    			DWORD dwAddress = ( dwStart + 15 ) - ( 0xFFFFFFFF - *(DWORD*)( dwStart + 16 ) ) + 4;
    			const char * pszString = *(const char**)( dwAddress + 0x49 + *(DWORD*)( dwAddress + 0x44 ) );
    
    			gpWoWX->GetLog()->Add( "%s ID: %03X - %p", pszString, dwIndex, pDatabase );
    		}
    Credits: Kynox!
    Attached Files
    Last edited by bobbysing; 03-15-2008 at 01:39 PM. Reason: Fixed GetField!

  2. #2
    Join Date
    Feb 2005
    Location
    OoOoOoooo0oOO
    Posts
    711
    Thanks
    5
    Thanked 3 Times in 1 Post
    Rep Power
    97
    Nice, not sure why my implementation didn't work for you though :<. Probably a struct size update at some point in time.

    gj

  3. #3
    Join Date
    Dec 2005
    Posts
    1,295
    Thanks
    1
    Thanked 11 Times in 3 Posts
    Rep Power
    101

    3.0.3

    The update changed some stuff, I've taken some time to update the interface as well, it should be easier to understand now.

    PHP Code:
    #pragma once

    #include <windows.h>
    #include <map>
    //#include "Database_Enum.h"

    class CClientDB // size = 0x24
    {
    public:
        class 
    CRow
        
    {
            
    DWORD pdwFields;
        public:
            
    bool IsValid()
            {
                return 
    pdwFields != 0;
            }

            
    unsigned long GetFieldunsigned long dwIndex );
        };

        
    DWORD GetMinIndex();
        
    DWORD GetMaxIndex();

        
    CRow GetRowunsigned long dwIndex );

    private:
        
    DWORD pdwCustomVMT;
        
    DWORD dwUnknown[2];
        
    DWORD dwMaxIndex;
        
    DWORD dwMinIndex;
        
    DWORD dwUnknown2[3];
        
    CRow pRows;
    };

    class 
    CWoWClientDB
    {
        
    std::map<const unsigned longCClientDB *> m_mDatabases;
    public:
        
    CWoWClientDB();

        
    CClientDB GetDatabaseunsigned long dwIndex );
    }; 
    PHP Code:
    #include "CClientDB.h"

    DWORD CClientDB::CRow::GetFieldDWORD dwIndex )
    {
        return 
    pdwFields[dwIndex];
    }

    DWORD CClientDB::GetMaxIndex()
    {
        return 
    dwMaxIndex;
    }

    DWORD CClientDB::GetMinIndex()
    {
        return 
    dwMinIndex;
    }

    CClientDB::CRow CClientDB::GetRowDWORD dwIndex )
    {
        if( 
    dwIndex GetMinIndex() || dwIndex >= dwMaxIndex )
            return 
    0;

        
    CRow pRet = &pRowsdwIndex GetMinIndex() ];
        
        if( !
    pRet->IsValid() )
            return 
    0;

        return 
    pRet;
    }

    CWoWClientDB::CWoWClientDB()
    {
    #pragma pack( push, 1 )
        
    struct SConstructorCall
        
    {
            
    BYTE bPushOpcode1;
            
    DWORD dwPushValue1;

            
    BYTE bPushOpcode2;
            
    DWORD dwPushValue2;

            
    BYTE bMovOpcode3;
            
    DWORD dwMovValue3;

            
    BYTE bCallOpcode;
            
    DWORD dwCallAddress;
        };
    #pragma pack( pop )

        
    for( SConstructorCall pCall reinterpret_cast<SConstructorCall*>( gpWoWX->GetFindPattern()->GetAddress"RegisterBase_ClientDB" ) ); pCall->bPushOpcode1 != 0xC3pCall reinterpret_cast<SConstructorCall*>( reinterpret_cast<DWORD>( pCall ) + sizeofSConstructorCall ) ) )
        {
        
    //    DBGLOG( "DB( " << pCall->dwPushValue1 << " ) -> " << pCall->dwMovValue3 );
            
    m_mDatabasespCall->dwPushValue1 ] = reinterpret_cast<CClientDB*>( pCall->dwMovValue3 );
        }
    }

    CClientDB CWoWClientDB::GetDatabaseDWORD dwIndex )
    {
        return 
    m_mDatabasesdwIndex ];

    "RegisterBase_ClientDB" is currently located at 0x005A48C0 and I used
    PHP Code:
    DWORD dwValue dwFindPattern"\x8B\x40\x28\x83\xF8\xFF\x74\x09\x85\xC0\x74\x33\x83\xF8\x01\x75""xx?xx?x?xxx?xx?x" ) + 0x19;
    dwValue = *(DWORD*)dwValue dwValue 4// we get it from a function call 
    If you want to loop through all entries of a database for example, the code would look like this
    PHP Code:
    CClientDB pLangDB GetClientDB()->GetDatabaseDB_Languages );
    if( 
    pLangDB )
    {
        for( 
    DWORD dwIndex pLangDB->GetMinIndex(); dwIndex pLangDB->GetMaxIndex(); dwIndex++ )
        {
            
    CClientDB::CRow pRow pLangDB->GetRowdwIndex );
            if( 
    pRow // don't assume they're always valid
            
    {
                const 
    char pszLanguage reinterpret_cast<const char*>( pRow->GetField) );

                
    MessageBoxA0pszLanguage"Language entry!");
            }
        }

    You can use DumpDatabases.idc from my "IDC scripts" post to dump the database ids like "DB_Languages".

    Have fun!
    Last edited by bobbysing; 11-09-2008 at 05:40 AM.

  4. #4
    Join Date
    Jan 2008
    Location
    Kynox's sister's bedroom
    Posts
    811
    Thanks
    1
    Thanked 5 Times in 4 Posts
    Rep Power
    80
    Nice work on the update. Just one thing though, the 'new' pointer type which you're getting from 'ConstructorCall' only works on CClientDB::GetRow, if you want to use CClientDB::GetLocalizedRow you have to pass it the 'old' pointer type. Its really quite stupid. I didn't look into why that is, it was just discovered when updating everything for 3.0.x.

    PS. Fix that random crash I'm getting kynox you jew.

  5. #5
    Join Date
    Dec 2005
    Posts
    1,295
    Thanks
    1
    Thanked 11 Times in 3 Posts
    Rep Power
    101
    Quote Originally Posted by Chazwazza View Post
    the 'new' pointer type which you're getting from 'ConstructorCall' only works on CClientDB::GetRow, if you want to use CClientDB::GetLocalizedRow
    I've just tried it in both english and german language settings, no problems.
    What do you mean exactly ?

    Quote Originally Posted by Chazwazza View Post
    PS. Fix that random crash I'm getting kynox you jew.
    Is this code causing random crashes for you ? When and how are you using it ?

  6. #6
    Join Date
    Jan 2008
    Location
    Kynox's sister's bedroom
    Posts
    811
    Thanks
    1
    Thanked 5 Times in 4 Posts
    Rep Power
    80
    Quote Originally Posted by bobbysing View Post
    I've just tried it in both english and german language settings, no problems.
    What do you mean exactly ?
    In the DBC files there are groups of entries that contain localized strings. So a single entry may take 5-10 columns. Theres a function inside WoW that instead of returning the raw row, 'compresses' all the entries into one (the base entry/column) in which the string for the current locale is stored. You're not using it currently but I'm just pointing it out incase you decided to (its quite useful).

    Props to Greyman for pointing it out to me.


    Quote Originally Posted by bobbysing View Post
    Is this code causing random crashes for you ? When and how are you using it ?
    No, talking about a totally unrelated crash on a seperate project that kynox is too crap to fix. :p

  7. #7
    Join Date
    Dec 2005
    Posts
    1,295
    Thanks
    1
    Thanked 11 Times in 3 Posts
    Rep Power
    101
    Quote Originally Posted by Chazwazza View Post
    In the DBC files there are groups of entries that contain localized strings. So a single entry may take 5-10 columns. Theres a function inside WoW that instead of returning the raw row, 'compresses' all the entries into one (the base entry/column) in which the string for the current locale is stored. You're not using it currently but I'm just pointing it out incase you decided to (its quite useful).

    Props to Greyman for pointing it out to me.
    I recently ran into exactly that problem, thanks for pointing it out to me!

    I've attached a rar containing the same database stuff with the addition of the functions CompressFields and GetLocalizedField. To figure out when to use GetLocalizedField, print out the database ptrs and check what/(if any) function is used to access the fields. As example you can use DB_Spell ( you need to use GetLocalizedField to retrieve the values ).
    Attached Files
    Last edited by bobbysing; 12-03-2008 at 03:17 PM.

  8. #8
    Join Date
    Jan 2008
    Location
    Kynox's sister's bedroom
    Posts
    811
    Thanks
    1
    Thanked 5 Times in 4 Posts
    Rep Power
    80
    Quote Originally Posted by bobbysing View Post
    I recently ran into exactly that problem, thanks for pointing it out to me!

    I've attached a rar containing the same database stuff with the addition of the functions CompressFields and GetLocalizedField. To figure out when to use GetLocalizedField, print out the database ptrs and check what/(if any) function is used to access the fields. As example you can use DB_Spell ( you need to use GetLocalizedField to retrieve the values ).
    Heh, thought you'd come across it eventually.

    Most of the file-based DBC editors also suffer from the same problem.

+ Reply to Thread

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

     

Similar Threads

  1. Help with ac-client
    By mrjackson99 in forum Anti-Cheat Development
    Replies: 3
    Last Post: 02-12-2007, 01:17 PM
  2. Just one client func
    By Dark Horse in forum Beginner
    Replies: 1
    Last Post: 04-23-2004, 06:09 AM
  3. Client Screenshot
    By LeeN in forum Intermediate
    Replies: 3
    Last Post: 03-21-2004, 12:19 PM
  4. CS 1.6 Client.dll here
    By xen in forum Beginner
    Replies: 7
    Last Post: 11-20-2003, 06:04 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