Download Source Code From GitHub | Watch Sample Movie(v1.0.5.0)
What is Process Viewer?
I can say that Process Viewer 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 run time. There withal 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 Process Viewer?
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 Process Viewer change the WM_LBUTTONDOWN message to he WM_RBUTTONDOWN.
I think this is enough for “How We Use Process Viewer?” Let us speak about the “How Process Viewer 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 Process Viewer 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 CallWindowProcfunction. 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 SetWindowLongfunction 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 library 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 want 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 sent 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 was sent 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 useWM_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 process'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 target 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!!!