[4.3.4] Access Violation when invoking a function menu

User Tag List

Results 1 to 7 of 7
  1. #1
    Averige's Avatar Member
    Reputation
    1
    Join Date
    Nov 2009
    Posts
    3
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [4.3.4] Access Violation when invoking a function

    Hey,

    I've been trying to create a simple morpher for 4.3.4, but so far I am stuck at attempting to invoke a function. From what I've researched on this forum, in order to change the appearance of a equipped item slot I have to write to the memory location that holds the item ID of that given slot and then call a function called UpdateDisplayInfo. Currently I am able to sucessfully read and write to these locations, however when attempting to invoke the said update function, my game crashes, throwing a Access Violation error. The said function should be located at the 0x5D9BD0 offset, which I have decompiled by using IDA and found out this function is of the void type, is using a thiscall calling convention and requires two int parameters. From what I've gathered these parameters should be the location of the player base object and a bool parameter to force the update. I believe the bug is not in the way I am calling the function. I was wondering whether someone more experienced could help me with my issue. I am accessing the WoW process memory internally by injecting a DLL. Here are the relevant pieces of code:

    Main:
    Code:
    DWORD WINAPI MainThread(HMODULE hModule)
    {
    	uintptr_t baseAddr;
    	uintptr_t objMgrAddr;
    	uintptr_t playerBaseAddr;
    	uintptr_t playerDescriptor;
    
    	DWORD wowBuild;
    	DWORD playerGUID;
    	DWORD playerHealth;
    	DWORD playerItemChest;
    
    	typedef void(__thiscall * CGUnit_C__UpdateDisplayInfo)(int arg1, int arg2);
    
    	/* Allocate a console for debugging */
    	AllocConsole();
    	FILE *fDummy;
    	freopen_s(&fDummy, "CONIN$", "r", stdin);
    	freopen_s(&fDummy, "CONOUT$", "w", stderr);
    	freopen_s(&fDummy, "CONOUT$", "w", stdout);
    
    	while (1)
    	{
    		if (GetAsyncKeyState(VK_END) & 1) 
    			break;
    
    		if (GetAsyncKeyState(VK_HOME) & 1) {
    			system("cls");
    
    			baseAddr = reinterpret_cast<uintptr_t>(GetModuleHandle(NULL));
    			std::cout << "Base add: " << std::hex << baseAddr << std::endl;
    
    			wowBuild = GetWoWBuild(baseAddr);
    			if (wowBuild != 15595) // 4.3.4 build number
    			{
    				std::cout << "This version of WoW is not supported. Terminating." << std::endl;
    				break;
    			}
    
    			objMgrAddr = FindObjMgrAddr(baseAddr);
    			std::cout << "Obj Mgr Addr: " << std::hex << objMgrAddr << std::endl;
    
    			if (objMgrAddr == 0)
    			{
    				std::cout << "Object Manager couldn't be located. Terminating." << std::endl;
    				break;
    			}
    
    			playerGUID = GetPlayerGUID(objMgrAddr);
    			std::cout << "Player GUID: " << playerGUID << std::endl;
    
    			playerBaseAddr = GetPlayerBaseAddr(objMgrAddr, playerGUID);
    			std::cout << "Player base add: " << std::hex << playerBaseAddr << std::endl;
    
    			playerDescriptor = playerBaseAddr + 0xC;
    			playerDescriptor = *(uintptr_t*)playerDescriptor;
    			std::cout << "Player descriptor: " << std::hex << playerDescriptor << std::endl;
    
    			playerHealth = *(DWORD*)(playerDescriptor + 0x20 + 0x12 * 4);
    			std::cout << "Player health: " << std::dec << playerHealth << std::endl;
    
    			playerItemChest = *(DWORD*)(playerDescriptor + 0x20 + 0x8A * 4 + 0x10D * 4);
    			std::cout << "Chest ID: " << std::dec << playerItemChest << std::endl;
    
    			try
    			{
    				CGUnit_C__UpdateDisplayInfo _udi = (CGUnit_C__UpdateDisplayInfo)(baseAddr + 0x5D9BD0);
    				_udi(playerBaseAddr, 1); // <- Access Violation error
    			}
    			catch (...)
    			{
    			}
    
    				
    	}
    
    
    	std::cout << "Terminating..." << std::endl;
    	fclose(fDummy);
    	FreeConsole();
    	FreeLibraryAndExitThread(hModule, 0);
    	
    	return 0;
    }
    GetPlayerBaseAddress:

    Code:
    uintptr_t GetPlayerBaseAddress(uintptr_t objMgrAddress, DWORD playerGUID)
    {
    	uintptr_t objType;
    	uintptr_t objGUID;
    	uintptr_t playerBaseAddress = objMgrAddress;
    
    	playerBaseAddress += 0xC0;
    	objType = *(uintptr_t*)playerBaseAddress;
    	objType += 0x14;
    	objType = *(uintptr_t*)objType;
    
    	while (objType <= 7 && objType > 0)
    	{
    		playerBaseAddress = *(uintptr_t*)playerBaseAddress;
    		objGUID = playerBaseAddress + 0x30;
    		objGUID = *(uintptr_t*)objGUID;
    
    		if (objGUID == playerGUID)
    		{
    			std::cout << "Found GUID " << std::hex << objGUID << " at " << std::hex << playerBaseAddress << std::endl;
    
    			return playerBaseAddress;
    		}
    
    		playerBaseAddress += 0x3C;
    		objType = *(uintptr_t*)playerBaseAddress;
    		objType += 0x14;
    		objType = *(uintptr_t*)objType;
    	}
    
    	std::cout << "Didn't find our GUID !" << std::endl;
    	return 0;
    }

    Thanks in advance.

    [4.3.4] Access Violation when invoking a function
  2. #2
    ejt's Avatar Contributor
    Reputation
    209
    Join Date
    Mar 2008
    Posts
    166
    Thanks G/R
    3/111
    Trade Feedback
    0 (0%)
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    From the top of my head, if it is a __thiscall then the object pointer (this) needs to be in the ecx register, when you call it like you do, it just pushes the value to the stack.

    So either A:

    Code:
    CGUnit_C__UpdateDisplayInfo _udi = (CGUnit_C__UpdateDisplayInfo)(baseAddr + 0x5D9BD0);
    __asm
    {
      mov ecx, playerBaseAddress
      push 1
      call _udi
      // if the function returns a value you catch it here
    }
    or B: make a player struct with the correct offsets and vftable and just
    Code:
    struct Player
    {
      // vftable and members that correctly mirrors the player object
    }
    
    Player* player = reinterpret_cast<Player*>(playerBaseAddress);
    player->UpdateDisplayInfo(1);
    This way all the stack management and moving the this pointer is generated by the compiler automatically.

    I prefer to follow the principle of option B in all my programs because it gives you a good representation of objects and full control of everything without any hassle.

    However as with everything in life, it has the huge drawback of taking a lot of time to get all the object structures together and such. You should probably go with option A.

    Because I only have my league hack open right now and cba to open my wow project, here is an example of how such structure may look like:

    Code:
    			// sizeof = 0x22C
    			struct GameObject
    			{
    				// functions = 51
    				virtual void* vfunc_0() {};				// 0
    				virtual void vfunc_1() {}				// 1
    				virtual void DestructAndFree() {}		// 2
    				virtual void vfunc_3(void* a2) {}		// 3
    				virtual bool vfunc_4() {}				// 4
    				virtual LeagueString* GetName() {}		// 5
    				// Lots of vfuncs here...
    				virtual float vfunc_48() {}				// 48
    				virtual void vfunc_49() {}				// 49
    				virtual void vfunc_50() {}				// 50
    
    				UObject uobject;			// 0x0004
    				uint16_t index;				// 0x0020
    				uintptr_t unk_0024;			// 0x0024
    				// ... lots of members here
    				HashedValue32 unk_01F8;		// 0x01F8
    				HashedValue8 unk_0214;		// 0x0214
    				Vec3 position;				// 0x0220
    			};
    That being said, you should really look at how the function is called and how to prologue handles the stack and such to figure out what call convention is used. IDA is wrong a lot about stuff like that.

    Edit: Forgot to say, this is only relevant to 32-bit processes because it works completely different on 64-bit processes. So for current wow (retail, classic) which is 64-bit you don't need to do all of this.
    Last edited by ejt; 08-16-2020 at 09:01 PM.

  3. #3
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by ejt View Post
    From the top of my head, if it is a __thiscall then the object pointer (this) needs to be in the ecx register, when you call it like you do, it just pushes the value to the stack.
    He defines it as __thiscall, so it will of course pass the parameters in the correct registers and stack space. The issue is probably something to do with invoking it outside of the client's main thread.

  4. #4
    Averige's Avatar Member
    Reputation
    1
    Join Date
    Nov 2009
    Posts
    3
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Jadd View Post
    He defines it as __thiscall, so it will of course pass the parameters in the correct registers and stack space. The issue is probably something to do with invoking it outside of the client's main thread.
    This seems to be the case, because whenever I try to invoke different functions, I get the same result. This is how I am injecting my code into the WoW.exe memory space:


    Code:
    BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
        {
    		case DLL_PROCESS_ATTACH:
    			CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)MainThread, hModule, 0, nullptr));
    		default:
    			break;
        }
    	
        return TRUE;
    }
    (MainThread being my hack loop, as defined in my previous post).
    Is there anything I am missing?

  5. #5
    aeo's Avatar Contributor
    Reputation
    126
    Join Date
    Apr 2007
    Posts
    270
    Thanks G/R
    84/62
    Trade Feedback
    7 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Yeah, your thread dosnt contain all the information in TLS that the games nainthread does. So you cannot call some functions.

  6. #6
    Master674's Avatar Elite User
    Reputation
    487
    Join Date
    May 2008
    Posts
    578
    Thanks G/R
    2/23
    Trade Feedback
    1 (100%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by aeo View Post
    Yeah, your thread dosnt contain all the information in TLS that the games nainthread does. So you cannot call some functions.
    And regardless of that he needs to be on the main thread for this update call to succeed. If it happens to work on a different thread you just got super lucky that your client didn't instantly crash because of a race condition.

    A good way to execute your function would be to set a new wndproc with SetWindowLongPtr and then instantly post a message to it (SendMessage for example). This will call your newly registered wndproc on wows mainthread with your custom message. And inside that handler you can call whatever you require to be executed on the mainthread.

  7. #7
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Master674 View Post
    And regardless of that he needs to be on the main thread for this update call to succeed. If it happens to work on a different thread you just got super lucky that your client didn't instantly crash because of a race condition.

    A good way to execute your function would be to set a new wndproc with SetWindowLongPtr and then instantly post a message to it (SendMessage for example). This will call your newly registered wndproc on wows mainthread with your custom message. And inside that handler you can call whatever you require to be executed on the mainthread.
    Small thing to note - I've found some games/engines that handle the window message queue on a separate thread to the game's "main" thread. Not totally unusual, especially if the game uses directinput for example.

Similar Threads

  1. Memory Access Violation when using custom loading screen
    By Aodhann in forum WoW EMU Questions & Requests
    Replies: 5
    Last Post: 04-09-2015, 09:09 AM
  2. Access Violation Problem ( Rep if u can help me)
    By TheZaronz in forum World of Warcraft Emulator Servers
    Replies: 3
    Last Post: 04-16-2008, 02:12 PM
  3. [Ascent] Server Crash Access Violation Error [Help]
    By jklei18 in forum World of Warcraft Emulator Servers
    Replies: 3
    Last Post: 04-14-2008, 09:09 AM
  4. I got this error in ascent (52 level up informations generated) an access violation??
    By PRIMO12 in forum World of Warcraft Emulator Servers
    Replies: 7
    Last Post: 01-09-2008, 09:31 PM
  5. Access Violation
    By 777devil777 in forum World of Warcraft Emulator Servers
    Replies: 7
    Last Post: 11-19-2007, 07:11 PM
All times are GMT -5. The time now is 03:27 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