Don’t touch this – writing good drivers is really hard

kao

(This mini-entry is dedicated to Mr. eXoDia. He wanted me to write about kernelmode, so here goes..)

Remember Themida 1.0.0.5 that came with a driver? It caused vast majority of BSODs on my PC. Remember the Sony DRM rootkit that came with some CDs? Remember adware that was impossible to remove, because driver was protecting it?

Apparently, even Microsoft noticed the bad quality drivers and has done a lot in recent years to stop the plague. Drivers now need to be signed - and before Microsoft signs them, they must pass plenty of automatic tests.

Big companies still manage to write crap drivers - but that's a topic for another post..

Of course, sometimes reversers need drivers, too. For example, to hide a debugger from the target. Enter TitanHide, an open-source project by Mr. eXoDia:

TitanHide is a driver intended to hide debuggers from certain processes. The driver hooks various Nt* kernel functions (using SSDT table hooks) and modifies the return values of the original functions

It all sounds great and peachy, but how good this driver really is?

Crash, boom, bang

After few minutes of browsing the code, I was already having fun. How about this?
BSOD in TitanHide

Cause of the problem

Take a look at this code:

static NTSTATUS NTAPI HookNtSetContextThread(
	IN HANDLE ThreadHandle,
	IN PCONTEXT Context)
{
	ULONG pid = (ULONG)PsGetCurrentProcessId();
	bool IsHidden = Hider::IsHidden(pid, HideNtSetContextThread);
	ULONG OriginalContextFlags = 0;
	if (Context && IsHidden)
	{
		Log("[TITANHIDE] NtSetContextThread by %d\n", pid);
		OriginalContextFlags = Context->ContextFlags;
		Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
	}
	NTSTATUS ret = Undocumented::NtSetContextThread(ThreadHandle, Context);
	if (Context && IsHidden)
		Context->ContextFlags = OriginalContextFlags;
	return ret;
}

This is a great example how not to implement hooks.

Attacker has full control over the value passed as Context.

  • Lines 1-4 check if we want to hide debugger from this process.
  • Lines 7-8 take the unchecked Context value and stuff some data into it.
  • And only then the data are passed to the original NtSetContextThread function which does proper validation.

Ouch! I'm pretty sure I could turn it into full-featured ring-0 code execution, but that's not the point of the exercise.

But what if the source is not available?

In this case it was really easy to find a bug, because full source was available. When the source is not available, you can use specialized fuzzing tools, like BSODHook or IOCTLBF.

Here's BSODHook output showing the same bug:
BSODHook found the same bug
We can take a detailed look at these tools later, in a different blog post.

Sample executable showing the bug: https://www.mediafire.com/?1191p9mcfbks9sq - try running it in Olly with TitanHide enabled.

P.S. ZIP filesize=666 is accidental, I swear!
P.P.S. One more issue with TitanHide - build 0012 doesn't work on XP anymore. 😉

6 thoughts on “Don’t touch this – writing good drivers is really hard

    1. You're always welcome.

      Just to clarify - this post wasn't meant as a critique of your work. This sort of bug in hooking is really common - and open source makes it easier to demonstrate. 🙂

      1. I'm very glad, this made me come to my senses in terms of kernel development. Immediately started reading some beginner tutorials and bad-practice lists to see if there were more bugs.

  1. Hi, Kao
    Now, I research old version Themida 1.0.0.5 and Winlicen 1.0.0.3 demo version driver. I want make unpackme and try running in Windows XP then get BSODs(oreans.sys) on my computer. Do you know a solution to deal with these old versions?
    I attack old version, please help.Thank you
    {hidden link}

Leave a Reply

  • Be nice to me and everyone else.
  • If you are reporting a problem in my tool, please upload the file which causes the problem.
    I can`t help you without seeing the file.
  • Links in comments are visible only to me. Other visitors cannot see them.

 −  five  =  one