﷽
Hello, cybersecurity enthusiasts and white hackers!
Today, I just want to focus my research on another malware development trick: enum processes and find PID via WTSEnumerateProcesses
. It is a common technique that can be used by malware for AV evasion also.
WTSEnumerateProcessesA win api
The WTSEnumerateProcessesA
function is a Windows API function that retrieves information about the active processes on a specified terminal server:
BOOL WTSEnumerateProcessesA(
WTS_CURRENT_SERVER_HANDLE hServer,
DWORD Reserved,
DWORD Version,
PWTS_PROCESS_INFOA *ppProcessInfo,
DWORD *pdwCount
);
WTSEnumerateProcessesA
is primarily used for enumerating the processes running on a terminal server and can be useful for diagnostics and troubleshooting.
practical example
The WTS API functions are part of the wtsapi32.dll
, so we need to link against that DLL. In the code snippet,
#pragma comment(lib, "wtsapi32.lib")
is used to link against the library.
Then just create function to enum processes:
int findMyProc(const char * procname) { int pid = 0; WTS_PROCESS_INFOA * pi;
DWORD level = 1; // we want WTSEnumerateProcesses to return WTS_PROCESS_INFO_EX
DWORD count = 0;if (!WTSEnumerateProcessesA(WTS_CURRENT_SERVER_HANDLE, 0, level, &pi, &count))
return 0;for (int i = 0 ; i < count ; i++ ) {
if (lstrcmpiA(procname, pi[i].pProcessName) == 0) {
pid = pi[i].ProcessId;
break;
}
}
WTSFreeMemory(pi);
return pid;
}
As you can see, the logic is pretty simple, just compare process name and get PID.
Full source code is look like this (hack.c
):
/* * process find via WTSEnumerateProcessesA logic * author: @cocomelonc * https://cocomelonc.github.io/malware/2023/07/07/malware-tricks-34.html */ #include <windows.h> #include <stdio.h> #include <wtsapi32.h> #pragma comment(lib, "wtsapi32.lib")
int findMyProc(const char * procname) {
int pid = 0;
WTS_PROCESS_INFOA * pi;DWORD level = 1; // we want WTSEnumerateProcesses to return WTS_PROCESS_INFO_EX
DWORD count = 0;if (!WTSEnumerateProcessesA(WTS_CURRENT_SERVER_HANDLE, 0, level, &pi, &count))
return 0;for (int i = 0 ; i < count ; i++ ) {
if (lstrcmpiA(procname, pi[i].pProcessName) == 0) {
pid = pi[i].ProcessId;
break;
}
}WTSFreeMemory(pi);
return pid;
}
int main(int argc, char* argv) {
int pid = findMyProc(argv[1]);
if (pid > 0) {
printf(“pid = %d\n”, pid);
}
return 0;
}
Keep in mind that this function may not retrieve the process identifier for some types of processes, such as system processes or processes that are protected by certain types of security software. In addition, certain types of security software may block calls to this function entirely. The same applies if you’re running in an environment with restricted permissions.
Also, WTSEnumerateProcesses
requires the SeTcbPrivilege
to be enabled, but this is normally enabled for administrators, but I didn’t check it.
demo
Ok, let’s go to look this trick in action.
Compile it (hack.c
):
x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lwtsapi32
As you can see, you need to link against wtsapi32.lib
when building this program. I am using a GCC-based compiler (like MinGW
), so I can do this by adding -lwtsapi32
to my command.
Then, just run it at the victim’s machine (Windows 10 22H2 x64
in my case):
.\hack.exe <process>
As you can see, it’s worked perfectly, as expected :) =^..^=
As I wrote earlier, in theory, the user must have the Query Information
permission. Also, the calling process must have the SE_TCB_NAME
privilege. If the calling process is running in a user session, the WTSEnumerateProcesses
function only retrieves the process information for the session of the calling process.
In my opinion, if your malware or service run under the Local System you have enough permissions.
Keep in mind that this function may not retrieve the process identifier for some types of processes, such as system processes or processes that are protected by certain types of security software. In addition, certain types of security software may block calls to this function entirely. The same applies if you’re running in an environment with restricted permissions.
Also, maybe this trick can be used to bypass some cyber security solutions, since many systems only detect functions known to many like CreateToolhelp32Snapshot
, Process32First
, Process32Next
. For the same reason, this can be difficult for many malware analysts.
I haven’t seen this trick in the real-life malware and APT attacks yet. I hope this post spreads awareness to the blue teamers of this interesting malware dev technique, and adds a weapon to the red teamers arsenal.
WTSEnumerateProcessesA
Find PID by name and inject to it. “Classic” implementation.
Classic DLL injection into the process. Simple C++ malware
Taking a Snapchot and Viewing Processes
source code in github
This is a practical case for educational purposes only.
Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine
Article Link: Malware development trick - part 34: Find PID via WTSEnumerateProcesses. Simple C++ example. - cocomelonc