A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about user-mode development.
 #936  by Evilcry
 Thu Apr 29, 2010 7:50 am
Hi,

just wanted to share with kernelmode.info my latest paper:

After various articles on malware analysis I decided to talk about a little different topic, due to the fact that I'm involved into Kernel Mode Coding the Windows Internals Research become a truly important aspect of my Research. As the title suggests, this time I'm going to Study and Reverse a particular component used by the Windows Debugging System, more precisely a Device Driver involved into Local Kernel Debugging.

Not many people is aware that is possible to perform Local Kernel Debugging, one of the most used Debugging Configurations is the Remote Debugging. Local Kernel Debugging can offer many important vantages, like valuable informations on the Status of the Kernel and Inspect Kmode Components. LKD (Local Kernel Debugging) can be acheived by booting in Debug Mode, both kd and windbg fully supports LKD.

The essential question now is, How the Debugging Engine, let's consider for example Windbg, is able to obtain informations about the kernel by running from usermode?

The reply to this question not only will uncover the problem itself but also will open new interesting questions and possibilities, such as:

#1 - "It's possible to develop an application that can use the same
technology ?"

#2 - "How to access the involved components and what are parameter to
have access?"

To begin the study of this problem, we have in first instance to reproduce the environement necessary to start a Local Debugging Session.

I've used for these tests Windows 7 32-Bits Ultimate Edition and Windbg.

First step is to enable debug mode by running:

bcdedit -debug on then reboot.

At this point we have literally to debug our debugger to be able to reveal the mechanism and component involved in communication between the Debug Engine and Kernel.

In the past Windows Editions, the function used was NtSystemDebugControl, but from Windows Vista to Higher Versions this function is not immediately available.

To trace windbg activities I've used a great tool for professional API Tracing, called Blade API Monitor.

The hypothesis was, if windbg runs at usermode and accesses a kernel component it's obvious that will be used a Device Driver, by assuming true this statement, every application that deals directly with Device Drivers will use:

* CreateFile -> For Driver Opening
* ReadFile / WriteFile -> Device Driver Communication
* DeviceIoControl -> Data exchange between Driver and umode application
* NtSystemDebugControl


After setting the proper filter for these functions, let's run a Local Kernel Debugging Session and watch the results from API Monitor.

When debugger is loaded, we can launch some command, like:

!drvobj
!irpfind
!isr

From API Log emerges an important result, we have two threads:

#- First Thread

DeviceIoControl(...)
DeviceIoControl(...)
CreateFileW(wchar_t* lpFileName = C:\Windows\Fonts\staticcache.dat,...)

#- Second Thread

CreateFileW( wchar_t* lpFileName = C:\Windows\system32\kldbgdrv.sys )
return value -> void* return = 0x00000128

WriteFile(void* hFile = 0x00000128, .., unsigned long nNumberOfBytesToWrite = 0x000031F0 )

CreateFileW(wchar_t* lpFileName = \\.\kldbgdrv)
return value -> void* return = 0x00000170

DeviceIoControl(void* hDevice = 0x00000170)
IOCTL -> unsigned long dwIoControlCode = 0x0022C007
As you can see, from the second thread we obtain a valuable amount of informations.

WinDbg creates a driver called kldbgdrv.sys placed in %\system32\ this file is 0x31F0 long.

Successively this driver is openened and windbg starts to send IOCTL to this driver.

The IO Control Code used is 0x0022C007.

When debugging session finishes, kldbgdrv.sys it's deleted.

To reverse this driver we have obviously to dump it, so the first operation is to locate where is placed and successively carve out this.

The most probable location where can be located are the resources of windbg or kd executables, so let's explore their PE.

Between resources of windbg.exe we can see that the last one called "17476" which contains another subdir called "30583" by opening also this last directory finally appears our wldbgdrv.sys (can be easly detected by watching between strings)

From the starting address of this resource if we add the len of bytes ( nNumberOfBytesToWrite = 0x000031F0) we can easly into a new file kldbgdrv.sys

Now let's reverse this driver.

INIT:00010D1F push offset aKdsystemdebugc ; "KdSystemDebugControl"
INIT:00010D24 lea eax, [ebp+DestinationString]
INIT:00010D27 push eax ; DestinationString
INIT:00010D28 call ds:RtlInitUnicodeString
INIT:00010D2E lea ecx, [ebp+DestinationString]
INIT:00010D31 push ecx ; SystemRoutineName
INIT:00010D32 call ds:MmGetSystemRoutineAddress
INIT:00010D38 mov [ebp+var_1C], eax
INIT:00010D3B cmp [ebp+var_1C], 0
INIT:00010D3F jnz short loc_10D4B
INIT:00010D41 mov eax, STATUS_PROCEDURE_NOT_FOUND
INIT:00010D46 jmp loc_10DF5

this is a really interesting piece of code, here the driver attempts to obtain the Routine Address of the function KdSystemDebugControl()

INIT:00010D4B mov edx, [ebp+DriverObject]
INIT:00010D4E mov dword ptr [edx+34h], offset sub_10A10 ;DriverUnload
INIT:00010D55 mov eax, [ebp+DriverObject]
INIT:00010D58 mov dword ptr [eax+38h], offset sub_10A50 ;DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_10A50
INIT:00010D5F mov ecx, [ebp+DriverObject]
INIT:00010D62 mov dword ptr [ecx+40h], offset sub_10A50 ;DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_10A50
INIT:00010D69 mov edx, [ebp+DriverObject]
INIT:00010D6C mov dword ptr [edx+70h], offset sub_10A80 ;DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_10A80
INIT:00010D73 push offset aDeviceKldbgdrv ; "\\Device\\kldbgdrv"
INIT:00010D78 lea eax, [ebp+DeviceName]
INIT:00010D7B push eax ; DestinationString
INIT:00010D7C call ds:RtlInitUnicodeString
..
INIT:00010D96 call ds:IoCreateDevice

Here the device it's created \\Device\\kldbgdrv and

There are also four MajorFunctions associations:

DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_10A10;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_10A50; // IofCompleteRequest(Irp, 0)
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_10A50; // IofCompleteRequest(Irp, 0)
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_10A80; // Suddenly Reversed
Let's check the latest Dispatch Routine:

PAGE:00010AEE push edx
PAGE:00010AEF push eax ; PrivilegeValue
PAGE:00010AF0 call ds:SeSinglePrivilegeCheck
PAGE:00010AF6 movzx ecx, al
PAGE:00010AF9 test ecx, ecx
PAGE:00010AFB jnz short loc_10B09
PAGE:00010AFD mov [ebp+var_3C], STATUS_ACCESS_DENIED
PAGE:00010B04 jmp loc_10C5B
PAGE:00010B09 mov [ebp+var_4], 0
PAGE:00010B10 mov edx, [ebp+var_30]
PAGE:00010B13 mov [ebp+var_48], edx
PAGE:00010B16 cmp [ebp+var_48], 22C007h ; IOCTL = 22C007h
PAGE:00010B1D jz short loc_10B24
PAGE:00010B1F jmp loc_10C2B


When the IOCTL = 22C007h it's sent the first operation is to check if the action has the proper privileges "SeSinglePrivilegeCheck", successively this dispatch routine validates and sanitizes parameters sent with the IOCTL, by using MmUserProbeAddress and ProbeForWrite.

Finally we can say that kldbgdrv.sys works as wrapper for KdSystemDebugControl.

This function belongs to NtSystemDebugControl but can be accessed only at kernel mode.

Here it's prototipe:

NTSTATUS
NTAPI
KdSystemDebugControl(
SYSDBG_COMMAND Command,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
PULONG ReturnLength,
KPROCESSOR_MODE PreviousMode
);
_SYSDBG_COMMAND it's an enum, let's suppose we want SysDbgReadVirtual we have the corresponding struct:

typedef struct _SYSDBG_VIRTUAL
{
PVOID Address;
PVOID Buffer;
ULONG Request;
} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL;
At this point we have all elements to use with success kldbgdrv.sys, that means the possibility to have access to the kernel from an user mode application.

A little resume here:

=> Systems need to be in Debug Mode
=> SeDebugPrivilege Enabled
=> Access to \\Device\\kldbgdrv
=> SYSDBD Commands can be sent via DeviceIoControl by using IOCTL 0x22C007

Thanks goes to:

Ivanlef0u that produced a great blog post on how KdSystemDebugControl can be used.
http://www.ivanlef0u.tuxfamily.org/?p=382

Deroko for his support ;)

See you to the next post.. :)
Giuseppe 'Evilcry' Bonfa
 #938  by EP_X0FF
 Thu Apr 29, 2010 8:40 am
Hi,

thanks for sharing, it is very interesting.

So basically I have one question: as you stated KdSystemDebugControl works like NtSystemDebugControl (which was used by WinDBG in past for R/W operations) - is it possible to write something at kernel memory using request to kldbgdrv device, correct? :) (we assume, that some program forced system to be in debug mode, rebooted machine and enabled required privlege)

Regards.
 #939  by Evilcry
 Thu Apr 29, 2010 9:02 am
Hi,

Thank you :)

Your assumptions are correct :D

The usage of KdSystemDebugControl belongs to a class of dbgeng.dll called LocalLiveKernelTargetInfo, that has the following members
LocalLiveKernelTargetInfo::CheckLowMemory(void)
LocalLiveKernelTargetInfo::DebugControl(_SYSDBG_COMMAND,void *,ulong,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::GetDescription(ushort *,ulong,ulong *)
LocalLiveKernelTargetInfo::GetTargetContext(ThreadInfo *,unsigned __int64,void *)
LocalLiveKernelTargetInfo::GetTargetKdVersion(_DBGKD_GET_VERSION64 *)
LocalLiveKernelTargetInfo::InitDriver(void)
LocalLiveKernelTargetInfo::Initialize(void)
LocalLiveKernelTargetInfo::LocalLiveKernelTargetInfo(void)
LocalLiveKernelTargetInfo::ReadBusData(ulong,ulong,ulong,ulong,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadControl(ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadIo(ulong,ulong,ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadMsr(ulong,unsigned __int64 *)
LocalLiveKernelTargetInfo::ReadPhysical(unsigned __int64,void *,ulong,ulong,ulong *)
LocalLiveKernelTargetInfo::ReadVirtual(ProcessInfo *,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WaitForEvent(ulong,ulong,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteBusData(ulong,ulong,ulong,ulong,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteControl(ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteIo(ulong,ulong,ulong,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteMsr(ulong,unsigned __int64)
LocalLiveKernelTargetInfo::WritePhysical(unsigned __int64,void *,ulong,ulong,ulong *)
LocalLiveKernelTargetInfo::WriteVirtual(ProcessInfo *,unsigned __int64,void *,ulong,ulong *)
LocalLiveKernelTargetInfo::`scalar deleting destructor'(uint)
LocalLiveKernelTargetInfo::~LocalLiveKernelTargetInfo(void)
In some day I'll code some application around this =)

Have a nice Day!
 #1043  by Evilcry
 Sun May 09, 2010 5:40 am
Hi,

Finally a little application, based on the code of ivanlef0u, that works as kernel memory dumper

kdsupport.h
Code: Select all
#define IOCTL 0x22C007
#define DGBDEV "\\\\.\\kldbgdrv"
#define DBGSERVICE "kldbgdrv"

typedef enum _SYSDBG_COMMAND
{
    SysDbgQueryModuleInformation = 0,
    SysDbgQueryTraceInformation = 1,
    SysDbgSetTracepoint = 2,
    SysDbgSetSpecialCall = 3,
    SysDbgClearSpecialCalls = 4,
    SysDbgQuerySpecialCalls = 5,
    SysDbgBreakPoint = 6,
    SysDbgQueryVersion = 7,
    SysDbgReadVirtual = 8,
    SysDbgWriteVirtual = 9,
    SysDbgReadPhysical = 10,
    SysDbgWritePhysical = 11,
    SysDbgReadControlSpace = 12,
    SysDbgWriteControlSpace = 13,
    SysDbgReadIoSpace = 14,
    SysDbgWriteIoSpace = 15,
    SysDbgReadMsr = 16,
    SysDbgWriteMsr = 17,
    SysDbgReadBusData = 18,
    SysDbgWriteBusData = 19,
    SysDbgCheckLowMemory = 20,
    SysDbgEnableKernelDebugger = 21,
    SysDbgDisableKernelDebugger = 22,
    SysDbgGetAutoKdEnable = 23,
    SysDbgSetAutoKdEnable = 24,
    SysDbgGetPrintBufferSize = 25,
    SysDbgSetPrintBufferSize = 26,
    SysDbgGetKdUmExceptionEnable = 27,
    SysDbgSetKdUmExceptionEnable = 28,
    SysDbgGetTriageDump = 29,
    SysDbgGetKdBlockEnable = 30,
    SysDbgSetKdBlockEnable = 31,
    SysDbgRegisterForUmBreakInfo = 32,
    SysDbgGetUmBreakPid = 33,
    SysDbgClearUmBreakPid = 34,
    SysDbgGetUmAttachPid = 35,
    SysDbgClearUmAttachPid = 36,
} SYSDBG_COMMAND;

typedef struct _KLDBG
{
	SYSDBG_COMMAND DbgCommandClass;
	PVOID DbgCommand;
	DWORD DbgCommandLen;
}KLDBG, * PKLDBG;

typedef struct _SYSDBG_VIRTUAL
{
    PVOID Address;
    PVOID Buffer;
    ULONG Request;
} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL;
kdsubmarine.c
Code: Select all
/* KDSubmarine v 1.0 is a PoC on KdSystemDebugControl based on ivanlef0u PoC
 *
 *	Author: Giuseppe 'Evilcry' Bonfa'
 *	E-Mail: evilcry @ gmail . com
 *	Website: http://www.evilcodecave.blogspot.com
 *			 http://www.evilcry.netsons.org
 *
 *
 */

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x600

#include <windows.h>
#include <winioctl.h>
#include <shlwapi.h>

#include <stdio.h>
#include <stdlib.h>

#include "kdsupport.h"

#pragma comment(lib,"advapi32.lib")
#pragma comment(lib,"shlwapi.lib")

void hexdump(unsigned char *data, unsigned int amount) {
   unsigned int      dp, p;
   const char        trans[] =
      "................................ !\"#$%&'()*+,-./0123456789"
      ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm"
      "nopqrstuvwxyz{|}~...................................."
      "....................................................."
      "........................................";

   for (dp = 1; dp <= amount; dp++)  {
      printf ("%02x ", data[dp-1]);
      if ((dp % 8) == 0)
         printf (" ");
      if ((dp % 16) == 0) {
         printf ("| ");
         p = dp;
         for (dp -= 16; dp < p; dp++)
            printf ("%c", trans[data[dp]]);
         printf ("\n");
      }
   }
   if ((amount % 16) != 0) {
      p = dp = 16 - (amount % 16);
      for (dp = p; dp > 0; dp--) {
         printf ("   ");
         if (((dp % 8) == 0) && (p != 8))
            printf (" ");
      }
      printf (" | ");
      for (dp = (amount - (16 - p)); dp < amount; dp++)
         printf ("%c", trans[data[dp]]);
   }
   printf ("\n");
   return ;
}


void KDumper(PUCHAR pBuff, ULONG Len)
{
	HANDLE hFile;
	DWORD numberOfBytesWritten;


	hFile = CreateFileA("kdump.bin", GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile == INVALID_HANDLE_VALUE)
	{
		printf("Unable to create kdump.bin \n");
		return;
	}

	if(WriteFile(hFile, pBuff, Len, &numberOfBytesWritten, NULL))
	{
		printf("Unable to Write kdump.bin\n");
		CloseHandle(hFile);
		return;
	}

	printf("kdump.bin correctly written \n");
	CloseHandle(hFile);
}


void KernelDumper(HANDLE hDevice) //Kernel Dump
{
	KLDBG kldbg;
	SYSDBG_VIRTUAL Virtual;
	int userInput;
	ULONG address, Len;
	PUCHAR pBuff;
	DWORD BytesReturned;

	printf("Insert Address that you want to Dump: ");
	scanf_s("%x",&address);
	printf("\nInsert length: ");
	scanf_s("%x",&Len);

	kldbg.DbgCommand = &Virtual;
	kldbg.DbgCommandClass = SysDbgReadVirtual;
	kldbg.DbgCommandLen = sizeof(Virtual);

	pBuff = (PUCHAR)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,Len);

	Virtual.Address = (PVOID)address;
	Virtual.Buffer = pBuff;
	Virtual.Request = Len;

	if(DeviceIoControl(hDevice, IOCTL, &kldbg, sizeof(kldbg), &Virtual, sizeof(Virtual), &BytesReturned, NULL) == 0 )
	{
		printf("Unable to communicate with the driver \n");
		return;
	}

	printf("Do you want to Dump Kernel Memory? (1)Yes - (0)No: ");
	scanf_s("%d",&userInput);

	if((userInput == 0))
		hexdump(pBuff,Len);
	else
		KDumper(pBuff, Len);

	HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, pBuff);
}

BOOL GainPrivileges(void)
{
	BOOL privGain = false;
	HANDLE hToken;
	LUID luid;
	TOKEN_PRIVILEGES tokenPrivileges;

	privGain = OpenProcessToken(
								GetCurrentProcess(),
								TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
								&hToken
								);
	if (privGain)
	{
		privGain = LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &luid);

		if (privGain)
		{
			tokenPrivileges.PrivilegeCount = 1;
			tokenPrivileges.Privileges[0].Luid = luid;
			tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

			privGain = AdjustTokenPrivileges(
											  hToken,
											  false,
											  &tokenPrivileges,
											  sizeof(tokenPrivileges),
											  NULL,
											  NULL
											  );
		}
	}

	if(hToken)
		CloseHandle(hToken);

	return(privGain);
}

DWORD CheckDebugBoot(void)
{
	HKEY hKey;
	CHAR BootOptions[1024] = {0};
	DWORD Len = sizeof(BootOptions) - sizeof(CHAR);
	DWORD checkStatus = NULL;

	checkStatus = RegOpenKeyExA(
								HKEY_LOCAL_MACHINE,
								"System\\CurrentControlSet\\Control",
								NULL,
								KEY_QUERY_VALUE,
								&hKey
								);

	if (checkStatus != ERROR_SUCCESS)
	{
		printf(" Can't Verify if System is in Debug Mode \n");
		return(0);
	}

	checkStatus = RegGetValueA(
								hKey,
								NULL,
								"SystemStartOptions",
								RRF_RT_REG_SZ,
								NULL,
								BootOptions,
								&Len
								);


	if (checkStatus != ERROR_SUCCESS)
	{
		printf(" Can't Verify if System is in Debug Mode \n");
		return(0);
	}

	if ( StrStrIA(BootOptions,"DEBUG") == NULL )
	{
		RegCloseKey(hKey);
		return(0);
	}

	RegCloseKey(hKey);

	return(1);
}

DWORD StartSCService(LPCTSTR Service)
{
	SC_HANDLE hSCManager;
	SC_HANDLE hSCService;

	hSCManager = OpenSCManager(
								NULL,
								NULL,
								SC_MANAGER_ALL_ACCESS
								);

	if (hSCManager == NULL)
	{
		printf("Unable to open Service Control Manager \n");
		return(0);
	}

	hSCService = OpenService(
							  hSCManager,
							  Service,
							  SERVICE_START
							  );

	if (hSCService == NULL )
	{
		printf("Unable to open Service Manager \n");
		CloseServiceHandle(hSCManager);
		return(0);
	}

	if(StartService(hSCService, 0, NULL) == 0 && GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
	{
		printf("Service is Already Running \n");
		CloseServiceHandle(hSCManager);
		CloseServiceHandle(hSCService);
		return(0);
	}

	CloseServiceHandle(hSCManager);
	CloseServiceHandle(hSCService);
	return(1);
}

void KernelSubmarine(HANDLE hDevice) // Will be used to collect various aother functions in future
{
	KernelDumper(hDevice);
}


int main(void)
{
	HANDLE hDevice;

	printf("+------------------------------------------------------+\n");
	printf("+---KDSubmarine by Giuseppe 'Evilcry' Bonfa 2010-------+\n");
	printf("+------------------------------------------------------+\n");


	if(!GainPrivileges())
	{
		printf("Can't Enable Privileges \n");
		return(0);
	}

	if (CheckDebugBoot() == 0)
	{
		printf(" System need to be Booted in Debug Mode \n");
		return(0);
	}

	if(StartSCService(DBGSERVICE)==0)
	{
		printf("Unable to open Service Control Manager \n");
		return(0);
	}

	hDevice = CreateFileA(
							DGBDEV,
							GENERIC_READ | GENERIC_WRITE,
							FILE_SHARE_READ | FILE_SHARE_WRITE,
							NULL,
							OPEN_EXISTING,
							FILE_ATTRIBUTE_NORMAL,
							NULL
							);

	if ( hDevice == INVALID_HANDLE_VALUE )
	{
		printf("Unable to open Kernel Local Drive Debugger \n");
		return(0);
	}

	printf("Kernel Local Drive Debugger correctly loaded! \n");

	KernelSubmarine(hDevice);

	CloseHandle(hDevice);

	getchar();

	return(EXIT_SUCCESS);
}
Have a nice Day,
Giuseppe 'Evilcry' Bonfa
 #1045  by Evilcry
 Sun May 09, 2010 11:29 am
Hi,

I did not tested it against AVs but I suppose that surely will cause at least a popup due to classical Driver Loading approach,
unconventional approaches should work good if stealthness is researched ( FltMgr could be one of these) :)

Regards,
Evilcry