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

Control Flow Guard in Windows 8.1 and VS2015

kao

Introduction

Control Flow Guard (CFG) is less-known security feature in Microsoft Windows 8.1+. It is designed to prevent exploitation of indirect calls in executables.
For CFG protection to be effective, it must be enabled in both compiler/linker and operating system.

As for operating systems, Windows 8.1 Update 3 (released in November) and Windows 10 have enabled CFG. Older versions of Win8.1 had support for CFG, but it was disabled.

Currently, none of officially released versions of Visual Studio have support for CFG. In the preview version of VS2015, you can enable it manually by:

  1. Project Properties|Configuration Properties|C/C++|Command Line|Additional Options -> add /d2guard4
  2. Project Properties|Configuration Properties|Linker|Command Line|Additional Options -> add /guard:cf

Then build your project and you're done.

How it works

When compiler is about to emit indirect call like

call esi

it checks if Control Flow Guard is enabled. If it is, the following sequence is emitted instead:

mov     ecx, esi              ; Target
call    @_guard_check_icall@4 ; _guard_check_icall(x)
call    esi

When linker generates executable with enabled Control Flow Guard, it creates special entries in LoadConfig table.

.rdata:00418B70 __load_config_used 
.rdata:00418B70    dd 5Ch                  ; Size
.rdata:00418B74    dd 0                    ; Time stamp
.rdata:00418B78    dw 2 dup(0)             ; Version: 0.0
.rdata:00418B7C    dd 0                    ; GlobalFlagsClear
.rdata:00418B80    dd 0                    ; GlobalFlagsSet
.rdata:00418B84    dd 0                    ; CriticalSectionDefaultTimeout
.rdata:00418B88    dd 0                    ; DeCommitFreeBlockThreshold
.rdata:00418B8C    dd 0                    ; DeCommitTotalFreeThreshold
.rdata:00418B90    dd 0                    ; LockPrefixTable
.rdata:00418B94    dd 0                    ; MaximumAllocationSize
.rdata:00418B98    dd 0                    ; VirtualMemoryThreshold
.rdata:00418B9C    dd 0                    ; ProcessAffinityMask
.rdata:00418BA0    dd 0                    ; ProcessHeapFlags
.rdata:00418BA4    dw 0                    ; CSDVersion
.rdata:00418BA6    dw 0                    ; Reserved1
.rdata:00418BA8    dd 0                    ; EditList
.rdata:00418BAC    dd offset ___security_cookie ; SecurityCookie
.rdata:00418BB0    dd offset ___safe_se_handler_table ; SEHandlerTable
.rdata:00418BB4    dd 3                    ; SEHandlerCount
.rdata:00418BB8    dd offset ___guard_check_icall_fptr ; GuardCFCheckFunctionPointer
.rdata:00418BBC    dd 0                    ; Reserved2
.rdata:00418BC0    dd offset ___guard_fids_table ; GuardCFFunctionTable
.rdata:00418BC4    dd 2Bh                  ; GuardCFFunctionCount
.rdata:00418BC8    dd 3500h                ; GuardFlags

BTW, IDA 6.6+ correctly analyzes LoadConfig tables. 🙂

___guard_fids_table is array of all functions defined in the EXE:

.rdata:0041214C ___guard_fids_table 
.rdata:0041214C    dd rva _dynamic_initializer_for____badioinfo__ ; DATA XREF: .rdata:00418BC0o
.rdata:00412150    dd rva ?foo@@YAXH@Z     ; foo(int)
.rdata:00412154    dd rva ?bar@@YAXH@Z     ; bar(int)
.rdata:00412158    dd rva sub_401100
.rdata:0041215C    dd rva sub_401190
.rdata:00412160    dd rva _mainCRTStartup
...

___guard_check_icall_fptr is a pointer to GuardCFCheckFunction. By default it points to simple "retn" instruction.

.rdata:00412104 ___guard_check_icall_fptr dd offset nullsub

So, this executable will run without any problems on older OS.

But when a newer OS loads such executable, it will overwrite pointer to GuardCFCheckFunction with pointer to ntdll!LdrpValidateUserCallTarget:
Control Flow Guard on Win 8.1

Now every indirect call from this module will be checked by LdrpValidateUserCallTarget before execution. If called address is not present in GuardCFFunctionTable, OS will terminate process with:

Exception C0000409 (STACK_BUFFER_OVERRUN) - application was unable to process exception

What does it mean for reversers

As mj0011 said on Twitter

Control Flow Guard is awesome! Wish it could completely enable in RTM.@NTarakanov @JohnLaTwC @j00ru @WTFuzz

Maybe I'm not that excited but still think that CFG is a very useful feature.

List of all procedures in EXE

You don't need to guess anymore if this is code or data, EXE comes with a nice list of all functions.

One breakpoint to rule them all

No more guessing which bloody indirect call is the one you're interested in. Just put breakpoint on GuardCFCheckFunction and any indirect call will break. You even have an option to break on all indirect calls in EXE (putting breakpoint in EXE) or all modules (putting breakpoint in NTDLL). Hell yeah!

Packing your executable can break CFG

If your protector does not support CFG properly, your EXE will not work at all. Or it will work, but will not benefit from CFG. For example, packing my test EXE with standard UPX 3.91 produces an EXE file that will work in all OS until Win7, but will not run on Win8/Win10.

Simple workaround is to zero out LoadConfiguration directory RVA/Size in PE header, or to remove IMAGE_DLL_CHARACTERISTICS_GUARD_CF flag (value: 0x4000) from DllCharacteristics field in PE Optional Header.

Currently I'm not aware of any common protector that would correctly support CFG.

Pay more attention with patching EXE

If you're hooking some functions by overwriting entries in vtable (quite a few game cheats do that), you must pay special attention to CFG. You can either update GuardCFFunctionTable or remove CFG support from the patched module using one of the methods mentioned above.

Further reading

Visual Studio 2015 Preview: Work-in-Progress Security Feature
Exploring Control Flow Guard in Windows 10
Windows New Security Features - Control Flow Guard
How Control Flow Guard Drastically Caused Windows 8.1 Address Space and Behavior Changes
http://deroko.phearless.org/cfgicall.txt

Control Flow Guard enabled file for testing: https://www.mediafire.com/?3dbg7wyihbyn8pj

Improved CFF Explorer

kao

CFF Explorer is another invaluable tool for .NET reversers. Unfortunately it is closed-source and is not actively maintained anymore.

One of the most annoying problems is that it cannot correctly process .NET metadata in some assemblies protected by ConfuserEx (and few other protectors).
CFF shows garbage
As you can see, Module data make no sense and Methods also look weird.

Cause of the problem

The problem is caused by obscure and undocumented field in Metadata Table Stream. DNLib is one of the very few tools/libraries that properly supports it:

/// 
/// MDStream flags
/// 
[Flags]
public enum MDStreamFlags : byte {
	/// #Strings stream is big and requires 4 byte offsets
	BigStrings = 1,
	/// #GUID stream is big and requires 4 byte offsets
	BigGUID = 2,
	/// #Blob stream is big and requires 4 byte offsets
	BigBlob = 4,
	/// 
	Padding = 8,
	/// 
	DeltaOnly = 0x20,
	/// Extra data follows the row counts
	ExtraData = 0x40,
	/// Set if certain tables can contain deleted rows. The name column (if present) is set to "_Deleted"
	HasDelete = 0x80,
}

...

/// 
/// Gets the  bit
/// 
public bool HasExtraData {
	get { return (flags & MDStreamFlags.ExtraData) != 0; }
}

...

ulong valid = validMask;
var sizes = new uint[64];
for (int i = 0; i < 64; valid >>= 1, i++) {
	uint rows = (valid & 1) == 0 ? 0 : imageStream.ReadUInt32();
	if (i >= maxPresentTables)
		rows = 0;
	sizes = rows;
	if (i < mdTables.Length)
		mdTables[i] = new MDTable((Table)i, rows, tableInfos[i]);
}

if (HasExtraData)
	extraData = imageStream.ReadUInt32();

This extraData field is causing us troubles.. Oh, well, it's time to fix it! 🙂

Solution

Since CFF Explorer is closed-source, I had to reverse-engineer parts of it. Then I created a small code cave and added extra code that checks flag value and skips over extraData field, if necessary. If you're interested how exactly it was done, check address 004689CC and added code at 00589800.

CFF works fine
Much better, isn't it?

Download link for patched EXE: Please get latest version from this post

Improved dotNET Tracer

kao

dotNET Tracer is a great tool created by my friend Kurapica. It provides information really useful for analyzing different .NET protections, like which modules are being loaded, which functions are being JIT-compiled, and so on.
DotNET Tracer main window

Unfortunately, it is missing some features and has some small bugs. For example, DisguiserNET.Sample.GUI unpackme by li0nsar3c00l (another friend of mine!) detects dotNET tracer and refuses to run:

Disguiser detects dotNET Tracer

Cause of the problem

It's actually quite simple. dotNET Tracer is using .NET CLR Profiling APIs to gather the information. To do that, it needs to set several environment variables, as you can see in the Form_Main.cs:

ProcessStartInfo ProcessSInfo = new ProcessStartInfo(this.toolStripTextBox_FileName.Text, this.toolStripTextBox_args.Text);
ProcessSInfo.EnvironmentVariables.Add("COR_ENABLE_PROFILING", "1");
ProcessSInfo.EnvironmentVariables.Add("COR_PROFILER", "{7A460847-6B67-4C0C-883E-21B233A6DEF8}");
ProcessSInfo.EnvironmentVariables.Add("COMPLUS_ProfAPI_ProfilerCompatibilitySetting", "EnableV2Profiler");

After profiling dll (system.dll) is initialized, it resets first 2 environment variables, but forgot to reset the 3rd one. Disguiser detects presence of that variable and crashes. Oops.

Solution

After spending some time on both tracer and the unpackme, I'm happy to present a fixed version of dotNET Tracer. So far I have improved:

  1. Created workaround for li0n's anti-profiler trick;
  2. Added logging of "Module load finished" events. This prints imagebase for loaded DLLs and thus makes dumping resources from memory easier;
  3. When you close dotNET Tracer, traced process will be killed automatically.

Disguiser runningjust fine
Now I can spend more time on the unpackme.. 🙂

Download link for binaries: http://www.mediafire.com/?8zfaukefx39i32n
I respect Kurapica's wishes and therefore source code will not be made available.

Hello world!

kao

Hi,
I'm kao, I break stuff.

Decent reverser, average coder.. Not a member of any team, I do what I want whenever I feel like. Most of people consider me a freelancer.

What skills do I have? Well, who needs skills, when you have an opinion? And I have opinion about everything. 🙂 On a more serious note, I've worked with certain private game servers. File format analysis, network protocol analysis, data file unpackers, that kinda stuff. Unpacking executables is another of my favorite disciplines. And, of course, reversing .NET protections.

In this blog I'll be writing about things that interest me - software, reversing, tools and technology in general.

As Linus Torvalds recently said:

I'm just not a huge believer in politeness and sensitivity being preferable over bluntly letting people know your feelings.

If you don't like my way of expression, please f*ck off. Otherwise - welcome to my blog!