(UPDATED on 18/09/2008)
Added: Child Windows on Window Tooltip, Opacity setting
for Tooltip Window
(UPDATED on 15/09/2008)
Added: Window Tooltip, shows
window information during 'Find Window'
(UPDATED on 09/09/2008)
Added: Modifications and ModifiedMsg
added to the Breakpoints.
Modification:
Indicates that which sections of the message will be modified if the Action
is AutoChangeParameters.
ModifiedMsg:
If a breakpoint's action is AutoChangeParameters and Modification
has the Message bit,
then PV will change the incoming message to the given message.
For
ex: You can change the WM_LBUTTONDOWN to WM_RBUTTONDOWN automatically
for the target window.
Multilanguage: (English,
Turkish)
Process Viewer 1.0.7.0 (PV) is ready to download. Download
Source Codes | Watch Sample Movie (v1.0.5.0)
Below I
try to explain what is PV and how it is working. Also I put some
screenshots of the PV.

I finished PV 2 days ago, but I
was really very sleepy (it was about 4 AM and I had to go to work in
the morning) so I postpone this writing to a later time. Anyway today I
have some time to write something about PV. What it is? What it is for?
How it works?
First I will explain what PV is then I will try to
explain its codes a little bit more.
What is PV?
I can say that PV
is a program like Microsoft Spy++. Unlikely to Spy++ it allows you to
edit some properties of the Controls and also allows you to manage its
window messages. You can change Text, Visibility,
Enabled, Window Styles
and Window Ex Styles properties of a control by using
PV during runtime. Therewithal you can ignore window messages, change
incoming message to a different message or edit parameters of any window
messages by putting breakpoints to the control’s window procedure with
PV.

How can I use PV?
It is really very
simple to use PV. Run PV.exe and you will see the active Processes,
currently running in your desktop, in the Windows List at the left of
the program. Expand any process which you are interested in. You will
see the Windows (Controls) which are belong to that process in a
hierarchical list. Whenever you select a Window from the list PV will
highlight its area in the screen even the window is not visible at the
moment. If a Window is a container window which contains inner windows
in it you will see inner windows under the owner (parent) window node,
so you can expand parent window and see inner windows of it.
There
is also Find Window button at the top of the PV which
will help you to find Controls and select them easily. Just click to the
Find Window button and Drag it over the Controls while
you are pushing the Left mouse button. PV will highlight the Control
region which mouse currently on it. To select a window, release mouse
button when the mouse cursor is on that control. Window will be selected
automatically in the Windows List. If window is not in the list (this
might be, because PV generates Windows List while it is opening so if
the window you try to select was created after you opened PV then this
window won't be in the list), PV will shows an alert to you that the
window is not in the windows list at the moment, if you chose “Yes”
button it will automatically refreshes the Windows List and finds your
control.
When you select a window from the windows list you will
see its properties in the PropertyGrid at the right. You can change Visible,
Enabled, Text, WindowStyles
and WindowExStyles properties of that window by using
PropertyGrid. Also you can change the window’s Bounds, Size
and Location by the same way. If you wish to see the
Messages of that control, click to the Listen Messages
button at the top toolbar. A panel which contains a TextBox will appear
at the bottom of the PV which will shows you the messages of that
control. If you want to put breakpoints to the current window’s message
procedure click to the Edit Breakpoints button. Breakpoint Management
window will be shown and you can add breakpoints to that window by using
this window. Select a window message from the list (you can filter
messages by writing your message name to the filter textbox) to see
breakpoint properties for that message. Select your action (None,
Ignore, Manuel Edit, Auto
Change) by using PropertyGrid for the selected message(s). You
can do same things for the other messages which you are interested in.
To remove a message breakpoint just set its action to “None”.
After you finished you can close the Breakpoints window by clicking “Okey”
button. From now on your breakpoints will be activated and whenever a
message, which you define an action for that, arrives to that window PV
will do the action which you select for that message.
For
example; If you chose WM_LBUTTONDOWN, which system
sends to the control when mouse left button is down on that control,
from the message list and select ‘Ignore’ action then WM_LBUTTONDOWN
message will never send to the control. Or if you choose ManuelEditParameters
action for the WM_LBUTTONDOWN then PV will shows you Edit
Parameters Window to choose your action against this message
when this message arrives to the control. Either you can ignore this
message or you can change the wParam and lParam parameters of the
message. WPARAM (4 bytes) and LPARAM
(4 bytes) of a message contain message specific parameters and they have
different meanings according to the Message. If we speak about the WM_LBUTTONDOWN
message, the parameters will be;
wParam: Indicates
whether various Virtual Keys (MK_LBUTTON, MK_RBUTTON, MK_CONTROL,
MK_MBUTTON, MK_XBUTTON1, MK_XBUTTON2) are down. WParam can be one or
more of these values.
lParam: Lo-Order of the lParam
specifies the X-Coordinate of the cursor and Hi-Order of the lParam
indicates the Y-Coordinate of the cursor. Here Lo-Order means first 2
bytes of the lParam and Hi-Order means the last 2 bytes of the lParam.
If
you choose Manuel Edit Parameters action for the WM_LBUTTONDOWN then
you can change the parameters of It whenever system sends this message
to the target window. If you choose AutoChangeParameters action then PV
will change wParam and lParam of that message automatically as you
defined.
Examples
with Breakpoints;
Example 1:
1 – Open Visual Studio .NET
2 – Select Main Menu of the
VS.NET by using Find Window button of the PV.
3 – Click
“Listen Messages” button to start message hook for the main menu
4 – Click “Edit Breakpoints” button to set breakpoints
5 –
Select WM_LBUTTONDOWN from the message list and select
AutoChangeParameters action for this message.
Set
Modifications to the WParam and LParam (Indicates PV will modify only
WParam and LParam of a message before it send to the hooked window)
Set XParam to the 1 (LoWord:1, HiWord:0) and LParam to the
851998 (LoWord:30, HiWord:13). Click Okey button.
6 – Click
anywhere in the Main Menu, you will see that always File menu is shown.
This is because PV change the LParam to the HiWord:13 and
LoWord:30 which means (X=30, Y=13) the cursor position is over the File
menu. So main menu opens File menu always because it thinks that File
menu has been clicked.
Example 2:
1 – Open Visual Studio .NET
2 – Select Main Menu of the
VS.NET by using Find Window button of the PV.
3 – Click
“Listen Messages” button to start message hook for the main menu
4 – Click “Edit Breakpoints” button to set breakpoints
5 –
Select WM_LBUTTONDOWN from the message list and select
AutoChangeParameters action for this message.
Set
Modifications to the Message and set ModifiedMsg to the WM_RBUTTONDOWN.
Click Okey button to set the breakpoints.
6 – Click anywhere
in the Main Menu, you will see that even you click to the Left button of
the mouse the main menu always opens its Context Menu.
This is
because PV change the WM_LBUTTONDOWN message to he WM_RBUTTONDOWN. |

I think this is enough for “How
We Use PV?” Let us speak about the “How PV do this?”
We can split
this question in to two sections. One is “How PV finds Windows?” and
second is “How PV Listens and Edit Messages of a Control?”
For the
first question PV uses EnumChildWindows method to
enumerate child windows of a Window. It first tries to find the Desktop
Window’s child windows and then it finds the inner windows. After it
finds the windows, it uses GetClassName, GetWindowThreadProcessId
and GetParent methods to find the ClassName,
ProcessId, ThreadId and Parent
Window Handle of these windows.

For the second question “How PV
Listen and Edit Messages of a Control?”; it is a little bit harder than
the first section. To make this, first I wrote a C++ (Native) dll which
sets hook on WH_CALLWNDPROC for the given window’s
thread. Why I wrote this dll with C++? Actually listening a control’s
Windows Messages, is simple. Every control has a Window Procedure method
which system sends messages to that method. We can learn the address of
this method by using GetWindowLong method with the GWL_WNDPROC
option. To change the window procedure method of a control is also very
simple;
SetWindowLong(windowHandle, GWL_WNDPROC, [our
procedure Method]). By using this method we can replace the
message procedure method of a control to our method. So whenever system
sends a message to this control it will comes to our method and we can
do anything we want in this method. To redirect this message to the
original window procedure, we can use CallWindowProc
function. But the problem with this is; SetWindowLong
method works only for the windows which are belongs to same process. It
means that we can use this method for the controls which are in the same
process with us. So if we want to change windows procedure of a control
we have to be in the same process. How we can handle this problem? To
handle this problem I wrote a native dll which will be injected to the
target process and we will run the SetWindowLong
function from that dll within the target process. Injecting a dll to
another application is also a pain for us. There are some solutions for
this problem;
1 – We can use SetWindowsHookEx
method to set a WH_CALLWNDPROC hook. By setting a hook
Windows will automatically Loads our hook dll to all processes. But the
problem with this method is, we don’t know the target process is .NET or
not. So we have to write a native dll for this hook operation. Because
if the target process is not .NET this means there is no CLR attached to
that process and our .NET dll won’t be worked within this process. When
we call SetWindowsHookEx function systems attach the
dll, which contains the hook method, to all processes. At this point;
you have to know that for each process, system creates a new instance of
our dll and attach it to every process.
2 – LoadLibrary
method which is in kernel32.dll loads a library to the
caller application. So if we make a call to that function within the
target process we can load our library to the target application. How we
can call LoadLibrary function within another
application? To solve this problem we can use CreateRemoteThread
function which is in the kernel32.dll. CreateRemoteThread
function creates a remote thread within a given process and
runs the given method. To use CreateRemoteThread
function we have to also know the address of the target method which we
want to run in the target processes memory block. Actually this is hard
to find the address of a function in a remote application. GetProcAddress
function returns the address of a method, but when we call this method
this will be the address of that method within our application because
again GetProcAddress will return the address of the
function according to the caller process memory. But here is a good
trick with the LoadLibrary. LoadLibrary
method is in the kernel32.dll and kernel32.dll
will always load to the same address for all the windows applications
according to nature of windows. So if we take the address of the
LoadLibrary function within our application’s memory, this address also
will be same for other applications too. So we can take the address of
the LoadLibrary and call this method by using CreateRemoteThread
method within target process. This will load our dll to the target
procedure.
Anyway there are too many details
about dll injection so I pass it simply for now. May be we can talk
about this later more detailed. In PV I have been used the first way to
make dll injection. I wrote a C++ (Native) dll which starts
WH_CALLWNDPROC
hook on the target process.
WH_CALLWNDPROC installs a
hook procedure which monitors the messages before the system sends them
to the destination window. When the first time that system sends a
message to our target window we changed the target window’s message
procedure method by using
SetWindowLong function.
Actually we are using hook operation for two cases; one is injecting our
native dll to the target process and second triggering procedure
changing method (calling
SetWindowLong) from that dll.
Therefore
SetWindowLong function will be called by the
target process. As I said above we can do this in some different ways
but I chose this way. Because this way needs less native codes than the
others and I want to write less native codes in this example.
// In the PV
[DllImport("ProcessViewer.Hooks.dll")]
public static extern int
StartHook(IntPtr hWnd, IntPtr notifyWindow);
[DllImport("ProcessViewer.Hooks.dll")]
public static
extern void
EndHook();
// In the native dll
PROCESSVIEWERHOOKS_API int WINAPI
StartHook(HWND hWnd, HWND notifyParent)
{
originalWindowProcedure
= 0;
// Load
our libary and get its handle
HINSTANCE phookDll =
LoadLibrary((LPCTSTR)_T("ProcessViewer.Hooks.dll"));
EndHook();
hookWindow = hWnd;
notifyWindow = notifyParent;
// get Thread Id of the Window, which we will
hook
DWORD threadId =
GetWindowThreadProcessId(hWnd, NULL);
// get address of our Hook method in this dll
HOOKPROC proc =
(HOOKPROC)GetProcAddress(phookDll, "MessageHookProcedure");
// set Hook (This will injects our dll in to all
process
// which are created by the given thread)
hhk = SetWindowsHookEx(WH_CALLWNDPROC,
proc,
phookDll,
threadId);
return (DWORD)hhk;
}
// this is our WH_CALLWNDPROC
hook method
LRESULT CALLBACK MessageHookProcedure(int nCode, WPARAM wParam, LPARAM lParam)
{
if (originalWindowProcedure ==
0)
{
// the first time this method is called
// for the our hooked window, replace its window
procedure
// to our procedure method
// do this just for the control which we wnat to
listen its messages
CWPSTRUCT* str =
(CWPSTRUCT*)lParam;
if (str->hwnd == hookWindow)
{
// take original windows procedure
of this control
originalWindowProcedure
= GetWindowLong(hookWindow, GWL_WNDPROC);
// set new windows procedure
SetWindowLong(hookWindow,
GWL_WNDPROC, (LONG)OnMessage);
}
}
return CallNextHookEx(hhk,
nCode, wParam, lParam);
}
// ends hook operation
PROCESSVIEWERHOOKS_API void WINAPI EndHook()
{
// if we have been taken procedure of the window,
// set its
procedure to the its original
if (originalWindowProcedure)
SetWindowLong(hookWindow,
GWL_WNDPROC, originalWindowProcedure);
if (hhk != NULL)
UnhookWindowsHookEx(hhk);
originalWindowProcedure = 0;
hhk
= NULL;
}
|
From now on any message that system
sends to the target window will come to our method because system thinks
that window procedure method for that control is our method. Now we
need to send this message to the PV to process it. At this point I used
VM_COPYDATA
message to transfer message to the PV. But to whom I will send this
message? I wrote a
Listener Control in the PV and I
pass its handle to the hook dll while PV starts the hook operation. Hook
dll sets this passed handle to a
shared variable in
it. This way every instance of the native dll has the same value for
that variable. So native dll in the target process also has the same
variable which holds our listener controls handle. From the native dll
which handles the target window’s messages I redirect messages to the
Listener Control which is in the PV. Whenever a new
WM_COPYDATA
messages send to the Listener Control it resolves the original message
from the LParam of that message and process it according to our
Breakpoints.
LParam of the
WM_COPYDATA
message contains a
COPYDATASTRUCT which has original
message in it is
lpData field.
|
private class ListenerControl : Control
{
static int
HM_MESSAGE_RESULT;
public ListenerControl()
{
HM_MESSAGE_RESULT = NativeMethods.RegisterWindowMessage(
"ProcessViewer_MessageResult");
Parent
= MainForm;
}
protected override
void WndProc(ref Message
m)
{
if (m.Msg == (int)NativeMethods.Msgs.WM_COPYDATA)
{
// message was sended by
ProcessViewer.Hooks.dll from the Hooked application when
// a new message comes to that
window
// LPARAM of this message contains a
COPYDATASTRUCT which has HOOK_MSG struct in it
NativeMethods.COPYDATASTRUCT cdata =
(NativeMethods.COPYDATASTRUCT)m.GetLParam(
typeof(NativeMethods.COPYDATASTRUCT));
// This is the information of the
message which is sended to the hooked window
NativeMethods.HOOK_MSG msg = (NativeMethods.HOOK_MSG)
Marshal.PtrToStructure(cdata.lpData,
typeof(NativeMethods.HOOK_MSG));
// process message and set its
result (0 ignore, 1 do nothing,
// other
values replace parameters)
m.Result = MainForm._SubClass.ProcessMessage(m.WParam, ref msg);
Marshal.DestroyStructure(m.LParam,
typeof(NativeMethods.COPYDATASTRUCT));
}
else
if (m.Msg == HM_MESSAGE_RESULT
&& Properties.Settings.Default.HandleMessageResults)
{
// this message sended by hooked window to give
// information about the result of a message
MainForm._SubClass.ProcessMessageResult(m.WParam.ToInt32(),
m.LParam);
}
else
{
base.WndProc(ref m);
}
}
}
|
While PV processing message
native dll is waiting for the result and does its action according to
this result which comes from the PV. If we don’t have a breakpoint for
that message PV returns 1 which means “send message to the original
window procedure”. If we have a breakpoint for this message which the
action is “
Ignore message”, then PV returns 0 to the
native dll and it ignores that message. The hard section of breakpoints
is what if we have the “
Edit Parameters Manually“ or “
Auto
Change Parameters” actions for that message. It is hard
because we need to transfer changed parameters to the native dll which
is running in the target application. We solve this problem by
allocating a memory block in the target applications memory by using
VirtualAllocEx
function. Then we write our changed values to that block by using
WriteProcessMemory
function. These functions allow us to allocate a memory block in the
target application memory block and write to this block. Now we have the
address of that memory so we can pass this address to the native dll as
the address of new wParam and lParam values. If native dll takes a
return value differs from 1 and 0 it knows that this is a memory address
which contains new wParam and lParam values. So it reads new values of
the parameters from this address and redirects message to the original
window procedure by using these parameters. This way we changed the
parameters of the message. We can also use this
WriteProcessMemory
and
VirtualAllocEx methods to transfer message from
native dll to PV instead of using
VM_COPYDATA. But I
transfer data by using
VM_COPYDATA because
VM_COPYDATA
message is for transferring data between two processes. Why I don’t use
WM_COPYDATA message for transferring result of the
message from PV to the native dll instead of using
WriteProcessMemory
function? The answer is simple. Because I send the incoming message to
the PV, by using
SendMessage function from window
procedure method in the native dll. Thread which calls the
SendMessage
function will block until it takes a result. So if I try to send return
value by using
SendMessage to the target window this
will be a deadlock.
IntPtr
WriteToTargetProcessMemory(IntPtr
wParam, IntPtr lParam, NativeMethods.Msgs modifiedMsg)
{
// Open hooked window's process
IntPtr hProcess = NativeMethods.OpenProcess(
NativeMethods.PROCESS_VM_OPERATION
|
NativeMethods.PROCESS_VM_READ
|
NativeMethods.PROCESS_VM_WRITE,
false, _Window.ProcessId);
// allocate memory from target proces's memory
block
// 12
bytes : 4 modified msg + 4 wParam + 4 lParam
IntPtr memAddress = NativeMethods.VirtualAllocEx(hProcess,
IntPtr.Zero,
12,
NativeMethods.MEM_COMMIT,
NativeMethods.PAGE_READWRITE);
if (memAddress == IntPtr.Zero)
return IntPtr.Zero;
int written = 0;
// write our new parameter values to the tarhet
process memory
bool hr = NativeMethods.WriteProcessMemory(hProcess,
memAddress,
new int[]
{(int)modifiedMsg, wParam.ToInt32(),
lParam.ToInt32()},
12, out written);
// close handle
NativeMethods.CloseHandle(hProcess);
if (!hr)
return IntPtr.Zero;
return memAddress;
}
// In the native dll
// new
window procedure for the selected window
LRESULT CALLBACK
OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// prepare message information, for sending it to
our ProcessViewer program
HOOK_MSG
message;
message.msg = msg;
message.wParam
= wParam;
message.lParam = lParam;
// this is for the WM_COPYDATA message,
// WM_COPYDATA transfers data between 2 process
by using this structure
COPYDATASTRUCT
cdata;
cdata.dwData = 0;
cdata.cbData
= sizeof(message);
cdata.lpData
= &message;
// send a WM_COPYDATA to our ProcessViewer which
will give information about the message
// to our program
LRESULT
result = SendMessage(notifyWindow, WM_COPYDATA, (WPARAM)hWnd,
(LPARAM)&cdata);
// return values, 1: ignore message, 0 do nothing,
// different from 0 and 1 : memory address of
data which contains the changed wParam and lParam
if (result == 1)
return 0;
if (result != 0)
{
// if result is not 0 or 1
// this means we are going to change the wParam
and lParam
// which we write new values of these variables to
the
//
address (result indicates this address) in the hooked program's memory
HOOK_RESULT* hr = (HOOK_RESULT*)result;
msg = hr->msg;
wParam
= hr->wParam;
lParam =
hr->lParam;
}
// send message to original window procedure
LRESULT ret =
CallWindowProc((WNDPROC)originalWindowProcedure, hWnd, msg, wParam,
lParam);
// send
message to the ProcessViewer application which
// give information about the result of the
message
SendMessage(notifyWindow,
HM_MESSAGE_RESULT, msg, ret);
return ret;
} |
This is simply how PV works. Of
course we can write this procedure in different ways but as I said above
I’m trying to write this example by using .NET.

Anyway it is just a two night’s code and I think it is a
good start for Spying windows. Have fun!!!