Solved

Accessing other programs' memory

Posted on 2004-10-27
274 Views
Last Modified: 2010-04-05
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.
0
Question by:Rohan32
    19 Comments
     
    LVL 17

    Expert Comment

    by:TheRealLoki
    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
     
    LVL 20

    Expert Comment

    by:Madshi
    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
     

    Author Comment

    by:Rohan32
    Thank you for the suggestions, I will give it a try later today and see if it works.
    0
     
    LVL 2

    Expert Comment

    by:DeNavigator
    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
     
    LVL 2

    Expert Comment

    by:DeNavigator
    Oeps. Just noticed TheRealLoki says the same. :( Still if you need a more worked out example. :)
    0
     

    Author Comment

    by:Rohan32
    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
     
    LVL 2

    Expert Comment

    by:DeNavigator
    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
     

    Author Comment

    by:Rohan32
    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
     
    LVL 20

    Expert Comment

    by:Madshi
    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
     

    Author Comment

    by:Rohan32
    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
     

    Author Comment

    by:Rohan32
    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
     
    LVL 20

    Expert Comment

    by:Madshi
    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
     

    Author Comment

    by:Rohan32
    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
     
    LVL 20

    Expert Comment

    by:Madshi
    First you said OpenProcess returned 0. Now you speak about an access violation. That are 2 different thing. Could you please clarify?
    0
     

    Author Comment

    by:Rohan32
    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
     
    LVL 20

    Expert Comment

    by:Madshi
    You can't use GetLastError like that. It returns an integer value, not a string. You need to use IntToStr(GetLastError).
    0
     

    Author Comment

    by:Rohan32
    Replaced it with

    ShowMessage(SysErrorMessage(GetlastError()));

    And got "Access denied".
    0
     
    LVL 20

    Accepted Solution

    by:
    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
     

    Author Comment

    by:Rohan32
    Thank you, that bit of code fixed it :)
    0

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    How to run any project with ease

    Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
    - Combine task lists, docs, spreadsheets, and chat in one
    - View and edit from mobile/offline
    - Cut down on emails

    Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
    Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
    Need more eyes on your posted question? Go ahead and follow the quick steps in this video to learn how to Request Attention to your question. *Log into your Experts Exchange account *Find the question you want to Request Attention for *Go to the e…
    Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…

    933 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    17 Experts available now in Live!

    Get 1:1 Help Now