When software is good enough

kao

Microsoft, Google and Apple make software for everybody. Millions of users run this software every day. It must be stable and user friendly, so that Aunt Judy and Average Farmer Joe can use it. If it crashes, clueless user can't do much about it - and that's bad. That's why these companies spend thousands of hours in testing and improving usablility.

On the other hand, reversers make tools. A specialized software for solving small and nasty problems, like hiding debugger, defeating specific protection or bypassing some authorization check. Tools are made by a reverser for a reverser, so there are completely different expectations for them. Nobody expects that today's DNGuard unpacker will work with next year's DNGuard binaries, or that DRM authors won't change their encryption mechanisms.

That's why reversers make tools that are just "good enough".

Olly, Confuser and de4dot

Funny thing happens when reversing tools suddenly become extremely popular. Newbies start using them, ordinary users start using them - and the expectations change. Suddenly the author is overwhelmed with extremely helpful "bug reports" like "cannot unpack latest reactor" or "obfuscation fails for my application". It's annoying, wastes reverser's time and is not helpful in any way. Therefore I totally understand 0xd4d's reaction:

There's no support. Don't email me if you can't use it or if it fails to deobfuscate a file obfuscated with an updated obfuscator.

Instead, try to update de4dot yourself. It's a lot easier than you think. If you can't, search the Internet and you should find a couple of forums where you can ask your question.

TitanHide is good enough

Earlier this month I made few posts about bugs in TitanHide. Are these real bugs? Yes. Is it important to fix them? Not really. Let's face it - there are literally dozens of ways to detect TitanHide. But until commercial protectors start doing that, nobody cares.

TitanHide works and does its job well - that's all that matters. 🙂

The two bugs I mentioned earlier

First bug was a confusion about CONTEXT_DEBUG_REGISTERS flags. You see, CONTEXT_DEBUG_REGISTERS is defined as

#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // DB 0-3,6,7

which is quite unexpected. 🙂 So, the code

OriginalContextFlags = Context->ContextFlags;
Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
...
NTSTATUS ret = Undocumented::NtSetContextThread(ThreadHandle, Context);

was accidentally removing CONTEXT_i386 flag from ContextFlags. Such call to should fail, I'm pretty sure it did fail in some cases in my VMWare, but in real world it works just fine.

Second bug is in checking if CONTEXT structure is writeable when calling SetThreadContext. Why should it be - SetThreadContext is only reading from it.. So, this pseudo-code lets you defeat TitanHide hardware breakpoint protection with ease:

CONTEXT cont = VirtualAlloc(0, sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
cont.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &cont);
cont.Dr0 = 0x00401000;
cont.Dr7 = 0x000f0101;
VirtualProtect(&cont, sizeof(CONTEXT), PAGE_READONLY, &dummy);
SetThreadContext(hThread, &cont);

Again, it's a small bug, nobody is abusing it yet, so there is no real reason to fix it.

Writing good drivers is really hard, part 2

kao

Mr. eXoDia was very quick in fixing the bug I described few days ago.. That's a great work! 🙂

This is part of his new code:

...
if (IsHidden)
{
	Log("[TITANHIDE] NtSetContextThread by %d\n", pid);
	__try
	{
		ProbeForWrite(&Context->ContextFlags, sizeof(ULONG), 1);
		OriginalContextFlags = Context->ContextFlags;
		Context->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return Undocumented::NtSetContextThread(ThreadHandle, Context);
	}
}
NTSTATUS ret = Undocumented::NtSetContextThread(ThreadHandle, Context);
...

There are 2 bugs hiding in this short snippet, can you spot them?

Hints

Bug #1 is old and breaks the functionality of SetContextThread. It's much easier to spot it in disassembly than in source code.
Bug #2 makes it easy to bypass TitanHide. It was introduced in this commit.

Let the analysis begin! I'm waiting for your comments.. 🙂

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. 😉