Malware development trick - part 34: Find PID via WTSEnumerateProcesses. Simple C++ example

Hello, cybersecurity enthusiasts and white hackers!

hack

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

hack

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>

hack

hack

hack

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