[Delphi] Dumpowanie pamięci procesu do pliku

0

Witam. Próbuje przy użyciu poniższego kodu zdumpować pamięć pewnego procesu, który nie jest
mojego autorstwa. Jednak plik wynikowy rożni się rozmiarem, a także niestety - co ważniejsze - w
porównaniu z plikiem, który uzyskałem na przykład w LordPE (robiąc pełen dump exeka procesu),
różni się adresami virtualnymi. A potrzebuje mieć takie same adresy jak widać pod debugerem, bo
chcę zrobić uniwersalny loader. Dlatego najpierw chciałem przeszukać pamięć. Dodam, że program
docelowy nie jest niczym pakowany. Co robię nie tak? Prosił bym, jeżeli ktoś dysponuje przykładem
kodu do dumpowania procesu pod Delphi. Bo w googlach nie znalazłem nic, zaś źródła programu
Peeping Tom nie bardzo mi pomogły, tam nie ma bezpośredniego kodu na dumpowanie procesów.

var
  WindowH : HWND;
  hProcess, ProcessId : THandle;
  Temp : array[1..4096] of Byte;
  I : integer;
  MemStr : TMemoryStream;
  BytesRead : Cardinal;
const
  WindowClass = 'TOknoProgramu';
begin
  WindowH := FindWindow(WindowClass, nil);
  if WindowH = 0 then
    begin
    ShowMEssage('Nie uruchomiono procesu o klasie okna: ' + WindowClass);
    Exit;
    end;
  GetWindowThreadProcessId(WindowH, @ProcessId);
  hProcess := OpenProcess(PROCESS_VM_READ, False, ProcessId);
  MemStr := TMemoryStream.Create;
  I := 0;
  repeat
    ReadProcessMemory(hProcess, Ptr($400000 + I), @Temp, SizeOf(Temp), BytesRead);
    MemStr.Write(Temp, SizeOf(Temp));
    I := I + BytesRead;
  until BytesRead = 0;
  CloseHandle(hProcess);
  MemStr.SaveToFile('D:\memstr.bin');
  MemStr.Free;
  ShowMessage('Pamięć procesu Zapisane do pliku.');
end;
0

Być może chodzi o wyłączenie optymalizacji {$O-}

lub spróbuj:

(*
An example of self-modifying code.

Tested with Delphi 4 on Windows 98 and NT 4.

You may use this code freely, but make sure you
know what you are doing.

Global disclaimer: It's not my fault! ;-)

Feb '99 - Marcus Moennig - [email protected]
*)

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Green: TButton;
    Red: TButton;
    procedure CallModifiedCode(Sender: TObject);
    procedure ModifyCode(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{$O-} //make sure nop's don't get optimized out of the compiled exe
procedure TForm1.CallModifiedCode(Sender: TObject);
  var b:boolean;
      c:TColor;
  label 1;
begin
c:=clgreen;
b:=true;
// The next line will get modified in memory.
// The comments show what the asm code and the hexadecimal
// representation of the asm code in the compiled exe will look like.
// You can set a breakpoint on the next line and open Delphi's CPU view
// to see the change.

if b then goto 1;      // "jnz SOME_ADDRESS"  "$75 SOME_ADDRESS"

// We will modify the line above to:
// "jz SOME_ADDRESS"  "$74 SOME_ADDRESS" what means:
// if NOT(b) then goto 1;

// The following asm code does nothing (nop=no operation).
// It's simply used as a marker to find the position
// of the jump above in memory.
asm
nop                      // $90
nop                      // $90
nop                      // $90
nop                      // $90
nop                      // $90
nop                      // $90
end;
c:=clred;
1:
form1.Color:=c;
end;


procedure TForm1.ModifyCode(Sender: TObject);

const BUFFMAX=65536;

type TBytes6=Array[0..5] of byte;
     TMemblock=array[0..BUFFMAX-1] of byte;

  Function ReadBufferFromMemory(ad, size:integer; var MB:TMemBlock):cardinal;
  var cnt:cardinal;
  begin
  ReadProcessMemory(Getcurrentprocess, pointer(ad), @MB[0], size, cnt);
  // return number of bytes read
  ReadBufferFromMemory := cnt;
  End;

  procedure WriteByteToMemory(ad:cardinal;rt:byte);
  var cnt:cardinal;
      oldprotect:dword;
  begin
  //make sure we have write access to this address
  VirtualProtect(pointer(ad),sizeof(rt),PAGE_EXECUTE_READWRITE,@oldprotect);
  WriteProcessMemory(Getcurrentprocess, pointer(ad), @rt, sizeof(rt), cnt);
  //restore the previous access protection
  VirtualProtect(pointer(ad),sizeof(rt),oldprotect,@oldprotect);
  End;

var st:TBytes6;
    rt:Byte;
    stcount:word;
    BytesRead:cardinal;
    sad,ead,ad:cardinal;
    x,y,z:cardinal;
    found:boolean;
    MemBlock:TMemBlock;
begin
//define the "search term"
st[0]:=$90;       //in asm this means: nop
st[1]:=$90;
st[2]:=$90;
st[3]:=$90;
st[4]:=$90;
st[5]:=$90;
stCount:=6;
//replace term depending on which button was clicked
if (sender=red) then rt:=$74  // in asm: jz
                else rt:=$75; // in asm: jnz
//address range to be searched
sad:=($00400000);
ead:=($7fffffff);
//current address
ad:=sad;
found:=false;
repeat
  //read one block of length BUFFMAX from the current address ad
  BytesRead:=ReadBufferFromMemory(ad, BUFFMAX, MemBlock);
  //no bytes read; so get out
  if BytesRead=0 then break;
  // make sure we don't miss the search term when it spans two blocks
  If BytesRead=BUFFMAX Then BytesRead:=BytesRead-stCount;
  // search this MemoryBlock
  // cycle through the MemoryBlock
  For x:=0 To BytesRead-1 do
      begin
      found:=true;
      // check for search term
      For y:=0 To stCount-1 do
          If MemBlock[x+y]<>st[y] then
             begin
             found:=false;
             break;
             end;
      If found Then
         begin
         // the search term starts at ad+x+y-stcount
         z:=ad+x+y-stcount;
         // the code we want to replace starts 2 bytes before
         WriteByteToMemory(z-2,rt);
         break; // stop searching
         end;
      end;
  ad:=ad+BytesRead;
  until (ad>=ead) or found;
end;
0

Co ten kod robi? Full dump w Lordzie == zrzucenie głównego modułu danego procesu, od image base, rozmiar oparty o image size wczytane z nagłówka pliku. Ten kod nie ma z tym nic wspólnego - poczytaj o formacie PE.

0

Dziękuję Opi - ten kod widziałem wcześniej chyba w wycinkach na ExpertExchange, ale nie przyjrzałem się
mu zbyt dokładnie, bo myślałem, że on tylko modyfikuje sam exeka procesu który go wywołał, a jednak jak
dla mnie okazał się poprawny. Wyszukuje adres występowania ciągu sześciu bajtów poprawnie, jednak do
MemoryStream znowu nie zrzuca jak należy. Różni się zawartość od tej z LordPE. Może asdf mógłbyś tutaj
podać jakiś przykład jak poprawnie zdumpować do pliku główny moduł jakiegoś proceśu? Z góry dziękuję ;)

procedure TForm1.ModifyCode(Sender: TObject);
const
  BUFFMAX = 65536;
type
  TBytes6 = array[0..5] of Byte;
  TMemblock = array[0..BUFFMAX - 1] of Byte;
var
  WindowH : THandle;
  ProcessId : integer;
  hProcess : THandle;

function ReadBufferFromMemory(Ad, Size: integer; var MB: TMemBlock): Cardinal;
var
  Cnt : Cardinal;
begin
  ReadProcessMemory(hPRocess, Pointer(Ad), @MB[0], Size, Cnt);
  ReadBufferFromMemory := Cnt;
end;

var
  St : TBytes6;
  StCount : word;
  BytesRead : Cardinal;
  Sad, Ead, Ad : Cardinal;
  X, Y, Z : Cardinal;
  Found :  Boolean;
  MemBlock : TMemBlock;
  MemStr : TMemoryStream;
const
  WindowClass = 'TOknoProgramu';
begin
  St[0] := $01;
  St[1] := $02;
  St[2] := $03;
  St[3] := $04;
  St[4] := $05;
  St[5] := $06;
  StCount := 6;
  Sad := ($00400000);
  Ead := ($7fffffff);
  Ad := Sad;
  Found := False;
  MemStr := TMemoryStream.Create;
  WindowH := FindWindow(WindowClass, nil);
  GetWindowThreadProcessId(WindowH, @ProcessId);
  if WindowH = 0 then
  begin
    ShowMessage('Nie uruchomiono procesu o klasie okna: ' + WindowClass);
    Exit;
  end;
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
  repeat
    BytesRead := ReadBufferFromMemory(Ad, BUFFMAX, MemBlock);
    if BytesRead = 0 then
      Break;
    if BytesRead = BUFFMAX then
      BytesRead := BytesRead - StCount;
    for X := 0 to BytesRead - 1 do
    begin
      Found := True;
      for Y := 0 to StCount - 1 do
        if MemBlock[X + Y] <> St[Y] then
        begin
          Found := False;
          Break;
        end;
        if Found then
        begin
          Z := Ad + X + Y - StCount;
          ShowMessage('Ciąg bajtów znaleziono pod adresem: ' + IntToHex(Z, 6));
          Break;
        end;
      end;
      Ad := Ad + BytesRead;
      MemStr.write(MemBlock, SizeOf(MemBlock));
    until (Ad >= Ead);
    MemStr.SaveToFile('D:\memstr.bin');
    MemStr.Free;
    ShowMessage('Koniec szukania.');
end;
0

Zależy o jaki dump chodzi, jeżeli chodzi o taki żeby się zrzuconą binarkę dało odpalić to jest 'trochę' zabawy.

0

Nie koniecznie, bo wiem że z tym roboty, bawiłem się kiedyś w patchowanie, ale zawsze zagadnienia
rozpakowywania execow i poprawiania nagłówków i takie tam były dla mnie zagadką. A wystarczył by
mi kod w Delphi, który zdumpował by proces w taki sam sposób jak robi to LordPE - jeżeli da radę :)

0

LordPE w full dumpie nie odbudowuje jedynie importów jeżeli te były usunięte przez packer - jeżeli są 'oryginalne' to zostaną odbudowane poprawnie, robi pełną przebudowę.

0

Aha, rozumiem, ale jednak adresy się zgadzają z tym co było w oryginalnym pliku przynajmnije jeśli
plik nie bł spakowany tak mi się wydaje. Jednak tym sposobem co ja robiłem dump procesu, mam
zupełnie gdzie indziej te same dane. Początek pliku jest chyba taki sam jak w LordPE, jednak to co
jest już póxniej jest przesuniere. Może masz jakiś kod, który robi to inaczej - w sposób prawidłowy?

0

Kodu nie mam (nigdy nie napisałem linijki kodu w czymś pascalowatym :P), te przesunięte adresy wynikają z różnic w rozmiarach fizycznych i wirtualnych oraz wyrównaniu. Wyrównanie pliku to zwykle 512 (czyli rozmiar najmniejszego bloku w pliku), rozmiar strony pamięci, a co za tym idzie bloku w pamięci, to 4096. Więc u Ciebie początek pierwszej sekcji jest pod offsetem 4096, w oryginale na 512 (oczywiście wartości mogą się różnić, wszystko zapisane w nagłówku PE). To główna różnica, drugą są rozmiary sekcji - wirtualne i fizyczne mogą się różnić, najczęściej w przypadku sekcji danych. Poszukaj jakiegoś edytora PE\dumpera pisanego w Delphi, coś takiego open source było.

0

Ok, dzięki. W sumie chciałem mieć taki kod jako ciekawostkę, bo najbrdziej na początku zależało mi
na znalezieniu w pamięci procesu ciągu bajtów i podanie pod jakim adresem się znajdują no i to dzięki
Opi'emu się udało. A może pamiętasz nazwę ów dumpera, do którego dostępne są źródła w Delphi?

0

Uuu, niestety nie, jeżeli sobie przypomnę\natknę się na takowy to dam znać.

0

Ok, dzieki z góry.

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