In one of my previous posts (check Suspending processes in Windows), I was trying to discuss the well-known and less popular techniques making it possible to suspend threads or entire processes working under Microsoft Windows OS control. I also announced that a specific way of TaskMgr.exe modification – extending it with the interesting functionality – would be described in the next post. Although, before getting straight to the point (this is – changing the executable binary code), we have to consider some other important matters. Namely, we intend to have the modification applied to every single Task Manager instance running on the system. Right here, we have a few possible paths to go:
- Perform a one-time TaskMgr.exe system file alteration on hard disk.
- Create an additional executable file, called the loader – associated with the Task Manager and launched as its debugger.
- Leave the hard disk contents unchanged – modify only the virtual memory of all active processes, meeting some specific requirements (in our case – executable image path).
Each option presented above has it’s own set of drawbacks. When it comes to physical HDD modification, we start playing with system file data, which should be always considered dangerous and unwanted behavior. What is more, since Windows keeps many backup copies of core system applications, we would have to get rid of or modify all the existing Task Manager backups. In general, I find the method a little bit too messy to use in practice. If you would like to read more about this subjects, Windows File Protection and Windows Resource Protection should be a good place to start.
It is an interesting idea to create a specific loader – this is – a program executed every time, when some other application tries to launch TaskMgr.exe. If we are able to have our code executed every time the manager is about to be created, we know about all the running instances by definition. The loader’s job would be simply modifying memory in the context of a particular process, which should not cause much trouble. Associating external applications with executable files of specific name is possible using the following registry key:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
In our case, the only change applied to the registry would be adding a TaskMgr.exe key, and then creating a string value in its context, named Debugger, and with the following value:
More about Image File Execution Options can be found here.
In my opinion, the best choice we can make is to modify the process virtual memory itself, without writing to system critical files of any kind. This method extends our practical abilities of changing the application execution flow – we have access to all the data (and modules) used by the target process. For example, setting up an inline hook on one of the statically imported functions (such as kernel32.SetPriorityClass) is much easier to perform on a running process, then by altering the data inside TaskMgr.exe executable image itself.
Choosing the third method, we must consider the practical way of its implementation. Monitoring the list of processes running in the system is not a trivial problem. Moreover, there are many possible ways of solving it, differing not only in effectiveness, but also required user privileges and system mechanisms being used. In my opinion, all of them are worth taking a deeper look – when necessary, you will be able to choose the one best fitting your needs.
Today, I will try to describe one way of “tracing” the active applications – probably the most intuitive periodical process list updating, and comparing the results with older versions, in order to extract the interesting differences.
Some of the issues, which should draw our attention yet at the first glance:
- The problem of choosing the correct delay time. Since the discussed technique is undoubtedly active, sending specific requests to the operating system every, say, second – creates a possibility of accidentally missing a process. This is because it is expected to be possible to create a process, do its job and close itself within the 1 second between respective requests. The author is obviously able to customize the delay value in such a way to eliminate the described bug, though one must remember that the more often a current process list is retrieved from the kernel (or wherever else), the more processor time is consumed. Because of this, it is necessary to find a value that is reliable enough for us on one hand, and doesn’t generate too much processor usage, on the other.
- How about the source of (trusted) process list information, and the appropriate interface providing access to this info?
Even though the first problem can be solved in an experimental way, depending on the needs and habits of the programmer, the second issue requires further consideration. As it turns out, there is quite a big number of system interfaces (better or worse documented), allowing us to access the interesting information. Some of them are listed below:
- ToolHelp32 – a set of API functions exported by kernel32.dll, which have been already introduced in Microsoft Windows 3.1. They make it possible to access information regarding heaps, modules, threads and processes. Moreover, the interface is compatible with all Windows versions since Windows 9x/Me, except Windows NT 4.0. The enumeration area (per-session, per-system) depends on the current user privileges, in whose context these functions are called.
- Process Status API (PSAPI) – a very well described Windows help library, responsible for facilitating the access to information about processes, modules and device drivers present in the system. One of it’s exported functions is EnumProcesses, letting us obtain the identification numbers of all running processes. What should be noted here, is that the amount of information being retrieved this way is very poor – we get no additional information but the PIDs. If we wanted to get any additional data, we would be forced to open a handle to every single process (i.e. using the OpenProcess function), which is not always possible, due to some obvious security-related reasons.
- NtQuerySystemInformation(SystemProcessInformation) – undocumented, internal function exported by ntdll.dll. It provides complete information about the process in consideration – the result of it’s execution consists of an array of SYSTEM_PROCESS_INFORMATION structures. In practice, both methods described before rely on calling the NtQuerySystemInformation function with the SystemProcessInformation parameter, and then parsing the results in a specific way. What it means, is that every time the syscall’s returned structures are altered by some 3rd party software (like rootkits), EnumProcesses and ToolHelp32 will also return modified results.
- NtQuerySystemInformation(SystemHandleInformation) – another example of how the NtQuery~ system call can be used for our own purposes, when called with an appropriate parameter. The function is designed to enumerate all the opened object handle owned by all running processes. After proper interpretation of the provided information (such as detecting, which of the objects describe processes), we are able to create a complete list. The interesting part is that such a list is entirely independent from the kernel-mode EPROCESS linked list. The technique is commonly used as a way of rootkit detection, as some of them tend to pay too much attention on kernel-mode hiding, and therefore forget to consider the user-mode parts of the system (like CSRSS.EXE). Some more information can be found here (HPD with CSRSS Process Handle Enumeration)
- OpenProcess + GetLastError API – making brutal use of legitimate functions meant to get legal access to specified process object. According to the author’s research, obtaining a complete list of active processes is possible to be accomplished by trying to access every possible PID value, and – based on the OpenProcess and GetLastError return values – distinguish the PIDs associated with running processes from the “empty” ones. This technique has nothing in common with effectivenes though, hence it should not be used as a method of periodical process information gathering, under no circumstances.
- Windows Management Instrumentation (WMI) – “a set of extensions to the Windows Driver Model that provides an operating system interface through which instrumented components provide information and notification.” (via Wikipedia)
- Client/Server Runtime Subsystem (CSRSS) – in this case, we take advantage of the fact, that the CSRSS.EXE process is responsible for maintaining it’s own process list, fully independent from the EPROCESS chain (kernel-mode). Keeping such a list up to date is possible because every single process being created must be “registered” by it’s parent, using standard CSRSS communication routines (such as ntdll.CsrClientCallServer). Provided we have appropriate user privileges (allowing to open and read SYSTEM process virtual memory), we are able to obtain the process list created by CSRSS (which means all the processes, in theory), based on user-mode data only. This technique was once described by the Diablo reseacher, and described here.
As for now, all the aforementioned mechanisms can be considered functional (it is possible to found at least one source code illustrating how the given technique works in practice). As can be seen, even in case of a problem as easy as listing the active processes, it is possible to find many equivalent (not in a strict sense) solutions. However, the above list might be incomplete, since it contains only the ideas that occured to me personally. As always, you should feel encouraged to share new ideas in the comments! ;>
As we already know how one can implement a periodical process list updating, I am going to describe other ways of process list management, that do not require any active performance (like sending specific requests to the system from time to time), in the future. After introducing other ways of monitoring existing applications to the reader, I will show how to alter the TaskMgr.exe memory properly.