@@ -7,12 +7,24 @@ typedef NTSTATUS(*ptrNtResumeProcess)(HANDLE hProcess);
77void * ProcessManager::ptrResumeProcess = nullptr ;
88void * ProcessManager::ptrSuspendProcess = nullptr ;
99
10+ // (1). Because we use not fully documented functions such as SuspendProcess and ResumeProcess
11+ // I have implemented a setup funcionality in the begining of the constructor
12+ // Basically, we get those functions from the ntdll.dll module which is loaded by default
13+ // in every windows executable. Because of that, we can obtain their addresses with GetProcAddres.
14+ // Of course, we need to provide proper prototyping so we can use them in code.
15+
16+ // (2). Here, I try, if a process id has been provided, to acquire some process information.
17+ // The MOST IMPORTANT member variable here is "hProcess" because it is a handle to the process
18+ // obtained through the specified process id and it is used basically for EVERY windows api process
19+ // related functions.
20+ // We also try to get the full path of the executable used to create the process specified by the user.
21+ // Finally, we trunk imagePathName so we can store just the executable name, without its full path.
1022ProcessManager::ProcessManager (const DWORD pid) : processId(pid),
1123 hProcess(nullptr ),
1224 imagePathName(L" (none)" ),
1325 processName(L" (none)" ), peParser(imagePathName) {
1426
15- // Set up suspend/resume function ptrs
27+ // (1)
1628if (ptrSuspendProcess == nullptr || ptrResumeProcess == nullptr ) {
1729HMODULE hNtdll = ::GetModuleHandle (L" ntdll.dll" );
1830if (!hNtdll) {
@@ -24,6 +36,7 @@ ProcessManager::ProcessManager(const DWORD pid) : processId(pid),
2436ProcessManager::ptrResumeProcess = ::GetProcAddress (hNtdll, " NtResumeProcess" );
2537}
2638
39+ // (2)
2740if (pid == 0 )
2841return ;
2942
@@ -33,20 +46,33 @@ ProcessManager::ProcessManager(const DWORD pid) : processId(pid),
3346
3447}
3548
36-
49+ // (1). The destructor of the class uses a private Cleanup function to guarantee that
50+ // it frees every resource allocated properly
3751ProcessManager::~ProcessManager () {
52+ // (1)
3853this ->Cleanup ();
3954}
4055
41-
56+ // (1). Just function just exists to enable the programmer who is using our class
57+ // to update the current process based on a process name instead of a process id
58+ // giving the ProcessManager class more versatility.
4259void ProcessManager::UpdateProcess (const std::wstring& procName) {
43- // Just a wrapper
60+ // (1)
4461this ->UpdateProcess (this ->GetProcessIdByName (procName));
4562}
4663
4764
65+ // (1). First, we call the cleanup function to release every resource previously allocated.
66+ // So we can properly update the current process information without having memory leaks and
67+ // memory related bugs in general.
68+ // (2). We update each member variable and do some error checking.
69+ // (3). We also reload the PEParser class instance so it refers to the executable currently
70+ // specified.
4871void ProcessManager::UpdateProcess (const DWORD pid) {
72+ // (1)
4973this ->Cleanup ();
74+
75+ // (2)
5076this ->processId = pid;
5177this ->hProcess = this ->getHandleFromPid (pid);
5278if (!this ->hProcess )
@@ -55,11 +81,15 @@ void ProcessManager::UpdateProcess(const DWORD pid) {
5581if (!this ->imagePathName .compare (L" (none)" ))
5682return ;
5783this ->processName .assign (imagePathName.substr (imagePathName.find_last_of (' \\ ' ) + 1 ));
84+
85+ // (3)
5886this ->peParser .Reload (this ->imagePathName );
5987}
6088
61-
89+ // (1). Function that returns a handle to a process object
90+ // using the process identifier (pid).
6291HANDLE ProcessManager::getHandleFromPid (const DWORD pid) {
92+ // (1)
6393HANDLE hTemp = ::OpenProcess (PROCESS_ALL_ACCESS, FALSE , pid);
6494if (!hTemp) {
6595printf (" [-] Failed acquiring handle to process\n " );
@@ -68,8 +98,10 @@ HANDLE ProcessManager::getHandleFromPid(const DWORD pid) {
6898return hTemp;
6999}
70100
71-
101+ // (1). We obtain the full path of the executable file associated with the
102+ // loaded process image.
72103const std::wstring ProcessManager::getImagePathName (const HANDLE _hProcess) {
104+ // (1)
73105DWORD pathSize{ MAX_PATH };
74106std::wstring _imagePathName;
75107_imagePathName.resize (pathSize + 1 );
@@ -83,8 +115,11 @@ const std::wstring ProcessManager::getImagePathName(const HANDLE _hProcess) {
83115return _imagePathName;
84116}
85117
86-
118+ // (1). We close the process handle IF IT IS OPEN.
119+ // We set variables to zero, nullptr or "(none)" depending on their types
120+ // We also call a cleaning function from the PEParser class
87121void ProcessManager::Cleanup (void ) {
122+ // (1)
88123this ->processId = 0 ;
89124if (this ->hProcess )
90125::CloseHandle (this ->hProcess);
@@ -94,8 +129,21 @@ void ProcessManager::Cleanup(void) {
94129this ->peParser .clean ();
95130}
96131
97- // Functionality
132+ // (1). This is probably the most important function for the MinimalProcessManager solution.
133+ // I first start initializing some variables properly following the microsoft documentation.
134+ // Here, we are using th32help.h library to list all existing process on the system.
135+ // This function can be used for the following purposes: display process list and
136+ // search for a particular process (based on its name) returing its PID.
137+ // (2). Stores a handle to an object that represents a snapshot on the system.
138+ // Also, the program does some error handling (its probably going to be updated to use exceptions in the future)
139+ // (3). Finally, all the logic is implemented in this third section. The function receiveis a boolean variable
140+ // that tells if the function is to be used as a filter or should simply list all processes.
141+ // In case the filter is off, we just print all the process information in a formatted way.
142+ // In case the filter is on AND the user has specified a valid process name (we never know),
143+ // then we compare with the current process name being displayed and, if they match, we return
144+ // the process id of that particular process beign searched.
98145DWORD ProcessManager::DisplayProcessList (const bool filter, const std::wstring& targetProcName) const {
146+ // (1)
99147PROCESS_INFORMATION procInfo{ 0 };
100148PROCESSENTRY32W procEntry{ 0 };
101149HANDLE hSnapshot{ nullptr };
@@ -104,13 +152,14 @@ DWORD ProcessManager::DisplayProcessList(const bool filter, const std::wstring&
104152::ZeroMemory (&procEntry, sizeof (PROCESSENTRY32W));
105153procEntry.dwSize = sizeof (PROCESSENTRY32W);
106154
107-
155+ // (2)
108156hSnapshot = ::CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0 );
109157if (!hSnapshot) {
110158printf (" [-] Failed acquiring snapshot for process list\n " );
111159return 0 ;
112160}
113161
162+ // (3)
114163::Process32First (hSnapshot, &procEntry);
115164
116165if (!filter)
@@ -134,17 +183,16 @@ DWORD ProcessManager::DisplayProcessList(const bool filter, const std::wstring&
134183
135184}
136185
137-
186+ // (1). We display some process information. The use of all the windows API functions used here
187+ // should be very straightforward to learn with the microsoft documentation.
138188void ProcessManager::DisplayProcessInfo (void ) const {
189+ // (1)
139190std::wstring priorityClass{ L" (none)" };
140191DWORD handleCount{ 0 };
141192BOOL priorityBoost{ FALSE };
142193bool is64bit{ this ->peParser .is64bit () };
143194SIZE_T minWorkSize{ 0 };
144195SIZE_T maxWorkSize{ 0 };
145-
146-
147-
148196
149197if (this ->hProcess ) {
150198::GetProcessPriorityBoost (this ->hProcess, &priorityBoost);
@@ -159,41 +207,97 @@ void ProcessManager::DisplayProcessInfo(void) const {
159207printf (" - - - - - - - - - - - - - - - - - - - -" );
160208}
161209
162-
210+ // (1). This is a wrapper function that calls DisplayProcessList with the filtering
211+ // mdoe enabled. It allows using this member function to obtain process id based
212+ // only on a process name.
163213DWORD ProcessManager::GetProcessIdByName (const std::wstring& procName) {
164214return this ->DisplayProcessList (true , procName);
165215}
166216
167-
217+ // (1). Nothing special here. We just call the documented TerminateFunction passing
218+ // the process handle that the class currently stores.
168219void ProcessManager::TerminateProcess (DWORD ExitCode) {
169220::TerminateProcess (this ->hProcess, ExitCode);
170221this ->Cleanup ();
171222}
172223
173-
224+ // (1). Now, in this function and the next one (ResumeProcess) I do something that may seem
225+ // weird for those who are new to this type of programming. To hide the function prototypes from
226+ // the main program, I define VOID function pointers in the class declaration. Obviously, we need
227+ // to cast those two pointers (ptrSuspendProcess and ptrResumeProcess) to the apropriate prototype
228+ // so we can call the function from memory. Because the prototypes of those two functions are
229+ // in this .cpp file, the main.cpp cannot see them.
174230void ProcessManager::SuspendProcess (void ) {
175231if (this ->hProcess )
176232static_cast <ptrNtSuspendProcess>(ProcessManager::ptrSuspendProcess)(this ->hProcess );
177233}
178234
179-
235+ // (1). Read SuspendProcess explanation
180236void ProcessManager::ResumeProcess (void ) {
181237if (this ->hProcess )
182238static_cast <ptrNtResumeProcess>(ProcessManager::ptrResumeProcess)(this ->hProcess );
183239}
184240
241+
242+ // (1). Here we will have a large discussion.
243+ // Basically, consider that you have a CPU chip with 4 cores, 8 threads.
244+ // You probaly heard that before. Lets just rename "8 threads" to "8 logical processors".
245+ // Now, threads are the units of execution. They are the entities that actually RUN code.
246+ // It is a common misconception to beleive that processes "run". They dont, they are just
247+ // a container whose purpose is to MANAGE a group of threads.
248+ // Now, suppose that you have 8 logical processors. So you can run 8 threads simultaneasly.
249+ // But what if you have 20,40 or 2000 threads? (check your task manager) In that case we have to discuss something called
250+ // "thread states" and "priorities".
251+ // Begining with thread states, they just describe what that thread is doing at that particular moment.
252+ // Ready State -> Means that the thread WANTS to execute and is probably queued for a logical processor already.
253+ // Wait State -> Thread DOES NOT WANT TO RUN, it is waiting for some kind of event to occur (i.g user input).
254+ // Running State -> Is actually executing code.
255+ // So, based on that, we can correctly conclude that MOST of the threads are in the WAIT state.
256+ // Now, every thread has a priority which is defined by a combination of: thread relative priority (relative to the thread) + priority class (relative to the process)
257+ // By default, the task manager shows the Priority class ONLY and so doesn't tell us everything about the threads priority.
258+ // Points to note:
259+ // 1 - Threads Priority Range from 0 to 31 (considering kernel). In practice, for user mode programming: (1..31)
260+ // 2 - Threads Priority = Threads Relative Priority + Priority Class
261+ // -----------------------------------------------------------------------
262+ // PRIORITY CLASS | VALUE THREADS RELATIVE PRIORITY | VALUE
263+ // IDLE 4 [1-15] IDLE -15
264+ // BELOW NORMAL 6 [1-15] LOWEST -2
265+ // NORMAL 8 [1-15] BELOW NORMAL -1
266+ // ABOVE NORMAL 10 [1-15] NORMAL +0
267+ // HIGH 13 [1-15] ABOVE NORMAL +1
268+ // REAL TIME 24 [16-31] HIGHEST +2
269+ // -------------------------------- TIME CRITICAL +15
270+ //
271+ // 3 - Priority Class also defines the range of the final threads priority. For every priority class, with the exception of
272+ // the REAL TIME, the final threads priority is in the range [1,15]. The REAL TIME one defines the range [16,31].
273+ //
274+ // So, we basically sum the two numbers and if the value exceeds one of the extreme points (1 and (31 or 15)) then the value
275+ // considered is the closest extreme point. For example, if we have BELOW_NORMAL (6) priority class and IDLE relative priority (-15), we have a
276+ // FINAL thread priority of 1 and NOT -9.
277+ // [Round Robin Example]
278+ // [Thread Ideal Processor]
279+ // [Affinity and Processor Groups]
280+ //
281+ //
185282void ProcessManager::SetPriorityClass (DWORD newPriority) {
186283if (this ->hProcess )
187284::SetPriorityClass (this ->hProcess, newPriority);
188285}
189286
287+ // (1). Function that creates a process with a fake parent process id (you can see the process three with ProcessExplorer.exe)
288+ // This function is just a wrapper that allows executing with two string parameters.
190289void ProcessManager::CreateSpoofedPProcess (const std::wstring& processPath, const std::wstring& parentProcessPath) {
191290this ->CreateSpoofedPProcess (processPath, this ->GetProcessIdByName (parentProcessPath));
192291}
193292
194-
293+ // (1). Initialize all the variables properly.
294+ // (2). Initialize LPPROC_THREAD_ATTRIBUTE_LIST and allocate memory
295+ // (3). Update the process and thread attribute list with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
296+ // passing the parent process handle as parameter. Finally, it creates the process with the
297+ // extended version of startup info.
195298void ProcessManager::CreateSpoofedPProcess (const std::wstring& exePath, const DWORD pid) {
196299
300+ // (1)
197301// These variables need to be nonconst due to CreateProcess documentation. So we pass them to the stack without const modifier.
198302std::wstring procPathCpy (exePath);
199303SIZE_T procThreadAttrListSize{ 0 };
@@ -212,7 +316,7 @@ void ProcessManager::CreateSpoofedPProcess(const std::wstring& exePath, const DW
212316return ;
213317}
214318
215-
319+ // (2)
216320::InitializeProcThreadAttributeList (nullptr , 1 , 0 , &procThreadAttrListSize);
217321
218322procThreadAttr = (LPPROC_THREAD_ATTRIBUTE_LIST)(new byte[procThreadAttrListSize]);
@@ -222,6 +326,7 @@ void ProcessManager::CreateSpoofedPProcess(const std::wstring& exePath, const DW
222326goto Cleanup;
223327}
224328
329+ // (3)
225330if (!::UpdateProcThreadAttribute (procThreadAttr, 0 , PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParent, sizeof (HANDLE), nullptr , nullptr )) {
226331printf (" [-] Failed initializing proc_thread_attr values\n " );
227332goto Cleanup;
0 commit comments