W

Wave

Guest
#1
Hello everyone.

In this thread I will be sharing source code for a program I've just written which will allow you to terminate processes which are set as critical; the program works by first checking if the program is critical, and if it is, it'll use the Native API function NtSetSystemInformation to remove the critical status (BreakOnTermination value in the correct ProcessInformationClass - 29 to be precise).

As an addition to this thread, I pushed the code into a C++ DLL which can be used from within the .NET Framework through the usage of Platform Invokation for any .NET developers who may wish to use this (e.g. AM scanners which want to terminate a process which is critical -> would cause BSOD unless they remove the critical status).

If you wish to learn how to set a process as critical, or read the theory details I wrote in the previous threads regarding critical processes, please check the following two threads:
Critical Processes [Theory & C++/C#/VB]
Tutorial - Check if a process is critical or not [C++]

-----------------------------------------------------

Now for the main C++ application:
Code:
#include <Windows.h>
#include <winternl.h>
#include <iostream>
using namespace std;

typedef struct _CLIENT_ID {
    PVOID UniqueProcess;
    PVOID UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef NTSTATUS(NTAPI *pdef_RtlAdjustPrivilege)(ULONG Privilege, BOOLEAN bEnable, BOOLEAN bCurrentThread, PBOOLEAN pbEnabled);

typedef NTSTATUS(NTAPI *pdef_NtSetInformationProcess)(HANDLE ProcessHandle, ULONG ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);

bool enable_priv(int priv_method)
{
    BOOL bResult = FALSE;
    if (priv_method == 1) // use NTAPI
    {
        BOOLEAN bRes;
        LPVOID RtlAdjustPrivilege_addr = GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlAdjustPrivilege");
        if (!RtlAdjustPrivilege_addr)
        {
            cout << "Unable to obtain the address of ntdll!RtlAdjustPrivilege!\n";
        }
        pdef_RtlAdjustPrivilege RtlHandle = (pdef_RtlAdjustPrivilege)RtlAdjustPrivilege_addr;
        if (NT_SUCCESS(RtlHandle(20, TRUE, FALSE, &bRes)))
        {
            cout << "Successfully enabled Debugging Rights (SeDebugPrivilege) via ntdll!RtlAdjustPrivilege!\n";
            bResult = TRUE;
        }
        else
        {
            cout << "Failed to enable Debugging Rights (SeDebugPrivilege) via ntdll!RtlAdjustPrivilege: " << GetLastError() << endl;
            bResult = FALSE;
        }
    }
    else if (priv_method == 2) // use Win32 API
    {
        HANDLE TokenHandle = { 0 };
        TOKEN_PRIVILEGES TokenPriv = { 0 };
        LUID lValueId = { 0 };
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
        {
            cout << "Failed to open the process token: " << GetLastError() << endl;
            bResult = FALSE;
        }
        if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &lValueId))
        {
            cout << "Failed to lookup the privilege value: " << GetLastError() << endl;
            bResult = FALSE;
        }
        TokenPriv.PrivilegeCount = 1;
        TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        TokenPriv.Privileges[0].Luid = lValueId;
        if (AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPriv, sizeof(TokenPriv), NULL, NULL))
        {
            cout << "Successfully adjusted the token privileges via AdjustTokenPrivileges!\n";
            bResult = TRUE;
        }
        else
        {
            cout << "Failed to adjust the token privileges: " << GetLastError() << endl;
            bResult = FALSE;
        }
    }
    return bResult;
}

BOOL WaveIsTheProcessCritical(HANDLE ProcessHandle)
{
    BOOL bCritical = FALSE; // used to store value to represent if process is critical or not
    BOOL bCheck = IsProcessCritical(ProcessHandle, &bCritical); // check and send result to the bCritical boolean variable
    if (!bCheck) // fail
    {
        cout << "The function failed: " << GetLastError() << endl;
        getchar(); // allow us to read the error before closing
        return 0; // exit program
    }
    if (!bCritical) // process isn't critical
    {
        return FALSE; // return false
    }
    return TRUE; // success and critical ;)
}

BOOL RemoveCritical(HANDLE ProcessHandle)
{
    FARPROC fpTargetAddr = (FARPROC)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtSetInformationProcess");
    if (!fpTargetAddr)
    {
        return FALSE;
    }
    ULONG ulStop = 0;
    pdef_NtSetInformationProcess NtHandle = (pdef_NtSetInformationProcess)fpTargetAddr;
    NTSTATUS NtRet = NtHandle(ProcessHandle, 0x1D, &ulStop, sizeof(ULONG));
    if (!NT_SUCCESS(NtRet))
    {
        return FALSE;
    }
    if (WaveIsTheProcessCritical(ProcessHandle))
    {
        return FALSE;
    }
    return TRUE;
}

int main()
{
    enable_priv(2); // enable debugging rights
    HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 6496); // open handle to target process
    if (ProcessHandle) // we acquired the handle
    {
        if (WaveIsTheProcessCritical(ProcessHandle)) // check if process is critical with handle
        {
            if (RemoveCritical(ProcessHandle)) // attempt to remove handle
            {
                // success
                cout << "Process is no longer critical" << endl;
                TerminateProcess(ProcessHandle, 0);
            }
            else
            {
                // fail to inject
                cout << "The process is still critical" << endl;
            }
        }
        else
        {
            // no need to do anything
            cout << "The process is not critical" << endl;
        }
    }
    else
    {
        // fail to get handle
        cout << "Failed to obtain process handle: " << GetLastError() << endl;
    }
    getchar(); // wait for input to close
    return 0; // close program
}
-----------------------------------------------------

Here is the C++ DLL source code

dllmain.cpp
Code:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <TlHelp32.h>
using namespace std;

typedef NTSTATUS(NTAPI *pdef_NtSetInformationProcess)(HANDLE ProcessHandle, ULONG ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);

DWORD retProcessId(std::string targetProcessName)
{
    HANDLE hProcess;
    PROCESSENTRY32 processEntry32;
    processEntry32.dwSize = sizeof(PROCESSENTRY32);
    hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    do {
        if (!strcmp(processEntry32.szExeFile, targetProcessName.c_str()))
        {
            CloseHandle(hProcess);
            return processEntry32.th32ProcessID;
        }
    } while (Process32Next(hProcess, &processEntry32));
    CloseHandle(hProcess);
    return 0;
}

BOOL WaveIsTheProcessCritical(HANDLE ProcessHandle)
{
    BOOL bCritical = FALSE;
    BOOL bCheck = IsProcessCritical(ProcessHandle, &bCritical);
    if (!bCheck)
    {
        cout << "The function failed: " << GetLastError() << endl;
        getchar();
        return 0;
    }
    if (!bCritical)
    {
        return FALSE;
    }
    return TRUE;
}

BOOL RemoveCritical(HANDLE ProcessHandle)
{
    FARPROC fpTargetAddr = (FARPROC)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtSetInformationProcess");
    if (!fpTargetAddr)
    {
        return FALSE;
    }
    ULONG ulStop = 0;
    pdef_NtSetInformationProcess NtHandle = (pdef_NtSetInformationProcess)fpTargetAddr;
    NTSTATUS NtRet = NtHandle(ProcessHandle, 0x1D, &ulStop, sizeof(ULONG));
    if (!NT_SUCCESS(NtRet))
    {
        return FALSE;
    }
    if (WaveIsTheProcessCritical(ProcessHandle))
    {
        return FALSE;
    }
    return TRUE;
}

extern "C" {
    __declspec(dllexport) BOOL __stdcall WaveRemoveCriticalStatus(LPSTR lpProcessName)
    {
        HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, retProcessId(lpProcessName));
        if (ProcessHandle)
        {
            if (WaveIsTheProcessCritical(ProcessHandle))
            {
                if (RemoveCritical(ProcessHandle))
                {
                    TerminateProcess(ProcessHandle, 0);
                }
                else
                {
                    return FALSE;
                }
            }
            else
            {
                return TRUE;
            }
        }
        else
        {
            return FALSE;
        }
        return TRUE;
    }
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

stdafx.h
Code:
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <winternl.h>
#include <iostream>

// TODO: reference additional headers your program requires here
typedef struct _CLIENT_ID {
    PVOID UniqueProcess;
    PVOID UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
-------------------------------------------------------

VB.NET example source code:
Code:
Imports System.Runtime.InteropServices
Public Class Form1

    <DllImport("WaveCriticalDll", CallingConvention:=CallingConvention.StdCall)>
    Private Shared Function WaveRemoveCriticalStatus(<MarshalAs(UnmanagedType.LPStr)> ByVal lpProcessName As String) As Boolean
    End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If WaveRemoveCriticalStatus(TextBox1.Text) = True Then
            MsgBox("worked")
        Else
            MsgBox("no")
        End If
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Process.EnterDebugMode()
    End Sub
End Class
Make sure that the DLL is in the same file location as the MSIL executable so it can find the DLL for the Platform Invokation when it starts-up. Also make sure to rename the DLL in the <DllImport...> and the function name if you decide to re-name them... No, I'm not kidding, some people honestly copy-paste it without reading a single thing and then wonder why it doesn't work, haha.

-----------------------------------------------------
After the process has been removed from the critical state, I added an extra call to kernel32.dll!TerminateProcess to make the target program terminate; you can remove this if you want, customize to whatever needs...
-----------------------------------------------------

You can download the project files if reading-to-learn/copy-paste isn't good enough for you:
C++ example project: Private file
C++ DLL project: Private file
VB.NET example project: Private file

Please note that the function IsProcessCritical which was used in the above source code is only available from Windows 8.1, therefore if you are on an OS version below Windows 8.1, you'll need to adapt the code to use NtQueryInformationProcess and pass in 29 as the ProcessInformationClass - check the value on BreakOnTermination (1 = critical, 0 = not critical). You will also require debugging rights to use the source code (in the VB.NET example I enabled it during the Form1_Load subroutine).

This is the fourth code-related tutorial I've written today... Hopefully it'll be found useful to someone.

Stay safe,
Wave. ;)
 
W

Wave

Guest
#3
Very nice Wave,i will still stay tuned about your tutorials man(For learning more C++), great !

Best Regards.
I posted some more earlier, I'm not sure if you've seen them already. If you go onto my profile -> Postings -> Find all threads -> now you can see the ones I posted today and some other ones code-related from the past. :)

Thank you for the support!
 

Anker_by

Level 4
Verified
Joined
Jun 23, 2015
Messages
187
OS
Windows 10
Antivirus
Kaspersky
#4
Which version (Editor) you use on Windows platform (10 or 7)?Visual Studio Community?
 

JM Security

Level 31
Verified
Joined
Apr 12, 2015
Messages
2,038
#6
An indespensable thread to read for who wants to start developing a security product, like an Anti-Malware or even an AV. Because force-terminating a malicious process is fundamental for real-time protection, and obviously it is not so easy sometimes to terminate malware processes (like some types of ransomware processes) in a normal way by trying to close the process like with Task Manager.
Thanks for the thread @Wave :)
 
W

Wave

Guest
#7
An indespensable thread to read for who wants to start developing a security product, like an Anti-Malware or even an AV. Because force-terminating a malicious process is fundamental for real-time protection, and obviously it is not so easy sometimes to terminate malware processes (like some types of ransomware processes) in a normal way by trying to close the process like with Task Manager.
Thanks for the thread @Wave :)
Thank you for your kind words! :)

Task Manager will call TerminateProcess which is exported by kernel32.dll however if the process is critical it will require you to shutdown to terminate the process... lol :D Yeah pretty useless there then haha

TerminateProcess will end up calling NtTerminateProcess.

The best method of termination is from kernel-mode via PsLookupProcessByProcessById -> ObOpenObjectByPointer -> now NtTerminateProcess (because NtOpenProcess can be blocked off easily via kernel-mode callback or hooks) :)

I actually wrote a driver to do that here: Anti-Rootkit development [C/C++ - kernel-mode & user-mode] :)

But this could be a good check before sending the PID to the driver to do the actual termination or before using the handle with NtTerminateProcess from user-mode :)
 
Joined
Feb 13, 2017
Messages
1,468
OS
Windows 10
Antivirus
Emsisoft
#8
Thanks for this valuable input you have provided :)
Usually I use AWL and Ladder programming languages because of my job (PLC programmer) but always...always learning in life ;)