Thursday, March 1, 2012

RtlSetProcessIsCritical

The "RtlSetProcessIsCritical" function is one of the ntdll.dll undocumented functions, which can be very useful for software protections and malware.

The function has the following prototype:
int __cdecl RtlSetProcessIsCritical(bool IsCritical, bool* pOld,void* u);

It is easy to see from the function prototype that if the function is called with the first parameter set to true, the process becomes critical and vice versa. For those who don't know, if a process is critical and it crashes or gracefully exits, the whole system is taken down after a blue screen of death is displayed.

The interesting thing about the code in the image above is that if it is run in OllyDbg, the system crashes upon any attempt to terminate the process or OllyDbg, while this does not happen if the code runs without a debugger. The reason behind this behavior is that the "RtlSetProcessIsCritical" function requires the debug privilege, SeDebugPrivilege, to come into effect and OllyDbg passes this privilege down to its debuggees. This can be very useful for anti-debugging, since any failed debugging attempt e.g. failure to handle an exception due to the author-defined unhandled exception filter not being called, would result into taking down the whole system.


I have seen some anti-anti-debug plugins, which patch OllyDbg process memory to prevent it from acquiring the debug privilege and subsequently depriving debuggees from inheriting that privilege and becoming critical. To circumvent those plugins, we can explicitly acquire that privilege by calling the "AdjustTokenPrivileges" function with the appropriate parameters. The only drawback here is that any crash to our process or any graceful termination attempt would result in a blue screen of death. We can take care of this by patching the "ZwTerminateProcess" function or any similar technique.


Some demos to play with can be found here.

Side note:
The "RtlSetProcessIsCritical" function is just a wrapper of the "ZwSetInformationProcess" function with the "ProcessInformationClass" parameter set to 0x1D.

In response to some malware, i have created an OllyDbg plugin to handle such situations. The plugin simply calls the "ZwSetInformationProcess" to detox (remove criticality of) the process being debugged and thus safely debugging it without caring about the more likely debugging crashes.

Version 0.1 of the plugin can be found here.

Version 0.2 of the plugin can be found here.

The source code can be found here.

Any comments or ideas are very welcome.

You can follow me on Twitter @waleedassar 

4 comments:

  1. Wrote an article on this several years ago (back in the early XP days).

    Back then (and this might still true), what actually happened was that smss.exe was signaled upon critical process termination and then proceeded to invoke NtRaiseHardError that in turn BSODs the box.

    So to by-pass it, just terminate smss (you need to termiante some other processes in some correct order if i recall correctly, but it can be done).

    If you want to garantuee a bsod, you are better off just calling NtRaiseHardError yourself.

    ReplyDelete
    Replies
    1. Bypassing that is easy. RtlSetProcessIsCritical is nothing but a wrap up of the "ZwSetInformationProcess" function with the "ProcessInformationClass" parameter set to ProcessBreakOnTermination 0x1D. We can then call ZwSetInformationProcess after acquiring a handle to the naughty process.
      Otherwise, we can just create a remote thread into the process that tries to deprive it from the SeDebugPrivilege.

      Delete
    2. Regarding NtRaiseHardError, yes it can give a BSOD. But this can not be used as anti-debug, since the call requires the SeShutdownPrivilege to succeed and no debuggers try to acquire that privilege.

      Delete
    3. #1 Yea, that too would by-pass it. Just saying that if you are looking at some malware or whatever, its probably easier to just terminate smss. No code required.

      #2 Ah right, its SeShutdownPrivilege and not SeDebugName, my bad. Also, you wouldnt know when to call NtRaiseHardError(), since your process would be terminated at that point =) So im not really sure what I was thinking.

      Guess you could elevate SeShutdownPrivilege if you have SeDebugName set, spawn yourself twice and have both processes wait for each other, and raise the error if either one dies. Like 10x more code but would achieve the same result, a be bit a little bit trickier to disable.

      Read all your articles now, very high quality and they make for some interesting reading :)

      Delete