From: d_purple_devil on
Hi all, I have a program that needs to pick up the contents of a
SysListView in another process. As you are all aware this requires me
allocating memory for my strings and the LVITEM structure in the other
process, then writing to them and sending the message and then reading
from them.

My code works, this might seem like a strange post but wait the plot
thickens.I tested this program on the task manager, It has four of
these SysListViews hanging around so It seemed as good a candidate as
any for my experiment. I watch the process locate the window (the title
changes so I had to do an enumwindows to find it) then I did an
enumchildwindows in order to find all the SyslistViews. Now I getting
somewhere I think to my self. I call my home-grown C dll with my window
handles and I get the thread and process responsible for these
critters.

I set my debug priveleges and and ask for my window handle "all
access". I allocate my memory watch my lil pointers come back and send
my message and readprocessmemory back into my address space, and
gleefully see the pointers filled with strings from the task manager.

This is where it gets interesting. I change my window title to the name
of the the title to the part of the name of the MDI window that I am
interested in. I get my handles, my process handle, and allocate my
memory, The first call works great, then the problems start ;( The
second call to virtualallocex succeeds but the pointes now have the
"bad ptr" following the address that was allocated, "that wasn't there
on the other window" I think to myself, and sure enough the strings
come back empty.

So I decide to google this one. Two days of googling and no cigar.
While googling I also decided that virtualallocex wasn't to be trusted
to find the base address in the other process, so I decided to walk the
vad and find my own address, this worked as well, and I locate memory
locations large enough to hold my memory , but still no cigar. It works
for the task manager but not the target program ;(

By this time I have almost read anything I could find on virtualallocex
and by passing most of the other virtual memory functions, I fiddled
with virtualqueryex and virtualprotectex, trying to find the problem.
FormatMessage came to mind pretty quickly but "The operation completed
successfully" isn't much help when trying to debug code that is
anything but successful.

At this point let me try to give you a description of my environment:
Window XP PRO SP2 latest updates.
VS.NET 2003, latest updates Enterprise Architect.
An old, P III 500 that if I could get the project to work would provide
me with some cash to buy one of the latest beasts available.
256 Mb of ram (always a drag that swapping ;()

The program I am trying to infiltrate is
EFX Navigator on www.efxgroup.com

For my info tell me what you need, I can get back to you.

Here is my soucre code, The first part is a VB.net module the second
part is as VC++ Win32 dll (hence why I posted here)
This code is anything but tidy but with all the debuging I'm doing
cleaning up old code (that works on the taskmanager and other programs)
doesn't seem like a good approach

Any way here it is:
VB.net Code First
-----------------------------------------------------------------------------------------------------------------------------------------------------
Imports System
Imports System.Runtime.InteropServices
Imports System.Text
Module MainModule
'FormatMessage
<DllImport("kernel32.dll", EntryPoint:="FormatMessageA", _
CharSet:=CharSet.Ansi, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Private Function FormatMessage( _
ByVal dwFlags As Int32, ByVal lpSource As Int32, _
ByVal dwMessageId As Int32, ByVal dwLanguageId As Int32, _
ByVal lpBuffer As StringBuilder, _
ByVal nSize As Int32, ByVal Arguments As Int32) As Int32
End Function
Private Const FORMAT_MESSAGE_ALLOCATE_BUFFER As Long = &H100
Private Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000
Private Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = &H200
Private Declare Function GetLastError Lib "kernel32.dll" () As
Integer
'declare API functions
Private Delegate Function EnumChildProcDelegate _
(ByVal hWnd As IntPtr, _
ByVal lParam As Integer) As Boolean

Private Declare Function EnumChildWindows Lib "user32" _
(ByVal hWndParent As IntPtr, _
ByVal lpEnumFunc As EnumChildProcDelegate, _
ByVal lParam As Integer) As Boolean

Declare Function GetClassName Lib "user32.dll" Alias
"GetClassNameA" _
(ByVal hwnd As IntPtr, _
ByVal lpClassName As StringBuilder, _
ByVal nMaxCount As Long) As Long

Private Declare Ansi Function FindWindow Lib "user32" Alias
"FindWindowA" _
(ByVal lpClassName As StringBuilder, ByVal lpWindowName As
StringBuilder) As IntPtr

Public Declare Sub GetWindowText Lib "user32.dll" _
Alias "GetWindowTextA" (ByVal hWnd As IntPtr, _
ByVal lpString As StringBuilder, _
ByVal nMaxCount As Integer)

Private Declare Function SendMessage Lib "user32" Alias
"SendMessageA" _
(ByVal hwnd As IntPtr, ByVal wMsg As Integer, _
ByVal wParam As Integer, _
ByVal lParam As String) As Integer

Public Declare Function GetWindow Lib "user32.dll" _
Alias "GetWindow" (ByVal hwnd As Integer, _
ByVal wCmd As Integer) As Integer
'Top Level enumeration
Public Delegate Function EnumWindowsCallback(ByVal hWnd As IntPtr,
_
ByVal lParam As
Integer) As Boolean

Public Declare Function EnumWindows Lib "user32.dll" _
Alias "EnumWindows" (ByVal callback As EnumWindowsCallback, _
ByVal lParam As Integer) As Integer

<DllImport("user32.dll", EntryPoint:="EnumWindows",
SetLastError:=True, _
CharSet:=CharSet.Ansi, ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function EnumWindowsDllImport(ByVal callback As
EnumWindowsCallback, _
ByVal lParam As
Integer) As Integer
End Function
'My DLL Declares
Private Declare Function CopyListViewToListBox Lib "Extract Data
Library.dll" _
Alias "CopyListViewToListBox" (ByVal SourceHwnd As IntPtr, ByVal
TargetHwnd As IntPtr, _
ByVal Append As Boolean) As Integer

Private Declare Function FillListBox Lib "Extract Data Library.dll"
_
Alias "FillListBox" (ByVal targetHwnd As IntPtr, ByVal item As
String, _
ByVal subitem As String, ByVal Append As
String) As Integer

'Variables
Dim iFound As Int32
Dim aihWnds As ArrayList
Private Const WM_GETTEXT = &HD

'Listbox Functions
Private Declare Function LockWindowUpdate Lib "user32" (ByVal
hwndLock As IntPtr) _
As Long
'Listbox Constants
Const LB_RESETCONTENT = &H184
Const LB_GETCOUNT = &H18B
Const LB_GETTEXT = &H189
Const LB_ADDSTRING = &H180
Const LB_GETITEMDATA = &H199
Const LB_SETITEMDATA = &H19A

Function GetGlass(ByVal hwnd) As String 'classe de la fenetre
Dim sClassName = New StringBuilder(256)
GetClassName(hwnd, sClassName, 256)
sClassName = Left$(sClassName, InStr(sClassName, Chr(0)) - 1)
Return sClassName
End Function

Function GetCaption(ByVal hwnd As IntPtr) As String 'titre du
document
Dim sCaption = New StringBuilder(256)
GetWindowText(hwnd, sCaption, 256)
sCaption = sCaption.ToString
If Len(sCaption) > 1 Then
Debug.WriteLine(sCaption)
If (InStr(sCaption, Chr(0)) - 1) > 0 Then 'trim null
sCaption = Left$(sCaption, InStr(sCaption, Chr(0)) - 1)
End If
End If
Return sCaption
End Function

Function GetCaptionFromMessage(ByVal hwnd) As String 'texte titre
fenetre
Dim sCaption = New String(Chr(0), 100)
Dim retval = SendMessage(hwnd, WM_GETTEXT, 100, sCaption)
sCaption = Left$(sCaption, InStr(sCaption, Chr(0)) - 1)
Return sCaption
End Function

Function EnumChildrenProc(ByVal hwnd As IntPtr, ByVal lParam As
Integer) As Boolean
Dim retval As Long
'error handling goes here good old formatmessage from system
Dim ClassName As New StringBuilder(256)
retval = GetClassName(hwnd, ClassName, 256)

If ClassName.ToString = "SysListView32" Then
'found listbox
Debug.WriteLine(hwnd)
'FillListbox(hwnd)
MainModule.aihWnds.Insert(iFound, hwnd)
iFound = iFound + 1
End If
'If iFound = 1 Then
'Return False 'Found Both of the Listbox Stop enumeration
'Else
Return True 'Still haven't got them both
'End If
End Function

Sub GetListBoxes()
'need to loop through all windows to get the first characters
and compare to level II ...
'Dim sWindowCaption = "Windows Task Manager"
'Dim hwnd = FindWindow(vbNullString, sWindowCaption)
Dim proc As New EnumWindowsCallback(AddressOf
MainModule.EnumWindowProc)
EnumWindows(proc, 0)
'DuplicateListBox(MainForm.Listboxes(0).Handle,
MainForm.Listboxes(1))
End Sub
Private Function EnumWindowProc(ByVal hwnd As IntPtr, ByVal lParam
As Integer) As Boolean
If InStr(GetCaption(hwnd).ToString, "Level II") Then
ProcessEFXChildren(hwnd)
Return False
End If
Return True
End Function

Private Function ProcessEFXChildren(ByVal hwnd As IntPtr)
iFound = 0
aihWnds = New ArrayList
Dim i As Integer
Dim proc As New EnumChildProcDelegate(AddressOf
MainModule.EnumChildrenProc)
Dim retval = EnumChildWindows(hwnd, proc, 0&)
For i = 0 To iFound - 1
CopyListViewToListBox(aihWnds(i),
MainForm.Listboxes(0).Handle, False)
Next
End Function
End Module

---------------------------------------------------------------------------------------------------------------------------------------------

VC++.NET DLL CODE

// Extract Data Library.cpp : Defines the entry point for the DLL
application.
//
//Defines
#define WIN32_LEAN_AND_MEAN
//Includes
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#define My_EXPORTS //Define in order to
export functions
#include "Extract Data Library.h"
#include <string>
#include <cstdlib>
#include "stdlib.h"
//Declares Exported Functions

//Delcares Internal Functions
DWORD DoFormatMessage();
DWORD DoDebugFormatMessage();
int SetDebugPrivileges(void);
MEMORY_BASIC_INFORMATION WalKingTheVAD(HANDLE hProcess, DWORD
RequiredSpace);
//Exported Variables

//Global Variables

//DLL ENTRRY POINT
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

//Code Starts here
My_API_Export int __stdcall CopyListViewToListBox(HWND SourceHwnd, HWND
TargetHwnd, BOOL Append)
{
SetDebugPrivileges();
HWND listview= SourceHwnd;

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;
MEMORY_BASIC_INFORMATION mbi;
LPVOID BaseAddress = 0;
GetWindowThreadProcessId(listview, &pid);
//process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
//
PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);
process=OpenProcess(PROCESS_ALL_ACCESS,false, pid);
//DoFormatMessage();

mbi = WalKingTheVAD(process, (DWORD)(sizeof(LVITEM)) );
if (mbi.BaseAddress ==NULL)
{
MessageBox(NULL, "Insufficient Memory in Target Process
to Extract
Data","Fatal Error", MB_ICONERROR);
return -1;
}
BaseAddress = mbi.AllocationBase;
_lvi=(LVITEM*)VirtualAllocEx(process, BaseAddress,
sizeof(LVITEM),

MEM_COMMIT, PAGE_READWRITE);
if (_lvi == NULL) DoFormatMessage();
mbi = WalKingTheVAD(process, (DWORD)(100) );
if (mbi.BaseAddress ==NULL)
{
MessageBox(NULL, "Insufficient Memory in Target Process
to Extract
Data","Fatal Error", MB_ICONERROR);
return -1;
}
BaseAddress = mbi.AllocationBase;
_item=(char*)VirtualAllocEx(process, BaseAddress, 100,
MEM_COMMIT,

PAGE_READWRITE);
DoFormatMessage();
_subitem=(char*)VirtualAllocEx(process,NULL, 100, MEM_COMMIT,

PAGE_READWRITE);
DoFormatMessage();
/*
_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;//0
lvi.pszText=_item;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM),
NULL);
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i,
(LPARAM)_lvi);

lvi.iSubItem=1;//1
lvi.pszText=_subitem;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM),
NULL);
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i,
(LPARAM)_lvi);

ReadProcessMemory(process, _item, item, 100, NULL);
ReadProcessMemory(process, _subitem, subitem, 100,
NULL);

//printf("%s - %s\n", item, subitem);
//MessageBox(NULL, item, subitem,MB_OK);
}

VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
VirtualFreeEx(process, _item, 0, MEM_RELEASE);
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);
VirtualProtectEx(process, mbi.BaseAddress,
mbi.RegionSize,mbi.Protect,
&mbi.Protect); //restore Old
Protection stored in .Protect
CloseHandle(process);
return 0;
}
My_API_Export int __stdcall FillListBox(HWND targetHwnd, char *item,
char *subitem, BOOL Append)
{
if (Append) LockWindowUpdate(targetHwnd);
SendMessage(targetHwnd, LB_ADDSTRING, NULL, (LPARAM)item);
SendMessage(targetHwnd, LB_ADDSTRING, NULL, (LPARAM)subitem);
if (Append) LockWindowUpdate(NULL);
return 0;

}

DWORD DoFormatMessage()
{
//Implement as Class later
void *m_szErrMsg = NULL;
DWORD err = GetLastError();
int nLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
(LPTSTR)&m_szErrMsg,
1,
NULL );
MessageBox(NULL, (LPCSTR)m_szErrMsg, "Extract Data", MB_OK |
MB_ICONEXCLAMATION);
LocalFree(m_szErrMsg); /*free memory*/
return err;
}

DWORD DoDebugFormatMessage()
{
//Implement as Class later
void *m_szErrMsg = NULL;
DWORD err = GetLastError();
int nLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
(LPTSTR)&m_szErrMsg,
1,
NULL );
OutputDebugString("Msg From System:\n");
OutputDebugString((LPCSTR)m_szErrMsg);
LocalFree(m_szErrMsg); /*free memory*/
return err;
}

//No PISSY PROGRAM IS GOING TO DENY US A PROCESS HANDLE
int SetDebugPrivileges(void)
{
DWORD err = 0; // define error holder, used to store the error
code in
case of failure
TOKEN_PRIVILEGES Debug_Privileges;

//STEP 1
if (!LookupPrivilegeValue (NULL, // Privieleges for the local
system

SE_DEBUG_NAME, // define the name of the privilege
&Debug_Privileges.Privileges[0].Luid))
// will get the LUID value into this variable
{ //if function failed, cannot proceed to the next step
err = DoFormatMessage();
return err; //terminate the outer function
}

//STEP 2

HANDLE hToken = 0; // instantiate a token handle
if (!OpenProcessToken (GetCurrentProcess (), // current process
ID
handle
TOKEN_ADJUST_PRIVILEGES| TOKEN_QUERY, //set
the desired access
&hToken)) // handle to the token will be
held here
{ // if function failed, cannot proceed to the next step
err = DoFormatMessage();
if (hToken) // if handle is still valid
CloseHandle (hToken); // destroy it
return err; //terminate the outer function
}

//STEP3
Debug_Privileges.Privileges[0].Attributes =
SE_PRIVILEGE_ENABLED; //
set to enable privilege
Debug_Privileges.PrivilegeCount = 1; // working with only one
privilege

if (!AdjustTokenPrivileges (hToken, // access token handle
FALSE,
// do not disable privileges

&Debug_Privileges, // pointer to the token structure
0, //
no need for a buffer
NULL,
// previous state not set
NULL))
// no need for a buffer
{
err = DoFormatMessage();
if (hToken) // if handle is still valid
CloseHandle (hToken); // destroy it
return err; //terminate the outer function
}

return err;

}

MEMORY_BASIC_INFORMATION WalKingTheVAD(HANDLE hProcess, DWORD
RequiredSpace)
{
SYSTEM_INFO si;
MEMORY_BASIC_INFORMATION mbi;
LPVOID lpMem;
TCHAR szFile[MAX_PATH];
char szNumber[20];
char *State;
char *Type;
char istr[65];
char RegionSizeStr[65];
int tmp = 0;
DWORD dwPageSize;

/* Get maximum address range from system info */
GetSystemInfo(&si);

/* walk process addresses */
lpMem = si.lpMinimumApplicationAddress;
dwPageSize = si.dwPageSize;
while (lpMem < si.lpMaximumApplicationAddress)
{
OutputDebugString("Testing New Block: \n");
mbi.RegionSize = 0;
VirtualQueryEx(hProcess, lpMem, &mbi,
sizeof(MEMORY_BASIC_INFORMATION));
//DoFormatMessage();
OutputDebugString("RegionStart: ");
itoa((int)lpMem,istr,16);
OutputDebugString(istr);
OutputDebugString("\nRegionSize: ");
itoa(mbi.RegionSize,RegionSizeStr,16);
OutputDebugString(RegionSizeStr);

OutputDebugString("\nState: ");
tmp = mbi.State;
State = (char *)((mbi.State == MEM_FREE ? "MEM_FREE"
:
(mbi.State == MEM_COMMIT) ?
"MEM_COMMIT" :
(mbi.State == MEM_RESERVE) ?
"MEM_RESERVE" :
itoa(tmp, &szNumber[0], 16) ));
OutputDebugString(State);

OutputDebugString("\nType: ");
tmp = mbi.Type;
Type = (char *)((mbi.Type == MEM_MAPPED ?
"MEM_MAPPED" :
mbi.Type == MEM_PRIVATE ? "MEM_PRIVATE"
:
mbi.Type == MEM_IMAGE ? "MEM_IMAGE"
:
mbi.Type == 0 ? "N/A (0)"
:
itoa(tmp, &szNumber[0], 16)));
OutputDebugString(Type);
OutputDebugString("\nProtection on Region : ");
OutputDebugString(istr);
OutputDebugString("\n");
if (mbi.Protect & PAGE_NOCACHE)
OutputDebugString("NoCache ");
if (mbi.Protect & PAGE_GUARD) OutputDebugString("Guard
");
switch (mbi.Protect & ~(PAGE_NOCACHE|PAGE_GUARD))
{
case 0: OutputDebugString("none\n"); break;
case PAGE_NOACCESS:
OutputDebugString("NoAccess\n"); break;
case PAGE_WRITECOMBINE:
OutputDebugString("WriteCombine\n"); break;
case PAGE_READONLY:
OutputDebugString("Read\n"); break;
case PAGE_READWRITE:
OutputDebugString("ReadWrite\n"); break;
case PAGE_WRITECOPY:
OutputDebugString("WriteCopy\n"); break;
case PAGE_EXECUTE:
OutputDebugString("Execute\n"); break;
case PAGE_EXECUTE_READ:
OutputDebugString("Execute Read\n"); break;
case PAGE_EXECUTE_READWRITE:
OutputDebugString("Execute Read
Write\n"); break;
case PAGE_EXECUTE_WRITECOPY:
OutputDebugString("Execute Read
WriteCopy\n"); break;
default: OutputDebugString("none\n");
}
/*if (mbi.Type == MEM_MAPPED &&
GetMappedFileName(GetCurrentProcess(),
(LPVOID) i, szFile,
MAX_PATH))
{
printf("File Name: %s\n", szFile);
}
*/

OutputDebugString("\n");

if (mbi.RegionSize == 0) mbi.RegionSize = 1;
//increment in order to
avoid infinte loops

//shouldn't happen but right now it is
necessary remove
later
lpMem = (LPVOID)((DWORD)mbi.BaseAddress +
(DWORD)mbi.RegionSize);
//Test Allocation
if (mbi.State == MEM_FREE) //Hopefully Free
{
if (RequiredSpace < mbi.RegionSize)
{
LPVOID tmp =
VirtualAllocEx(hProcess,mbi.AllocationBase,mbi.RegionSize,

MEM_RESERVE,PAGE_NOACCESS);
if (DoDebugFormatMessage() == 0)
{ //Success found a slot return
info to validate
mbi.BaseAddress = tmp;
mbi.AllocationBase = tmp;
return mbi;

}
//VirtualFreeEx(hProcess,tmp,0,
MEM_RELEASE);
}
}
}
mbi.BaseAddress = NULL;
mbi.AllocationBase = NULL;
return mbi;

}

The Vb is pretty standard so

CopyListViewToListBox(aihWnds(i), MainForm.Listboxes(0).Handle, False)

is where I depart from VB into C and it is just there to avoid there
being anything missing.

In CopyListViewToListBox(...) is where the fun starts. Now as I said
this code works on the TaskManager and a demo program, so smart alecs
can refrain from flameing me please, This problem is a problem only on
the target application that I wish to infiltrate.

In need of Win32 guru assistance,
Charles

 | 
Pages: 1
Prev: Problem with CSocket
Next: size of Picture Control