Hello
finally i found a solution to read memory from a 64bit Application like World of Warcraft or League of Legends using a 32 bit Programm. This also includes a solution to find the Module Base Adress of any 64 bit Programm which you will need to add the Base Pointer (from cheat engine) to find a variable.
This example read the amount of Gold from league of legends. If you want to try it, you have to change the processId (in my case it was dec 3672 (league of legends)).I also used a Timer, Memofield and a Button on the Form. Compiled using Delphi 10 Seattle. Code explanation below
I found out if i add $10 to the PBI.PebBaseAdress i will get the Programm entry point which i need to add to the BaseAdress from CheatEngine. If you found a Base Pointer in Cheat engine its like :"League of legends.exe"+169C294 +$48 +$44. (value for gold)
In 32 bit programms it was no Problem to Read with:
but this does not work for 64 bit programms. More and more programms like world of warcraft are only avaliable in 64 bit in the next time, so you can use this new code.
finally i found a solution to read memory from a 64bit Application like World of Warcraft or League of Legends using a 32 bit Programm. This also includes a solution to find the Module Base Adress of any 64 bit Programm which you will need to add the Base Pointer (from cheat engine) to find a variable.
This example read the amount of Gold from league of legends. If you want to try it, you have to change the processId (in my case it was dec 3672 (league of legends)).I also used a Timer, Memofield and a Button on the Form. Compiled using Delphi 10 Seattle. Code explanation below
Code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Timer1: TTimer;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
type
NTSTATUS = Integer;
PROCESS_BASIC_INFORMATION = packed record
Reserved1: UINT64;
PebBaseAddress: UINT64;
Reserved2: array [0 .. 1] of UINT64;
UniqueProcessId: UINT64;
Reserved3: UINT64;
end;
PPROCESS_BASIC_INFORMATION = ^PROCESS_BASIC_INFORMATION;
TNtQueryInformationProcess = function(ProcessHandle: THANDLE; ProcessInformationClass: ULONG; ProcessInformation: Pointer; ProcessInformationLength: ULONG; ReturnLength: Pointer): NTSTATUS; stdcall;
TNtReadVirtualMemory = function(ProcessHandle: THANDLE; BaseAddress: UINT64; Buffer: Pointer; BufferLength: UINT64; ReturnLength: Pointer): NTSTATUS; stdcall;
private
{ Private-Deklarationen }
public
var
NtQueryInformationProcess: TNtQueryInformationProcess;
NtReadVirtualMemory: TNtReadVirtualMemory;
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function AddCurrentProcessPrivilege(PrivilegeName: WideString): Boolean;
var
TokenHandle: THandle;
TokenPrivileges: TTokenPrivileges;
ReturnLength: Cardinal;
begin
Result := False;
if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, TokenHandle) then
begin
try
LookupPrivilegeValueW(nil, PWideChar(PrivilegeName), TokenPrivileges.Privileges[0].Luid);
TokenPrivileges.PrivilegeCount := 1;
TokenPrivileges.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
if AdjustTokenPrivileges(TokenHandle, False, TokenPrivileges, 0, nil, ReturnLength) then
Result := True;
finally
CloseHandle(TokenHandle);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Form1.Timer1.Enabled=True
then
begin
Form1.Button1.Caption:='start';
Form1.Timer1.Enabled:=False;
end else
begin
Form1.Button1.Caption:='stop';
Form1.Timer1.Enabled:=True;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
hLibrary: HMODULE;
ProcessHandle: THandle;
PBI: PROCESS_BASIC_INFORMATION;
ReturnLength: UINT64;
i: ULONG;
Buffer : cardinal;
begin
AddCurrentProcessPrivilege('SeDebugPrivilege');
hLibrary := LoadLibrary('ntdll.dll');
if hLibrary <> 0 then
begin
@NtQueryInformationProcess := GetProcAddress(hLibrary, 'NtWow64QueryInformationProcess64');
@NtReadVirtualMemory := GetProcAddress(hLibrary, 'NtWow64ReadVirtualMemory64');
end;
ProcessHandle:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, True, 3672);
if NtQueryInformationProcess(ProcessHandle, 0, @PBI, SizeOf(PBI), nil) = 0 then
begin
if NtReadVirtualMemory(ProcessHandle, PBI.PebBaseAddress + $10, @Buffer, 4, @ReturnLength) = 0 then
begin
if NtReadVirtualMemory(ProcessHandle, Buffer+$169C294 , @Buffer, 4, @ReturnLength) = 0 then
begin
if NtReadVirtualMemory(ProcessHandle, Buffer+$48, @buffer, 4, @ReturnLength) = 0 then
begin
if NtReadVirtualMemory(ProcessHandle, Buffer+$44, @buffer, 4, @ReturnLength) = 0 then
form1.Memo1.Clear;
form1.memo1.text:= form1.memo1.text+ inttostr(buffer)+#13#10;
end;
end;
end;
end;
CloseHandle(ProcessHandle);
end;
I found out if i add $10 to the PBI.PebBaseAdress i will get the Programm entry point which i need to add to the BaseAdress from CheatEngine. If you found a Base Pointer in Cheat engine its like :"League of legends.exe"+169C294 +$48 +$44. (value for gold)
In 32 bit programms it was no Problem to Read with:
Code:
function GetModuleBaseAddress(PHandle: Thandle; MName: String): Pointer;
var
Modules : Array of HMODULE;
cbNeeded, i : Cardinal;
ModuleInfo : TModuleInfo;
ModuleName : Array[0..MAX_PATH] of Char;
// Melong : LongBool;
begin
Result := nil;
SetLength(Modules, 1024);
//cbNeeded := 0;
if (PHandle <> 0) then
begin
try
EnumProcessModules(PHandle, @Modules[0], 1024 * SizeOf(HMODULE),cbNeeded);
except
Exit;
end;
SetLength(Modules, cbNeeded div SizeOf(HMODULE)); //Setting the number of modules
if (Length(Modules) > 0) then
begin
for i := 0 to Length(Modules) - 1 do //Start the loop
begin
try
GetModuleBaseName(PHandle, Modules[i], ModuleName, SizeOf(ModuleName));
if (Pos(MName, ModuleName) > 0) then
begin
GetModuleInformation(PHandle, Modules[i], @MoDuleInfo, SizeOf(ModuleInfo));
Result := ModuleInfo.lpBaseOfDll;
Exit;
end;
Except
end;
end;
end;
end;
but this does not work for 64 bit programms. More and more programms like world of warcraft are only avaliable in 64 bit in the next time, so you can use this new code.