Trainer do gry, jak znaleźć Base Address w Windows 7 64bit

0

Witam
Próbuje napisać trainer do gry, która wykorzystuje DMA. Mam problem ze znalezieniem base address procesu z gry.
Adresy i pointery wyszukuje za pomocą Cheat Engine. W CE pisze np. "demigod.exe"+$offset i nie potrafię znaleźć tego adresu bazowego.
Przeszukałem to forum i jak i inne zagraniczne, znalazłem kilka opisów i żadne nie działają, prawdopodobnie z powodu windows 7 64bit.

Na pewno nie działa ten sposób:

function GetModuleBaseAddress(ProcessID: Cardinal; MName: String): Pointer;
var
  Modules         : Array of HMODULE;
  cbNeeded, i     : Cardinal;
  ModuleInfo      : TModuleInfo;
  ModuleName      : Array[0..MAX_PATH] of Char;
  PHandle         : THandle;
begin
  Result := nil;
  SetLength(Modules, 1024);
  PHandle := OpenProcess(PROCESS_QUERY_INFORMATION + PROCESS_VM_READ, False, ProcessID);
  if (PHandle <> 0) then
  begin
    EnumProcessModules(PHandle, @Modules[0], 1024 * SizeOf(HMODULE), cbNeeded); //Getting the enumeration of modules
    SetLength(Modules, cbNeeded div SizeOf(HMODULE)); //Setting the number of modules
    for i := 0 to Length(Modules) - 1 do //Start the bucle
    begin
      GetModuleBaseName(PHandle, Modules[i], ModuleName, SizeOf(ModuleName)); //Getting the name of module
      if AnsiCompareText(MName, ModuleName) = 0 then //If the module name match with the name of module we are looking for...
      begin
        GetModuleInformation(PHandle, Modules[i], @ModuleInfo, SizeOf(ModuleInfo)); //Get the information of module
        Result := ModuleInfo.lpBaseOfDll; //Return the information we want (The image base address)
        CloseHandle(PHandle);
        Exit;
     end;
    end;
  end;
end;

Jeśli na sztywno w piszę base address wyliczony z CE to wszystko ładnie działa.

dodanie znacznika <code class="delphi"> - fp

0

Pobierz sobie handle do okna tej gry i po nim poszukaj pid i go wpisz tak będzie prosciej jak z automatu za kazdym razem będzie się podpisać

0

Zacznijmy od tego czy gra jest 64bit czy tylko Windows? Dla 64bitowych aplikacji trzeba użyć EnumProcessModulesEx zamiast EnumProcessModules. Poza tym od czego masz debugger użyj go i sprawdź która funkcja zwraca błąd. To WinApi więc nawet z powodzeniem można użyć GetLastError.

ShowMessage(SysErrorMessage(GetLastError)) ;
0

Pobieram Handle z gry oraz PID, z tym nie ma problemu. Chodzi tu o jakiś adres początkowy, który się zmienia po każdym uruchomieniu gry.
Wg tego kodu w pierwszym poście, zawsze mam $40000.

Funkcja nie zwraca mi błędu tylko zły adres:

Pokaże kod programu:


Function ProcessIDFromAppname32( appname: String ): DWORD;
  Var
    snapshot: THandle;
    processEntry : TProcessEntry32;
  Begin
    Result := 0;
    appName := UpperCase( appname );
    snapshot := CreateToolhelp32Snapshot(
                  TH32CS_SNAPPROCESS,
                  0 );
    If snapshot <> 0 Then
    try
      processEntry.dwSize := Sizeof( processEntry );
      If Process32First( snapshot, processEntry ) Then
      Repeat
        If Pos(appname,
               UpperCase(ExtractFilename(
                             StrPas(processEntry.szExeFile)))) > 0
        Then Begin
          Result:= processEntry.th32ProcessID;

          Break;
        End; { If }
      Until not Process32Next( snapshot, processEntry );
    finally
      CloseHandle( snapshot );
    End;
  End;

function AddressOfMultiLevelPointer(Access: THandle; InitialAddress: Cardinal;
  Offsets: array of Cardinal): Cardinal;
var
  Address: Cardinal;
  Buff: Cardinal;
  Read: NativeUint;
  i: integer;
begin
  Address := InitialAddress + Offsets[ High(Offsets)];
  ReadProcessMemory(Access, Pointer(Address), @Buff, SizeOf(Buff), Read);
  for i := High(Offsets) - 1 downto 1 do
  begin
    Address := Buff + Offsets[i];
    ReadProcessMemory(Access, Pointer(Address), @Buff, SizeOf(Buff), Read);
  end;
  Result := Buff + Offsets[0];
end;

function GetModuleBaseAddress(ProcessID: Cardinal; MName: String): Pointer;
var
  Modules         : Array of HMODULE;
  cbNeeded, i     : Cardinal;
  ModuleInfo      : TModuleInfo;
  ModuleName      : Array[0..MAX_PATH] of Char;
  PHandle         : THandle;
begin
  Result := nil;
  SetLength(Modules, 1024);
  PHandle := OpenProcess(PROCESS_QUERY_INFORMATION + PROCESS_VM_READ, False, ProcessID);
  if (PHandle <> 0) then
  begin
    EnumProcessModules(PHandle, @Modules[0], 1024 * SizeOf(HMODULE), cbNeeded); //Getting the enumeration of modules
    SetLength(Modules, cbNeeded div SizeOf(HMODULE)); //Setting the number of modules
    for i := 0 to Length(Modules) - 1 do //Start the bucle
    begin
      GetModuleBaseName(PHandle, Modules[i], ModuleName, SizeOf(ModuleName)); //Getting the name of module
      if AnsiCompareText(MName, ModuleName) = 0 then //If the module name match with the name of module we are looking for...
      begin
        GetModuleInformation(PHandle, Modules[i], @ModuleInfo, SizeOf(ModuleInfo)); //Get the information of module
        Result := ModuleInfo.lpBaseOfDll; //Return the information we want (The image base address)
        CloseHandle(PHandle);
        Exit;
     end;
    end;
  end;
end;

Przykład użycia:

 
procedure TForm1.Button1Click(Sender: TObject);
var
  Address, PID: Cardinal;
  InitialAddress :Cardinal;
  Buff : NativeUINT;
  Value: Cardinal;
  H: THandle;
const
  OFFSET1: Cardinal = $26c;
  OFFSET2: Cardinal = $158;
  OFFSET3: Cardinal = $84;

Begin
  PID := ProcessIDFromAppname32('Demigod.exe');
  H := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,PID);
  if (H>0) then
  Begin
    InitialAddress := dword(GetModuleBaseAddress(PID, 'demigod.exe'));
    Address := AddressOfMultiLevelPointer(H, DWORD(InitialAddress)+$00D0E6B8, [OFFSET3, OFFSET2, OFFSET1]);
    ReadProcessMemory(H, pointer(Address), @Value, SizeOf(Value), Buff);
    Label1.Caption:=Inttostr(value); 
  End;
End;

Powyższy kod nie działa, wyświetla w label1 złą wartość, lecz po wpisaniu w InitialAddress adresu "demigod.exe", który można obliczyć w CE, kod działa i w label1 jest poprawna wartość (money w grze).
Nie wiem czy poprawnie nazywam ten adres jako "Base Address". Używając aplikacji typu "Process Hacker", odczytuje Base Address procesu "demigod.exe" i jest $400000 tak samo jak w moim programie zwraca
funkcja GetModuleBaseAddress, lecz InitialAddress powinien być po każdym uruchomieniu gry inny.

0

No wychodzi na to że źle ponazywałeś to :D Bazowe moduły (z tego co kojarzę to dllki) to nie to samo co zmienne które chcesz czytać no i dziko ty to wszystko pobierasz nie mówiąc o zmiennych Read i PHandle które normalnie są typami.
Ale pomijając to jeżeli chodzi to znaczy że zmienne są deklarowane dynamicznie i nie wiadomo do końca gdzie się pojawią w pamięci. Nigdy się nie zajmowałem tym pod kątem dynamicznym więc na pierwszą myśl mi nachodzi by przechwycić metodę która tworzy dynamiczną zmienną odpowiadającą za $$ ew jeżeli to ma jakąś strukturę to szukać jej jak puzzli czy pasuje do szablonu i tak czytać z tego wartości.

0

Dynamiczne adresy można znaleźć za pomocą wskaźników, tak jak to robi Cheat Engine. W Cheat Engine wystarczy, że wpiszę do tabelki wcześniej znaleziony wskaźnik i offsety, wczytam proces i wszystko działa.
Adres które go szukam CE podaje jako "demigod.exe", do którego dodaje stałą wartość i 3 offsety

Screen z CE:
user image

20C5EFA8 - docelowy adres pod którym znajduje się wartość (money w grze);
"Demigod.exe"+00D0E6B8 - Tego szukam, jaki adres kryje się pod nazwą "Demigod.exe" (00D0E6B8 - to jest wartość stała)

Jeśli od adresu 20C0B2B8 odejmę wartość po znaku "+" tj. 00D0E6B8 otrzymam adres którego szukam i który się zmienia po każdym uruchomieniu gry.
Cheat Engine jakoś go odczytuje bo wczytaniu procesu

0

Jeśli wytrzymasz do jutra wieczora. To podeśle tutaj kod jakim ja wyszukuje BaseAdress na podstawie podobnych kodów w google Oraz kody które zapisują dane w pamięci dla Pointerów tak jak robi to Cheat Engine. Natomiast samo ustalenie adresu pointera powinno udać się zrobić pod Cheat Engine, chociaż sam mie musiałem do tej pory ogarniać gry w wersji 64 bitowej. Póki co to drugie powinieneś móc znależć w moim kodzie trainera do Maxa payne'a w WinAPI. Odpowiedni artykuł wraz z kodem znajduje się na moim blogu (link poniżej).

0

Wytrzymam do jutra:-). Walczę z tym już piąty dzień, zazwyczaj znajduje rozwiązania problemów czytając fora i inne strony, lecz to zagadnienie chyba mnie przerosło i postanowiłem stworzyć ten wątek.
Gra nie jest 64bit tylko windows.

1

Przetłumacz to sobie na Delphi (chyba nie powinieneś mieć z tym problemu) http://www.p-programowanie.pl/cpp/odczytywanie-baseaddress/

1

Wracam do odpowiadania. Chociaż kod, który podał @kAzek sporo wyjaśnia, wystarczy go przetłumaczyć na Delphi. Co przy fakcie, że jest on w czystym WinAPI nie powinno stanowić problemu. Jakby co poniżej metoda, której akurat podałem PID do uruchomionej Opery.

function GetModuleBaseAddress(ProcessId : Cardinal; MName : string; var ImageBase, ImageSize : DWORD) : boolean;
var
  PHandle : THandle;
  CbNeeded, I : Cardinal;
  ModuleInfo : TModuleInfo;
  Modules : array of HMODULE;
  ModuleName : array[0..MAX_PATH - 1] of Char;
begin
  ImageBase := 0;
  ImageSize := 0;
  Result := False;
  SetLength(Modules, 1024);
  PHandle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, ProcessId);
  if (PHandle <> 0) then
  begin
    EnumProcessModules(PHandle, @Modules[0], 1024 * SizeOf(HMODULE), CbNeeded);
    SetLength(Modules, CbNeeded div SizeOf(HMODULE));
    for i := 0 to Length(Modules) - 1 do
    begin
      GetModuleBaseName(PHandle, Modules[I], ModuleName, SizeOf(ModuleName));
      if AnsiCompareText(MName, ModuleName) = 0 then
      begin
        GetModuleInformation(PHandle, Modules[I], @ModuleInfo, SizeOf(ModuleInfo));
        Result := True;
        ImageBase := DWORD(ModuleInfo.lpBaseOfDll);
        ImageSize := DWORD(ModuleInfo.SizeOfImage);
        CloseHandle(PHandle);
        Break;
      end;
    end;
  end;
end;

function GetMainModuleNameFromPid(PidToCheck : DWORD) : string;
var
  FullProcPath : PChar;
  ProcessHandle : THandle;
begin
  Result := '';
  ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, PidToCheck);
  if ProcessHandle > 0 then
  begin
    GetMem(FullProcPath, MAX_PATH);
    GetModuleFileNameEx(ProcessHandle, 0, FullProcPath, MAX_PATH);
    Result := ExtractFileName(FullPRocPath);
  end;
end;

procedure TForm1.Button1Click(Sender : TObject);
const
  A_Pid = $1320;
var
  IBase, ISize : DWORD;
begin
  if GetModuleBaseAddress(A_Pid, GetMainModuleNameFromPid(A_Pid), IBase, ISize) then
  begin
    Caption := Format('%X / %X', [IBase, ISize]);
  end;
end;

Co do odczytywania i zapisywania Pointerów to przykładowe kody masz poniżej. Można tylko dorobić sprawdzanie błędów.

type
  TValueType = (vtByte, vtWord, vtDword);

function ReadPointer(DestPid : DWORD; PointerAddress, PointerOffset : DWORD; ValueType : TValueType) : DWORD;
const
  TypeArr : array[TValueType] of Byte = (1, 2, 4);
var
  BytesRead, HProcess, PtrVar : Cardinal;
begin
  HProcess := OpenProcess(PROCESS_ALL_ACCESS, False, DestPid);
  ReadProcessMemory(HProcess, Ptr(PointerAddress), @PtrVar, SizeOf(PtrVar), BytesRead);
  ReadProcessMemory(HProcess, Ptr(PtrVar + PointerOffset), @Result, TypeArr[ValueType], BytesRead);
  CloseHandle(HProcess);
end;

procedure WritePointer(DestPid : DWORD; PointerAddress, PointerOffset : DWORD; NewValue : DWORD; ValueType : TValueType);
const
  TypeArr : array[TValueType] of Byte = (1, 2, 4);
var
  BytesRead, BytesWrite, HProcess, PtrVar : Cardinal;
begin
  HProcess := OpenProcess(PROCESS_ALL_ACCESS, False, DestPid);
  ReadProcessMemory(HProcess, Ptr(PointerAddress), @PtrVar, SizeOf(PtrVar), BytesRead);
  WriteProcessMemory(HProcess, Ptr(PtrVar + PointerOffset), @NewValue, TypeArr[ValueType], BytesWrite);
  CloseHandle(HProcess);
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1