Accessing other programs' memory

Hello. I have been trying to find a way to read a few bytes from another program's memory. Basically I'm trying to read values from other programs by using the memory adress, no luck so far. So what I'm looking for is basically working code that reads a given amount of bytes from a given adress in a given program, basically like a standard memory viewer.
Rohan32Asked:
Who is Participating?
 
MadshiConnect With a Mentor Commented:
Are you sure that it is a normal application and that you have admin rights?

Eventually calling this in the initialization of  your program helps:

procedure EnableAllPrivileges;
type TTokenPrivileges = record
       PrivilegeCount : dword;
       Privileges     : array [0..maxInt shr 4 - 1] of TLUIDAndAttributes;
     end;
var c1, c2 : dword;
    i1     : integer;
    ptp    : ^TTokenPrivileges;
    backup, restore : int64;
begin
  if OpenProcessToken(windows.GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, c1) then
    try
      c2 := 0;
      GetTokenInformation(c1, TokenPrivileges, nil, 0, c2);
      if c2 <> 0 then begin
        ptp := pointer(LocalAlloc(LPTR, c2 * 2));
        if GetTokenInformation(c1, TokenPrivileges, ptp, c2 * 2, c2) then begin
          // enabling backup/restore privileges breaks Explorer's Samba support
          if not LookupPrivilegeValue(nil, pchar(DecryptStr(CSeBackupPrivilege )), backup ) then backup  := 0;
          if not LookupPrivilegeValue(nil, pchar(DecryptStr(CSeRestorePrivilege)), restore) then restore := 0;
          for i1 := 0 to integer(ptp^.PrivilegeCount) - 1 do
            if (ptp^.Privileges[i1].Luid <> backup ) and
               (ptp^.Privileges[i1].Luid <> restore) then
              ptp^.Privileges[i1].Attributes := ptp^.Privileges[i1].Attributes or SE_PRIVILEGE_ENABLED;
          AdjustTokenPrivileges(c1, false, PTokenPrivileges(ptp)^, c2, PTokenPrivileges(nil)^, cardinal(pointer(nil)^));
        end;
        LocalFree(dword(ptp));
      end;
    finally CloseHandle(c1) end;
end;
0
 
TheRealLokiSenior DeveloperCommented:
I use a SharedMemoryMap unit I made to share memory across applications

but it basically does

        HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE,
        0, SHAREDMAPFILESIZE, pchar(MAPNAME));

MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

then use mutexes for "locking" so you don't get update issues

        HMapMutex := CreateMutex(nil, false, pchar('LOCK' + MapName));
//  if HMixMutex = 0 then we were not able to lock, try again, or wait

0
 
MadshiCommented:
ReadProcessMemory is the way to go. It's actually quite easy to use.

You can check which memory areas are allocated by using VirtualAllocEx.

Both APIs want a process handle, which you can get by using OpenProcess.
0
Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

 
Rohan32Author Commented:
Thank you for the suggestions, I will give it a try later today and see if it works.
0
 
DeNavigatorCommented:
Maybe it would be easier to use Memory Mapped Files. If you need a example just shout and I'll post some sample code
0
 
DeNavigatorCommented:
Oeps. Just noticed TheRealLoki says the same. :( Still if you need a more worked out example. :)
0
 
Rohan32Author Commented:
I was able to read the bytes with FileMapping but only the beginning (I assume that's a static part). When I tried to read the points of interest all I got from the program was 00 bytes (even though a mem viewer showed plenty of non-zero bytes there). I assume that has something to do with that the points of interest are initialised as empty bytes and then changed during runtime? If so then I need the filemapper to update somehow.
0
 
DeNavigatorCommented:
type
  TMijnData = record
    Naam: String[30];
    Meldingsdatum: TDateTime;
    Straat: String[30];
    Huisnummer: Integer;
    Postcode: String[7];
    Plaats: String[25];
  end;
  PMijnData = ^TMijnData;

procedure TForm1.ButtonReadClick(Sender: TObject);
var
  MijnData: PMijnData;
begin
  WaitForSingleObject(MijnMutex, INFINITE);
  try
    MijnData := MapViewOfFile(MijnMemBestand, FILE_MAP_READ, 0, 0, SizeOf(TMijnData));
    try
      LabeledEditNaam.Text := MijnData^.Naam;
      LabeledEditStraat.Text := MijnData^.Straat;
      LabeledEditNummer.Text := IntToStr(MijnData^.Huisnummer);
      LabeledEditPostcode.Text := MijnData^.Postcode;
      LabeledEditPlaats.Text := MijnData^.Plaats;
      DateTimePicker1.Date := MijnData^.Meldingsdatum;
    finally
      UnmapViewOfFile(MijnData);
    end;
  finally
    ReleaseMutex(MijnMutex);
  end;
end;

procedure TForm1.ButtonWriteClick(Sender: TObject);
var
  MijnData: PMijnData;
begin
  WaitForSingleObject(MijnMutex, INFINITE);
  try
    MijnData := MapViewOfFile(MijnMemBestand, FILE_MAP_WRITE, 0, 0, SizeOf(TMijnData));
    try
      MijnData^.Naam := LabeledEditNaam.Text;
      MijnData^.Straat := LabeledEditStraat.Text;
      MijnData^.Huisnummer := StrToInt(LabeledEditNummer.Text);
      MijnData^.Postcode := LabeledEditPostcode.Text;
      MijnData^.Plaats := LabeledEditPlaats.Text;
      MijnData^.Meldingsdatum := DateTimePicker1.Date;
    finally
      UnmapViewOfFile(MijnData);
    end;
  finally
    ReleaseMutex(MijnMutex);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MijnMemBestand := CreateFileMapping($FFFFFFFF, nil,
                       PAGE_READWRITE, 0, SizeOf(TMijnData), MMF_Naam);
  if MijnMemBestand <> 0 then begin
    if GetLastError = ERROR_ALREADY_EXISTS then begin
      CloseHandle(MijnMemBestand);
      MijnMemBestand := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, MMF_Naam);
    end;
  end;
  MijnMutex := CreateMutex(nil, False, Mutex_Naam);
  if (MijnMemBestand = 0) or (MijnMutex = 0) then begin
    ButtonSchrijf.Enabled := False;
    ButtonLees.Enabled := False;
    ShowMessage('MMF of Mutex konden niet worden gemaakt.');
  end;
end;

This is what I use!
0
 
Rohan32Author Commented:
ReadProcessMemory seems to partially work. I was able to read some memory from one process but then when I tried it on the desired process the OpenProcess command failed. I'm guessing this is because it couldn't get the desired access (PROCESS_VM_READ). Is there any way to go around this? I figured I would try SetSecurityInfo() but it doesn't seem to be defined in Delphi.
0
 
MadshiCommented:
Which is the desired process? Is it a normal application/game, or is it a system process or service? Do you have admin rights? SetSecurityInfo won't help.
0
 
Rohan32Author Commented:
It's a normal application that runs as a process, not a system process or anything like that. And yes I have admin rights. OpenProcess just returns zero instead of an ID so I would assume that means that it failed.
0
 
Rohan32Author Commented:
Here's the code I got so far (in case I made any mistakes in it):

procedure TForm1.Button1Click(Sender: TObject);
var
  ProcessID:Integer;
begin
  ProcessID:=GetProcessID('desiredprocess');                  //This step works fine
  ProcessID:=OpenProcess(PROCESS_VM_READ,False,ProcessId);  //This stage returns 0 even though it works for other processes
  if ProcessID=0 then
    Exit;
  Caption:=IntToStr(ReadAddress(ReadAddress($848A18,ProcessID)+$0aac,ProcessID));
end;

function TForm1.ReadAddress(Address,ProcessID:Integer):Integer;
var
  Contents:Int64;
  BytesRead:Cardinal;
begin
  Contents:=0;
  if not ReadProcessMemory(ProcessID,Ptr(Address),@Contents,4,BytesRead) then
    Application.MessageBox('Error reading the process memory.','Error',MB_OK);
  Result:=Contents;
end;

function GetProcessID(ProcessName:String):Integer;
var
  ContinueLoop:BOOL;
  FSnapshotHandle:THandle;
  FProcessEntry32:TProcessEntry32;
begin
  Result:=0;
  FSnapshotHandle:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  FProcessEntry32.dwSize:=Sizeof(FProcessEntry32);
  ContinueLoop:=Process32First(FSnapshotHandle, FProcessEntry32);
  while Integer(ContinueLoop)<>0 do
  begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile))=UpperCase(ProcessName))
     or (UpperCase(FProcessEntry32.szExeFile)=UpperCase(ProcessName))) then
    begin
      Result:=FProcessEntry32.th32ProcessID;
      Break;
    end;
    ContinueLoop:=Process32Next(FSnapshotHandle,FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;
0
 
MadshiCommented:
Please check what "ProcessID" contains before calling OpenProcess. If OpenProcess returns 0, please check what GetLastError sais afterwards.

Btw, please don't forget to call CloseHandle on the process handle you got from OpenProcess (only, if it returned something <> 0, of course). Otherwise your code has a handle leak.
0
 
Rohan32Author Commented:
It contains a non-zero value before calling OpenProcess. This is the error that I receive:

EAccessViolation with message 'Access violation at adress 77D5E176 in module 'user32.dll'. Read of adress 00000005'
0
 
MadshiCommented:
First you said OpenProcess returned 0. Now you speak about an access violation. That are 2 different thing. Could you please clarify?
0
 
Rohan32Author Commented:
OpenProcess does indeed return 0 and from GetLastError I get the error about access violation (which probably results in the zero being returned).

  ProcessID:=OpenProcess(PROCESS_VM_READ,False,ProcessId);  //This stage gives ProcessID the value 0.
  if ProcessID=0 then
  begin
    Application.MessageBox(PChar(GetLastError()),'Error',MB_OK);  //This gives the access violation message.
    Exit;
  end;
0
 
MadshiCommented:
You can't use GetLastError like that. It returns an integer value, not a string. You need to use IntToStr(GetLastError).
0
 
Rohan32Author Commented:
Replaced it with

ShowMessage(SysErrorMessage(GetlastError()));

And got "Access denied".
0
 
Rohan32Author Commented:
Thank you, that bit of code fixed it :)
0
All Courses

From novice to tech pro — start learning today.