[ASK] about memory address

08/27/2010 02:16 Smurfin#1
Can anyone please tell me things about memory address like base address/pointer/offset/base address ?

like in PWI, we have :
Base_Address=10862540 , or in hex = A5BFCC
Base_AddressFZ=10863676, or in hex = A5C43C

does Base_AddressFZ mean a pointer ? because we can directly use that memory address without using offset and Base_Address.

for example, we have the offset for HP in PWI -> HP_OffSet=1140, when using nomad memory in autoit, to get to this HP memory address, we have to declare something first like this before we can read or write to it :
Code:
Global $OFFSET_HP[3]
$OFFSET_HP[1]=32
$OFFSET_HP[2]=1140
to read the value we use this, but the result in which the value of HP is stored is in $HP[1]
Code:
$HP=_MEMORYPOINTERREAD($Base_Address, $PID, $OFFSET_HP)
while if using a pointer like for freezing/unfreezing elementclient, we can directly use this and the result is stored in $Freeze directly
Code:
$pid=wingetprocess("Element Client")
$mid = _MemoryOpen($pid)
$Freeze = _MemoryRead(0x00A5C43C, $mid)
now how do I get a pointer for HP with that base address and hp offsets, so I can just use Base_AddressHP without using arrays ?

or even for the more complicated one like pet hp because it has more arrays
Code:
Global $OSPetHp[5]
$OSPetHp[1] = Dec("20")
$OSPetHp[2] = Dec("be0") ;it's changed already, just for an example
$Hex = ($PetNo - 1) * 4 + 10
$OSPetHp[3] = Dec($Hex)
$OSPetHp[4] = Dec("38")
is it possible to get a direct pointer like for freezing/unfreezing, but for HP/PetHP if we have the elementclient's base address and the respective offsets ??

*sorry if I use incorrect terms for describing them because I'm still confused

what I want to accomplish by understanding this is to use autoit to search for pointers of other things, like pointer for peeking at other player's HP, if using CE I can find the address but I dunno how to get its offset/pointer if it's multileveled :(

one more thing, the base address for PWI is 10862540 or 0xA5BFCC , does that mean the pointer for everything will always be above 0xA5BFCC ? what is the 'end' address, if any ?
08/27/2010 04:02 No0oB#2
1. Base_AddressFZ is not a pointer it's a static address
(pointers have a StaticBase and Offstets).
2. the value is stored in $HP[1] because _MemoryPointerRead() returns an array with the destination address and the value.
3. It's stored directly because _MemoryRead() only returns the value
4. If you want to read from a pointer using NomadMemory.au3 you can use this:
Code:
#include <NomadMemory.au3>
#RequireAdmin

$BaseAddress = 0x00A5BFCC
Dim $aHpOffset[2] = [0x20,0x474] ; Your char hp
Dim $aPetSlot1_HpOffset[4] = [0x20,0x100C,0x10,0x38] ; HP of the pet in slot 1
$PID = ProcessExists("elementclient.exe")

$HP = _ReadPointerValue($PID,$BaseAddress,$aHpOffset)
$PetHP = _ReadPointerValue($PID,$BaseAddress,$aPetSlot1_HpOffset)

MsgBox(0,"_ReadPointerValue","HP = " & $HP & @CRLF & "PetHP = " & $PetHP)

Func _ReadPointerValue($PID, $Address, $aOffsets, $Type = 'dword')
	Local $i,$hProc,$Value
	$hProc = _MemoryOpen($PID)
	For $i = 0 To UBound($aOffsets) - 1 Step 1
		$Address = ("0x" & Hex(_MemoryRead($Address,$hProc)))+$aOffsets[$i]
	Next
	$Value = _MemoryRead("0x" & Hex($Address),$hProc,$Type)
	_MemoryClose($hProc)

	Return $Value
EndFunc
hope this helps ^^
5.
Quote:
one more thing, the base address for PWI is 10862540 or 0xA5BFCC , does that mean the pointer for everything will always be above 0xA5BFCC ? what is the 'end' address, if any ?
no ^^... 0xA5BFCC is not the real base it's 0xA5BFB0 (0xA5BFCC-0x1C) and the memory of elementclient.exe has a range from 0x00400000 to XXX sry idk the end address ^^
and pointers can point to any address in the memory ^^ (lower and higher as the baseaddress)
08/27/2010 10:37 Smurfin#3
thanks No0oB, that explains everything, I thought every memory address could have a static base address, and I thought a pointer would always be static and have no offsets, but a static address can still be called a pointer, right ?


then it's impossible to use autoit to search the address for peeking other player's hp :( , it must have many offsets seeing the result in CE. And that's why I couldn't find a fixed memory address searching from base address until any address, I even tried -> for $i=1 to 10000000 step 4 and still won't give me a fixed address for HP [Only registered and activated users can see links. Click Here To Register...]

btw I used this code to enable walk through walls/houses/trees and when it's enabled, the char even falls down if the platform we stand on is considered an object, but it doesn't work anymore.

The address is static, and the value is known, I've tried to scan memory addresses from base address until much greater than it but still couldn't find "0x4189c1d9" , is it possible to find the pointer again for this hack ? because most of the values in PW never change, like jump we can still see 0,1,2, for fly state and not flying still using the old values, and also mount on/off pet ride. Maybe also for this direct wallhack but I can't find that "0x4189c1d9" anymore.

Code:
			$ProcessID = WinGetProcess($hackthiswindowname)
			$OpenMemory = _MemoryOpen($ProcessID)
			$Stat = _MemoryRead(0x004052D3, $OpenMemory)
			If $Stat = "0x4189c1d9" then GUICtrlSetData($ButtonWH2Status,"OFF")
			If $Stat = "0x4189c0d9" then GUICtrlSetData($ButtonWH2Status,"ON")
			_MemoryClose($OpenMemory)
08/27/2010 12:50 lolkop#4
sry but thats not correct @ all.
pointers are adresses, which point to something else.

for example letz take pwi.
the baseadress in pwi is 0xA5B90C
its called base, because it gets used by the client directly. this adress is infact a pointer. it points to another memory area.

in that area we need to get the next important adress for ourselves.
well if we use memory read (dword as result) on that adress, we'll get 0xA5BFB0 as result. thats not the adress we're looking for. the client does something like this
Code:
mov eax, [0xA5B90C]
mov eax, [eax+0x1C]
that means the value stored in 0xA5B90C gets stored in eax.
and now it saves the value stored in eax+1C to eax.
so it could also be expressed like this:
Code:
mov eax, [[0xA5B90C]+0x1C]
what does this mean?
everything surrounded by "[...]" (returning an adress) is a pointer. it points to somewhere else.
now to get to the charbase, we have to add another offset (0x20). so the clientcode would look like this:
Code:
mov eax, [0xA5B90C]
mov eax, [eax+0x1C]
mov eax, [eax+0x20]
which is logically:
Code:
mov eax, [[[0xA5B90C]+0x1C]+0x20]
so this pointer gets calculated by the base adress and 2 offsets, and points to the charbase.
in autoit we can use it exactly in the same way:
Code:
$charbase = memread(memread(memread(0xA5B90C) + 0x1C) + 0x20)
Edit:
Quote:
Originally Posted by Smurfin View Post
then it's impossible to use autoit to search the address for peeking other player's hp :( , it must have many offsets seeing the result in CE. And that's why I couldn't find a fixed memory address searching from base address until any address, I even tried -> for $i=1 to 10000000 step 4 and still won't give me a fixed address for HP

The address is static, and the value is known, I've tried to scan memory addresses from base address until much greater than it but still couldn't find "0x4189c1d9" , is it possible to find the pointer again for this hack ?
i'm 100% sure that the value 0x4189c1d9 never gets checked by the client.
you missed one thing. not every valued gets stored as dword/int value. some are stored as byte, word, long, float, double... that means looping through the client with step 4 can't work, and the value you're looking for is not realy stored as dword. it's stored with another type. thats why you can't find it anymore.

and last thing to mention is, that it is possible to search through the memory with autoit.
the problem is that dllcalls are kinda slow. so if you wanna search through the memory, you should allways read huge parts of the memory @ once and search through it with regexp functions.

the less dllcalls you use, the faster your tool will be.
08/27/2010 13:10 Interest07#5
Quote:
Originally Posted by Smurfin View Post
thanks No0oB, that explains everything, I thought every memory address could have a static base address, and I thought a pointer would always be static and have no offsets, but a static address can still be called a pointer, right ?


then it's impossible to use autoit to search the address for peeking other player's hp :( , it must have many offsets seeing the result in CE. And that's why I couldn't find a fixed memory address searching from base address until any address, I even tried -> for $i=1 to 10000000 step 4 and still won't give me a fixed address for HP [Only registered and activated users can see links. Click Here To Register...]

btw I used this code to enable walk through walls/houses/trees and when it's enabled, the char even falls down if the platform we stand on is considered an object, but it doesn't work anymore.

The address is static, and the value is known, I've tried to scan memory addresses from base address until much greater than it but still couldn't find "0x4189c1d9" , is it possible to find the pointer again for this hack ? because most of the values in PW never change, like jump we can still see 0,1,2, for fly state and not flying still using the old values, and also mount on/off pet ride. Maybe also for this direct wallhack but I can't find that "0x4189c1d9" anymore.

Code:
			$ProcessID = WinGetProcess($hackthiswindowname)
			$OpenMemory = _MemoryOpen($ProcessID)
			$Stat = _MemoryRead(0x004052D3, $OpenMemory)
			If $Stat = "0x4189c1d9" then GUICtrlSetData($ButtonWH2Status,"OFF")
			If $Stat = "0x4189c0d9" then GUICtrlSetData($ButtonWH2Status,"ON")
			_MemoryClose($OpenMemory)
you can use memoryRead to see other players HP. If they are not in party, their HP is only updated if you select them though, just like monster HP. You can also see their MP which is kinda funny. Just browse the player list for all surrounding players and use the same offsets for HP as you use for your own character.

playerlist can be found at [[[[[baseAddress] + 0x1C] + 0x8] + 0x20] + 0xA0]
playercount at [[[[[baseAddress] + 0x1C] + 0x8] + 0x20] + 0x14]
08/27/2010 15:09 Smurfin#6
@lolkop
thanks for further explanation lolkop, still a bit in pieces but kinda understand it more.

I notice in the source codes you have posted in this forum, you use a base that has different pointer mostly used here.(the pointer might already changed by now)
Code:
Global Const $base = 0x98ADDC, $select_call = 0x5B7B70, $pick_call = 0x5B7B00
in PW Indo, the usual base address used for mhs/zevorc/prophet is 009C1514 but using your function, the one that returns base and call addresses, the result is $base = 009C0E6C, these two bases must also be different in PWI.

I'm completely lost looking at your functions, can't even understand what's in it but I use them to mod my tools and it works :D I'm only afraid if future big patches for PW will make it not working again. Hope you'll still around when that happens.




@Interest07 : I usually use CE for peeking at other player's hp, by targetting my other char first which I know the HP, then target myself and repeat the search. I'll test the pointer/offsets you posted to see if it works for PW Indo.
-edit-
I checked both pointers' values but they're all zero, what should I do with those offsets?

btw what tools do you all use for getting those needed pointer/offsets, I can only use CE and can only get the offset for easy things like HP/MP/jump/etc, coz by clicking [what writes to this memory address] in CE, the offset can be determined by just looking at it, even by noobs :p
08/27/2010 18:23 lolkop#7
those functions should allways work, since functions shouldn't get changed =)

as allready said bots like prophets etc are not using the real base adress.
they are using [base] + 0x1C as baseadress.

i don't know why they are doing so but they do :P

so if you need the fakeadress they are using, you can simply use
Code:
$fakebase = memread($base) + 0x1C
08/27/2010 19:53 Smurfin#8
hehe, nice, I like forever working things lol, less effort needed to do mods with later patches. Kinda like ntkid's mhs retriever, though the base retriever is not working anymore, but it can still retrieve other needed offsets.

but if $fakebase = memread($base) + 0x1C , then why 009C0E6C($base)+0000001C(0x1C) not equals to 009C1514 (supposed to be the $fakebase)



@No0oB : could you please post the _WritePointerValue too ?
it's more convenient to use dim coz not taking too much lines, but also need the function to write.
08/27/2010 21:26 Shareen#9
It is hard to start learning advance programming techniques without decent programming knowledge before hand. Add to this the fact you are approaching things from the "wrong" end, observing and retrieving data from memory into some sort of user generated "holder" (variable or struct to hold data you retrieve from memory).

I will try to show first, how Pw data is organized from programmers stand point, before moving on to how it's structured in memory.

I would provide examples in AutoIt, but being I've never used it and am not familiar with how it works (when and how pointers are used and if), that wouldn't be of much help not to mention probably wrong.

Instead examples will be based on Delphi (Pascal).

Let us imagine we want to build a game, or more precisely, part that will handle players. Focusing on holding data only and ignoring any routines for manipulating it, we can start with a simplified rule set:
1. players have lots of data associated with them
2. there will be cases where we will encounter more than one.

We can try and define player as
Code:
  TPlayer: Cardinal;
(Cardinal = in Delphi, 4 byte unsigned type)

but that wouldn't hold a lot of data.

So instead, what we'll do, is define a structure. Structure is known as record in Delphi and struct in C++ and in both, it describes the same thing.

Code:
TPlayer = record
  Id: Cardinal;
  HPMax: Cardinal;
  HPCurr: Cardinal;
  MPMax: Cardinal;
  MPCurr: Cardinal;
  Name: Array[0..15] of WideChar;
  PosX: Single;
  PosZ: Single;
  PosY: Single;
end;
(Cardinal = in Delphi, 4 byte unsigned type)
(Single = in Delphi, 4 byte floating point type, 7-8 significant digits)
(Array[0..15] of WideChar = let's leave this one for later)

That's better, now our player has both HP values, both MP values, it has Id we can use to track and address it, it has position so we know where it is and it's has a name.
Real player would have much more, but our game is simple and that's really all we need.

If we use this type in code now, it would look like this:
Code:
Player: TPlayer; // we declare variable Player to be of type TPlayer defined above

Player.Id := 1; // First player, yey.
Player.HPMax := 400;
Player.HPCurr := 350;
Player.MPMax := 200;
Player.MPCurr := 150;
Player.Name := 'My player';
Player.PosX := 5.152;
Player.PosZ := 22.346;
Player.PosY := 17.454;
Player variable now holds player data. Name might be boring, but in his defence, he is the first character on server, so that's ok :).
Please note that "Player.Name := 'My player';" wouldn't compile, but it was defined array on purpose and will be explained later.

Assuming it would compile, Player variable would be written to memory and it would look like this:
01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41

Bloody mess at first glance, let us break it down:

01 00 00 00 <- Id (if read as 4 byte unsigned type, Cardinal)
90 01 00 00 <- HpMax (ditto above)
5E 01 00 00 <- HpCurr (ditto above)
C8 00 00 00 <- MPMax (ditto above)
96 00 00 00 <- MPCurr (ditto above)
00 00 00 00 <- Name (pointer, explanation incoming soon and it wouldn't be 00 00 00 00)
2F DD A4 40 <- PosX (if read as 4 byte floating point type, Single)
9C C4 B2 41 <- PosZ (ditto above)
CB A1 8B 41 <- PosY (ditto above)

First thing to note is that endian is reversed.
01 00 00 00 = reverse endian = 00 00 00 01 hex = 1 dec = Player.Id
90 01 00 00 = reverse endian = 00 00 01 90 hex = 400 dec = Player.HpMax
and so on...

Second, that player data is written in exact order as defined in TPlayer, starting with Id, HPMax, HPCurr, MPMax, MPCurr, etc,...

Another way to put it, Id is offset by 0 bytes from start of Player. MpMax is offset by 4 bytes from start of Player.

But what is "start of player"?

It turns out, that when we declared Player: TPlayer, what happened was a pointer was made for us. It's not shown as one to programmer and yet, it is one.
In fact, a whole lot of other things were done "behind our backs" so to speak.
First, size of TPlayer was calculated:
Size of TPlayer = size of type(Id) + size of type(HPMax) + size of type(HPCurr) + size of type(MPMax) + size of type(MPCurr) + size of type(Name) + size of type(PosX) + size of type(PosZ) + size of type(PosY).

Comes out as 36 bytes, all of types used in creating TPlayer occupy 4 bytes and there are 9 of them. Yes, Name as well.

Then, memory for it is allocated, 36 bytes in all.
Data is written into allocated memory space.
Address to start of that data block is written into Player, or to put it another way, POINTER pointing to start of block is written into Player.

Now it gets interesting.
In order for assembler to read/write to any value (field) in Player, it needs to:
- get address Player points to, in order to know where data starts. It does this by dereferencing pointer, which means it gets value of pointer.
- read/write from/to field requested

Code:
mov eax, [Player] // Dereferences Player pointer, eax will now contain address of start of data block
mov [eax+00], 1 // Writes value of 1 into ecx+00, in other words, writes 1 into Player.Id, remember Id is address Player points to + 00.
mov [eax+04], 400 // Writes value of 400 into ecx+04, in other words, writes 400 into Player.HpMax, HpMax is address Player points to + 04.
Note that in assembly, 1, 400,... will be written in hex. I wrote it in decimal so it's clear what get's written, without the need for you to convert it.

Starting to look familiar?

Imagine it's running and we wish to make offset.ini for some bot to use.
It would end up looking like this:
player_Id = 0
player_HPMax = 4
player_HPCurr = 8
player_MPMax = C (dec. 12)
player_MPCurr = 10 (dec. 16)
player_NameAddress = 14 (dec. 20)
player_PosX = 18 (dec. 24)
player_PosZ = 1C (dec. 28)
player_PosY = 20 (dec. 32)

See the pattern, it's in the same order as TPlayer was defined in:
Code:
TPlayer = record
  Id: Cardinal;  // from 00 to 03, it occupies 4 bytes, 00 01 02 03
  HPMax: Cardinal; // 04-07, again, occupies 4 bytes
  HPCurr: Cardinal; // 08-0B
  MPMax: Cardinal; // 0C-0F
  MPCurr: Cardinal; // 10-13
  Name: Array[0..15] of WideChar; // 14-17
  PosX: Single; // 18-1B
  PosZ: Single; // 1C-1F
  PosY: Single; // 20-23
end;

We understand structures now, we can finally get to that Name.

If you recall, it was defined as:
Code:
Name: Array[0..15] of WideChar;
You saw, that trough out the entire explanation, we treated Name as a 4 byte type. It is in fact a pointer and pointers are always 4 bytes long. Yes, again with pointers.

So name is a pointer, and what it points to is start of data block that holds player name. Like with Player pointer, this one is no different.

What "Array[0..15] of WideChar" means is, allocated me a space in memory where I can put 16 characters of type WideChar. WideChar in Delphi is a type that occupies 2 bytes, that is 2 bytes per every character. So if it needs to allocate space to store 16 of characters, it needs to allocate 16*2 bytes = 32 bytes.

And when it's done allocating memory, it stores address to start of it into Name, effectively turning Name into a pointer.

If we read Name directly (offset 14, we will not get 'My player', we will get address that points to 'My player'. What we do then is take that address and read it.
Player + 14 -> points to Name address, address holding the actual value
(Player + 14) + 00 -> gives us 'My player'

Another offset was added, you see, because Name is also a pointer. It points to first letter of player name and since we don't want to read 5th letter of player name, offset 00 will work fine for us, bringing us to start of data block holding 'My player'. Characters in array are also placed one after another in memory.
In fact, any elements in array, be it characters or other data types, are placed one after another in memory. If they are 4 bytes or less in length, they hold their values. However, if they are longer than 4 bytes, they hold address that points to the value. Like the case with Name.


Right, so the first rule is meet, player can have lots of data associated with it.

What about the second one, "there will be cases where we will encounter more than one."?

Since all players in our game will have same types of data associated with them, TPlayer is ok.
But declaration:
Code:
Player: TPlayer;
can only hold data for one player and our goal is to potentially hold many more.

We will thus define another type, one that is capable of holding data of many players. We will define array of TPlayer.

Code:
TPlayers: array of TPlayer;
and to use it, we declare:
Code:
Players: TPlayers;
Another way of doing it, would be to skip defining array all together and declare variable as such:
Code:
Players: array of TPlayer;
Either way, it will be written to memory in the same way.

It can now be used, like so:
Code:
SetLength(Players, 5);

Players[index].Id
Players[index].HpMax
....
Where Index is dynamic and changes depending on which player we are referring to. It can range from 0 to 4, since we made array 5 elements big with a call to SeLength.

And why SetLength?

"array of TPlayer;" is so called dynamic array, it's called dynamic because it has no fixed length set when it's declared, unlike "Array[0..15] of WideChar" which is a static one (length set to 16 WideChars in declaration).
What compiler knows is that it's an array of types TPlayer. It thus knows length of TPlayer, but it doesn't know how many there will be. We tell it that by a call to SetLength, passing array and number of records.
Compiler now knows all it needs to, to allocate memory and write data.
But how does it do it...

As already mentioned before, array is collection of elements. If elements are 4 bytes or less in length, their value is written, if longer pointers to data.
In our case, we have array of TPlayer, and since TPlayer occupies 36 bytes, what we've created is esentially array of pointers. Omg no, again with the pointers. I'm afraid so.

Every record in array:
Players[index] = pointer to Player (of type TPlayer).
and like already established every Player = pointer to values of player (id, HpMax, etc,...).

In the end, what snuck in, was another offset.

While "Player + 04" still gives us HpMax, we now also need to know which Player is in question.

Players (array of Player) is a pointer to start of array. By using index, we can traverse this array and access different players in it. Essentially, by using index, we can get the address of Player we want.

Does this look familiar:

Code:
eax, [ecx+4*edx]
What it means is, write into eax address of record in array, located at offset 4*edx. Edx in this case is the same as Index when using array in Delphi:

Code:
Players[Index]
Index gets incremented (or decremented) by steps of 1. So does edx.
But edx, in our example above, is multiplied by 4.
Back to theory.. array of pointers.. pointers occupy 4 bytes. That means "4*edx" is moving in steps of 4 bytes trough a list of pointers.
ecx = start address of array
4*edx = offset

What it also means, is that pointers are written one after another in memory, else simply taking address doing "[Address+4*Index]" wouldn't work.

How very nice of them, putting them selves in line like this.
And it turns out it's not only pointers that are written like that.

If we take a look at how Players are written to memory, starting at address pointed to by Players, it would look somewhat like this:
00 00 00 00 24 00 00 00 48 00 00 00 6C 00 00 00 90 00 00 00

Broken down (ecx hold address of start of array, known also as Players pointer):
00 00 00 00 <- pointer to address of Player at Index 0 ([ecx+4*edx], edx = 0)
24 00 00 00 <- pointer to address of Player at Index 1 ([ecx+4*edx], edx = 1)
48 00 00 00 <- pointer to address of Player at Index 2 ([ecx+4*edx], edx = 2)
6C 00 00 00 <- pointer to address of Player at Index 3 ([ecx+4*edx], edx = 3)
90 00 00 00 <- pointer to address of Player at Index 4 ([ecx+4*edx], edx = 4)

Each line represents pointer to Player, address at which Player[Index] starts.
After first one "00 00 00 00", each is 36 bytes bigger than the one before, because 36 bytes is the size of TPlayer.

And assuming all players have the same values as Player in the first exmple, looking at these addresses, memory view would be:
01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41

Or broken down (4 bytes before - is memory address, it is for clarification purposes and isn't written in memory):
00 00 00 00 - 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 <- data for player at index 0
24 00 00 00 - 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 <- data for player at index 1
48 00 00 00 - 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 <- data for player at index 2
6C 00 00 00 - 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 <- data for player at index 3
90 00 00 00 - 01 00 00 00 90 01 00 00 5E 01 00 00 C8 00 00 00 96 00 00 00 00 00 00 00 2F DD A4 40 9C C4 B2 41 CB A1 8B 41 <- data for player at index 4

Note that addresses used in example above, while being correct in sense of increment, they are not correct in the sense of it's initial value.
Windows do not allocate address of 00 00 00 00 to any application, so when searching for and looking at addresses in real world, they will be higher.
I've used 00 00 00 00 as a starting address to avoid additional confusion.


Back to offsets now, before we had Player that pointed to various fields like Id, HpMax,...
Player + 00 -> Id
Player + 04 -> HpMax

That didn't change.
What did change is value of Player, start address of Player.

It can now be different depending on which player we wish to access.
What exactly it's address, is given to use by array
(Players + 4*Index) -> gives us Player start address

So if we wished to access HpMax of first player in array, offsets would be:
(Players + 4*0) + 04

Players being start address of array of Players
4*0 = 4 bytes per every record, since they are pointers, and 0 for first player. Arrays are zero indexed thus first record is located at index 0.
04 = offset in Player to retrieve HpMax


We have now satisfied both rules for our game.
1. players have lots of data associated with them
They can thanks to use of structures, like TPlayer

2. there will be cases where we will encounter more than one.
They can thanks to use of array, like array of TPlayer, or TPlayers.


But even arrays can't exist on their own, they are always owned by some other type (or object) and/or used inside other objects.
Understanding, that deep down all is handled by pointers, it isn't hard to imagine why, for example, players offsets in PW are usually nested deeper than 2 offsets.
08/27/2010 21:56 lolkop#10
Quote:
Originally Posted by Smurfin View Post
hehe, nice, I like forever working things lol, less effort needed to do mods with later patches. Kinda like ntkid's mhs retriever, though the base retriever is not working anymore, but it can still retrieve other needed offsets.

but if $fakebase = memread($base) + 0x1C , then why 009C0E6C($base)+0000001C(0x1C) not equals to 009C1514 (supposed to be the $fakebase)



@No0oB : could you please post the _WritePointerValue too ?
it's more convenient to use dim coz not taking too much lines, but also need the function to write.

Code:
Global $kernel32 = DllOpen('kernel32.dll')
Global $mid, $base = 0xA5B90C

memopen(ProcessExists('elementclient.exe'))
ConsoleWrite(Hex(memread($base) + 0x1C)&@CRLF)
memclose()
DllClose($kernel32)

Func memopen($pid)
	$mid = DllCall($kernel32, 'int', 'OpenProcess', 'int', 0x1F0FFF, 'int', 1, 'int', $pid)
	$mid = $mid[0]
EndFunc

Func memread($adress, $type = 'dword')
	Local $struct = DllStructCreate($type)
	DllCall($kernel32, 'int', 'ReadProcessMemory', 'int', $mid, 'int', $adress, 'ptr', DllStructGetPtr($struct), 'int', DllStructGetSize($struct), 'int', '')
	Return DllStructGetData($struct, 1)
EndFunc

Func memclose()
	DllCall($kernel32, 'int', 'CloseHandle', 'int', $mid)
EndFunc
returns the base prophets etc are using for me @ pwi.
you have to use the base returned by my script for your client.
08/27/2010 22:16 Smurfin#11
@lolkop:
thanks again lolkop, turns out things in memory cannot be treated as simple math even though it's using the same symbol [Only registered and activated users can see links. Click Here To Register...] , I'll just use your other function to get the real base and combine it with this function to get the second address others are using as a base then, and call it a base address retriever :p



@Shareen :
hi Shareen, thank you very much for the times and efforts to make a very detailed explanation, I'll use it as reference to learn about memory stuffs. I should be able to absorb the informations in time, I'm starting to get it together now.

That's why some offsets never change, because the structure is the same, only needs to get the key offset to match the other offsets, and that's why all of those addresses are always nearby. Would take a lot of times and efforts for the game developer to change all of them everytime and people like you who understand all this will still get them in the end, so why bother :D
08/28/2010 01:19 lolkop#12
you can easily build up a function for all adresses you need. using one regexp function will do all that in just a few milliseconds:
Code:
#include <array.au3>
$file = FileOpen("elementclient.exe", 16)
$read = FileRead($file, FileGetSize("elementclient.exe"))
FileClose($file)

$offsets = StringRegExp($read,  '.*?895D0089BE(.{8})'& _            ;PhyDefSearch
                                '.*?89AE(.{8})'& _                  ;EvasioSearch
                                '.*?8B0D(.{8})898D40F1FFFF6A01'& _  ;BaseAddress
                                '.*?33C98986(.{8})8A4F03'& _        ;LevelSearch
                                '.*?898E(.{8})'& _                  ;CultiSearch
                                '.*?8B57148996(.{8})8B4718'& _      ;ExpSearch
                                '.*?8B4F04898E(.{8})8B570C'& _      ;HpSearch
                                '.*?8996(.{8})'& _                  ;MpSearch
                                '.*?8B471C8986(.{8})8B4F08'& _      ;ChiSearch
                                '.*?898E(.{8})'& _                  ;MaxHpSearch
                                '.*?8B57108996(.{8})8B4720'& _      ;MaxMpSearch
                                '.*?8986(.{8})8A4702'& _            ;MaxChiSearch
                                '.*?8BC28991(.{8})8B0D'& _          ;TargetSearch
                                '.*?8B108996(.{8})8B4004'& _        ;GoldSearch
                                '.*?898E(.{8})8BC8898E'& _          ;JumpSearch
                                '.*?6A0A8986(.{8})'& _              ;StateSearch
                                '.*?8986(.{8})8B40048BC8'& _        ;CastingIDSearch
                                '.*?A1(.{8})578B482081C1EC'& _      ;BaseCall
                                '.*?8B8E(.{8})3BCB740655'& _        ;PetSearch
                                '.*?8B8E(.{8})8B47503BC8'& _        ;StrSearch
                                '.*?8B96(.{8})8B47543BD0'& _        ;DexSearch
                                '.*?8B86(.{8})8B4F5C3BC1'& _        ;VitSearch
                                '.*?8B8E(.{8})8B47603BC8'& _        ;MagSearch
                                '.*?8B96(.{8})8B47643BD0'& _        ;ReputationSearch
                                '.*?EB5D8B8E(.{8})B801'& _          ;CharClassSearch
                                '.*?8B83(.{8})8D4C243C', 1)         ;NameSearch

For $i=0 To UBound($offsets)-1
    $offsets[$i] = rev($offsets[$i])
Next
_ArrayDisplay($offsets)

Func rev($string)
    Local $all
    For $i = StringLen($string) + 1 To 1 Step -2
        $all = $all & StringMid($string, $i, 2)
    Next
    Return $all
EndFunc
well i've taken No0obs pattern since i'm to lazy to look all that shit up on my own :P
haven't realy checked if all patterns are working correctly... maybe some need to be reworked.
08/28/2010 02:40 Smurfin#13
I just tried running the script but it didn't display the offsets table.

seems to me like it retrieves offsets directly by reading the file and compare the content with a given pattern which you know will reveal an offset instead of going through memory while the file is running, it's very interesting, can get offsets even before running the game :D

still wondering how it actually works since I can't do consolewrite to some variables I'd like to inspect in there yet.

thanks for posting this, if you or noOoB have the time, please fix the script so it'll show the table :handsdown:
08/28/2010 07:35 No0oB#14
for me it works idk why it didn't show the table to you maybe you don't have runned it in your ..\element\ folder of your pw
the script needs to be in the same dir with 'elementclient.exe' file ^^
08/28/2010 08:02 unnang#15
Hi again.. :)
i've trying this code for normal attack
Func NormalAttack()
;call 005F51A0 = function attack call
MOV_ECX($APP_BASE_INJECT)
MOV_EDX(Dec("005F51A0")) ;normal attack
CALL_EDX()
POPAD()
RET()
INJECTCODE($PROCESS_ID)
endfunc


and it's work while E'Client is backgrounded, same to do meditate skill.
but asm really confusing for me.

i'm trying to learn asm and injecting code, pliss someone teach me how to read and understanding the code (in olly, idapro or w32asm), and how do i create a function (dll i mean) that can passing parameters/argument from / to client app?

and is that possible, if we send a keystroke to client just by writing it's mem offset?
seem like, that Client is using hotkey function (user32.GetAsyncKeyState etc), perhaps there is a mem offset for it just like HP/MP offset? or can i make an intercept routine to intercept the keyboard input buffer before Client read the value (Client is doing loop on reading keybuffer at 0040511E (ollydbg breakpoint) ?
pliss someone give me a hint.. thanks b4. :)