Introductory Questions menu

Shout-Out

User Tag List

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

    Introductory Questions

    A little background: I've been reading this forum and gamehacking.com quite a bit in regard to memory reading techniques. I am also pretty familiar with C/C++/C# (I write embedded C for work). I've done some assembly but not much and even less for x86 (although the concepts are the same).

    So I have a couple questions:

    1. How do people originally detect memory addresses? All of the tutorials that I see mention memory address they've already found. For something simple like finding health, I know that you can search, lower your health, etc. (gone through the Cheat Engine tutorial and done this), but how about finding the beginning of the player struct then or (more difficult?) finding the list of enemies and their information?

    2. Similarly, (maybe answered by #1's answer) how do you re-detect the addresses when the game is patched / re-run? Do you have to go through the process again?

    3. Are there existing joint projects to make bots? It seems like a pretty time consuming task to get reasonable AI and a good set of features. It seems like generic code for movement, combat, etc. could be setup an distribute for people to contribute to with more guarded code for the memory read (this could then be abstracted per game easily, too).

    4. Is there API to detect if another process is reading your memory? e.g. Does Warden only operate by having a list of processes and then checks for memory changes (I assume it makes a hash of its memory periodically?).


    Any tutorial/articles links would greatly be appreciated.

    Thanks for any information & help.
    Last edited by bnovc; 01-05-2010 at 07:43 PM.

    Introductory Questions
  2. #2
    wanyancan's Avatar Member
    Reputation
    1
    Join Date
    May 2009
    Posts
    40
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by bnovc View Post
    A little background: I've been reading this forum and gamehacking.com quite a bit in regard to memory reading techniques. I am also pretty familiar with C/C++/C# (I write embedded C for work). I've done some assembly but not much and even less for x86 (although the concepts are the same).

    So I have a couple questions:

    1. How do people originally detect memory addresses? All of the tutorials that I see mention memory address they've already found. For something simple like finding health, I know that you can search, lower your health, etc. (gone through the Cheat Engine tutorial and done this), but how about finding the beginning of the player struct then or (more difficult?) finding the list of enemies and their information?

    2. Similarly, (maybe answered by #1's answer) how do you re-detect the addresses when the game is patched / re-run? Do you have to go through the process again?

    3. Are there existing joint projects to make bots? It seems like a pretty time consuming task to get reasonable AI and a good set of features. It seems like generic code for movement, combat, etc. could be setup an distribute for people to contribute to with more guarded code for the memory read (this could then be abstracted per game easily, too).

    4. Is there API to detect if another process is reading your memory? e.g. Does Warden only operate by having a list of processes and then checks for memory changes (I assume it makes a hash of its memory periodically?).


    Any tutorial/articles links would greatly be appreciated.

    Thanks for any information & help.
    Answer 1. There're many memory address people are mentioning, for example function address, object status offsets, other enums or structs. They are found in different ways. For lua functions, there's an easy way, just search the lua function names, say 'UnitAura". In ida, you'll find
    Code:
    .data:00A3C96C                 dd offset sub_69F5C0
    .data:00A3C970                 dd offset aUnitbuff     ; "UnitBuff"
    .data:00A3C974                 dd offset sub_6A9990
    .data:00A3C978                 dd offset aUnitdebuff   ; "UnitDebuff"
    .data:00A3C97C                 dd offset sub_6A99E0
    .data:00A3C980                 dd offset aUnitaura     ; "UnitAura"
    .data:00A3C984                 dd offset sub_6A9A30
    For for object status offsets, you can search "OBJECT_FIELD_GUID". It can be used to build a list of descriptors struction.
    Code:
    void genDescriptors(){
    	/* storage struct:
    		{obj,item} for item
    		{obj,item, container} for container
    		{obj,unit} for unit
    		{obj,unit,player} for player
    		{obj,gameobject}
    		{obj,dynamic}
    		{obj,corpse}
    	*/
    	char *types[8] = {"obj","container","item","unit","player","gameobject","dynamic","corpse"};
    	TCHAR *datatype[6] = {L"unk00", L"int32", L"unk02", L"float32",L"GUID",L"chars"};
    	UINT prevSecSize = 0;
    	UINT baseptr = 0x105CF10; //// <<<<<<<<<<<< ptr to "OBJECT_FIELD_GUID"
    	for(int sec=0;sec<8;sec++){
    		if(ReadInt(baseptr)==0) baseptr+=4; ///// shift by one DWORD. special treatment for boundry between 'player' and 'unit'. there's an additional 0x00000000;
    		UINT i = 0;
    		UINT ptr,curoffset,cursize,other,endmark;
    		TCHAR str[64];
    		TCHAR outp[200];
    		char outpp[400];
    		FILE *pf;
    		fopen_s(&pf, types[sec],"w");
    		while(true){
    			ptr = ReadInt(baseptr);
    			curoffset = ReadInt(baseptr+0x4); //total relative index count in DWORD
    			cursize = ReadInt(baseptr+0x8);
    			other = ReadInt(baseptr+0xC); 
    			if (other>=6) 
    				other = 0;
    			endmark = ReadInt(baseptr+0x18);  // the next offset , used to test whether this section has ended.
    			ReadUTF8((LPCVOID)ptr,str,64);
    
    			_stprintf_s(outp,L"[%08X]%s[%08X]\t=\t0x%08X[+%08X] \t ; // type: %s(%d), size: %d * DWORD\n",baseptr,str,ptr,curoffset*4,prevSecSize,datatype[other%6],other,cursize);
    
    			int ascLen = WideCharToMultiByte(CP_ACP,0,outp,-1,(LPSTR)outpp,0,NULL,NULL);
    			ascLen = WideCharToMultiByte(CP_ACP,0,outp,-1,(LPSTR)outpp,ascLen, NULL,NULL);
    
    			fwrite(outpp, sizeof(char),strlen(outpp), pf);
    			if(endmark!=curoffset+cursize) {
    				prevSecSize += (cursize+curoffset)*4;
    				baseptr+=0x14;
    				break;
    			}
    			baseptr+=0x14;
    		}
    		fclose(pf);
    		printf("descriptor %s has been dumped\n",types[sec]);
    	}
    }
    For generating the basePtr of list of currently visible units (Assuming you already opened the process),
    Code:
    BOOL GenBP()
    {
    
        HANDLE snaphnd = CreateToolhelp32Snapshot(0x4,0);
        if (!snaphnd)
        {
            //errmsg(L"Can't create toolhelp32 snapshot.\n");
            return false;
        }
    
        THREADENTRY32 info;
        info.dwSize = 7*sizeof(UINT);
        BOOL more_thread = true, found = false;
    
        THREAD_BASIC_INFORMATION tbi;
        ULONG retlong;
        tbi.TebBaseAddress = 0;
    
    	playerguid.LowPart = playerguid.HighPart = basepoint =  0;
       // Get a handle to the DLL module.
     
        HINSTANCE hinstLib = LoadLibrary(TEXT("ntdll")); 
    	pfnNtQueryInformationThread ProcAdd;
     
        // If the handle is valid, try to get the function address.
     
        if (hinstLib != NULL) 
        { 
            ProcAdd = (pfnNtQueryInformationThread) GetProcAddress(hinstLib, "NtQueryInformationThread"); 
     
            // If the function address is valid, call the function.
     
            if (NULL == ProcAdd) 
            {
    			printf("can't get NtQueryInformationThread function!");
            }
     
             FreeLibrary(hinstLib); 
    	}else{
    		printf("can't get loadlibrary ntdll function!");
    	}
    
     
        if (Thread32First(snaphnd, &info) && NULL != ProcAdd)
        {
            while (more_thread && !found)
            {
                if (info.th32OwnerProcessID==wowpid)
                {
                    if ((thrdhnd = OpenThread(0x40, false, wowtid)))
                    {
    //                    printf("Handle thread successfully opened: %08X\n", (UINT)thrdhnd);
                        if (!ProcAdd(thrdhnd,(THREADINFOCLASS )0, &tbi, 28, &retlong))
                        {
    //                        hexdump(&tbi, sizeof(tbi));
    //                        hexdump((LPDWORD)(tbi.TebBaseAddress), 0x100);
    //                        hexdump((void*)(tbi.TebBaseAddress), 0x100);
    //                        hexdump(&(tbi.TebBaseAddress), 0x100);
                            tlsoffset = ReadInt((PBYTE)(tbi.TebBaseAddress) + 0x2c);
                            targetslot = ReadInt((PBYTE)tlsoffset);
                            basepoint = ReadInt((PBYTE)(targetslot+16));  // sometimes it'll be exchanged with playerguid 
                            playerguid = ReadGuid((PBYTE)(targetslot+8));
                            found = true;
    //                        printf("****************\ntlsoffset(fs:[2c]): %08X\n\
    //targetslot([tlsoff]): %08X\n\
    //basepoint([tarslot+16]): %08X\n\
    //guid: %08X%08X\n****************\n",tlsoffset, targetslot, basepoint,playerguid.HighPart, playerguid.LowPart);
                        }
                        else
                        {
                            printf("can't query thread information");
                        }
                        CloseHandle(thrdhnd);
                    }
                    else
                    {
                        printf("can't open thread");
                    }
                }
                more_thread = Thread32Next(snaphnd, &info);
            }
            if (!found)
                ;//errmsg(L"Wired! no thread found for wow.exe.\n");
        }
        else
        {
            //errmsg(L"Thread32First failed.\n");
        }
    	CloseHandle(snaphnd);
        return found;
    }
    With the basepoint (It's the start of list of all the loaded units, players, gameobjects, items), you can build a list of units by enumerating them as:
    Code:
    BOOL enumAllObj(WOWOBENUMPROC fn)
    {
    	UINT currentptr = ReadInt((LPCVOID)(basepoint + 0x0C));	
    	int cnt = 0;
    	int mobind;
    	while ((currentptr != 0) && ((currentptr &1) ==0) && (currentptr !=0x1c)){
    		freeObj(&all[cnt]);
    		updateObj(currentptr, &all[cnt]);
    
    		if (IsValidOb(&all[cnt]))
    		{
    			//            if unitobj.guid==mbt.playerguid:
    			//                playerobj = currentptr
    			//            else:
    			//                oblist.append(unitobj)
    			//if (ob.type!= W_CONTAINER && ob.type!=W_ITEM)
    			if(playerguid==all[cnt].guid) {
    				updateObj(currentptr, &player);
    			}else if(IsValidOb(&player) && 
    				szMobList.find(_bstr_t(all[cnt].name))!=std::string::npos && 
    				( !all[cnt].isDead()||
    				all[cnt].isLootable())){
    				for(mobind=0;mobind<MAXTRACK;mobind++){
    					if(!mobsnearby[mobind] || 
    						!IsValidOb(mobsnearby[mobind]) || 
    						player.distanceObj(&all[cnt])<=player.distanceObj(mobsnearby[mobind])){
    						mobsnearby[mobind] = &all[cnt];
    						break;
    					}
    				}
    			}
    			if (all[cnt].type==W_CORPSE && fn(&all[cnt]))
    				break;
    			cnt++;
    		}else freeObj(&all[cnt]);
    		currentptr = ReadInt(LPCVOID(currentptr + 0x3c));
    	}
    }
    For other functions, you can trace from the found lua functions, and if you're really good at reversing you'll find other useful functions. Of course, it's much easier to use the info dumps threads to check what other people have found. Big Thank goes to Apoc. The key is to find some signature or patterns which are kept the same no matter how wow patches. Some strange strings would be the best choices since they are easy to search in ida.

    Answer 2. All these methods are the same since wow 1.x times (five or six years ago)... And I was like you searching for these information. I learnt a lot from a radar program called "bwh" loader or something like that. It's open source! Great credits to the author "bwh". That's why I want to share my source as well, to teach the next generations..

    Answer 3. To me , the most challenging part is the bot's AI. I sincerely hope to have a library which allows user to write scripts(xml seems to be a good choice) to run his customized bot.

    Answer 4. I've barely zero knowledge about warden. Left to be answered by Cypher or other people. But until yesterday, my bot is not detected as I'm using ReadProcessMemory only. I began to use WriteProcessMemory now to call the wow functions. Bless my bot..

  3. #3
    bnovc's Avatar Member
    Reputation
    1
    Join Date
    Dec 2006
    Posts
    3
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by wanyancan View Post
    Answer 1. There're many memory address people are mentioning, for example function address, object status offsets, other enums or structs. They are found in different ways. For lua functions, there's an easy way, just search the lua function names, say 'UnitAura". In ida, you'll find
    Code:
    .data:00A3C96C                 dd offset sub_69F5C0
    .data:00A3C970                 dd offset aUnitbuff     ; "UnitBuff"
    .data:00A3C974                 dd offset sub_6A9990
    .data:00A3C978                 dd offset aUnitdebuff   ; "UnitDebuff"
    .data:00A3C97C                 dd offset sub_6A99E0
    .data:00A3C980                 dd offset aUnitaura     ; "UnitAura"
    .data:00A3C984                 dd offset sub_6A9A30
    What program did you use to search / dump this info from? How do you use this information meaningfully afterward?

    Originally Posted by wanyancan View Post
    For for object status offsets, you can search "OBJECT_FIELD_GUID". It can be used to build a list of descriptors struction.
    Code:
    void genDescriptors(){
    	/* storage struct:
    		{obj,item} for item
    		{obj,item, container} for container
    		{obj,unit} for unit
    		{obj,unit,player} for player
    		{obj,gameobject}
    		{obj,dynamic}
    		{obj,corpse}
    	*/
    	char *types[8] = {"obj","container","item","unit","player","gameobject","dynamic","corpse"};
    	TCHAR *datatype[6] = {L"unk00", L"int32", L"unk02", L"float32",L"GUID",L"chars"};
    	UINT prevSecSize = 0;
    	UINT baseptr = 0x105CF10; //// <<<<<<<<<<<< ptr to "OBJECT_FIELD_GUID"
    	for(int sec=0;sec<8;sec++){
    		if(ReadInt(baseptr)==0) baseptr+=4; ///// shift by one DWORD. special treatment for boundry between 'player' and 'unit'. there's an additional 0x00000000;
    		UINT i = 0;
    		UINT ptr,curoffset,cursize,other,endmark;
    		TCHAR str[64];
    		TCHAR outp[200];
    		char outpp[400];
    		FILE *pf;
    		fopen_s(&pf, types[sec],"w");
    		while(true){
    			ptr = ReadInt(baseptr);
    			curoffset = ReadInt(baseptr+0x4); //total relative index count in DWORD
    			cursize = ReadInt(baseptr+0x8);
    			other = ReadInt(baseptr+0xC); 
    			if (other>=6) 
    				other = 0;
    			endmark = ReadInt(baseptr+0x18);  // the next offset , used to test whether this section has ended.
    			ReadUTF8((LPCVOID)ptr,str,64);
    
    			_stprintf_s(outp,L"[%08X]%s[%08X]\t=\t0x%08X[+%08X] \t ; // type: %s(%d), size: %d * DWORD\n",baseptr,str,ptr,curoffset*4,prevSecSize,datatype[other%6],other,cursize);
    
    			int ascLen = WideCharToMultiByte(CP_ACP,0,outp,-1,(LPSTR)outpp,0,NULL,NULL);
    			ascLen = WideCharToMultiByte(CP_ACP,0,outp,-1,(LPSTR)outpp,ascLen, NULL,NULL);
    
    			fwrite(outpp, sizeof(char),strlen(outpp), pf);
    			if(endmark!=curoffset+cursize) {
    				prevSecSize += (cursize+curoffset)*4;
    				baseptr+=0x14;
    				break;
    			}
    			baseptr+=0x14;
    		}
    		fclose(pf);
    		printf("descriptor %s has been dumped\n",types[sec]);
    	}
    }
    For generating the basePtr of list of currently visible units (Assuming you already opened the process),
    Code:
    BOOL GenBP()
    {
    
        HANDLE snaphnd = CreateToolhelp32Snapshot(0x4,0);
        if (!snaphnd)
        {
            //errmsg(L"Can't create toolhelp32 snapshot.\n");
            return false;
        }
    
        THREADENTRY32 info;
        info.dwSize = 7*sizeof(UINT);
        BOOL more_thread = true, found = false;
    
        THREAD_BASIC_INFORMATION tbi;
        ULONG retlong;
        tbi.TebBaseAddress = 0;
    
    	playerguid.LowPart = playerguid.HighPart = basepoint =  0;
       // Get a handle to the DLL module.
     
        HINSTANCE hinstLib = LoadLibrary(TEXT("ntdll")); 
    	pfnNtQueryInformationThread ProcAdd;
     
        // If the handle is valid, try to get the function address.
     
        if (hinstLib != NULL) 
        { 
            ProcAdd = (pfnNtQueryInformationThread) GetProcAddress(hinstLib, "NtQueryInformationThread"); 
     
            // If the function address is valid, call the function.
     
            if (NULL == ProcAdd) 
            {
    			printf("can't get NtQueryInformationThread function!");
            }
     
             FreeLibrary(hinstLib); 
    	}else{
    		printf("can't get loadlibrary ntdll function!");
    	}
    
     
        if (Thread32First(snaphnd, &info) && NULL != ProcAdd)
        {
            while (more_thread && !found)
            {
                if (info.th32OwnerProcessID==wowpid)
                {
                    if ((thrdhnd = OpenThread(0x40, false, wowtid)))
                    {
    //                    printf("Handle thread successfully opened: %08X\n", (UINT)thrdhnd);
                        if (!ProcAdd(thrdhnd,(THREADINFOCLASS )0, &tbi, 28, &retlong))
                        {
    //                        hexdump(&tbi, sizeof(tbi));
    //                        hexdump((LPDWORD)(tbi.TebBaseAddress), 0x100);
    //                        hexdump((void*)(tbi.TebBaseAddress), 0x100);
    //                        hexdump(&(tbi.TebBaseAddress), 0x100);
                            tlsoffset = ReadInt((PBYTE)(tbi.TebBaseAddress) + 0x2c);
                            targetslot = ReadInt((PBYTE)tlsoffset);
                            basepoint = ReadInt((PBYTE)(targetslot+16));  // sometimes it'll be exchanged with playerguid 
                            playerguid = ReadGuid((PBYTE)(targetslot+8));
                            found = true;
    //                        printf("****************\ntlsoffset(fs:[2c]): %08X\n\
    //targetslot([tlsoff]): %08X\n\
    //basepoint([tarslot+16]): %08X\n\
    //guid: %08X%08X\n****************\n",tlsoffset, targetslot, basepoint,playerguid.HighPart, playerguid.LowPart);
                        }
                        else
                        {
                            printf("can't query thread information");
                        }
                        CloseHandle(thrdhnd);
                    }
                    else
                    {
                        printf("can't open thread");
                    }
                }
                more_thread = Thread32Next(snaphnd, &info);
            }
            if (!found)
                ;//errmsg(L"Wired! no thread found for wow.exe.\n");
        }
        else
        {
            //errmsg(L"Thread32First failed.\n");
        }
    	CloseHandle(snaphnd);
        return found;
    }
    With the basepoint (It's the start of list of all the loaded units, players, gameobjects, items), you can build a list of units by enumerating them as:
    Code:
    BOOL enumAllObj(WOWOBENUMPROC fn)
    {
    	UINT currentptr = ReadInt((LPCVOID)(basepoint + 0x0C));	
    	int cnt = 0;
    	int mobind;
    	while ((currentptr != 0) && ((currentptr &1) ==0) && (currentptr !=0x1c)){
    		freeObj(&all[cnt]);
    		updateObj(currentptr, &all[cnt]);
    
    		if (IsValidOb(&all[cnt]))
    		{
    			//            if unitobj.guid==mbt.playerguid:
    			//                playerobj = currentptr
    			//            else:
    			//                oblist.append(unitobj)
    			//if (ob.type!= W_CONTAINER && ob.type!=W_ITEM)
    			if(playerguid==all[cnt].guid) {
    				updateObj(currentptr, &player);
    			}else if(IsValidOb(&player) && 
    				szMobList.find(_bstr_t(all[cnt].name))!=std::string::npos && 
    				( !all[cnt].isDead()||
    				all[cnt].isLootable())){
    				for(mobind=0;mobind<MAXTRACK;mobind++){
    					if(!mobsnearby[mobind] || 
    						!IsValidOb(mobsnearby[mobind]) || 
    						player.distanceObj(&all[cnt])<=player.distanceObj(mobsnearby[mobind])){
    						mobsnearby[mobind] = &all[cnt];
    						break;
    					}
    				}
    			}
    			if (all[cnt].type==W_CORPSE && fn(&all[cnt]))
    				break;
    			cnt++;
    		}else freeObj(&all[cnt]);
    		currentptr = ReadInt(LPCVOID(currentptr + 0x3c));
    	}
    }
    So WoW just has a list of all items, players, etc. that you referenced as 'ptr to "OBJECT_FIELD_GUID"'. That seems strange. Is that common in games? Maybe I misread?

    Is that pointer in the same location then each time you open WoW?

    Originally Posted by wanyancan View Post
    Answer 2. All these methods are the same since wow 1.x times (five or six years ago)... And I was like you searching for these information. I learnt a lot from a radar program called "bwh" loader or something like that. It's open source! Great credits to the author "bwh". That's why I want to share my source as well, to teach the next generations..
    So, what's your normal process to update your pointer after a WoW update?

    Originally Posted by wanyancan View Post
    Answer 3. To me , the most challenging part is the bot's AI. I sincerely hope to have a library which allows user to write scripts(xml seems to be a good choice) to run his customized bot.
    That seems like it to me, that's what I was hoping that there were some projects for that already.


    Thanks for all your info.

Similar Threads

  1. Model Changing Question
    By MasterYuke in forum World of Warcraft General
    Replies: 6
    Last Post: 09-17-2006, 09:56 PM
  2. Couple Private Server questions
    By Jboz in forum World of Warcraft General
    Replies: 21
    Last Post: 07-26-2006, 07:37 PM
  3. Sorry..newb mail question
    By nolbishop in forum World of Warcraft General
    Replies: 2
    Last Post: 06-07-2006, 07:21 PM
  4. Question..
    By janzi9 in forum Community Chat
    Replies: 3
    Last Post: 04-02-2006, 10:20 AM
  5. A GALB question
    By bassman in forum World of Warcraft General
    Replies: 4
    Last Post: 03-28-2006, 09:49 AM
All times are GMT -5. The time now is 08:12 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search