Blocking Windows Processes
Sometimes, there are processes that spinup automatically on Windows that you don’t want to run. Maybe its because you have a number of HDDs attached as data storage, and every once in a while some process starts spinning them all up. Maybe these disks are really loud, and you find it annoying that suddenly they start spinning up when you aren’t accessing them.
This is exactly the behaviour I was getting from Windows Telemetry, specifically the compattelrunner.exe
file. A few years ago, I wrote a simple python script that I called “telemetrykiller.py”. Here it is in all its glory:
import os
import time
while True:
# Return value when both are dead is 128, else 0.
ret = os.system("taskkill /im SearchUI.exe /im compattelrunner.exe /f")
if not ret:
print(time.asctime())
time.sleep(30)
A seriously impressive script, I know. I would then launch this script as Admin every once in a while, but it was far from automated.
I decided to revisit this problem because I was tired of launching the script, and I didn’t want a python process running all the time. There had to be a better way than polling. I searched around to see if there was some sort of Event queue I could join to see when processes are launched, but then stumbled across this registry key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
How does it work?
The HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
registry key allows you specify debuggers for executables. You add a subkey with your executable, and in that key you can add a REG_SZ
value with the name “debugger”, and the data contains the file/path of your debugger.
I wrote a small program called processblocker.exe
to fill in as that debugger value. Once registered as the debugger, now my program is responsible for launching the executable. So, it simply doesn’t launch it. Process blocked!
The Code
Maintained at: https://github.com/guffre/processblocker/
//compile: cl processblocker.c
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "advapi32.lib")
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
const char OPTIONS_KEY[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\";
const char PROCESSBLOCKER[] = "processblocker.exe";
BOOL CreateKey(LPCSTR IMAGE_KEY);
BOOL DeleteKey(LPCSTR IMAGE_KEY);
VOID ListKeys(LPCSTR KEYNAME);
VOID LogData(int argc, char *argv[]);
void __cdecl main(int argc, char *argv[])
{
LPSTR IMAGE_KEY = NULL;
DWORD keysize;
// If there are two arguments and the second argument is valid (contains ".exe")
if (argc == 3 && strstr(argv[2], ".exe")) {
// Create string of desired registry key. argv[2] is the executable name
keysize = sizeof(OPTIONS_KEY) + strlen(argv[2]) + 2;
IMAGE_KEY = calloc(keysize, sizeof(char));
snprintf(IMAGE_KEY, keysize, "%s%s", OPTIONS_KEY, argv[2]);
// Add a key if the argument is "-a"
if (!strcmp(argv[1], "-a")) {
printf("Creating key: %s\n", IMAGE_KEY);
if (CreateKey(IMAGE_KEY)) {
printf("%s blocking setup was successful.\n", argv[2]);
}
else {
printf("Error setting up blocking for %s\n", argv[2]);
}
goto cleanup;
}
// Remove a key if the argument is "-r" or "-d"
else if ( !(strcmp(argv[1], "-r")) || !(strcmp(argv[1], "-d")) ) {
printf("Deleting key: %s\n", IMAGE_KEY);
if (DeleteKey(IMAGE_KEY)) {
printf("%s unblocking setup was successful.\n", argv[2]);
}
else {
printf("Error unblocking %s. (It might not have been blocked).\n", argv[2]);
}
goto cleanup;
}
}
else if (argc == 2 && !(strcmp(argv[1], "-l")) ) {
ListKeys(OPTIONS_KEY);
return;
}
printf("Usage:\n");
printf("\t(block): %s -a <filename_to_block>\n", argv[0]);
printf("\t(unblock): %s -[rd] <filename_to_unblock>\n", argv[0]);
printf("\t(list): %s -l\n", argv[0]);
// Log the fact the executable was run. Records argv arguments
LogData(argc, argv);
cleanup:
if (IMAGE_KEY)
free(IMAGE_KEY);
}
BOOL CreateKey(LPCSTR IMAGE_KEY) {
BOOL success;
DWORD err;
HKEY hKey = NULL;
success = FALSE;
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, IMAGE_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
err = RegSetValueEx(hKey, "debugger", 0, REG_SZ, PROCESSBLOCKER, sizeof(PROCESSBLOCKER));
if (ERROR_SUCCESS != err)
{
printf("Error setting debugger value for key: %s\n", IMAGE_KEY);
goto cleanup;
}
}
else {
printf("Error creating key: %s\n", IMAGE_KEY);
goto cleanup;
}
success = TRUE;
cleanup:
if (hKey)
RegCloseKey(hKey);
return success;
}
BOOL DeleteKey(LPCSTR IMAGE_KEY) {
BOOL success = FALSE;
if (RegDeleteKeyEx(HKEY_LOCAL_MACHINE, IMAGE_KEY, KEY_WOW64_32KEY, 0) == ERROR_SUCCESS) {
printf("Deleted 32-bit Registry Key: %s\n", IMAGE_KEY);
success = TRUE;
}
if (RegDeleteKeyEx(HKEY_LOCAL_MACHINE, IMAGE_KEY, KEY_WOW64_64KEY, 0) == ERROR_SUCCESS) {
printf("Deleted 64-bit Registry Key: %s\n", IMAGE_KEY);
success = TRUE;
}
return success;
}
VOID ListKeys(LPCSTR KEYNAME) {
HKEY hKey;
HKEY hSubKey;
CHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
DWORD cSubKeys; // number of subkeys
DWORD cValues; // number of values for key
DWORD i, j;
LPSTR data;
DWORD data_size;
CHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
CHAR SUBKEY[MAX_VALUE_NAME];
if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, KEYNAME, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
printf("Error opening key: %s\n", KEYNAME);
return;
}
// Query the OPTIONS_KEY in order to get count of subkeys
RegQueryInfoKey(
hKey, // key handle
NULL, // buffer for class name
NULL, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
NULL, // longest subkey size
NULL, // longest class string
NULL, // number of values for this key
NULL, // longest value name
NULL, // longest value data
NULL, // security descriptor
NULL); // last write time
// Enumerate the subkeys, until RegEnumKeyEx fails.
data = calloc(MAX_VALUE_NAME, sizeof(char));
for (i = 0; i < cSubKeys; i++) {
cbName = MAX_KEY_LENGTH;
if (RegEnumKeyEx(hKey, i, achKey, &cbName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
// Create new string representing the subkey
memset(SUBKEY, 0, sizeof(SUBKEY));
strcat_s(SUBKEY, sizeof(SUBKEY), KEYNAME);
strcat_s(SUBKEY, sizeof(SUBKEY), achKey);
// Open the subkey to enumerate its values
if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, SUBKEY, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS) {
// This pulls out the number of values, as well as the max data size
if (RegQueryInfoKey(hSubKey, NULL, NULL, NULL, NULL, NULL, NULL, &cValues, NULL, &data_size, NULL, NULL) == ERROR_SUCCESS) {
// Make sure we have enough buffer to receive the data
if (data_size > MAX_VALUE_NAME) {
data = realloc(data, data_size); // This is "bad", but program will exit immediately after this return if it fails
if (data == NULL) {
printf("Error allocating memory!\n");
return;
}
}
// Enumerate the key values. We are looking for the "debugger" value
for (j = 0; j < cValues; j++) {
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
memset(data, 0, data_size);
if (RegEnumValue(hSubKey, j, achValue, &cchValue, NULL, NULL, data, &data_size) == ERROR_SUCCESS) {
if ( !(_stricmp("debugger", achValue)) ) {
// Found a debugger value, pull the data portion out:
printf("%s:\n value: %s\n data: %s\n", achKey, achValue, data);
}
}
}
}
else
printf("Error getting info from subkey: %s\n", SUBKEY);
}
else
printf("Error opening subkey: %s\n", SUBKEY);
}
}
if (data)
free(data);
}
VOID LogData(int argc, char *argv[]) {
SYSTEMTIME st;
HANDLE hFile;
BOOL bErrorFlag;
LPSTR dataBuf;
int buf_size;
int i;
GetSystemTime(&st);
hFile = CreateFile("C:\\Windows\\Temp\\processblocker.log", // name of the file
FILE_APPEND_DATA, // open for appending
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create file if it doesn't exist, open if it does
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return;
}
// buf_size is the max size of the buffer. 120 bytes (20 for the date, 100 just because), plus 1 byte for each argument (accounts for the ":" delimiter)
buf_size = 120 + argc;
for (i = 0; i < argc; i++) {
buf_size += strlen(argv[i]);
}
// Integer overflow. Weird for argv... lets log 16kb
if (buf_size <= 0)
buf_size = 1024 * 16;
// Allocate the buffer
dataBuf = calloc(buf_size, sizeof(char));
// Add timestamp and data to buffer. This format string (ASCII) has a length of 20 bytes
snprintf(dataBuf, buf_size, "%04d-%02d-%02d:%02d:%02d:%02d:", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
for (i = 0; i < argc; i++) {
strcat_s(dataBuf, buf_size, argv[i]);
strcat_s(dataBuf, buf_size, ":");
}
strcat_s(dataBuf, buf_size, "\n");
// Write the log
bErrorFlag = WriteFile(
hFile, // open file handle
dataBuf, // start of data to write
strlen(dataBuf), // number of bytes to write
NULL, // number of bytes that were written
NULL); // no overlapped structure
if(dataBuf)
free(dataBuf);
CloseHandle(hFile);
}