[DELPHI] Dynamic Offsets WOW Player Coords

09/20/2010 20:02 piotr55#1
Hi there,

would be kind if some1 could give me a hint what i am doing wrong with this:

Code:
function MemRCoords(adresse,size:integer): Integer;
var p : pointer;
    puffer : Integer;
    BytesRead: Cardinal;
    WindowName,ProcessId,HandleWindow, Base, BaseOffset1, BaseOffset2  :  integer;
begin
        WindowName  :=  FindWindow(nil, 'World of Warcraft'); //get Handle
        GetWindowThreadProcessId(WindowName,@ProcessId);
        HandleWindow  :=  OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
        Base := $00CD87A8;
        BaseOffset1 := $34;
        BaseOffset2 := $24;
        ReadProcessMemory( HandleWindow, ptr(Base) , @puffer, size, BytesRead );
        ReadProcessMemory( HandleWindow, ptr(puffer + BaseOffset1) , @puffer, size, BytesRead );
        ReadProcessMemory( HandleWindow, ptr(puffer + BaseOffset2) , @puffer, size, BytesRead );
        ReadProcessMemory( HandleWindow, ptr(puffer + adresse) , @puffer, size, BytesRead );
        closehandle(HandleWindow);//close
        MemRCoords := Puffer;
end;
Id like to get this func working for dynamic mem reading of the player coords.

(Static Reading is noch problem ;) )

thx N greetz
piotr
09/23/2010 02:21 Cencil#2
Da ist so einiges verkerht.

- Du solltest prüfen ob die Handles auch alle korrekt sind
- Die Positionen sind Float Werte (Single in Delphi).

Führst du deine Anwendung außerhalb der Delphi IDE aus wirst du bei WoW SeDebugPrivilege() einsetzen müssen. Google nach einer fertigen Funktion und führe sie einmalig z.B. beim FormCreate aus. Ansonsten wirst du schon bei OpenProcess() scheitern.

Um Werte zurückzugeben musst du außerdem result := ... verwenden.

Code:
function GetCoords(adresse, size: integer): Single;
var
  h: hwnd;
  hProc: THandle;
  fTemp: Single;
  dwRead, dwpId, dwTemp: Cardinal;
const
  dwBase        = $00CD87A8;
  dwBaseOffset1 = $34;
  dwBaseOffset2 = $24;
begin
  result := 0.0;

  h :=  FindWindow(nil, 'World of Warcraft');

  if (h <> 0) then
  begin
    GetWindowThreadProcessId(h, @dwpId);
    hProc :=  OpenProcess(PROCESS_ALL_ACCESS, false, dwpId);

    if (hProc <> 0) then
    begin
      ReadProcessMemory(hProc, ptr(dwBase), @dwTemp, size, dwRead);
      ReadProcessMemory(hProc, ptr(dwTemp + dwBaseOffset1), @dwTemp, size, dwRead);
      ReadProcessMemory(hProc, ptr(dwTemp + dwBaseOffset2), @dwTemp, size, dwRead);
      ReadProcessMemory(hProc, ptr(dwTemp + adresse), @fTemp, size, dwRead);

      CloseHandle(hProc);

      result := fTemp;
    end;
  end;
end;
Das X Offset für Units ist 0x798, ein Aufruf deiner Funktion könnte also so aussehen:

Code:
Caption := Format('Spieler X: %f', [GetCoords($798, 4)]);
y, z sind jeweils y = 0x798+0x4 / z=0x798+0x8 (das $ ersetzt in Delphi in diesem Fall das 0x)

Ich würde die Positionen aber in einer Struktur auslagern, so musst du nicht für jede Achse eine Funktion erstellen.

Code ist ungetestet aber sollte so funktionieren.
09/30/2010 19:07 piotr55#3
hab in der zwischenzeit so einiges geändert und es läuft soweit ziemlich gut.

auf SetDebugPrivilege() konnte ich verzichten, läuft auch so alles gut :D

nur hab ich nen problem beim auslesen der playernamen, einheiten also gegner klappen schon.

ersteinmal kann ich die aktuelle adresse (3.3.5) für den player name ptr nicht finden, hab hier schon alle threads durchgewälzt :(
[EDIT] hab ihn: 0xC5D938 das andere problem besteht allerdings noch [/EDIT]

ich versuche, die bestehende c++ function "PlayerNameFromGuid" in delphi umzustricken:
Code:
public string PlayerNameFromGuid(ulong guid) 
        {
            const ulong nameStorePtr = 0x1137CE0 + 0x8;  // Player name database
            const ulong nameMaskOffset = 0x024;  // Offset for the mask used with GUID to select a linked list
            const ulong nameBaseOffset = 0x01c;  // Offset for the start of the name linked list
            const ulong nameStringOffset = 0x020;  // Offset to the C string in a name structure

            ulong mask, base_, offset, current, shortGUID, testGUID;

            mask = WowReader.ReadUInt32((IntPtr)(nameStorePtr + nameMaskOffset));
            base_ = WowReader.ReadUInt32((IntPtr)(nameStorePtr + nameBaseOffset));

            shortGUID = guid & 0xffffffff;  // Only half the guid is used to check for a hit
            offset = 12 * (mask & shortGUID);

            current = WowReader.ReadUInt32((IntPtr)(base_ + offset + 8));
            offset = WowReader.ReadUInt32((IntPtr)(base_ + offset));
            //current == 0 || (current & 0x1)
            if ((current & 0x1) == 0x1) { return ""; }

            testGUID = WowReader.ReadUInt32((IntPtr)(current));

            while (testGUID != shortGUID)
            {
                current = WowReader.ReadUInt32((IntPtr)(current + offset + 4));

                if ((current & 0x1) == 0x1) { return ""; }
                testGUID = WowReader.ReadUInt32((IntPtr)(current));
            }

            // Found the guid in the name list...
            //ReadBytesIntoBuffer(current + nameStringOffset, numBytes, name);
            return WowReader.ReadString((IntPtr)(current + nameStringOffset));
        }
dabei bereitet mir:
Code:
shortGUID = guid & 0xffffffff;  // Only half the guid is used to check
kopfzerbrechen.

ich verstehe nicht, was dort mit meiner guid (bsp. 12345) passiert
wird diese halbiert oder ein teil abgeschnitten?

mein code sieht momentan so aus:
Code:
function PlayerFromGUID(GUID:UInt64): String;
var
  mask, base, shortGUID, testGUID, offset, current: Integer;
begin
  mask := MemRInteger(nameStorePtr + nameMaskOffset);
  base := MemRInteger(nameStorePtr + nameBaseOffset);

  shortGUID := GUID + $ffffffff;

    if mask = $FFFFFFFF
    then Result := '';

  offset  := 12 * (mask + shortGUID);
  current := MemRInteger(base + offset + $8);
  offset  := MemRInteger(base + offset);

    if (current = 0) or ((current + $1) = $1)
    then Result := '';

  testGUID := MemRInteger(current);
  while testGUID <> current do begin
    current := MemRInteger(current + offset + $4);

      if (current = 0) or ((current + $1) = $1)
      then Result := '';

    testGUID := MemRInteger(current);
  end;
  Result := MemRString(current + nameStringOffset);
end;
mit offsets:
Code:
  nameStorePtr      =   $C5D938 + $8;
  nameMaskOffset    =   $24;
  nameBaseOffset    =   $1C;
  nameStringOffset  =   $20;
vielen dank
gruß piotr
10/01/2010 17:55 HardCore.1337#4
wobei du dwtemp als PChar dekladieren musst.
Zum speichern aus ReadProcessMemory muss ein speicherbereich(GetMem) angefordert werden, und dieser geht nur mit Pointer und PChar.
10/01/2010 18:54 piotr55#5
thx für die antwort

das ist zum glück längst nicht mehr das problem,

im meinem 2ten post ist mein neues problem geschildert :)

gruß
piotr
10/01/2010 19:16 Bot_interesierter#6
Code:
shortGUID = guid & 0xffffffff;
& ist der bitwise and Operator, es werden also die einzelnen Bits der GUID mit den Bits von 0xffffffff mit einem logischen Und verknüpft.
0xffffffff sind 32 Bit die auf 1 gesetzt sind, also bleiben die ersten 32 Bit der GUID erhalten und die restlichen 32 Bit werden auf 0 gesetzt.
Übrigens, dieser Code wahrscheinlich kein C++, ich vermute stark das es sich um CSharp handelt.
10/01/2010 19:21 piotr55#7
thx :)

ich hab in delphi mal das & durch and ersetzt:

Code:
function PlayerFromGUID(GUID:UInt64): String;
var
  nameMask, nameBase, testGUID, offset, current: Integer;
  shortGUID: UInt64;
begin
  nameMask := MemRInteger(nameStorePtr + nameMaskOffset);
  nameBase := MemRInteger(nameStorePtr + nameBaseOffset);

  shortGUID := GUID and $FFFFFFFF;      // Only half the guid is used to check for a hit
    if nameMask = $FFFFFFFF then
    begin
      Result := 'Fehler1';
      Exit;
    end;
  offset  := 12 * (nameMask and shortGUID);  // select the appropriate linked list
  current := MemRInteger(nameBase + offset + 8);
  offset  := MemRInteger(nameBase + offset);
      if (current = (current and $1)) or (current = 0) then
      begin
        Result := 'Fehler2';
        Exit;
      end;
  testGUID := MemRInteger(current);
  while testGUID <> current do begin
    current := MemRInteger(current + offset + 4);
      if (current = (current and $1)) or (current = 0) then
      begin
        Result := 'Fehler3';
        Exit;
      end;
    testGUID := MemRInteger(current);
  end;
  Result := MemRString(current + nameStringOffset);
end;
trotzdem bekomm ich nur zeichenwirrwar raus :/

gruß
piotr
10/01/2010 19:23 Bot_interesierter#8
Deine ShortGUID muss auch ein UInt64 sein...
10/01/2010 19:34 piotr55#9
^^ immernoch wirrwarr
muss offset auch Uint64 sein?

[Only registered and activated users can see links. Click Here To Register...]
10/01/2010 19:50 Bot_interesierter#10
Nein muss es nicht, allerdings kann ich auch nicht Prüfen in wie weit der Code den du gepostet hast korrekt ist, geschweigeden ob die Offsets noch alle stimmen.

Ein Mögliches Problem ist das in WoW Strings in Unicode vorliegen, wenn du versuchst einen String aus WoW als ANSI String zu interpretieren kommt dann Kauderwelsch heraus.

Edit:
Speicher Adressen in WoW sind 32bit lang, da WoW soweit mir bekannt ist nur als 32bit executable vorliegt, darum macht es wenig sind offset und die anderen Adressvariablen als UInt64 zu definieren, allerdings tut der Code den du hier gepasted hast genau das, ein ulong ist in C# nämlich 8byte lang und ein Bezeichner für System.UInt64 .
10/01/2010 20:03 piotr55#11
also der fehler liegt nicht in der kodierung, da ich npc namen ganz normal aufgeben lassen kann.

due uint64 wird ja für die guid benötigt, die ist als 64bit integer im speicher abgelegt

gruß
piotr