Here you have a bit more than a hint about how to properly get a node from the GOM.
The function I am going to reverse is one which gets the node for the Player from the GOM.
It is loaded at swtor.exe + 7AF7B0
Now we are at CALL LEVEL 0, address swtor.exe + 7AF7B0
PUSH EBP
MOV EBP, ESP -- Calling convention in C
MOV EAX, [swtor.exe + 1075E48 ] -- Access static pointer and store value pointed by it in EAX. This will eventually get us the player ID
MOV EAX, [ EAX+4 ] -- Access 2nd element in some structure pointed
... skipping tests for NULL.....
MOV ECX, [ EAX+108 ] -- Get the low ID for the player
MOV EDX, [ EAX+10C ] -- Get the high ID for the player.
Thus we have another way to get the player ID different than the one I got from mem scanning.
[ [ [ swtor.exe + 1075E48 ] + 4 ] 108 ] = Player ID, int64
Although this was not the purpouse of this reversing so let's continue.
PUSH 01 -- 3rd parameter to next function to be called by C convention
PUSH EDX -- High Id 2nd parameter to next func to be called
PUSH ECX -- Low Id 1st parameter to next func to be called
Or it could be that EDX and ECX make the first parameter which would be a int64, and 01 the second parameter.
Both things are equivalent from our point of view.
MOV ECX, [EBP+C] -- Mov the 2nd parameter to this function to ECX. This paremeter is a pointer which was passed by
the function which called this function and will be used to return a result by reference in a parameter.
CALL swtor.exe + C7F50
Now we are at CALL LEVEL 1, address swtor.exe + C7F50
PUSH EBP
MOV EBP, ESP -- Calling convention in C
AND ESP, F8 -- Alling ESP
MOV EAX, [EBP+10] - EAX <- 3rd parameter of this function (1)
MOV EDX, [EBP+8 ] - EDX <- 1st parameter of this func ( low ID )
PUSH ESI - Preserve
PUSH EDI - Preserve
MOV ESI, ECX -- The pointer to parameter by reference
MOV ECX, [EBP+C] - ECX <- 2nd parameter of this func ( high ID )
MOV EDI, [ESI] - EDI <- value of the parameter passed by reference
PUSH EAX - 3rd parm next func ( 1 )
PUSH ECX - 2nd parm next func ( high ID )
PUSH EDX - 1st parm next func ( low ID )
CALL swtor.exe + A3BE0
We are now at CALL LEVEL 2, address swtor.exe + A3BE0
PUSH EBP
MOV EBP, ESP -- Calling convention in C
AND ESP, F8 -- Alling ESP
MOV EAX, FS:[0] -- Related to exceptions
PUSH FF
PUSH swtor.exe+BF5028 -- Parameter for exception
PUSH EAX -- Parameter for exception
MOV FS:[0], ESP -- Related to exceptions
MOV EAX, [swtor.exe+1074EBC] -- Here we are reading what in previous posts I had used as the base of MA (Mob Array )
As we will soon see it is actually the base for the GOM.
SUB ESP, 58 -- Space for local variables
PUSH ESI -- Preserve
XOR ESI, ESI -- ESI <- 0
CMP EAX, ESI -- Check if the base for the GOM is NULL
JNE -- Jump if the base is not null (jump taken )
If the jump were not taken, that is if the GOM was null, this would be executed :
PUSH some parameter
PUSH address of string "Attempt to get GOM when none exists"
This is quite a hint, isn't it?
Now let's continue with the code which is executed if we have a valid GOM, the one the previous JNE took us to.
MOV ECX, [ESP+5C] -- Exception related
MOV FS:[0], ECX -- Exception related
POP ESI -- recover
MOV ESP, EBP
MOV POP EBP
RET -- Ending part of C calling convention
Now we are at CALL LEVEL 1 AT swtor.exe + C7F6D
MOV ECX, EAX -- Exception related
MOV EAX, EDI -- The parameter which was passed by reference
CALL swtor.exe + 50070
Now we are at CALL LEVEL 2 AT swtor.exe + 50070
PUSH EBP
MOV EBP, ESP -- Calling convention in C
MOV EDX, [EBP+8] -- EDX <- 1st parm to this func ( low ID )
PUSH EBX -- Preserve
PUSH ESI -- Preserve
PUSH EDI -- Preserve
MOV EDI, ECX -- Exception related
MOV ESI, EAX -- Parameter by reference
MOV EAX, [EBP+C] -- EAX <- 2nd parm to this func ( high ID )
OR ECX, EAX -- Check if both high and low ID are zero
JNE -- jump if the int64 ID is not zero. ( jump taken )
MOV ECX, EAX -- High ID
AND ECX, E0000000 -- Kepp only 3 higher bits of high ID
XOR EBX, EBX -- EBX <- 0
JNE -- Inconditional relative short jump (compiler optimization trick )
CMP ECX, E0000000 -- Check if 3 higher bits of high ID are 1
JNE -- Jump if some of those 3 bits are not 1 ( jump taken )
MOV ECX, [EBP+10] -- 3rd parm to this function (1)
PUSH ECX -- 5th parm to next function we are about to call (1)
PUSH ESI -- 4th parm to next func we are about to call (parameter by reference)
PUSH EAX -- 3rd parm to next func we are about to call ( high ID )
PUSH EDX -- 2nd parm to next func we are about to call ( low ID )
LEA ECX, [EDI+628] -- Calculating address of an element in the GOM which will eventually get us to the table of mobs.
PUSH EDI -- 1st parm to next func we are about to call ( GOM )
CALL swtor.exe + B4110
Now we are at CALL LEVEL 3, at address swtor.exe + B4110
PUSH EBP
MOV EBP, ESP -- Calling convention in C
PUSH FF
PUSH something related to exception
MOV EAX,FS:[0] -- excepts rel
SUB ESP, 1C -- Space for local vars
PUSH EBX - Preserve
PUSH ESI - Preserve
PUSH EDI - Preserve
MOV ESI, ECX -- This is [GOM]+628
LEA EAX, [EBP-24] -- Calculate pointer to a local var. Will pass by reference to next function we are about to call
ADD ESI, 04 -- We had [GOM]+628, so now we have [GOM]+62C. Remember the [swtor.exe+something]+62C I posted earlier as base for Mob array?
Well, it was not really a mob array but it was something :P
PUSH EAX -- 1st parm for next func we are about to call (pointer to local var)
LEA EAX, [EBP+C] -- Pointer to 2nd parm of this func (low ID )
MOV [EBP-14], ESI -- Local var <- [GOM]+62C
CALL swtor.exe + B4570
We are now at CALL LEVEL 4, address swtor.exe + B4570
PUSH EBP
MOV EBP,ESP -- Calling convention in C
PUSH ECX - Preserve
CMP DWORD[ESI+10], 00 -- CHECK IF [[GOM]+63C] is 0
PUSH EBX - Preserve
PUSH EDI - Preserve
JE -- Jump if [[GOM]+63C] is 0 (not taken )
MOV EDI,[EAX] -- 2nd parm of previous func (low ID )
MOV EBX,[EAX+4] -- 3rd parm of previous func ( high ID )
MOVZX EAX, BYTE PTR[ESI+C] -- EAX <- Zero extended byte at [[GOM]+638] (useless in this trace )
PUSH EBX -- 2nd parm of func we are about to call ( high ID )
PUSH EDI - 1st parm of func we are about to call ( low ID )
LEA ECX, [EAX+ESI+D] -- Calculate an address (usseles in this trace because ECX will soon be overwritten without being read)
CALL swtor.exe + C0490
We are now at CALL LEVEL 5, at address swtor.exe + C0490
This function is interesting. Receives an object ID and calculates a hash.
PUSH EBP
MOV EBP, ESP -- Calling convention in C
AND ESP, F8 -- Allign ESP
MOV ECX, [EBP+C] - 2nd parm of this func (high ID)
MOV EDX, ECX -- copy high ID
SHL EDX, 6 -- EDX <- highID << 6
ADD EDX, [EBP+8] -- Now EDX is : ( highID<<6 ) + lowID
MOV EAX, ECX -- copy high ID
SHR EAX, 2 -- EAX <- highID >> 2
ADD EAX, EDX -- Now EAX is : (highID<<6) + lowID + (highID>>2)
XOR EAX, ECX -- Now EAX is : ( (highID<<6) + lowID + (highID>>2) ) ^ highID
Which completes the calculation of the hash. Will be returned to caller function in EAX
as per calling convention in C
MOV ESP, EBP
POP EBP
RED 8
We are now at CALL LEVEL 4, at address swtor.exe + B4590
XOR EDX, EDX -- EDX <- 0. (EDX will be higher bits of dividend)
DIV [ESI+4] -- Divide the hash by [[GOM]+630] to calculate the position in a hash table where we will
get the pointer to the Node for the player. The remainder (in EDX) will be the position. Quotient (in EAX)
is ignored.
MOV ECX, [ESI] -- ECX <- [[GOM]+62C]
MOV EAX, [ECX+EDX*4] -- Obtain the Player node. This instruction I had previously confused as being an indexing of
an array. Hence the incorrect [[[GOM]+62C]+4*<mob number>].
From here onwards it is doing several checks and returning values to calling functions. But we have already gotten to what was my objective this time.
-----------------------------------------------------------------------------------------
WHAT WE LEARNED FROM PREVIOUS REVERSING:
The GOM has a hash table located at [[GOM]+62C] with GOM = swtor.exe + 1074EBC
To calculate the hash from an ID we do :
hash = ( (highID<<6) + lowID + (highID>>2) ) ^ highID
To calculate the position in the hash table we do :
position = hash % [[GOM]+630]
To calculate the address of the node in the GOM we do :
node = [[[GOM]+62C]+4*position]
Things we did not learn from previous reversing but are related :
This node points to a Hero Class. Of interest is the data contained in :
[[[[[[[[[node+14]+48]+14]+10]+0]+14]+14]+48]+8] = PS. Player struct
[PS-10] = Force. float. (For force users only I guess)
[PS-2C] = HP. float.
[PS+0] = Experience, int32.
[PS+8] = Experience to level up, int32.
[PS+10] = Level, int32.
------------------------------------------------------------------------
This should answer most of the PMs I have received and not replied to.
Surely there is more info there. And there is yet to reverse access to monster nodes, harvest nodes, etc.
Plus the negative offsets look suspicious.
I am also releasing an updated quick and dirty travel bot which uses all this information.
Previous travelBot read memory from fixed memory positions in swtor.exe. This worked for WinXP but does not work with operating systems which randomize positions at which programs are loaded. In this update I properly obtain the base for swtor.exe and read from there.
It compiles and works for x64. Very little changes are required to make it work on x86. I'll get to it soon enough.
After opening the project remember to change to Release - x64 to get a working executable.
Previous travelBot was released in the public domain. This one I am releasing under GPLv3.
travelBot-v02.rar