[Delphi][WinAPI] Czytanie adresu aplikacji

0

Witajcie.

Przepisuje program napisany w Delphi na WinAPI (zależy mi na jak najmniejszym rozmiarze i szybkości - aplikacja bezokienkowa) i pojawił się problem. Chcę odczytać adres pewnej aplikacji:

function MemReadString(Address: Integer): String;
var
  NB: LongWord;
  Temp: ARRAY [1..255] OF Byte;
  I: Byte;
  IDProcess, proc_ID: Cardinal;
begin
  GetWindowThreadProcessID(FindWindow('NAZWA_OKIENKA_APLIKACJI', nil), @proc_ID);
  IDProcess := OpenProcess(PROCESS_ALL_ACCESS, false, proc_ID);
  Result := '';
  ReadProcessMemory(IDProcess, Ptr(Address), @Temp[1], 255, NB);
  for I := 1 to 255 do
  begin
    if ((Temp[I] = 0) or (Temp[I] = $0F)) then
    Break;
    Result := Result + Chr(Temp[I]);
  end;
end;

Adresy aplikacji są dobre, funkcja też jest dobra - sprawdzone na zwykłej formie, a w WinAPI już nie chcę pójść.
Wyrzuca jakieś złe wartości.

Pozdrawiam

0

A na pewno wskazana klasa okna jest prawidłowa? Bo na szybko
zrobiłem taki test w WinAPI dla WinAmpa w oknie o starym GUI,
no i bez problemów pokazał mi nazwę pliku. A kod masz poniżej

program Project2;

{$APPTYPE GUI}

uses
  Windows, Messages;

const
  WinAmp_Class = 'Winamp v1.x';

var
  DesktopH : HWND;

function GetWinampFilename: string;
var
  TempStr : string;
  WinAmp_HWND, TempHandle : THandle;
  Dat2 : array[0..500] of Char;
  TrackPos : integer;
  Temp, MPointer : cardinal;
begin
  WinAmp_HWND := FindWindow(WinAmp_Class, nil);
  TrackPos := SendMessage(WinAmp_HWND, WM_USER, 0, 125);
  MPointer := SendMessage(WinAmp_HWND, WM_USER, TrackPos, 211);
  GetWindowThreadProcessId(WinAmp_HWND, TempHandle);
  WinAmp_HWND := OpenProcess(PROCESS_ALL_ACCESS, False, TempHandle);
  ReadProcessMemory(WinAmp_HWND, Pointer(MPointer), @Dat2, 500, Temp);
  CloseHandle(WinAmp_HWND);
  TempStr := Dat2;
  Result := TempStr;
end;

begin
  DesktopH := FindWindow('ProgMan', nil);
  MessageBox(DesktopH, PChar(GetWinampFilename), 'Test', MB_ICONINFORMATION + MB_OK);
end.
0

Działa na zwykłej formie, kodu nie tknąłem.

A jak chce pod WinAPI to już nie działa.

0

To pewnie adres, ktory podajesz się zmienił. Ja zrobiłem test na WinAmpie i wartości pobranej z exe
Hex Edytorem, pod którą będzie w większości exeków komunikat o niemożliwości uruchomienia pod
DOSem. I kod zadziałał, to pewnie nie jest wina WinAPI, tylko coś pokręciłeś, bo powinno działać ;/ I
najlepiej przetestuj ten kod poniżej. Albo też ciąg odczytanych znaków na początku ma $0 lub $0F.

program Project2;

{$APPTYPE GUI}

uses
  Windows, Messages;

const
  WinAmp_Class = 'Winamp v1.x';

var
  DesktopH : HWND;

function MemReadString(Address: Integer): String;
var
  NB: LongWord;
  Temp: ARRAY [1..255] OF Byte;
  I: Byte;
  IDProcess, proc_ID: Cardinal;
begin
  GetWindowThreadProcessID(FindWindow(WinAmp_Class, nil), @proc_ID);
  IDProcess := OpenProcess(PROCESS_ALL_ACCESS, false, proc_ID);
  Result := '';
  ReadProcessMemory(IDProcess, Ptr(Address), @Temp[1], 255, NB);
  for I := 1 to 255 do
  begin
    if ((Temp[I] = 0) or (Temp[I] = $0F)) then
    Break;
    Result := Result + Chr(Temp[I]);
  end;
end;

begin
  DesktopH := FindWindow('ProgMan', nil);
  MessageBox(DesktopH, PChar(MemReadString($40004E)), 'Test', MB_ICONINFORMATION + MB_OK);
end.

Zwraca MessageBox'a:


Test

This program cannot be run in DOS mode.

$

OK

0

Poddaje się...

Twój kod działa i pokazuje poprawne wartości jak skompiluje na formie (dam np. jako instrukcje po naciśnieciu przycisku), a jak robię pod WinAPI to wywala:

http://i53.tinypic.com/2rrqonb.png

Adres czytanej wartości jest stały i jest w postaci np.

jakisciag123

0

To nie mam pojęcia dlaczego. Jak się da to zdumpuj w całości proces docelowy
do pliku używając na przykład LordPE i sprawdź HexEdytorem co się kryje pod
tym adresem. Bo jedyna rzecz jaka może się jeszcze różnić, między aplikacją
VCL, a WinAPI to być może opcje kompilatora. Więcej pomysłów nie mam, to
może ktoś inny coś doradzi. I ja tylko kiedyś spotkałem się w pewnym kodzie
z czymś takim, że poniższy kod działał, a kiedy się użyło go w pętli już nie, a
wiadomo że taka konstrukcja kodu sama z siebie nasuwa myśl, że powinno z
pętli się skorzystać. No ale program jest pisany pod VCL, bo korzysta z IRCa
przy pomocy pakietu IRC Vortex, który okazal się lepszy od możliwości Indy.

//...
function TMainForm.ReadNames(AnAddress : integer) : string;
var
  KBS : string;
  BytesRead : DWORD;
  I, B, NBS : integer;
begin
  I := 0;
  NBS := 1;
  while I < 23 do
  begin
    I := I + 1;
    ReadProcessMemory(HandleWindow,
      Ptr(AnAddress - 1 + I), @B, SizeOf(Char), BytesRead);
    if ReadValue(AnAddress - 1 + I) <> 0 then
    begin
      if NBS = 1 then
        Result := Result + Char(B)
      else
      begin
        KBS := Char(B);
        KBS := LowerCase(KBS);
        Result := Result + KBS;
      end;
      if (Char(B) = ' ') or (Char(B) = '-') or (Char(B) = '.') then
        NBS := 1
      else
        NBS := 0;
    end
    else
      I := 23;
  end;
end;

//...
  PlayerA[0].Name := ReadNames(AdrPlyA[0]);
  PlayerA[1].Name := ReadNames(AdrPlyA[1]);
  PlayerA[2].Name := ReadNames(AdrPlyA[2]);
  PlayerA[3].Name := ReadNames(AdrPlyA[3]);
  PlayerA[4].Name := ReadNames(AdrPlyA[4]);
  PlayerA[5].Name := ReadNames(AdrPlyA[5]);
  PlayerA[6].Name := ReadNames(AdrPlyA[6]);
  PlayerA[7].Name := ReadNames(AdrPlyA[7]);
  PlayerA[8].Name := ReadNames(AdrPlyA[8]);
  PlayerA[9].Name := ReadNames(AdrPlyA[9]);
  PlayerA[10].Name := ReadNames(AdrPlyA[10]);
  PlayerA[11].Name := ReadNames(AdrPlyA[11]);
  PlayerA[12].Name := ReadNames(AdrPlyA[12]);
  PlayerA[13].Name := ReadNames(AdrPlyA[13]);
  PlayerA[14].Name := ReadNames(AdrPlyA[14]);
  PlayerA[15].Name := ReadNames(AdrPlyA[15]);
  PlayerB[0].Name := ReadNames(AdrPlyB[0]);
  PlayerB[1].Name := ReadNames(AdrPlyB[1]);
  PlayerB[2].Name := ReadNames(AdrPlyB[2]);
  PlayerB[3].Name := ReadNames(AdrPlyB[3]);
  PlayerB[4].Name := ReadNames(AdrPlyB[4]);
  PlayerB[5].Name := ReadNames(AdrPlyB[5]);
  PlayerB[6].Name := ReadNames(AdrPlyB[6]);
  PlayerB[7].Name := ReadNames(AdrPlyB[7]);
  PlayerB[8].Name := ReadNames(AdrPlyB[8]);
  PlayerB[9].Name := ReadNames(AdrPlyB[9]);
  PlayerB[10].Name := ReadNames(AdrPlyB[10]);
  PlayerB[11].Name := ReadNames(AdrPlyB[11]);
  PlayerB[12].Name := ReadNames(AdrPlyB[12]);
  PlayerB[13].Name := ReadNames(AdrPlyB[13]);
  PlayerB[14].Name := ReadNames(AdrPlyB[14]);
  PlayerB[15].Name := ReadNames(AdrPlyB[15]);
//...
0
  1. Po co PROCCESS_ALL_ACCESS skoro wystarcza Ci tylko prawa do odczytu pamieci?
  2. Jest OpenProccess, wiec wypadaloby dac na koniec rowniez CloseHandle ;)
  3. Sprawdz jaka wartosc zwraca ReadProccesMemory, jezeli zwrcaca blad to zobacz jaki (GetLastError()).
0

Runtime error 216 at 757DA690


w SPACJA końcu znalazłem błąd. Jak mam włączonego Kaspersky Internet Security 2011 to nie mogę czytać pamięci aplikacji, a jak go wyłącze to mogę... ;o
Jakieś rozwiązania?


Dzięki cyriel za nakierowanie - poradziłem sobie, wystarczyło zamiast PROCESS_ALL_ACCESS dać PROCESS_VM_READ.

0

Pewnie Kaspersy uwaza za podejrzane, to ze chcesz prawa do wszystkiego i dlatego, Ci na to nie pozwala.
A co do bledow - jak juz wiesz jaki to blad, to sprawdz tutaj o co dokladnie chodzi. Czesto to potrafi BARDZO pomoc.

0

@cyriel: ja tylko dodam, że akurat u mnie Kaspersky 2009, nie czepia się takiego kodu i pozwala
tak otworzyć proces i czytać jego pamięć, a także zapisywać. Ale u pytającego może być inaczej.

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