Introduction
I was recently trying to steal string
s from another program's listview
control. You need to pass a pointer so it knows where to put the string
. Normally, this wouldn't be a problem, but because Windows uses virtual memory, pointers are not valid across programs.
Virtual memory is how Windows deals out memory to all its programs. It makes programs think they have 2 Gb of memory to use. It also protects programs from using each other's memory so if one program crashes, it doesn't take down the whole system with it.
So after coding a fair bit, I realized my pointers were all invalid and it wouldn't work. A few hours of digging through MSDN brought me to the functions VirtualAllocEx()
, VirtualFreeEx()
, WriteProcessMemory()
and ReadProcessMemory()
. Armed with this new information, I set out to modify my code. Here is what I had so far:
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
int main(void) {
HWND hwnd=FindWindow(NULL, "Stealing Program's Memory: ListView");
HWND listview=FindWindowEx(hwnd, NULL, "SysListView32", NULL);
int count=(int)SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
int i;
char item[512], subitem[512];
LVITEM lvi;
lvi.cchTextMax=512;
for(i=0; i<count; i++) {
lvi.iSubItem=0;
lvi.pszText=item;
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)&lvi);
lvi.iSubItem=1;
lvi.pszText=subitem;
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)&lvi);
printf("%s - %s\n", item, subitem);
}
return 0;
}
As I said before, this won't work. The pointers to lvi
, item
, and subitem
all get screwed when they go across process. The solution? Use WriteProcessMemory()
and ReadProcessMemory()
to use the other programs memory, perform LVM_GETITEMTEXT
on it, and read it back. Hackish yes, but then again reading items from another program's listview
control is one giant hack.
First, we get the process of the listview
like this:
unsigned long pid;
HANDLE process;
GetWindowThreadProcessId(listview, &pid);
process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);
Next, we create three pointers, LVITEM *_lvi
, char *_item
, and char *_subitem
and allocate them in the other program's virtual memory space with VirtualAllocEx()
:
LVITEM *_lvi=(LVITEM*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
MEM_COMMIT, PAGE_READWRITE);
char *_item=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
char *_subitem=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
Now, we point lvi.pszText
to _item
, and copy its memory to _lvi
using WriteMemoryProcess()
:
lvi.pszText=_item;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
Now that we have an LVITEM
pointer that is valid in the other programs virtual memory, we can shoot off LVM_GETITEMTEXT
to listview
and copy _item
's text into item so we can read it in our program:
SendMessage(hwnd, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process, _item, item, max, NULL);
Repeat that for subitem, then free the memory we used in the other program's memory:
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
VirtualFreeEx(process, _item, 0, MEM_RELEASE);
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);
Yay, all done. In case that didn't make too much sense to you, here is our new code, all fixed up:
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
int main(void) {
HWND hwnd=FindWindow(NULL, "Stealing Program's Memory: ListView");
HWND listview=FindWindowEx(hwnd, NULL, "SysListView32", NULL);
int count=(int)SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
int i;
LVITEM lvi, *_lvi;
char item[512], subitem[512];
char *_item, *_subitem;
unsigned long pid;
HANDLE process;
GetWindowThreadProcessId(listview, &pid);
process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);
_lvi=(LVITEM*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
MEM_COMMIT, PAGE_READWRITE);
_item=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
_subitem=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
lvi.cchTextMax=512;
for(i=0; i<count; i++) {
lvi.iSubItem=0;
lvi.pszText=_item;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
lvi.iSubItem=1;
lvi.pszText=_subitem;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process, _item, item, 512, NULL);
ReadProcessMemory(process, _subitem, subitem, 512, NULL);
printf("%s - %s\n", item, subitem);
}
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
VirtualFreeEx(process, _item, 0, MEM_RELEASE);
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);
return 0;
}
If you're looking to use a program's memory for another reason, or have had a similar problem to mine, adapting this should be fairly easy.
This article was originally written for int64.org.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.