[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 261
  • Last Modified:

ReadProcessMemory (200pts!)

Will increase points to 200 when correct answer is given.

I need a function I can put in a DLL, which I would call like this:

TheData := ReadProcMem(ProcessID);

where TheData is a pchar.

It mustn't use the 'string' type as it means I would need to include an extra file with the dll (you can use pchar).

I want it to read in the entire memory of an app, starting from the base address (normally $400000), and reading till the end of the program.

I think it would need to use openprocess, readprocessmemory and closehandle. I have tried lots but I can't get it to work. Also I don't know the API to find the base address/end address.
0
plasmatek
Asked:
plasmatek
  • 7
  • 5
1 Solution
 
MadshiCommented:
Hi plasmatek...

function ReadProcMem(processID: dword) : pointer;
var ph  : dword;
    p1  : pointer;
    mbi : TMemoryBasicInformation;
    dw1 : dword;
begin
  result := nil;
  ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, processID);
  if ph <> 0 then
    try
      p1 := nil;
      while VirtualQueryEx(ph, p1, mbi, sizeOf(TMemoryBasicInformation)) = sizeOf(TMemoryBasicInformation) do begin
        if (mbi.BaseAddress <> nil) and (mbi.BaseAddress = mbi.AllocationBase) then begin
          result := AllocMem(mbi.RegionSize);
          if (not ReadProcessMemory(ph, mbi.BaseAddress, result, mbi.RegionSize, dw1)) or
             (dw1 <> mbi.RegionSize) then begin
            FreeMem(result);
            result := nil;
          end;
          break;
        end;
        p1 := pointer(cardinal(p1) + mbi.RegionSize);
      end;
    finally CloseHandle(ph) end;
end;

Am awaiting 200 points...   :-)

Regards, Madshi.
0
 
LischkeCommented:
Madshi, this is the kind of question you are living for, don't you :-)))?

Ciao, Mike
0
 
MadshiCommented:
Hehe... Yes, exactly...   :-))

Regards, Madshi.
0
Learn to develop an Android App

Want to increase your earning potential in 2018? Pad your resume with app building experience. Learn how with this hands-on course.

 
plasmatekAuthor Commented:
procedure TForm1.Button1Click(Sender: TObject);
var
mypoint: pointer;
mypchar: pchar;
begin
mypoint := readprocmem($ffe34c65);
mypchar := @mypoint;
memo1.text := mypchar;
//memo1.text := inttostr(length(mypchar));
end;

I called it with this, and I just get 3 characters appearing in memo1.text - I did length(mypchar) and it said it was only 3 characters as well, and the characters change each time. Perhaps I am doing something wrong?
$ffe34c65 was the processid of a running process.
0
 
MadshiCommented:
Yes, you're doing something wrong. What do you expect? You're copying CODE, no TEXT. Windows' string types (= pchar) stop working at the first #0 character. So the if there's a #0 character in the code somewhere, the string gets cut off. That means in your case it's evident, that the application header has a #0 character in the 4th position. And that's very good possible.
If you want to look at the complete binary data, you can't put it into a memo, you have to convert it somehow, so that it's readable, like a HexEditor.

Okay, anyway, you surely need the length of the copied code. So I think we should change the function parameters. I think the easiest way would be to return a Delphi string. There you can ask Length(DelphiString) and that doesn't get cut off at the first #0 character...

function ReadProcMem(processID: dword) : string;
var ph  : dword;
    p1  : pointer;
    mbi : TMemoryBasicInformation;
    dw1 : dword;
begin
  result := '';
  ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, processID);
  if ph <> 0 then
    try
      p1 := nil;
      while VirtualQueryEx(ph, p1, mbi, sizeOf(TMemoryBasicInformation)) = sizeOf(TMemoryBasicInformation) do begin
        if (mbi.BaseAddress <> nil) and (mbi.BaseAddress = mbi.AllocationBase) then begin
          SetLength(result, mbi.RegionSize);
          if (not ReadProcessMemory(ph, mbi.BaseAddress, pointer(result), mbi.RegionSize, dw1)) or
             (dw1 <> mbi.RegionSize) then
            result := '';
          break;
        end;
        p1 := pointer(cardinal(p1) + mbi.RegionSize);
      end;
    finally CloseHandle(ph) end;
end;

Regards, Madshi.
0
 
plasmatekAuthor Commented:
Adjusted points to 200
0
 
plasmatekAuthor Commented:
Thanks! Just what I wanted :) Heres the points and an A Grade. Shame its a string, as I wanted to put it into a DLL and this means I will have to include that DLL, but I think I can manage to convert it into an array or something.

Thanks very much,
James. (plasmatek)
0
 
MadshiCommented:
Well, no problem, if you don't like strings, you can do it like this, too:

function ReadProcMem(processID: dword; var size: cardinal) : pointer;
var ph  : dword;
    p1  : pointer;
    mbi : TMemoryBasicInformation;
    dw1 : dword;
begin
  result := nil;
  size := 0;
  ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, processID);
  if ph <> 0 then
    try
      p1 := nil;
      while VirtualQueryEx(ph, p1, mbi, sizeOf(TMemoryBasicInformation)) = sizeOf(TMemoryBasicInformation) do begin
        if (mbi.BaseAddress <> nil) and (mbi.BaseAddress = mbi.AllocationBase) then begin
          size := mbi.RegionSize;
          result := AllocMem(size);
          if (not ReadProcessMemory(ph, mbi.BaseAddress, result, size, dw1)) or
             (dw1 <> size) then begin
            FreeMem(result);
            size := 0;
            result := nil;
          end;
          break;
        end;
        p1 := pointer(cardinal(p1) + mbi.RegionSize);
      end;
    finally CloseHandle(ph) end;
end;
0
 
plasmatekAuthor Commented:
Thanks very much! I have now adapted this to remove the size parameter and changed it to stdcall so it can be called from VB without any problems (make sure you declare the parameters as byval). This code is flawless! Thanks very much again Madshi.

function ReadProcMem(processID: dword) : pointer;stdcall;
var ph  : dword;
    p1  : pointer;
    mbi : TMemoryBasicInformation;
    dw1 : dword;
    size: cardinal;
begin
  result := nil;
  ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, processID);
  if ph <> 0 then
    try
      p1 := nil;
      while VirtualQueryEx(ph, p1, mbi, sizeOf(TMemoryBasicInformation)) = sizeOf(TMemoryBasicInformation) do begin
        if (mbi.BaseAddress <> nil) and (mbi.BaseAddress = mbi.AllocationBase) then begin
          size := mbi.RegionSize;
          result := AllocMem(size);
          if (not ReadProcessMemory(ph, mbi.BaseAddress, result, size, dw1)) or
             (dw1 <> size) then begin
            FreeMem(result);
            result := nil;
          end;
          break;
        end;
        p1 := pointer(cardinal(p1) + mbi.RegionSize);
      end;
    finally CloseHandle(ph) end;
end;
0
 
plasmatekAuthor Commented:
I had a little problem. If I try to use this on a process which has been compressed (petite,shrinker,aspack,upx etc) then it doesn't display all the data. (Only displays about 4000 bytes).
0
 
MadshiCommented:
Please send such a 4000 bytes compressed exe to "madshi@gmx.net". I'll have a look at it...
0
 
MadshiCommented:
Bad, VERY bad. I don't know what's happening there. Well, I know how to solve it, but it's *VERY* (very, very) complicated. You have to enumerate the modules of the other application (and that's again different between win9x and winNT), then parse through the image module header and so on...    :-((
0
 
MadshiCommented:
Well, here comes the function that works perfectly for OUR process. But to make this work for other processes, too, is (as I said) VERY difficult.

function GetModuleNtHeaders(module: cardinal) : PImageNtHeaders;
const CENEWHDR = $003C;          // offset of new EXE header
      CEMAGIC  = $5A4D;          // old EXE magic id:  'MZ'
      CPEMAGIC = $4550;          // NT portable executable
begin
  result := nil;
  try
    if TPWord(module)^ = CEMAGIC then begin
      result := pointer(module + TPWord(module + CENEWHDR)^);
      if result^.signature <> CPEMAGIC then
        result := nil;
    end;
  except result := nil end;
end;

function GetMyProcMem : pointer;
begin
  with GetModuleNtHeaders(HInstance)^.OptionalHeader do begin
    result := AllocMem(SizeOfCode);
    Move(pointer(HInstance + BaseOfCode)^, result^, SizeOfCode);
  end;
end;
0

Featured Post

The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

  • 7
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now