We help IT Professionals succeed at work.

WriteProcessMemory in Windows7

MelissaCG
MelissaCG asked
on
2,109 Views
Last Modified: 2012-05-10
Hello Experts,

I was just looking at some posts in a forum and saw one interesting about manipulating a listview of another app. Actually the listviw is from the taskmgr in this code, but that's just for learning purposes i guess, since it's such an ugly code that noone could actually use it for bad things... Well, anyway, i tried it in my Windows 7 x64 Home Premium with UAC and DEP off and its not working... The string with the processes names are blank. I think it's something with write/readprocessmemory because i tested the handles and they arent nil... My friend tested in a windows XP and it worked... What might be wrong here??


unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

{Função para pegar o texto do Item da ListView}
function PegaTexto(s: string): string;
var
c: Char;
i: integer;
Src,Dst: PChar;
begin
i:=length(s);
SetLength(Result,i);
Src:=pointer(s);
Dst:=pointer(Result);
While i <> 0 do
  begin
  c:=Src^;
  if (c>='A') and (c<='Z') then
    Inc(c,32);
  Dst^:=c;
  Inc(Src);
  Inc(Dst);
  Dec(i);
  end;
end;

procedure Esconde(proc: string);
var
dwSize,dwNumBytes,PID,hProc: Cardinal;
PLocalShared,PSysShared: PlvItem;
h: THandle;
iCount,i: integer;
szTemp: string;
begin
{Pega o Handle da ListView}
h:=FindWindow('#32770',nil);
h:=FindWindowEx(h,0,'#32770',nil);
h:=FindWindowEx(h,0,'SysListView32',nil);

{Pega o número de itens da ListView}
iCount:=SendMessage(h, LVM_GETITEMCOUNT,0,0);
for i:=0 to iCount-1 do
  begin
  {Define o tamanho de cada item da ListView}
  dwSize:=sizeof(LV_ITEM) + sizeof(CHAR) * MAX_PATH;

  {Abre um espaço na memória do NOSSO programa para o PLocalShared}
  PLocalShared:=VirtualAlloc(nil, dwSize, MEM_RESERVE + MEM_COMMIT, PAGE_READWRITE);

  {Pega o PID do processo taskmgr}
  GetWindowThreadProcessId(h,@PID);

  {Abre o processo taskmgr}
  hProc:=OpenProcess(PROCESS_ALL_ACCESS,false,PID);

  {Abre um espaço na memória do taskmgr para o PSysShared}
  PSysShared:=VirtualAllocEx(hProc, nil, dwSize, MEM_RESERVE OR MEM_COMMIT, PAGE_READWRITE);

  {Define as propriedades do PLocalShared}
  PLocalShared.mask:=LVIF_TEXT;
  PLocalShared.iItem:=0;
  PLocalShared.iSubItem:=0;
  PLocalShared.pszText:=LPTSTR(dword(PSysShared) + sizeof(LV_ITEM));
  PLocalShared.cchTextMax:=100;

  {Escreve PLocalShared no espaço de memória que abriu no taskmgr}
  WriteProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Pega o texto to item i e passa pro PSysShared}
  SendMessage(h,LVM_GETITEMTEXT,i,LPARAM(PSysShared));

  {Passa o PSysShared para o PLocalShared}
  ReadProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Passa o texto do Item para szTemp}
  szTemp:=pchar(dword(PLocalShared)+sizeof(LV_ITEM));

  {Se esse texto contiver a string proc deleta o item}
  if pos(proc,PegaTexto(szTemp)) > 0 then
    ListView_DeleteItem(h,i);

  {Libera os espaços de memória utilizados}
  VirtualFree(pLocalShared, 0, MEM_RELEASE);
  VirtualFreeEx(hProc, pSysShared, 0, MEM_RELEASE);

  {Fecha o handle do processo}
  CloseHandle(hProc);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
esconde('notepad.exe');
end;

end.

Open in new window

Comment
Watch Question

Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:
The problem is that your app is 32 bit, and the remote process is 64 bit (Windows 7 64 bit task manager). The LVITEM structure is defined as follows:

  {$EXTERNALSYM tagLVITEMA}
  tagLVITEMA = packed record
    mask: UINT;
    iItem: Integer;
    iSubItem: Integer;
    state: UINT;
    stateMask: UINT;
    pszText: PAnsiChar;
    cchTextMax: Integer;
    iImage: Integer;
    lParam: LPARAM;
    iIndent: Integer;
  end;

The two items to note are:

pszText: PAnsiChar;

and

lParam: LPARAM;

In your application a pointer/LPARAM is 4 bytes in size. In the remote (64 bit process), these are 8 byte values. You could try re-defining this structure so the 2 fields are Int64, and then casting the pszText(Int64) to/from PChar. Can't test myself, as I don't have Delphi loaded on my Win7 64 bit system. It would look something like this though:

type
 tagLVITEM64A    = packed record
     mask:       UINT;
     iItem:      Integer;
     iSubItem:   Integer;
     state:      UINT;
     stateMask:  UINT;
     pszText:    Int64;
     cchTextMax: Integer;
     iImage:     Integer;
     lParam:     Int64;
     iIndent:    Integer;
  end;
  LV_ITEM64A     =  tagLVITEM64A;
  PLVITEM64      =  ^LV_ITEM64A;

var
  Form1: TForm1;

implementation
{$R *.DFM}

function PegaTexto(s: string): string;
var
c: Char;
i: integer;
Src,Dst: PChar;
begin
i:=length(s);
SetLength(Result,i);
Src:=pointer(s);
Dst:=pointer(Result);
While i <> 0 do
  begin
  c:=Src^;
  if (c>='A') and (c<='Z') then
    Inc(c,32);
  Dst^:=c;
  Inc(Src);
  Inc(Dst);
  Dec(i);
  end;
end;

procedure Esconde(proc: string);
var
dwSize,dwNumBytes,PID,hProc: Cardinal;
PLocalShared,PSysShared: PLVITEM64;
h: THandle;
iCount,i: integer;
szTemp: string;
begin
{Pega o Handle da ListView}
h:=FindWindow('#32770',nil);
h:=FindWindowEx(h,0,'#32770',nil);
h:=FindWindowEx(h,0,'SysListView32',nil);

{Pega o número de itens da ListView}
iCount:=SendMessage(h, LVM_GETITEMCOUNT,0,0);
for i:=0 to iCount-1 do
  begin
  {Define o tamanho de cada item da ListView}
  dwSize:=sizeof(LV_ITEM64A) + sizeof(CHAR) * MAX_PATH;

  {Abre um espaço na memória do NOSSO programa para o PLocalShared}
  PLocalShared:=VirtualAlloc(nil, dwSize, MEM_RESERVE + MEM_COMMIT, PAGE_READWRITE);

  {Pega o PID do processo taskmgr}
  GetWindowThreadProcessId(h,@PID);

  {Abre o processo taskmgr}
  hProc:=OpenProcess(PROCESS_ALL_ACCESS,false,PID);

  {Abre um espaço na memória do taskmgr para o PSysShared}
  PSysShared:=VirtualAllocEx(hProc, nil, dwSize, MEM_RESERVE OR MEM_COMMIT, PAGE_READWRITE);

  {Define as propriedades do PLocalShared}
  PLocalShared.mask:=LVIF_TEXT;
  PLocalShared.iItem:=0;
  PLocalShared.iSubItem:=0;
  PLocalShared.pszText:=Int64(PSysShared) + sizeof(LV_ITEM64A);
  PLocalShared.cchTextMax:=100;

  {Escreve PLocalShared no espaço de memória que abriu no taskmgr}
  WriteProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Pega o texto to item i e passa pro PSysShared}
  SendMessage(h,LVM_GETITEMTEXT,i,LPARAM(PSysShared));

  {Passa o PSysShared para o PLocalShared}
  ReadProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Passa o texto do Item para szTemp}
  szTemp:=pchar(dword(PLocalShared)+sizeof(LV_ITEM64A));

  {Se esse texto contiver a string proc deleta o item}
  if pos(proc,PegaTexto(szTemp)) > 0 then
    ListView_DeleteItem(h,i);

  {Libera os espaços de memória utilizados}
  VirtualFree(pLocalShared, 0, MEM_RELEASE);
  VirtualFreeEx(hProc, pSysShared, 0, MEM_RELEASE);

  {Fecha o handle do processo}
  CloseHandle(hProc);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
esconde('notepad.exe');
end;

-----

Regards,
Russell

CERTIFIED EXPERT
Top Expert 2011

Commented:
Hi MelissaCG,

I have tried your code in Win xp and Win 7 but seems your code does not really do what you are expecting it to do.

In Win xp the item is deleted but it comes back to it's place instantly (maybe because I am using low specification Laptop so the task flashes before the windows recovers the line). In Win 7 (high spesification) the function looks like it has no effect, but it gets the handle correctly, even you will get the correct number of lines (i.e.: you can try showmessage(inttostr(iCount)); it will get you the right number of lines in the listview, even you can try to maximize the listview (ShowWindow(h, SW_MAXIMIZE);) it will work too.
So your code actually works in Win xp and Win 7 but it does not do what you were told it does (according to the test that I did). You can try one thing, which is: delete all the items by removing the condition which is for matching and finding the intended task in the listview so it will enable the function to delete all the items and then you will see the items are deleted and recovered instantly (in both Win xp & Win 7).

{Funça~o para pegar o texto do Item da ListView}
function PegaTexto(s: string): string;
var
c: Char;
i: integer;
Src,Dst: PChar;
begin
i:=length(s);
SetLength(Result,i);
Src:=pointer(s);
Dst:=pointer(Result);
While i <> 0 do
  begin
  c:=Src^;
  if (c>='A') and (c<='Z') then
    Inc(c,32);
  Dst^:=c;
  Inc(Src);
  Inc(Dst);
  Dec(i);
  end;
end;

procedure Esconde(proc: string);
var
dwSize,dwNumBytes,PID,hProc: Cardinal;
PLocalShared,PSysShared: PlvItem;
h: THandle;
iCount,i: integer;
szTemp: string;
begin
{Pega o Handle da ListView}
h:=FindWindow('#32770',nil);
h:=FindWindowEx(h,0,'#32770',nil);
h:=FindWindowEx(h,0,'SysListView32',nil);

{Pega o nu'mero de itens da ListView}
iCount:=SendMessage(h, LVM_GETITEMCOUNT,0,0);
showmessage(InttoStr(iCount));
for i:=0 to iCount-1 do
  begin
  {Define o tamanho de cada item da ListView}
  dwSize:=sizeof(LV_ITEM) + sizeof(CHAR) * MAX_PATH;

  {Abre um espaço na memo'ria do NOSSO programa para o PLocalShared}
  PLocalShared:=VirtualAlloc(nil, dwSize, MEM_RESERVE + MEM_COMMIT, PAGE_READWRITE);

  {Pega o PID do processo taskmgr}
  GetWindowThreadProcessId(h,@PID);

  {Abre o processo taskmgr}
  hProc:=OpenProcess(PROCESS_ALL_ACCESS,false,PID);

  {Abre um espaço na memo'ria do taskmgr para o PSysShared}
  PSysShared:=VirtualAllocEx(hProc, nil, dwSize, MEM_RESERVE OR MEM_COMMIT, PAGE_READWRITE);

  {Define as propriedades do PLocalShared}
  PLocalShared.mask:=LVIF_TEXT;
  PLocalShared.iItem:=0;
  PLocalShared.iSubItem:=0;
  PLocalShared.pszText:=LPTSTR(dword(PSysShared) + sizeof(LV_ITEM));
  PLocalShared.cchTextMax:=100;

  {Escreve PLocalShared no espaço de memo'ria que abriu no taskmgr}
  WriteProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Pega o texto to item i e passa pro PSysShared}
  SendMessage(h,LVM_GETITEMTEXT,i,LPARAM(PSysShared));

  {Passa o PSysShared para o PLocalShared}
  ReadProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Passa o texto do Item para szTemp}
  szTemp:=pchar(dword(PLocalShared)+sizeof(LV_ITEM));

  {Se esse texto contiver a string proc deleta o item}
  // disable this condition so it deletes all tasks and here you will notice it works but Windows recovers the list quickly
  //if pos(proc,PegaTexto(szTemp)) > 0 then
    ListView_DeleteItem(h,i);

  {Libera os espaços de memo'ria utilizados}
  VirtualFree(pLocalShared, 0, MEM_RELEASE);
  VirtualFreeEx(hProc, pSysShared, 0, MEM_RELEASE);

  {Fecha o handle do processo}
  CloseHandle(hProc);
  end;
end;

Open in new window

Author

Commented:
@rllibby:
The VirtualAllocEx returns the right length pointer for my system?? And the first function im using pchar, which is 4 bytes too, should i change it to 8 bytes, i tested the way you told but something is missing i guess...


@jimyX:
Actually thats a normal behavior, it should go into a timer and all... As i said, im just using this code for learning purposes ;D
I had removed the condition and made it list the processes names but they are coming blank strings...
Software Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
CERTIFIED EXPERT
Top Expert 2011

Commented:
I just came to know that Windows Task Manager refreshes the processes list view every 500 ms, from:
http://www.codeproject.com/KB/system/Hack_Windows_Task_Manager.aspx

The program from the above link clears the task manager list, it works on my Win 7, you can try it on yours to find out.

Also you can imitate that program by adding Timer to your form and set the interval to 50 and add this line to your procedure Esconde. As follows:

ListView_DeleteAllItems(h);   // remove all lines

procedure Esconde(proc: string);
var
dwSize,dwNumBytes,PID,hProc: Cardinal;
PLocalShared,PSysShared: PlvItem;
h: THandle;
iCount,i: integer;
szTemp: string;
begin
{Pega o Handle da ListView}
h:=FindWindow('#32770',nil);
h:=FindWindowEx(h,0,'#32770',nil);
h:=FindWindowEx(h,0,'SysListView32',nil);

{Pega o nu'mero de itens da ListView}
iCount:=SendMessage(h, LVM_GETITEMCOUNT,0,0);
for i:=0 to iCount-1 do
  begin
  {Define o tamanho de cada item da ListView}
  dwSize:=sizeof(LV_ITEM) + sizeof(CHAR) * MAX_PATH;

  {Abre um espaço na memo'ria do NOSSO programa para o PLocalShared}
  PLocalShared:=VirtualAlloc(nil, dwSize, MEM_RESERVE + MEM_COMMIT, PAGE_READWRITE);

  {Pega o PID do processo taskmgr}
  GetWindowThreadProcessId(h,@PID);

  {Abre o processo taskmgr}
  hProc:=OpenProcess(PROCESS_ALL_ACCESS,false,PID);

  {Abre um espaço na memo'ria do taskmgr para o PSysShared}
  PSysShared:=VirtualAllocEx(hProc, nil, dwSize, MEM_RESERVE OR MEM_COMMIT, PAGE_READWRITE);

  {Define as propriedades do PLocalShared}
  PLocalShared.mask:=LVIF_TEXT;
  PLocalShared.iItem:=0;
  PLocalShared.iSubItem:=0;
  PLocalShared.pszText:=LPTSTR(dword(PSysShared) + sizeof(LV_ITEM));
  PLocalShared.cchTextMax:=100;

  {Escreve PLocalShared no espaço de memo'ria que abriu no taskmgr}
  WriteProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Pega o texto to item i e passa pro PSysShared}
  SendMessage(h,LVM_GETITEMTEXT,i,LPARAM(PSysShared));

  {Passa o PSysShared para o PLocalShared}
  ReadProcessMemory(hProc,PSysShared,PLocalShared,1024,dwNumBytes);

  {Passa o texto do Item para szTemp}
  szTemp:=pchar(dword(PLocalShared)+sizeof(LV_ITEM));

  ListView_DeleteAllItems(h);   // remove all lines

  {Libera os espaços de memo'ria utilizados}
  VirtualFree(pLocalShared, 0, MEM_RELEASE);
  VirtualFreeEx(hProc, pSysShared, 0, MEM_RELEASE);

  {Fecha o handle do processo}
  CloseHandle(hProc);
  end;
end;

Open in new window

Author

Commented:
Well, I've told you this code would go in a timer in the last post... The ideia of this question was not to accomplish the source objective, but to understand the process. Since I've just been told that it cant be done because of incompatibilities in 64bits and 32bits I guess there's no point carrying this on anymore.

I guess Borland should make a Delphi for 64bit OS, many people must have this problem with much more important things...

Thank you for the reply though.
Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:
Sad and very true. I hardly touch Delphi any more because of x64 requirements in the work I do.

Author

Commented:
Does C++ have these requirements?? I mean, is it easier to work with memory in x64 with it??
Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:
Yes, Visual C++ makes 64 bit development extremely simple, provided your familiar with C++ development to begin with.

Russell
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.