let's start right away.
what you can learn:
- trace and call ingame-functions like attack, cast spell, pick up, moveto, ...
- traverse object lists like items, mobs, players
- read ingame structures (class objects) and how they play together
- write your own fully client-side bot that doesnt need to simulate mouse/keyboard input and doesnt need to read pixels
tools needed:
- debugger (ollydbg, although i prefer softice but its kinda broken since xp)
- disassembler (ida pro and w32dasm)
- mem searcher (cheat engine)
- basic asm knowledge
- a brain
i have attached some pics which show some commented disassemblies, etc. you should download them and check them out when i reference them in the text (sry, i was too lazy to upload and link each of them, its just too many).
Theory:
----------
there are, more or less, 2 types of functions:
type 1:
these functions call a function that i named "PerformAction". all these functions perform some checks, and if everything seems alright, they setup the parameters for the "PerformAction" function and then call it, to create an action. most functions are this type and they will be extremely easy to find
type 2:
these function dont call "PerformAction" directly. i didnt investigate them any further, but i think they setup some stuff (like put something into a queue) and "PerformAction" may or may not be called some time later.
i will explain how to find each type of function in a little bit.
Calling Chain:
---------------
the general internal calling chain of most functions looks like this:
1) ... -> FinalDispatchHandler -> Function [ -> PerformAction ]
2) ... -> FinalDispatchHandler -> LeftClickHandler [ -> Function ] [ -> PerformAction ] (for actions that are executed via left mouse clicks)
FinalDispatchHandler:
-------------------------------
i wasnt sure how to name this one, i just left it with this name. this is the final handler for most events, actions and inputs. this function is passed an action code or something and then it calls the corresponding final function to execute the action. for example, if you press spacebar, it will call the jump function and if you left click (outside a gui element) it will call the LeftClickHandler and so on. so it forwards action to its final handlers. this handler is also called, whenever your hp/mana changes.
LeftClickHandler:
-----------------------------------------
i gave it this name because it is the final handler for a left mouse click if the mouse click doesnt resolve to a gui element. both SetTarget and MoveTo will be found inside this handler. however, both these "functions" are not seperate functions, their full code is inside this handler, so theres no actual function call to do a SetTarget or MoveTo.
Strategy:
------------
1) find a function that calls PerformAction (e.g. CastSpell)
2) get the address of this function
3) forwardtrace this function to get the PerformAction address
4) find the LeftClickHandler via bp on PerformAction when setting target or moving somewhere (both are left click functions)
5) backtrace the LeftClickHandler to get the address of FinalDispatchHandler
when we have done all this, we have all the functions we need to trace the other functions.
so, now let's find these functions via debugging
Debugging:
-------------
before you do anything, open up elementclient.exe in ida pro and w32dasm and once finished disassembling, save the disassembly outputs.
1) DrawStatusText:
-----------------------
we will need this one for some functions. this prints some text in the status window.
open your w32dasm disassembly of elementclient.exe. click the StrnRef button. search for the string "^^C8FF64". double click the string in the list. w32dasm will now jump to the code address that references this string. it should be 5946cf (see pic00 for the whole process if you need to).
now open up your ida pro disassembly. go to the address we just found (5946cf). to do this, press g and enter the address in the field like shown in pic01 and press the OK button. ida should now jump to the address and it should look similar to pic02. if your not in graph view, right click somewhere on the disassembly and select Graph View from the drop down menu. now scroll up to the start of the function. it should look similar to pic03. as you can see, the function address is 5941a0. lets rename the function to make the disassembly more readable. left click on sub_5941a0 and press n. a dialog should pop up. enter the fields like shown in pic04 and press the OK button. your disassembly should now look like shown in pic5.
DrawStatusText: 5941a0
--- beware: this function is also called for chat messages! ---
2) CastSpell #1 (pref. method, but needs venomancer lvl9 or above):
------------------------------------------------------------------------
try to cast Ironwood Scarab with insufficient chi and you will get an error message.
so what we do is, set a bp on DrawStatusText, cast ironwood scarab with insufficient chi, and then we trace from DrawStatusText to CastSpell in olly.
this one time im gonna show you how to do it in depth. after that, i will refer to this one.
so open up olly and attach it to elementclient.exe as shown in pic06. once attached, press f9 to resume the process.
now we set a bp on DrawStatusText (5941a0). to do this, we have to go to the function address of DrawStatusText. press ctrl+g. a dialog as shown in pic07 will show up. enter the address (5941a0) in the field as shown in pic07 and press the OK button. olly should now look as shown in pic08. press f2 to set a bp on the address. olly should now look as shown in pic09 (notice the red background of the address indicates a bp).
now our bp is set and we can finally cast ironwood scarab with insufficient chi. the bp will be hit and you will be right back in olly. now trace to the function that called DrawStatusText. to do this, first press ctrl+f9, this will "jump" to the end of the current function (DrawStatusText) as shown in pic10 (you should be at address 594b0b now). now press f8 and you will be inside the function that called DrawStatusText. you should now be at 4373cb as shown in pic11, which is inside one of the DrawStatusError functions, which loads a "predefined" error string by its id and then calls DrawStatusText accordingly. so press f8 3 more times to trace over the RETN at 4373d2 as shown in pic12. you should now be at address 45cd70 as shown in pic13, which is inside the function that called DrawStatusError. in other words, your now inside the CastSpell function. so now open ida and go to the address we just found (45cd70). to do this, press g and enter the address in the field and press OK as shown in pic13. your ida should now look similar to pic14.
go on at 4) CastSpell #3 after you read 3) CastSpell #2
3) CastSpell #2 (no requirements, just a class that can cast a damage spell):
--------------------------------------------------------------------------
CastSpell tries to use the current target, if no target is passed as a param. so if we dont have a target and try to cast a damage spell, CastSpell will read out the current target id. so what we do is, add the address of the current target id to our cheat engine address list and then find out what accesses this address and then we cast a damage spell with no current target.
pic21 and pic22 will show you how to add this address to your ce address list if you dont know how to do it. in fact, the address is resolved as following:
[[9b4594] + 0x20] + 0xaf0
there are also tuts on these forums on how to find the "base" address.
alright, now go ingame, and deselect any current target if you have one. now add the address to your ce list and find out what accesses this address. now go back ingame, wait a little, move around a little. ce should now show some addresses in the list. it should look similar to pic23. now cast a damage spell with no current target, ce should show 2 new addresses as shown in pic24. the address 45ccbb as shown in pic24 is the one we are looking for. this address is inside the CastSpell function. so now open ida and go to the address we just found (45ccbb). to do this, press g and enter the address in the field and press OK as shown in pic25. your ida should now look similar to pic26.
go on at 4) CastSpell #3
4) CastSpell #3 (function address and prototype):
-----------------------------------------------------
alright, now you got that address inside the CastSpell function. now scroll up to the function start in ida graph view. it should look similar to pic15. as you can see, the CastSpell function address is 45cc50. but before we go on, lets make the disassembly more readable. lets rename the function first. to do this, left click on sub_45cc50 and press n. a dialog should pop up. enter the fields as shown in pic16 and press OK. now lets set the function prototype. again, left click on the function name and press y. a dialog should pop up. enter the field as shown in pic17 and press OK. the function should now look as shown in pic18 (CastSpell prototype). as you can see, there are 2 params (dw0, dw1) that i didnt investigate further. setting both to 0 worked for me everytime, so i just left them like that. they have something to do with the action objects that i will cover later. if you want to know what these params are for, you can investigate further if you want to. pic19 and pic20 show some parameter stuff and how the function is called, check them out if you want to.
here are some spell ids that you can pass as a param.
i hope they are still up to date, i didnt test them in the latest version.
you can find out more yourself by setting a bp on CastSpell and checking the params for each spell.
#define SPELLID_VENOMOUS_SCARAB 0x12b
#define SPELLID_IRONWOOD_SCARAB 0x12c
#define SPELLID_BLAZING_SCARAB 0x12d
#define SPELLID_FROST_SCARAB 0x12e
#define SPELLID_NATURES_GRACE 0x134
#define SPELLID_METABOLIC_BOOST 0x133
#define SPELLID_SOUL_TRANSFUSION 0x137
#define SPELLID_HEAL_PET 0x14a
#define SPELLID_REVIVE_PET 0x149
#define SPELLID_TAME_BEAST 0x148
#define SPELLID_SUMMER_SPRINT 0x2fa
#define SPELLID_BRAMBLE_GUARD 0x132
#define SPELLID_FOX_FORM 0x138
#define SPELLID_TOWN_PORTAL 0xa7
CastSpell: 45cc50 (pic18)
5) PerformAction, CreateActionObj, PrepareActionObj:
----------------------------------------------------------
as already said, PerformAction is called inside the CastSpell function. in fact, its the very last call on a successfull spell cast inside the CastSpell function. from now on, all pics will show already processed disassemblies (which means that i already renamed the functions and set the prototypes). so from now on, you will have to rename the functions and set the prototypes yourself as we did it before. the pics will show you how i named the functions and declared the prototypes. you can, of course, choose other names.
so, open up the CastSpell function in ida and scroll down to the end of the function. pic27 will show you the 3 function calls and pic28-pic30 will show you the prototype of each function. as you can see, i didnt investigate too much into these functions, since i dont really need to call them (only to emulate a MoveTo(x,y,z)). you can of course investigate further if you want to know more about action objects and the like.
CreateActionObj: 466c70 (pic28)
PrepareActionObj: 46e300 (pic29)
PerformAction: 467070 (pic30)
for the following stuff, make sure you have no pet out and your at full hp/mp because pets and hp/mp changes might trigger FinalDispatchHandler.
8) LeftClickHandler / FinalDispatchHandler
-------------------------------------------
bp on PerformAction (467070). move somewhere via left mouse click. trace to the function that called PerformAction (in olly press ctrl+f9 and then f8). write down the addr (it should be 45f1a2, which is inside LeftClickHandler). now further trace via f8 in olly over the next RETN and find out what called the LeftClickHandler. also write down this addr (it should be 44fdda, which is inside the FinalDispatchHandler). open up both addresses in ida graph view and scroll up to the function starts. these are your LeftClickHandler / FinalDispatchHandler addresses.
LeftClickHandler: 45eaf0 (pic31)
FinalDispatchHandler: 44fd40 (pic32)
once we have all that, the rest isnt very hard.
9) Methods to find the other functions:
--------------------------------------
always make sure, no pet is out and be at full hp/mana
a) type 1 functions:
- CastSpell (already got that)
- Pickup / Gather (same function)
- Attack
bp on PerformAction. execute the corresponding action ingame. trace back to the function that called PerformAction (in olly press ctrl+f9 and then f8). write down the addr. open up this addr in ida graph view. scroll up to function start. thats your function addr.
Attack 462740 (pic33)
Pickup/Gather: 462800 (pic34)
b) type 2 functions:
- Jump
- CancelAction
- DoAction
bp on FinalDispatchHandler. execute the corresponding action ingame. forwardtrace to the very first call (in olly press f8 until you come across a call instruction). write down the addr that is being called. this is your function addr.
Jump: 455430 (pic35)
CancelAction: 4551f0 (pic36)
DoAction(Fly, Meditate, ...): 4576d0 (pic53) check 15) DoAction for more info
10) MoveTo:
------------------
bp on CreateActionObj. move somewhere ingame via left mouse click. trace to the function that called CreateActionObj (in olly press ctrl+f9 and then f8). write down the addr (it should be 45f033, which is inside the LeftClickHandler). open this addr in ida. you will see, that first CreateActionObj is called, then 2 more functions are called (sub_46a890 and sub_46a9e0). they probably convert mouse coords to game coords and store them in the actionobj. we have to find out, how these functions exactly alter the actionobj before PerformAction is called. if you want to, you can inspect these functions deeper, but i have some pseudo code, that alters the actionobj "enough" that it works. it doesnt do everything these 2 functions do, but it worked. hope it still works:
Code:
typedef struct __tagACTIONOBJMOVETO { char uk0[0x20]; // 0x00 float x; // 0x20 float z; // 0x24 float y; // 0x28 DWORD dwAction; // 0x2c 0: move } ACTIONOBJMOVETO, *LPACTIONOBJMOVETO; ACTIONOBJMOVETO *p; p = CreateActionObj(p_localplayer->p_actionbase0, 1); p->x = x; p->y = y; p->z = z; p->dwAction = 0; PerformAction(p_localplayer->actionbase0, 1, p, 1, 0);
11) SetTarget:
--------------------
as already stated, this one is also inside the LeftClickHandler. either bp on LeftClickHandler and forwardtrace it yourself or do this:
before SetTarget is called inside the LeftClickHandler, the new target id will be stored in a variable i named dwTmpNewTargetId. the address of this variable is the address of the current target id + 4. see pic37 on how to get the address if you dont know what i mean.
so, now via cheat engine, find out what writes to this address. now go ingame and set a new target. 2 addresses will pop up. use the first one, it should be 45eef7, which is inside the LeftClickHandler. open this addr in ida pro and check pic38. here you will see how SetTarget is called and how to resolve the class object that is being used (p_objdbbase4).
SetTarget: 5cc0c0 (pic39)
12) Equip/Use
------------------
bp on DrawStatusText. try to equip something you cannot equip or try to use something in your inventory that is on cooldown (-> an error message will be printed). trace to the function that called DrawStatusText (in olly pres ctrl+f9 and then f8). your now inside a DrawStatusError function. so further trace with f8 over the net RETN. you should now be at address 45c95e, which is inside the Equip/Use function. so open this address in ida pro. scroll up to the function start. thats your Equip/Use function.
Equip/Use: 45c280 (pic40)
but how do we get the class object (p_invobjdb)? look at the xrefs of this function as shwon in pic40. check the xref with the red circle in pic40. you will see that a function (i named it GetInvObjDB) is called which returns p_invobjdb in eax (check pic41). so check out this function now (compare pic42). its actually very easy, it starts out with a static pointer.
13) Mob/Player/NPC/Item/Resource Lists:
------------------------------------------
open the Attack function in ida pro and check pic43. as you can see, a function is being called which returns the object with the dwTargetId id. i named this function GetObjById. pic43 shows how to resolve the class object (p_worldobjdb) for the function.
GetObjById: 42c820 (pic44)
if you analyze this function a bit, you will see, that the id is checked with some flags to determine whether the id is a mob/npc, a player, or an item/resource. (there are 3 lists, 1 for mobs/npcs, 1 for players, 1 for items/resources).
pic45 and pic46 show the calls for the 3 types of ids. (different list pointers for each id type).
GetObjByIdFromObjList: 5ad4f0 (pic47)
this function shows you, how the lists are built up and how to traverse it (sort of). offset 0x18 into the list class is a pointer to a sequential list of pointers to listentries which store the id of the obj and the pointer to the obj. offset 0x04 into the listentry stores a pointer to the obj and offset 0x08 into the listentry stores the id of the obj.
GetPlayerByIdFromPlayerList: 5b66e0 (pic48)
this function actually calls GetObjByIdFromObjList, but before it checks whether the id belongs to the localplayer. it does this because the localplayer is not in the playerlist.
14) Inventory lists:
-------------------------
open the Equip/Use function in ida pro and check pic49. there are 7 lists for different inventory items. 1 list (id 0) is the list for items in your inventory (backpack), 1 list (id 1) is for items that are currently equipped ("normal" items), the other lists should be for quest items, costumes etc., but i didnt check them because i dont care.
GetInvItemListById: 45c1e0 (pic50)
this function returns the address of the list class object for the list with the id passed as param. actually this one is pretty easy. pic51 shows the offsets into the p_invobjdb for the pointers to the inventory items and normal equipped items lists.
once we have the list the item we want to use is in, we can finally call GetInvItemBySlotFromList
GetInvItemBySlotFromList: 48baa0 (pic52)
the class object for this member function is of course the corresponding list, the parameter is the item slot of the item we want to use (e.g. item slot 0 and the list with id 0 would be the very first item in your inventory backpack). this function shows you how the inventory lists are built and how to traverse them. offset 0x10 into the list class object stores the number of total item slots (any slot index >= this value is a bad slot index). offset 0x0c into the list class object stores a pointer to a sequential list of pointers to inventory item objects.
15) DoAction (Fly, Meditate, ...):
--------------------------------------
DoAction is called for Fly and Meditate, probably for all other actions like Nod, Salute, Provoke, etc. as well. this function is passed a pointer to a structure i named DOACTIONPARAM. i didnt fully "decrypt" the structure, only the fields i needed, here it is:
Code:
typedef struct __tagDOACTIONPARAM // param passed to DoAction { DWORD uk0; // 0x00 DWORD uk1; // 0x04 DWORD uk2; // 0x08 void *p_data; // 0x0c might be a pointer to some data DWORD dwAction; // 0x10 check DA_ constants } DOACTIONPARAM, *LPDOACTIONPARAM; #define DA_MEDITATE 0x6f #define DA_STOPMEDITATE 0x70 #define DA_FLY 0x60 #define DA_STOPFLY 0x61
Code:
DOACTIONPARAM dap; dap.dwAction = (bFly) ? DA_FLY : DA_STOPFLY; DoAction(p_localplayer, &dap);
i will provide some structure definitions written in c++. i dont play this game anymore, i just wrote this and debugged pw again just for fun. so some of the structures are not up to date and need some (minor?) updating. but i updated all the structures i was referring to above. structures like MOBOBJ, WORLDITEMOBJ and pet stuff, which i didnt cover, i didnt update because i really hate mem dumping and checking offset over offset. you can of course update them yourself if you havent already done so. maybe i will go over pet commands and the like later, but i dont know if i wanna do this now. the structures might give you a better idea on how to traverse lists so that you can find whatever you want. a small tip on traversing lists: go through all pointers from start to end, check for null pointers, check for valid ids, use exception handling. i hope theres no errors in the code because i updated some offsets and names to match the disassemblies i provided and havent tested it, but it should be fine.
note on the code:
i decided not to include any real function code. i included most important ingame structures which will enable you to write your own functions to traverse lists. you also have most important ingame function addresses which you can call if you want to. the code is best used from within the target process, since you can obtain objects very easily starting from p_base0 or p_objdbbase0 via structure pointers.
note on calling functions:
most ingame functions are member functions (__thiscall calling convention). this means, the first param is always the this pointer (passed in ecx), the other params are passed like in __stdcall (pushed onto stack right to left, called functions cleans stack).
Code:
#define MAKEPTR(base, offset) ((DWORD) base + (DWORD) offset) /////////////////////////////////////////////////////// // *** (class) object pointers *** /////////////////////////////////////////////////////// #define VA_BASE0 0x9b4594 // address of pointer to p_base1 #define VA_OBJDBBASE0 0x9b3eec // address of pointer to p_objdbbase1 #define OBJDBBASE4OFFSET 0xec // p_objdbbase4 = p_objdbbase0->p_objdbbase1->p_objdbbase3 + OBJDBBASE4OFFSET ////////////////////////////////////////////////////// // *** (class) object structures *** ////////////////////////////////////////////////////// #pragma pack(push,1) typedef struct __tagPOINT3D { float x; // 0x00 float z; // 0x04 float y; // 0x08 } POINT3D, *LPPOINT3D; typedef struct __tagACTIONBASE1 // *** up to date *** { DWORD uk0; // 0x00 DWORD dwActionState; // 0x04 + check AS_ constants char uk1[0x10]; // 0x08 //void *p_actionbase2; // 0x18 not used and probably outdated } ACTIONBASE1, *LPACTIONBASE1; typedef struct __tagACTIONBASE0 // *** up to date *** { DWORD uk0; // 0x00 void *obj; // 0x04 + pointer to player object (if mobs have this too, it would probably be pointer to mob object ;) ) char uk1[0x0c]; // 0x08 ACTIONBASE1 *p_actionbase1; // 0x14 + dynamically changed for every action (=> current action base) BOOL bPerformingAction; // 0x18 + careful with this one, if you cast a spell and are out of range // it will be set to 1 when walking, set to 0 when stop walking, then // the attack command is sent to the server and when confirmation is // received, it will be set back to 1 for casting (same for attacking) } ACTIONBASE0, *LPACTIONBASE0; typedef struct __tagPETOBJ // *** up to date *** { char uk0[0x04]; // 0x00 DWORD dwLoyalty; // 0x04 + DWORD dwHunger; // 0x08 + check PH_ constants char uk1[0x14]; // 0x0c DWORD dwLevel; // 0x20 + char uk2[0x04]; // 0x24 DWORD dwXP; // 0x28 + char uk3[0x08]; // 0x2c WCHAR *lpName; // 0x34 + DWORD dwHP; // 0x38 + // dwMaxHP can be found by finding the MOBOBJ of your pet in the moblist (your pet: dwOwnerId == p_localplayer->dwId) } PETOBJ, *LPPETOBJ; typedef struct __tagPETLISTHEADER // *** up to date *** { char uk0[0x08]; // 0x00 DWORD dwCurrentPet; // 0x08 + 0, 1, 2, ... 9 NOPET: no pet DWORD dwPetBagSize; // 0x0c + 1, 2, ... a size of pet bag (number of pet slots) PETOBJ *pet[0x0a]; // 0x10 + list of pointers to pet objects DWORD dwCurrentPetId; // 0x38 + char uk1[0x04]; // 0x3c DWORD dwPetMode; // 0x40 + check PM_ constants } PETLISTHEADER, *LPPETLISTHEADER; typedef struct __tagPLAYEROBJ // local player is not in the player list *** mostly up to date (+ indicates up to date) *** { char uk0[0x0c]; // 0x00 float player_angle0; // 0x0c ? orientation float uk1; // 0x10 float player_angle1; // 0x14 ? orientation char uk2[0x24]; // 0x18 POINT3D pos; // 0x3c + char uk3[0x410]; // 0x48 DWORD dwId; // 0x458 + DWORD uk4; // 0x45c DWORD uk5; // 0x460 DWORD dwLevel; // 0x464 + DWORD uk6; // 0x468 DWORD dwHP; // 0x46c + DWORD dwMP; // 0x470 + DWORD dwXP; // 0x474 + DWORD dwSpirit; // 0x478 + DWORD dwAvailableAP; // 0x47c + available attribute points DWORD dwChi; // 0x480 char uk7[0x10]; // 0x484 DWORD dwCon; // 0x494 + DWORD dwInt; // 0x498 + DWORD dwStr; // 0x49c + DWORD dwAgi; // 0x4a0 + DWORD dwMaxHP; // 0x4a4 + DWORD dwMaxMP; // 0x4a8 + char uk8[0x0c]; // 0x4ac float ground_speed; // 0x4b8 + char uk9[0x08]; // 0x4bc DWORD dwAccuracy; // 0x4c4 + DWORD dwMinPhysAtk; // 0x4c8 + DWORD dwMaxPhysAtk; // 0x4cc + char uk10[0x38]; // 0x4d0 DWORD dwMetalRes; // 0x508 + DWORD dwWoodRes; // 0x50c + DWORD dwWaterRes; // 0x510 + DWORD dwFireRes; // 0x514 + DWORD dwEarthRes; // 0x518 + DWORD dwPhysDef; // 0x51c + DWORD dwEva; // 0x520 + evasion char uk11[0x04]; // 0x524 DWORD dwMoney; // 0x528 + char uk12[0x64]; // 0x52c DWORD dwReputation; // 0x590 DWORD dwTransformState; // 0x594 0: human; 1: fox char uk13[0x70]; // 0x598 WCHAR *lpName; // 0x608 + unicode string char uk14[0x04]; // 0x60c DWORD dwClassId; // 0x610 DWORD dwGender; // 0x614 char uk15[0x04]; // 0x618 DWORD dwTransportMode; // 0x61c + 0: ground; 1: swim; 2: fly char uk16[0x5c]; // 0x620 BOOL bAggro; // 0x67c char uk17[0x470]; // 0x680 DWORD dwTargetId; // 0xaf0 + DWORD dwTmpTargetId; // 0xaf4 + char uk18[0xf4]; // 0xaf8 DWORD dwJumpState; // 0xbec + 0, 1, 2 char uk19[0x258]; // 0xbf0 ACTIONBASE0 *p_actionbase0; // 0xe48 + char uk22[0x14]; // 0xe4c PETLISTHEADER *p_petlist; // 0xe60 + } PLAYEROBJ, *LPPLAYEROBJ; typedef struct __tagMOBOBJ // also for npcs and pets (npcs and pets are in the mob list too) *** outdated *** { char uk0[0x0c]; // 0x00 float angle0; // 0x0c float uk1; // 0x10 float angle1; // 0x14 char uk2[0x24]; // 0x18 POINT3D pos; // 0x3c char uk3[0x6c]; // 0x48 DWORD dwType; // 0xb4 9: pet; 7: npc; 6: monster (check MOBTYPE_ constants) char uk4[0x64]; // 0xb8 DWORD dwId; // 0x11c char uk5[0x04]; // 0x120 DWORD dwLevel; // 0x124 char uk6[0x04]; // 0x128 DWORD dwHP; // 0x12c char uk7[0x2c]; // 0x130 DWORD dwMaxHP; // 0x15c char uk8[0xd8]; // 0x160 DWORD dwOwnerId; // 0x238 0: no owner; else pet owner id WCHAR *lpName; // 0x23c unicode string } MOBOBJ, *LPMOBOBJ; typedef struct __tagWORLDITEMOBJ // also for resources (resources on the ground are also in the item list) *** probably outdated *** { char uk0[0x14]; // 0x00 DWORD dwAmount; // 0x14 DWORD dwMaxAmount; // 0x18 DWORD dwPrice; // 0x1c char uk1[0xec]; // 0x20 DWORD dwId; // 0x10c char uk2[0x54]; // 0x110 WCHAR *lpName; // 0x164 } WORLDITEMOBJ, *LPWORLDITEMOBJ; typedef struct __tagGUIBASE1 // *** up to date *** { char uk0[0x18]; // 0x00 void *p_lastcmdsent; // 0x18 the gui obj that was sent the last cmd char uk1[0xb8]; // 0x1c void *p_focus; // 0xd4 the gui obj that the mouse is over (has the mouse focus) char uk2[0x1a4]; // 0xd8 void *p_petbag; // 0x27c char uk3[0x38]; // 0x280 void *p_petbar; // 0x2b8 char uk4[0x8c]; // 0x2bc void *p_mainbar; // 0x348 void *p_prbar; // 0x34c public relations bar } GUIBASE1, *LPGUIBASE1; typedef struct __tagGUIBASE0 // *** up to date *** { char uk0[0x08]; // 0x00 GUIBASE1 *p_guibase1; // 0x08 } GUIBASE0, *LPGUIBASE0; typedef struct __tagBASE1 // *** up to date *** { DWORD uk0; // 0x00 GUIBASE0 *p_guibase0; // 0x04 + pointer to guibase0 char uk1[0x18]; // 0x08 PLAYEROBJ *p_localplayer; // 0x20 + pointer to local player obj } BASE1, *LPBASE1; typedef struct __tagBASE0 { BASE1 *p_base1; // 0x00 (this whole struct is actually just a pointer) } BASE0, *LPBASE0; typedef struct __tagWORLDOBJLISTENTRY // check GetObjByIdFromObjList *** up to date *** { __tagWORLDOBJLISTENTRY *next; // 0x00 void *p_obj; // 0x04 pointer to OBJ (player, mob/npc/pet, item/resource) DWORD dwId; // 0x08 } WORLDOBJLISTENTRY, *LPWORLDOBJLISTENTRY; typedef struct __tagWORLDOBJLISTHEADER // check GetObjByIdFromObjList *** up to date *** { char uk0[0x14]; // 0x00 DWORD dwObjects; // 0x14 number of (valid) list entries (they are scattered over the entire list); invalid list entries == 0 WORLDOBJLISTENTRY **p_listentry; // 0x18 pointer to sequential list of WORLDOBJLISTENTRYs void *lpEndOfList; // 0x1c either end of list or pointer to another structure DWORD dwListEntries; // 0x20 the size of the list in number of entries DWORD dwIdEntryConversion; // 0x24 Id / dwIdEntryConversion = List Entry to start } WORLDOBJLISTHEADER, *LPWORLDOBJLISTHEADER; typedef struct __tagWORLDOBJDB // world object database (check GetObjById) *** up to date *** { char uk0[0x20]; // 0x00 WORLDOBJLISTHEADER *p_playerlist; // 0x20 players only (localplayer is not in this list) WORLDOBJLISTHEADER *p_moblist; // 0x24 mobs, npcs, pets WORLDOBJLISTHEADER *p_itemlist; // 0x28 items (on ground), resources (on ground) } WORLDOBJDB, *LPWORLDOBJDB; typedef struct __tagINVITEMOBJ // *** probably outdated *** { char uk0[0x14]; // 0x00 DWORD dwAmount; // 0x14 DWORD dwMaxAmount; // 0x18 DWORD dwPrice; // 0x1c char uk1[0xec]; // 0x20 WCHAR *lpName; // 0x50 DWORD dwId; // 0x10c char uk2[0x54]; // 0x110 } INVITEMOBJ, *LPINVITEMOBJ; typedef struct __tagINVOBJLISTHEADER // check GetInvItemBySlotFromList *** up to date *** { char uk0[0x0c]; // 0x00 INVITEMOBJ **p_item; // 0x0c pointer to sequential list of pointers to inv item objects DWORD dwTotalSlots; // 0x10 } INVOBJLISTHEADER, *LPINVOBJLISTHEADER; typedef struct __tagINVOBJDB // check GetInvItemListById *** up to date *** { char uk0[0xc34]; // 0x00 INVOBJLISTHEADER *p_invitemlist; // 0xc34 list id: 0 inventory items INVOBJLISTHEADER *p_normalequipitemlist; // 0xc38 id: 1 normal equipped items INVOBJLISTHEADER *p_list2; // 0xc3c id: 2 char uk1[0x30]; // 0xc40 INVOBJLISTHEADER *p_list3; // 0xc70 id: 3 INVOBJLISTHEADER *p_list4; // 0xc74 id: 4 INVOBJLISTHEADER *p_list5; // 0xc78 id: 5 INVOBJLISTHEADER *p_list6; // 0xc7c id: 6 } INVOBJDB, *LPINVOBJDB; typedef struct __tagOBJDBBASE2 // *** up to date *** { char uk0[0x08]; // 0x00 WORLDOBJDB *p_worldobjdb; // 0x08 world object database char uk1[0x14]; // 0x0c INVOBJDB *p_invobjdb; // 0x20 inventory item database } OBJDBBASE2, *LPOBJDBBASE2; typedef struct __tagOBJDBBASE1 // *** up to date *** { char uk0[0x1c]; // 0x00 OBJDBBASE1 *p_objdbbase2; // 0x1c void *p_objdbbase3; // 0x20 (includes p_objdbbase4) } OBJDBBASE1, *LPOBJDBBASE1; typedef struct __tagOBJDBBASE0 { OBJDBBASE1 *p_objdbbase1; // 0x00 (this struct is actually just a pointer) } OBJDBBASE0, *LPOBJDBBASE0; #pragma pack(pop) ///////////////////////////////////////////////////// // *** constants and macros *** ///////////////////////////////////////////////////// #define NOTARGET 0x00000000 #define INVALIDTARGET NOTARGET #define NOCURRENTPET 0xffffffff // dwCurrentPet // player action states #define AS_NONE 0 // standing still, doing nothing (also when flying and standing still) #define AS_MOVE 1 // either walking, jumping or flying somewhere #define AS_MOVEINTORANGE 2 // moving somewhere to execute an action (cast, atk, gather, talk to, ...) #define AS_ATTACK 3 // attacking #define AS_CAST 4 // casting (channeling) a spell #define AS_MEDITATE 0x0a // meditating #define AS_GATHER 0x0b // gathering resources // pet hunger states #define PH_FULL 0 #define PH_SATISFIED 1 #define PH_PECKISH 2 #define PH_HUNGRY0 3 #define PH_HUNGRY1 4 #define PH_STARVING 5 // pet modes #define PM_DEFEND 0 #define PM_AUTO 1 #define PM_MANUAL 2 // the pet mode must be set via ingame function to tell the server (modifying in mem doesnt work) // pet attack when casting a spell is totally server side // if you change pet mode in mem from auto to manual and dont tell the server, the pet will still attack when // you cast a spell // pet gui commands #define PETBAR_ATTACK "attack" #define PETBAR_FOLLOW "follow" #define PETBAR_STOP "stop" #define PETBAR_MANUALMODE "combat" #define PETBAR_DEFENDMODE "defensive" #define PETBAR_AUTOMODE "offensive" #define PETBAG_CALLX "summon%d" // X: 1 based pet index (not zero based) #define PETBAG_STOWX "recall%d" #define PETBAG_STATSX "detail%d" // mob stuff (these are only valid for MOBOBJ objects) #define MOBTYPE_MOB 6 #define MOBTYPE_NPC 7 #define MOBTYPE_PET 9 #define ISMOB(obj) ((BOOL) (obj->dwType == MOBTYPE_MOB)) #define ISNPC(obj) ((BOOL) (obj->dwType == MOBTYPE_NPC)) #define ISPET(obj) ((BOOL) (obj->dwType == MOBTYPE_PET)) ///////////////////////////////////////////////// // *** class object pointers *** ///////////////////////////////////////////////// BASE0 *p_base0 = (BASE0 *) VA_BASE0; OBJDBBASE0 *p_objdbbase0 = (OBJDBBASE0 *) VA_OBJDBBASE0; // class object macros #define LOCALPLAYER() p_base0->p_base1->p_localplayer #define OBJDBBASE1() p_objdbbase0->p_objdbbase1 #define OBJDBBASE4() ((void *) MAKEPTR(p_objdbbase0->p_objdbbase1->p_objdbbase3, OBJDBBASE4OFFSET)) // p_objdbbase4 #define MOBLISTHEADER() p_objdbbase0->p_objdbbase1->p_objdbbase2->p_worldobjdb->p_moblist #define PLAYERLISTHEADER() p_objdbbase0->p_objdbbase1->p_objdbbase2->p_worldobjdb->p_playerlist #define WORLDITEMLISTHEADER() p_objdbbase0->p_objdbbase1->p_objdbbase2->p_worldobjdb->p_itemlist #define INVITEMLISTHEADER() p_objdbbase0->p_objdbbase1->p_objdbbase2->p_invobjdb->p_invitemlist; #define EQUIPITEMLISTHEADER() p_objdbbase0->p_objdbbase1->p_objdbbase2->p_invobjdb->p_equipitemlist;
hope it helped a bit.
*** update ***
i checked some structures, the following structures are still up to date:
ACTIONBASE0
ACTIONBASE1
PETOBJ
PETLISTHEADER
i also updated the offset of the pointer to the petlistheader into the PLAYEROBJ.
*** update ***
BASE1, GUIBASE0, GUIBASE1 updated
Pet Functions / GUI Commands:
-----------------------------------
in pw, theres a function that sends gui commands to gui elements. its similar to the SendMessage api, but it tells the gui element what to do, instead of what has happened.
all gui element objects are stored inside p_base0->p_base1->p_guibase0->p_guibase1 (i will update these structures in the attached code). once you found the GuiCommand function and the gui object, you can send commands to this object. so lets start to find some stuff.
GuiCommand:
----------------
open elementclient.exe in w32dasm. search for the string "Dlg_Building". double click it. there should be one reference to it at address 5928a0. this address is inside the GuiCommand function. so open ida pro graph view, jump to this address and scroll up to the function start.
GuiCommand looks like this:
the first param is a string (not unicode) which contains the command to execute. the second param is a pointer to the gui object to send the command to.
Gui Command and Objects:
--------------------------------
now we need to get some commands and objects so that we can use GuiCommand. i will explain how to send the "stow pet x" command to the petbag gui object (this principle applies to all other commands as well, so dont worry if you dont have a pet).
first of all, we need a good bp inside GuiCommand. 59271a is a good one, since the pointer to the gui command string will be stored in ebx and the pointer to the gui object will be stored in esi.
so, first open up your petbag. now set a bp on 59271a. now stow your pet (in the following pic i stowed my first pet). your bp will hit. dont resume the process for the following actions. now check the following pic:
as you can see, ebx points to the string "recall1". so the command to stow pet x is "recallx". esi holds the petbag gui object.
now lets find out how to get the petbag gui object.
as already said, all gui objects are stored inside p_base0->p_base1->p_guibase0->p_guibase1. so all we need to do, is search this area for the pointer to the petbag gui object. the pic shows you how to do it. (1) shows you how to get the address of p_guibase1. enter this address into the search range start. search range end should be search range start + whatever, i chose + 0x3000, dont make it too small. enter the value stored in esi (the petbag object) into the value to search field (tab hex). now start the search. you will most likely find 3 addresses if you havent resumed the process, which you shouldnt. now resume the process and play around a bit with your mouse and click some gui elements etc. you will see, that 2 addresses will change whenever you do something. in fact it's like this: the first address we found is the address of a pointer to the gui object that received the last command. the second address we found is the address of a pointer to the gui object that the mouse is hovering (that gui object has the mouse "focus"). the third address is the address of the pointer to our petbag gui object. now to get the offset into the p_guibase1 structure, simply substract the address of p_guibase1 from the addresses you found (in my pic, the first address (the address of the pointer to the gui object that recieved the last gui command) would have the offset 14583018 - 14583000 = 0x18). the second address (focus) offset would be 0xd4 and the third (petbag) would be 0x27c.
Pet Functions:
------------------
so to find out how to do pet functions, summon a pet, set the bp like above, click the attack button, and do the same as above (ebx holds the command string "attack", esi is the gui obj (petbar)). filter out the gui object (eliminate the addresses with the offsets 0x18 and 0x4d, which i explained above). the pet command strings are included in the attached source.
so, finally to make your pet attack, do this (pseudo code):
GuiCommand("attack", p_base0->p_base1->p_guibase0->p_guibase1->p_petbar)
p_petbar: [[[[9baad4] + 4] + 8] + 2b8]
p_petbag: [[[[9baad4] + 4] + 8] + 27c]