Enumeration local-remote NT System Process
Author: eyas
Copyright: http://www.ey4s.org
Source: http://www.gamehigh.net/
In Windows, the tool taskmgr.exe can be used to view the process information of the current system in more detail. However, it is a Windows GUI program. sometimes do you think that the command line is more convenient? In fact, there are already many tools for listing system processes under the command line, and M $'s resource kit also seems to have, but to understand how they are implemented, do it yourself, is it more interesting :)
A process is usually defined as an instance of a running program. It consists of two parts:
<1> the kernel objects used by the operating system to manage processes. The kernel object is also used by the system to store statistics about processes.
<2> address space. It contains the code and data of all executable modules or DLL modules. It also contains space for dynamic memory allocation, such as the thread stack and heap allocation.
There are about four methods to implement enumeration of system processes. One of them can be used to enumerate the processes of a remote NT system, provided that the remote system administrator has permissions.
<Part 1: calling the psapi function to enumerate system processes>
M $'s Windows NT Development Team developed its own process status function, which is included in the psapi. dll file and can only be used in Versions later than NT4.0. There are 14 Functions in psapi [actual psapi. there are 19 DLL output functions, but five of them have two versions: ANSI and Unicode.] by calling these functions, we can easily obtain all information about the system process, such as the process name, process ID, parent process ID, process priority, and list of modules mapped to the process space. For convenience, the following example program only obtains the process name and ID.
A simple program is as follows:
/*************************************** **********************************
Module: PS. c
Note: Call the psapi function to enumerate the system process name and ID, only for NT/2000
**************************************** *********************************/
# Include
# Include
# Include "psapi. H"
# Pragma comment (Lib, "psapi. lib ")
Void printprocessnameandid (DWORD processid)
{
Char szprocessname [max_path] = "unknown ";
// Obtain the Process Handle
Handle hprocess = OpenProcess (process_query_information |
Process_vm_read,
False, processid );
// Obtain the process name
If (hprocess)
{
Hmodule hmod;
DWORD cbneeded;
If (enumprocessmodules (hprocess, & hmod, sizeof (hmod), & cbneeded ))
Getmodulebasename (hprocess, hmod, szprocessname,
Sizeof (szprocessname ));
}
// Echo process name and ID
Printf ("/n %-20 S %-20d", szprocessname, processid );
Closehandle (hprocess );
}
Void main ()
{
DWORD aprocesses [1024], cbneeded, cprocesses;
Unsigned int I;
// List system process IDS
If (! Enumprocesses (aprocesses, sizeof (aprocesses), & cbneeded ))
Return;
// Calculate how each process identifiers were returned.
// Number of computing processes
Cprocesses = cbneeded/sizeof (DWORD );
// Output the name and ID of each process
For (I = 0; I <cprocesses; I ++)
Printprocessnameandid (aprocesses [I]);
Return;
}
<Part 2: Call the toolhelp API to enumerate local system processes>
The psapi function mentioned in the first part can only enumerate NT system processes. In Windows9x environment, we can call the toolhelp API function to enumerate system processes. M $'s Windows NT development team did not add these functions to Windows NT because they did not like the toolhelp function, so they developed their own process status function, is the psapi mentioned in the first part. However, M $ has added the toolhelp function to Windows 2000. Toolhelp has 12 functions. By calling these functions, you can obtain detailed information about local system processes. In the following simple example, only three functions are called, obtain the name and ID of the system process. The procedure is as follows:
/*************************************** *******************************
Module: PS. c
Note: Call the toolhelp function to enumerate the process name and ID of the local system. Only for 9x/2000
**************************************** ******************************/
# Include
# Include
# Include
Int main ()
{
Handle hprocesssnap = NULL;
Processentry32 pe32 = {0 };
Hprocesssnap = createconlhelp32snapshot (th32cs_snapprocess, 0 );
If (hprocesssnap = (handle)-1)
{
Printf ("/ncreatemedilhelp32snapshot () failed: % d", getlasterror ());
Return 1;
}
Pe32.dwsize = sizeof (processentry32 );
Printf ("/nprocessname processid ");
If (process32first (hprocesssnap, & pe32 ))
{
Do
{
Printf ("/n %-20 S % d", pe32.szexefile, pe32.th32processid );
} While (process32next (hprocesssnap, & pe32 ));
}
Else
{
Printf ("/nprocess32firstt () failed: % d", getlasterror ());
}
Closehandle (hprocesssnap );
Return 0;
}
<Part 3: enumerating local system processes by calling APIs not made public in Ntdll. dll>
The first part and the second part refer to calling APIs exposed by Ms to enumerate system processes. In NTDLL. dll, there is actually an undisclosed API that can also be used to enumerate system processes. This method is from elsewhere. I can't find it myself. I cannot remember the source clearly. It seems like it is part of the source code in pwdump2.
OK! The undisclosed API is ntquerysysteminformation. The usage is as follows:
//////////////////////////////////////// ////////////////////////////////////////
# Include
# Include
# Include
Typedef unsigned long ntstatus;
Typedef unsigned short ushort;
Typedef unsigned long ulong;
Typedef unsigned long DWORD;
Typedef long;
Typedef _ int64 Longlong;
Typedef struct {
Ushort length;
Ushort maxlen;
Ushort * buffer;
} Unicode_string;
Struct process_info {
Ulong nextentrydelta;
Ulong threadcount;
Ulong reserved1 [6];
Large_integer createtime;
Large_integer usertime;
Large_integer kerneltime;
Unicode_string processname;
Ulong basepriority;
Ulong processid;
};
Typedef ntstatus (_ stdcall * ntquerysysteminformation1 )(
In ulong sysinfoclass,
In out pvoid systeminformation,
In ulong systeminformationlength,
Out Pulong retlen
);
Int main ()
{
Hinstance hntdll;
Ntquerysysteminformation1 ntquerysysteminformation;
Ntstatus RC;
Ulong ulneed = 0;
Void * Buf = NULL;
Size_t Len = 0;
Struct process_info * P;
Int done;
Hntdll = loadlibrary ("NTDLL ");
If (! Hntdll)
Return 0;
Ntquerysysteminformation = (ntquerysysteminformation1) getprocaddress (hntdll,
"Ntquerysysteminformation ");
If (! Ntquerysysteminformation)
Return 0;
Do {
Len + = 0x1000;
Buf = realloc (BUF, Len );
If (! Buf)
Return 0;
Rc = ntquerysysteminformation (5, Buf, Len, & ulneed );
} While (rc = 0xc0000004); // status_info_len_mismatch
If (RC <0 ){
Free (BUF );
Return 0;
}
Printf ("/nprocessname processid ");
P = (struct process_info *) BUF;
Done = 0;
While (! Done ){
If (P-> processname. buffer! = 0 ))
{
Printf ("/n %-20 S % d", p-> processname. buffer, p-> processid );
}
Done = p-> nextentrydelta = 0;
P = (struct process_info *) (char *) P) + P-> nextentrydelta );
}
Free (BUF );
Freelibrary (hntdll );
Return 0;
}
<Part 4: obtain local/remote system process information from PDH>
The preceding three methods can only enumerate local system processes. How can we enumerate remote system processes? Currently, I only know how to obtain process information from PDH.
OK! Let me briefly talk about What PDH is, Hoho ~ It's not difficult. PDH is short for performance data helper. Windows NT has been updating this database called performance data, which contains a lot of information, such as CPU usage, memory usage, a lot of useful information such as system process information can be accessed through registry functions. Note that this database is not configured in Windows 9X. However, the information layout in this database is very complex, and many people do not want to use it, including me. In addition, at the beginning, it did not have its own specific functions and can only be operated through the existing registry functions. Later, to make the database easy to use, Ms developed a set of performance data helper functions, which are included in the PDH. dll file.
Windows 2000 allows remote registry operations by default, so we can obtain the required system process information from its PDH by connecting to the registry of the remote system, of course, this requires the admin permission of the remote system.
OK! The following example uses the registry function to retrieve the required data from the local/remote PDH database. We did not use the pdh api.
The program code is as follows:
/*************************************** ***********************************
Module: PS. c
Author: mikeblas@nwlink.com
Modify: ey4s
Http: // www.ey4s.org
Date: 2001/6/23
**************************************** **********************************/
# Include
# Include
# Include
# Define initial_size 51200
# Define extend_size 12800
# Define regkey_perf "software // Microsoft // Windows NT // CurrentVersion // Perflib"
# Define regsubkey_counters "counters"
# Define process_counter "process"
# Define processid_counter "ID process"
# Define unknown_task "unknown"
# Define maxprocessnum 52 // maximum number of processes
# Pragma comment (Lib, "MIP. lib ")
Typedef struct processinfo
{
Char processname [128];
DWORD dwprocessid;
} PI;
Void banner ();
Int connipc (char *, char *, char *);
DWORD getprocessinfo (pI *, char *);
Int main (INT argc, char ** argv)
{
Int I, iret;
Pi tasklist [maxprocessnum];
Banner ();
If (argc = 1)
{
Iret = getprocessinfo (tasklist, null );
Printf ("/nprocess info for [local]:");
}
Else if (argc = 4)
{
Iret = getprocessinfo (tasklist, argv [1], argv [2], argv [3]);
Printf ("/nprocess info for [% s]:", argv [1]);
}
Else
{
Printf ("/nusage: % s", argv [0]);
Return 1;
}
If (iret> 0)
For (I = 0, printf ("/nprocessname processid ");
I
Printf ("/n %-20 S % d", tasklist [I]. processname, tasklist [I]. dwprocessid), I ++ );
Return 0;
}
DWORD getprocessinfo (pI * prolist, char * IP, char * user, char * pass)
{
Dword rc, dwtype, dwsize, I, dwprocessidtitle, dwprocessidcounter, dwret =-1;
Hkey hkeynames;
Lpstr Buf = NULL, P, P2;
Char szsubkey [1024], szprocessname [max_path];
Pperf_data_block pperf;
Pperf_object_type pobj;
Pperf_instance_definition pinst;
Pperf_counter_block pcounter;
Pperf_counter_definition pcounterdef;
Hkey ghperfkey = NULL, // get perf data from this key
Ghmachinekey = NULL; // get Title Index from this key
Bool bremote = false;
// Look for the list of counters. Always use the neutral
// English version, regardless of the local language. We
// Are looking for some special keys, and we are always
// Going to do our looking in English. We are not going
// To show the user the counter names, so there is no need
// To go find the corresponding name in the local language.
_ Try
{
If (IP) & (User) & (PASS ))
{
If (connipc (IP, user, pass )! = 0)
{
Printf ("/nconnect to % s failed.", ip );
_ Leave;
}
Else
Bremote = true;
}
// Connect to the local or remote registry
If (regconnectregistry (IP, hkey_performance_data,
& Ghperfkey )! = Error_success)
{
Printf ("/nregconnectregistry () 1 failed: % d", getlasterror ());
_ Leave;
}
'If (regconnectregistry (IP, HKEY_LOCAL_MACHINE, & ghmachinekey )! = Error_success)
{
Printf ("/nregconnectregistry () 2 failed: % d", getlasterror ());
_ Leave;
}
Sprintf (szsubkey, "% S // % 03x", regkey_perf, makelangid (lang_english, sublang_neutral ));
If (regopenkeyex (ghmachinekey, szsubkey, 0, key_read, & hkeynames )! = Error_success)
_ Leave;
// Obtain the required buffer size from counter names
If (regqueryvalueex (hkeynames, regsubkey_counters, null, & dwtype, null, & dwsize )! = Error_success)
_ Leave;
// Allocate memory
Buf = (lpstr) malloc (dwsize );
If (BUF = NULL)
_ Leave;
Memset (BUF, 0, dwsize );
// Read the counter names from the Registry
If (regqueryvalueex (ghperfkey, regsubkey_counters, null, & dwtype, (lpbyte) BUF, & dwsize )! = Error_success)
_ Leave;
// Now loop thru the counter names looking for the following counters:
// 1. "process" process name
// 2. "ID process" process ID
// The buffer contains multiple NULL terminated strings and then
// Finally NULL terminated at the end. The strings are in pairs
// Counter number and counter name.
P = Buf;
While (* P)
{
If (P> BUF)
For (P2 = P-2; isdigit (* P2); P2 --);
If (stricmp (p, process_counter) = 0)
{
// Look backwards for the counter number
For (P2 = P-2; isdigit (* P2); P2 --);
Strcpy (szsubkey, p2 + 1 );
}
Else if (stricmp (p, processid_counter) = 0)
{
// Look backwards for the counter number
For (P2 = P-2; isdigit (* P2); P2 --);
Dwprocessidtitle = atol (P2 + 1 );
}
// Next string
P + = (strlen (p) + 1 );
}
// Free the counter names Buffer
Free (BUF );
// Allocate the initial buffer for the performance data
Dwsize = initial_size;
Buf = (lpstr) malloc (dwsize );
While (true)
{
If (BUF = NULL)
_ Leave;
Memset (BUF, 0, dwsize );
Rc = regqueryvalueex (ghperfkey, szsubkey, null, & dwtype, (lpbyte) BUF, & dwsize );
Pperf = (pperf_data_block) BUF;
// Check for success and valid perf data block Signature
If (rc = error_success )&&
(Dwsize> 0 )&&
(Pperf)-> signature [0] ==( wchar) 'P '&&
(Pperf)-> signature [1] ==( wchar) 'E '&&
(Pperf)-> signature [2] ==( wchar) 'R '&&
(Pperf)-> signature [3] = (wchar) 'F ')
Break;
// If buffer is not big enough, reallocate and try again
If (rc = error_more_data)
{
Dwsize + = extend_size;
Buf = (lpstr) realloc (BUF, dwsize );
}
Else _ leave;
}
// Set the perf_object_type pointer
Pobj = (pperf_object_type) (DWORD) pperf + pperf-> headerlength );
// Loop thru the performance counter definition records looking
// For the process ID counter and then save its offset
Pcounterdef = (pperf_counter_definition) (DWORD) pobj + pobj-> headerlength );
For (I = 0; I <(DWORD) pobj-> numcounters; I ++)
{
If (pcounterdef-> counternametitleindex = dwprocessidtitle)
{
Dwprocessidcounter = pcounterdef-> counteroffset;
Break;
}
Pcounterdef ++;
}
Pinst = (pperf_instance_definition) (DWORD) pobj + pobj-> definitionlength );
// Loop thru the performance instance Data Extracting each process name
// And process ID
For (I = 0; I <(DWORD) pobj-> NumInstances-1 & I
{
// Pointer to the process name
P = (lpstr) (DWORD) pinst + pinst-> nameoffset );
// Convert it to ASCII
Rc = widechartomultibyte (cp_acp, 0, (lpcwstr) P,-1, szprocessname, sizeof (szprocessname), null, null );
// If we cant convert the string then use a default value
If (! RC) strcpy (prolist [I]. processname, unknown_task );
Else strncpy (prolist [I]. processname, szprocessname, sizeof (prolist [I]. processname)-1 );
// Get the process ID
Pcounter = (pperf_counter_block) (DWORD) pinst + pinst-> bytelength );
Prolist [I]. dwprocessid = * (lpdword) (DWORD) pcounter + dwprocessidcounter ));
// Next process
Pinst = (pperf_instance_definition) (DWORD) pcounter + pcounter-> bytelength );
}
Dwret = I;
} // End of try
_ Finally
{
If (BUF) Free (BUF );
Regclosekey (hkeynames );
Regclosekey (hkey_performance_data );
If (bremote)
{
Char TMP [52], tmp2 [96];
Strncpy (TMP, IP, sizeof (TMP)-1 );
Wsprintf (tmp2, "/// % S // IPC $", TMP );
Wnetcancelconnection2 (tmp2, connect_update_profile, true );
}
}
Return dwret;
}
//////////////////////////////////////// ////////////////////////////////////////
Int connipc (char * remotename, char * user, char * pass)
{
Netresource NR;
Char rn [50] = "////";
Strncat (RN, remotename, sizeof (RN)-11 );
Strcat (RN, "// IPC $ ");
Nr. dwtype = resourcetype_any;
Nr. lplocalname = NULL;
Nr. lpremotename = rn;
Nr. lpprovider = NULL;
If (wnetaddconnection2 (& NR, pass, user, false) = no_error)
Return 0;
Else
Return 1;
}
//////////////////////////////////////// ////////////////////////////////////////
Void banner ()
{
Printf ("/npslist ==> local and remote process list"
"/Npower by ey4s
"/Nhttp: // www.ey4s.org"
"/N2001/6/22/N ");
}
//////////////////////////////////////// ////////////////////////////////////////
The program is compiled and runs well in Windows 6.0 and VC ++ environments. Note: the remote machine must allow IPC connection and remote registry operation, and the admin permission is required.