Process-and Module-related Toolhelp32 Functions

MASM Approach

By Iczelion
email: Iczelion@galaxycorp.com

Toolhelp is an addon library for Windows 3.1. You can use toolhelp functions to enumerate processes, threads, modules and heaps. As such, it's a very valuable tool indeed. So when Windows 95 came out, toolhelp was integrated into the system. The version of toolhelp under Windows 95 is 32-bit so it's renamed to toolhelp32. However, toolhelp32 is not available on Windows NT 4.0.

Under the hood, toolhelp functions do their magic through intimate knowledge of the underlying operating system. In order to enumerate processes, they must know about the process databases. If you're interested in such information, you should read Windows 95 System Programming Secrets by Matt Pietrek.

We can divide functions in toolhelp32 into 4 groups:
  1. Process enumeration functions
  2. Module enumeration functions
  3. Heap enumeration functions
  4. Thread enumeration functions

The usage of each catergory is very similar so I'll show how to use process and module enumeration functions.
I have written an example program with source code that enumerates all processes and modules in each process. You can download the example here

Before you can use toolhelp32 functions, you must get a handle to a snapshot. A snapshot tells toolhelp32 what you want it to do. You must call CreateToolhelp32Snapshot function to obtain the snapshot handle first. Its syntax is below:

CreateToolhelp32Snapshot PROTO dwFlags : DWORD, th32ProcessID : DWORD

This function takes two parameters:

dwFlags : Specify the type of snapshot you want.

TH32CS_SNAPHEAPLIST
equ 1
TH32CS_SNAPPROCESS
equ 2
TH32CS_SNAPTHREAD
equ 4
TH32CS_SNAPMODULE
equ 8
TH32CS_SNAPALL
equ TH32CS_SNAPHEAPLIST + TH32CS_SNAPPROCESS
+ TH32CS_SNAPTHREAD + TH32CS_SNAPMODULE
TH32CS_INHERIT
equ 80000000h

For example if you want to enumerate processes, you should use TH32CS_SNAPPROCESS.

th32processID : The processID of the process which you want the function(s) to operate on. This parameter is used only when TH32CS_SNAPHEAPLIST or TH32CS_SNAPMODULE is specified because heaps and modules are dependent on processes. You can specify 0 as this parameter, which toolhelp32 will interpret as the processID of the current process.

CreateToolhelp32Snapshot returns a handle to the snapshot you request. After you're done with it, you must call CloseHandle to close it.

After we obtain the snapshot handle, we can call toolhelp32 functions. I'll show how to enumerate processes first.

To enumerate processes, we must call CreateToolhelp32Snapshot with TH32CS_SNAPPROCESS, and th32processID should be zero. Then we call Process32First to get the info on the first process and Process32Next to retrieve the remaining process info. These functions are defined as follows:

Process32First PROTO hSnapshot : DWORD, lppe : DWORD
Process32Next PROTO hSnapshot : DWORD, lppe : DWORD

hSnapshot : the snapshot handle from CreateToolhelp32Snapshot

lppe : pointer to a PROCESSENTRY32 structure which is defined like this:

PROCESSENTRY32 STRUCT DWORD
dwSize
DWORD?; The size of this structure
cntUsage
DWORD?; The number of instances(?)
th32ProcessID
DWORD?; This process's processID
th32DefaultHeapID
DWORD?; Default heap's ID
th32ModuleID
DWORD?; Associated exe's moduleID
cntThreads
DWORD?; Number of threads in the process
th32ParentProcessID
DWORD?; This process's parent processID
pcPriClassBase
DWORD?; Base priority of process's threads
dwFlags
DWORD?; Reserved
szExeFile
db 260 dup (?) ; Full filename (including path) of the exe file that owns the process.
PROCESSENTRY32 ENDS

Both Process32First and Process32Next return TRUE if the operation is successful, FALSE if there's no process left to enumerate.

Let's see a code snippet :

.data

hSnapshot DWORD ?
.data?

pe32 PROCESSENTRY32 <>

.code
invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS, 0
; obtain snapshot handle for process
; enumeration.
mov hSnapshot, eax
; Save the snapshot handle
mov pe32.dwSize, sizeof PROCESSENTRY32
; dwSize is the only member we must fill
invoke Process32First, hSnapshot, addr pe32
; Get info about the first process
.while eax==TRUE
; Enumerate until there's no process left
   <do something with the information in pe32 structure>
   invoke Process32Next, hSnapshot, addr pe32
; Get info about subsequent processes
.endw
invoke CloseHandle, hSnapshot
; Close snapshot handle if we're done.

Module enumeration is similar to the operation above but with a small difference: you must obtain the processID of the process you want to enumerate modules. You can use th32processID member from PROCESSENTRY32 structure as the second parameter of CreateToolhelp32Snapshot function. There are two functions that perform module enumeration:

Module32First PROTO hSnapshot : DWORD, lpme : DWORD
Module32Next PROTO hSnapshot : DWORD, lpme : DWORD

hSnapshot : the snapshot handle.

lpme : pointer to a MODULEENTRY32 structure which is defined like this:

MODULEENTRY32 STRUCT DWORD
dwSize
DWORD?; The size in bytes of this structure
th32ModuleID
DWORD?; This module
th32ProcessID
DWORD?; Owning process
GlblcntUsage
DWORD?; Global usage count on the module
ProccntUsage
DWORD?; Module usage count in th32ProcessID's context
ModBaseAddr
DWORD?; Base address of module in th32ProcessID's context
ModBaseSize
DWORD?; Size in bytes of module starting at modBaseAddr
hModule
DWORD?; The hModule of this module in th32ProcessID's context
szModule
db 256 dup (?) ; The module name (without path)
szExePath
db 260 dup (?) ; The full path name of the module
MODULEENTRY32 ENDS

Like Process32First and Process32Next, the return value is TRUE if the operation is successful and FALSE if there's no module left to enumerate.

Now, let's see them in action. We'll enumerate all modules in the current process.

.data

hSnapshot dd ?
.data
me32 MODULEENTRY32 <>

.code
invoke GetCurrentProcessID
; obtain the current process's processID
invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS,
; obtain snapshot handle for module
    eax
; enumeration.
mov hSnapshot, eax
; Save the snapshot handle
mov me32.dwSize, sizeof MODULEENTRY32
; dwSize is the only member we must fill
invoke Module32First, hSnapshot, addr me32
; Get info about first module in process
.while eax==TRUE
; Enumerate until there's no module left
   <do something with the information in me32 structure>
   invoke Module32Next, hSnapshot, addr me32
; Get info about subsequent modules
.endw
invoke CloseHandle, hSnapshot
; Close snapshot handle if we're done.

The story with the thread enumeration functions is the same as module functions. You have to specify the process which you want to enumerate the threads.

The functions in toolhelp32 are now stored in kernel32.dll so you don't need a separate dll for it. If you want similar functions under Windows NT 4.0, you must use psapi.dll.