Of Services and Processes – Part 2
(A tutorial to Windows 2000 Services)
By Rajesh Hrishikesh Thampi
This tutorial is a continuation of the series “Of Services and Processes”. This series deals with programmatically controlling Services and Processes on the Windows 2000 Platform using VC++ 6. I will be mentioning the next part at the end of every Part. This part specifically deals with Modules in a Process.
At the time of writing this tutorial, I was using Windows 2000 professional with VC++ 6 But to port it to other versions of Windows or VC there should not be much of a change although I have not tested it.
If you have suggestions and/or feedback regarding this tutorial that will improve its readability or ease of understanding mail me at email@example.com. Do put in a proper subject as I receive a lot of Spam. All feedback is appreciated.
You are allowed to freely distribute this tutorial as long as you do not make any changes to it.
The projects that have been stated as examples in this tutorial do not follow the conventional windows programming model. I have kept the project as a simple console exe. This so as to stick to learning what was intended rather than getting bogged down with the windows programming model which would include so much more code. Moreover, this is a direct continuation to Part 1 of this tutorial. The sections that I have described in Part 1 will not be repeated here.
- Digging into a Process.
1: - What we need.
2: - Process Viewer.
3: - Functions involved and a brief on how they work.
4: - Process.cpp dissected.
C. Part III.
B. Digging into a Process.
1:- What we need.
Before beginning download the application called “Process Viewer”. It is freely available and can be downloaded from one of these URL’s:
This application is necessary for you to understand Modules in a process. Other than this application and what you have already studied in Part 1 of this series you will not need anything more.
Do not compare Process Viewer to the Task manager in windows. The Process Viewer is way more advanced.
2: - Process Viewer.
Once you install this application it shows up as a pair of goggles (eye shades) in the system tray. Just double click it to open. Once the Process Viewer application is up and running you will find the following columns in the window – Name (name of the process), ID (PID of the process), Priority (as to which process should be given more priority in sharing hardware), CPU (CPU usage by the process), Mem Usage (memory usage by the Process), User Name (under which user the application was started) and Full Path (the full path where the application started up from).
Now let us take an example process. For example; the “Explorer.exe” process. This process will be present in every Windows machine. Moreover, even if you kill this process it will restart itself. Let us try it.
Open “Process Viewer” and in the Name column find “Explorer.exe”. Right click the process and click Kill. A new window with three options opens – Kill, Notify, and Cancel. Click Kill and you will notice that explorer.exe process disappears from the list. Also notice that your Windows Task Bar disappears and then both come right back up.
To learn about Modules, let me explain how to see the modules attached to a process using Process Viewer before we proceed further. Right click Explorer.exe, like before. Click Modules from the menu that pops up and you will see a new window called Modules. This Module window lists all the files that the process is linked to. These files are called modules. Now if you look into the list of modules you’ll find an explorer.exe running inside the process Explorer.exe. Weird??? Well think about it.
The main file that we start for it to become a process is a file as well, right. In that regards it’s a module. That is if I execute a file named Thampi.exe that file will show up as a process named Thampi But the file by itself is also a Module. In short a process can be looked as a empty pipe that contains and controls all the modules that do the actual work.
Every file that a process uses in its execution (.dll’s, .exe’s, .drv’s) is a module to that process. Got it?
Now that you do understand what a module is, let us see how we can list them out programmatically from a process.
3: - Functions involved and how they work.
We will use most of the code from Part 1. This way it will be easy to go through the code. However, you need to understand a few new functions as well. They are:
EnumProcessModules, GetModuleFileNameEx, and GetModuleBaseName.
The first function that we are going to look at is the EnumProcessesModules function. This function when given the necessary details about a process retrieves for us a handle for each module in the specified process.
BOOL EnumProcessModules (HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded );
hProcess: [in] Through this variable provide the function a handle to the process from which you would like to extract the data.
lphModule: [out] The function will return into this variable a pointer to the array that receives the list of module handles.
cb: [in] Through this variable you can tell the function the size of the lphModule array, in bytes.
lpcbNeeded: [out] And it is this variable through which the function will tell us the number of bytes the function took to store all module handles in the lphModule array.
If this function succeeds then it will return a nonzero value. Zero if it fails. You will use this function to pull out module handles.
This function will give you the entire path to the module including the file name or module name. But for this we have to provide the function with three pieces of information through the arguments.
DWORD GetModuleFileNameEx (HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename, DWORD nSize);
hProcess: [in] Through this argument you pass the handle to the process you want information on.
hModule: [in] Through this argument you pass the handle to the specific module in the process.
lpFilename: [out] The function returns into this argument a pointer to the null-terminated buffer that receives the fully-qualified path to the module. If the size of the file name is larger than the value of the nSize parameter, the function succeeds but the file name is truncated and null terminated.
nSize: [in] This should contain the size of lpFilename, in characters.
GetModuleBaseName, we will discuss the one argument that needs to be defined when stepping through the code.
3: - Module.cpp
<P>#include <span class="code-keyword"><windows.h></P>
</span><P>#include <span class="code-keyword"><stdio.h></P>
</span><P>#include <span class="code-keyword"><tchar.h></P>
</span><P>#include <span class="code-keyword"><psapi.h></P>
<P>void main( )</P>
<P>DWORD ProcessesIDs, cbNeeded, cProcesses, cModules, cbNeededM; </P>
<P>unsigned int i, n;</P>
<P>TCHAR szProcessName = TEXT("<unknown>");</P>
<P>if ( !EnumProcesses( ProcessesIDs, sizeof(ProcessesIDs), &cbNeeded ) )</P>
<P>cProcesses = cbNeeded / sizeof(DWORD);</P>
<P>for ( i = 0; i < cProcesses; i++ )</P>
<P>HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessesIDs[i] );</P>
<P> if (NULL != hProcess )</P>
<P> if ( EnumProcessModules( hProcess, hMod, sizeof(hMod), &cbNeededM) )</P>
<P> GetModuleBaseName( hProcess, NULL, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) );</P>
<P> _tprintf( TEXT("Processname = %s, PID = %u \n"), szProcessName, ProcessesIDs[i]);</P>
<P> for (n=0;n<cModules;n++)</P>
<P> GetModuleFileNameEx(hProcess, hMod[n], szModName, sizeof(szModName)/sizeof(TCHAR));</P>
<P> _tprintf(TEXT("ModuleName= %s,\n"), szModName);</P>
<P> CloseHandle( hProcess );</P>
<P> //End of for loop.</P>
4: - Module.cpp dissected.
Since most of the code you see above is almost exactly the same as part 1, let us stick to the changes.
Inside the “for” loop, you use “OpenProcess” to get a handle to the process till the loop goes into its next cycle. The next “if” statement checks if you have a handle on the process and then we come across “EnumProcessModules”. Now, what does this function do? Or what can this function be used for?
This function is used to get back not only all the handles to every module in a process but also to know how many modules are associated with the process. Which process??? The process to which you provided the handle to in the first argument.
The first argument takes in the handle to the process you wish to explore.
In the second argument, you provide the function with an array of 1024 elements long. This array is filled up by the function with handles to the modules in the process. That is why the array is of type “HMODULE “. You need to make your own call on how long the array needs to be. This array needs to be long enough to store all the modules in a process. So if you have a process having more than 1024 modules. The information of the rest of the modules would be lost.
The third argument needs to contain data as to the size of the second argument in bytes. Instead of deriving the size in bytes into another variable and then passing it into the function you can just do the math in the function by providing in the 3rd position “sizeof(hMod)”. Where hMod is the array of handles 1024 elements long.
Finally, through the last argument the function tells you the number of bytes the function used up in “hMod”. So now you know how much of “hMod” contains actual data and how much of “hMod” is empty.
And yes when this function succeeds it returns back a non-zero value. And a zero if it fails. Because of this we can put it safely in an “if” condition.
Once this function passes we would have all the handles to the modules in the process.
The next function we come across is GetModuleBaseName. This function is explained in part 1 excepting the second argument. If you notice, here too there is a NULL value. This argument is explained towards the end of this part. With this function you can print the name of the process you are exploring. Once this is printed you go into another “for” loop. You can use this loop to loop through every handle to the modules in the hMod array.
In this loop there are only two functions and one of them is to print the Module name. The other is GetModuleFileNameEx. Before you get into this function notice that you have derived the count of module handles you have in hMod which is stored in cModules.
Now the first argument in GetModuleFileNameEx is the handle to the “Process”.
The second argument should contain the specific handle to the Module. For this use hMod[n] where n will only go as high as cModules – 1.
The third argument is what the function returns. Into this variable the function will push the name of the Module along with its path in the system.
And finally through the last argument you should provide the function with the actual size of the 3rd argument in characters. The computation was done in the function itself where in you have taken the size of “szModName” and divided it by the size of TCHAR (which is a character).
Now since you have the information you need which is the name of the module, you can now go ahead and print it in the next line. This continues until the loop goes through all the modules and exits. The control passes to the outer loop goes through the next process and passes into the modules loop. In this manner you will be able to get every single process and all its modules working in the system.
Getting back to GetModuleBaseName… The second argument is NULL. Why? Because this argument contains the handle to the module provided by us to the function. And you can use this function to get the name of the Process. But you are already passing the handle to the Process through the first argument. The good thing is that if you leave this as NULL the function returns the name of the file used to create the calling process. And you get the name of the Process as well.
This is the only reason not to provide this argument with the handle to a module in the process.
C. Part III
The following part (Part III) contains information about getting status’ of a process, controlling processes, killing processes, and more.