A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #10971  by Brock
 Thu Jan 12, 2012 7:50 am
Keeping things documented with newer driver callbacks for operations is advisable to keep stability and reliability. Maybe this vennemars-alex person wants more control over individual APIs? If so, he/she could bypass KPP/PG without much trouble but at what cost? Next major OS SP update, maybe even a small update disguised as no to minimal kernel modifications. Small things like changing of KPP initialization, more transparent SEH handling, DPC/timer patches? It's nothing but a chain of service pack checks in order to load unsigned drivers and patch kernel mode code which wasn't meant to be patched from 3rd party software.
 #10985  by Vrtule
 Thu Jan 12, 2012 8:11 pm
vennemars-alex: What do you want to do exactly?

I think that "no hooking anymore" is not the exact thruth. Quite many AVs do hooking in Shadow SDT and/or inline hooking inside win32k.sys.
 #11764  by StriderH2
 Wed Feb 22, 2012 11:37 pm
Source Article:
http://bbs3.driverdevelop.com/read.php? ... ode-1.html

I'm trying to make sense of the inputs to run a process from a driver.

The code below was rumored to work on XP Sp2, the author of this code also claims to have run messagebox from a driver.
I'm not sure if this works with an existing process or creates a new one.
Tools used: OSRLOADER, Dbgview

I tested it from Windows XP service pack 3, it doesn't do anything of course.
2. I don't know where in particular to place the DbgPrint code as I cannot understand exactly what the VOID functions do.
3. Does this run a process from a given path, or does it attach to an existing process to run another child process?
4. There's a third body of code in the link above that mentions an imagefilename so that might be the correct one I don't know.

Any help?
=====================================================
To build:
Backup the original ntifs.h file located in your includes directory.
C:\winddk\7600.16385.1\inc\ddk
http://www.mediafire.com/file/ijcl75odhv86wgg/ntifs.h <--- revised ntifs.h, put this in the ddk folder
Note: This will get rid of the structure redefinitions, PEPROCESS,PrkProcess, KAPC, etc.
=================================================
Code: Select all
//************************************************************************
// NTSTATUS UtilInstallUserModeApcForCreateProcess(char* CommandLine, PKTHREAD pTargetThread, PKPROCESS pTargetProcess)
//
// Setup usermode APC to execute a process                                                                     
//************************************************************************/
//#define _WIN32_WINNT 0x400
#include "ntddk.h"
#include <ntifs.h>
char testchar[55]="\\Systemroot\\System32\\calc.exe"; //Probably incorrect

  NTAPI KeInitializeApc(PKAPC Apc,
                        PKTHREAD Thread,
                        CCHAR ApcStateIndex,
                        PKKERNEL_ROUTINE KernelRoutine,
                        PKRUNDOWN_ROUTINE RundownRoutine,
                        PKNORMAL_ROUTINE NormalRoutine,
                        KPROCESSOR_MODE ApcMode,
                        PVOID NormalContext);
           NTAPI KeInsertQueueApc(PKAPC Apc,
                        PVOID SystemArgument1,
                        PVOID SystemArgument2,
                        UCHAR unknown);
typedef enum _KAPC_ENVIRONMENT {
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment
} KAPC_ENVIRONMENT;

//

VOID UtilUserApcCreateProcessKernelRoutine( IN struct _KAPC * Apc , IN OUT PKNORMAL_ROUTINE * NormalRoutine ,
IN OUT PVOID * NormalContext , IN OUT PVOID * SystemArgument1 , IN OUT PVOID * SystemArgument2 )
{
PKEVENT pEvent ;
//KDebugPrint ( 1 ,("%s APC KernelRoutine called, freeing APC./n", MODULE));
 // free apc
 ExFreePool ( Apc );
 // set event to signal apc execution
 pEvent = ( PKEVENT )* SystemArgument1 ;
KeSetEvent ( pEvent , IO_NO_INCREMENT , FALSE );
} 
// This routine just frees the APC                                                                     
VOID UtilUserApcCreateProcessEnd()
{

}
__declspec(naked) void UtilUserApcCreateProcess(PVOID NormalContext, PVOID  SystemArgument1, PVOID SystemArgument2)
{
    __asm
    {
        push ebp
        mov     ebp,esp
        push ebx
        push esi
        push edi
        jmp  __startup;                   ; these are just functions.... skip   

__find_kernel32:
        push  esi                         ; Save esi
        push  0x30
        pop   ecx
        mov   eax, fs:[ecx]               ; Extract the PEB
        mov   eax, [eax + 0x0c]           ; Extract the PROCESS_MODULE_INFO pointer from the PEB
        mov   esi, [eax + 0x1c]           ; Get the address of flink in the init module list
        lodsd                             ; Load the address of blink into eax
        mov   eax, [eax + 0x8]            ; Grab the module base address from the list entry
        pop   esi                         ; Restore esi
        ret                               ; Return

__find_function:
        pushad                            ; Save all registers
        mov   ebp, [esp + 0x24]           ; Store the base address in eax
        mov   eax, [ebp + 0x3c]           ; PE header VMA
        mov   edx, [ebp + eax + 0x78]     ; Export table relative offset
        add   edx, ebp                    ; Export table VMA
        mov   ecx, [edx + 0x18]           ; Number of names
        mov   ebx, [edx + 0x20]           ; Names table relative offset
        add   ebx, ebp                    ; Names table VMA

__find_function_loop:
        jecxz __find_function_finished    ; Jump to the end if ecx is 0
        dec   ecx                         ; Decrement our names counter
        mov   esi, [ebx + ecx * 4]        ; Store the relative offset of the name
        add   esi, ebp                    ; Set esi to the VMA of the current name

        xor   edi, edi                    ; Zero edi
        xor   eax, eax                    ; Zero eax
        cld                               ; Clear direction

__compute_hash_again:
        lodsb                             ; Load the next byte from esi into al
        test  al, al                      ; Test ourselves.
        jz    __compute_hash_finished     ; If the ZF is set, we've hit the null term.
        ror   edi, 0xd                    ; Rotate edi 13 bits to the right
        add   edi, eax                    ; Add the new byte to the accumulator
        jmp   __compute_hash_again        ; Next iteration

__compute_hash_finished:        
        cmp   edi, [esp + 0x28]           ; Compare the computed hash with the requested hash
        jnz   __find_function_loop        ; No match, try the next one.
        mov   ebx, [edx + 0x24]           ; Ordinals table relative offset
        add   ebx, ebp                    ; Ordinals table VMA
        mov   cx, [ebx + 2 * ecx]         ; Extrapolate the function's ordinal
        mov   ebx, [edx + 0x1c]           ; Address table relative offset
        add   ebx, ebp                    ; Address table VMA
        mov   eax, [ebx + 4 * ecx]        ; Extract the relative function offset from its ordinal
        add   eax, ebp                    ; Function VMA
        mov   [esp + 0x1c], eax           ; Overwrite stack version of eax from pushad

__find_function_finished:
        popad                             ; Restore all registers
        ret 8

__begin:
        nop
        pop edi                        ; Pop address
        mov ebx, __execute
        sub ebx, __command_line
        sub edi, ebx                ; filename offset
        mov esi,edi                 ; filename to edi
        call __find_kernel32        ; Find kernel32 address
        mov ebx, eax                ; Save address in ebx
        jmp short __execute         ; Skip data

__startup:
        call __begin                ; Fetch our data address


__execute:
        push 0x0e8afe98             ; WinExec hash
        push ebx                   ; kernel32 base address
        call __find_function        ; find address

        xor ecx,ecx
        inc ecx                 ; ecx = 1
        push ecx                ; uCmdShow
        push esi                ; lpCmdLine. We already have the exe path in esi
        call eax                ; call WinExec
        jmp __end

__command_line:                    ; Space (~300 bytes) for commandline
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
__end:
        pop edi        ; restore registers
        pop esi
        pop ebx
        pop ebp
        ret 0x0c
    }
}
 //not PEPROCESS, the KeStackAttachprocess only allows
// PRKPROCEss and PRKAPC_STATE

PKTHREAD tthread;
PRKPROCESS pprocess;
NTSTATUS UtilInstallUserModeApcForCreateProcess( char * CommandLine , PKTHREAD pTargetThread , PRKPROCESS pTargetProcess )
//Fucntion
{
PRKAPC pApc = NULL ;
PMDL pMdl = NULL ;
 PVOID MappedAddress = NULL ;
  ULONG size ;
 KAPC_STATE ApcState ;
 PKEVENT pEvent = NULL ;
  // check params
if (! pTargetThread || ! pTargetProcess ) //if there is no thread or process. I'm assuming at this point it requires a process to launch the commandline of child process from.
     return STATUS_UNSUCCESSFUL ;
  // allocate memory for apc and event
 pApc = ExAllocatePool (NonPagedPool, sizeof (KAPC));
 if (! pApc )
    return STATUS_INSUFFICIENT_RESOURCES ;
  pEvent = ExAllocatePool (NonPagedPool, sizeof (KEVENT));
 if (! pEvent )
 {
     ExFreePool ( pApc );
     return STATUS_INSUFFICIENT_RESOURCES ;
 }
   // allocate mdl big enough to map the code to be executed
   size = ( unsigned char *)UtilUserApcCreateProcessEnd - ( unsigned char *)UtilUserApcCreateProcess;
  pMdl = IoAllocateMdl (UtilUserApcCreateProcess, size , FALSE , FALSE , NULL );
   if (! pMdl )
  {
      ExFreePool ( pEvent );
     ExFreePool ( pApc );
     return STATUS_INSUFFICIENT_RESOURCES ;
 }
  // lock the pages in memory
  __try
  {
       MmProbeAndLockPages ( pMdl ,KernelMode,IoWriteAccess);
  }
  __except ( EXCEPTION_EXECUTE_HANDLER )
  {
     IoFreeMdl ( pMdl );
     ExFreePool ( pEvent );
     ExFreePool ( pApc );
    return STATUS_UNSUCCESSFUL ;
 }
  // map the pages into the specified process
  KeStackAttachProcess (pTargetProcess,&ApcState);
  MappedAddress = MmMapLockedPagesSpecifyCache ( pMdl ,UserMode,MmCached, NULL , FALSE ,NormalPagePriority);
  if (! MappedAddress )
  {
     // cannot map address
     KeUnstackDetachProcess (& ApcState );
    IoFreeMdl ( pMdl );
    ExFreePool ( pEvent );
     ExFreePool ( pApc );
     return STATUS_UNSUCCESSFUL ;
 }
  // copy commandline
   memset (( unsigned char *) MappedAddress + 160 , 0 , 260 );
  memcpy (( unsigned char *) MappedAddress + 160 , CommandLine , strlen ( CommandLine ));
  KeUnstackDetachProcess (& ApcState );
   // initialize apc
   KeInitializeEvent ( pEvent ,NotificationEvent, FALSE );
  KeInitializeApc( pApc , pTargetThread , OriginalApcEnvironment,&UtilUserApcCreateProcessKernelRoutine,
      NULL , MappedAddress , UserMode, ( PVOID ) NULL );
 // schedule apc
 if (!KeInsertQueueApc( pApc , pEvent , NULL , 0 ))
{
      // failed apc delivery
      MmUnlockPages ( pMdl );
      IoFreeMdl ( pMdl );
      ExFreePool ( pEvent );
      ExFreePool ( pApc );
      return STATUS_UNSUCCESSFUL ;
  }
  // and fire it by manually alerting the thread (for reference, this set the KTHREAD.ApcState.KernelApcInProgress)
  // beware, this could be not compatible with everything ..... it works on 2k/XP anyway, tested on SP2 too.....
 *(( unsigned char *) pTargetThread + 0x4a )= 1 ;
  // apc is fired, wait event to signal completion
  KeWaitForSingleObject ( pEvent ,Executive,KernelMode, FALSE , NULL );
  // free event
  ExFreePool ( pEvent );
  // unmap and unlock pages / mdl . Note that there's no need to call MmUnmapLockedPages on paged locked with MmProbeAndLockPages,
 // since MmUnlockPages does this for us automatically.
  MmUnlockPages ( pMdl );
  IoFreeMdl ( pMdl );
  return STATUS_SUCCESS ;
}
/* // I was reserving this pseudo code in case the process to use must already exist.
 NTSTATUS findProcess( HANDLE ProcessID )
{
	PEPROCESS PeProcess = NULL; 
	HANDLE _PID = 0;
	PLIST_ENTRY	pNextEntry, pListHead; 
	PLIST_ENTRY BeforeProcess,Process,AfterProcess;
	PeProcess = PsGetCurrentProcess(); 
	ProcessID = 0;
	if(!PeProcess) 
		return STATUS_INVALID_PARAMETER;
	if( IsListEmpty( &PeProcess->ActiveProcessLinks ) )
		return STATUS_INVALID_PARAMETER;
	else
	{
		pListHead = &PeProcess->ActiveProcessLinks;
		pNextEntry = pListHead->Flink;
		while(pNextEntry != pListHead) 
		{
			PeProcess = CONTAINING_RECORD( pNextEntry,PEPROCESS,ActiveProcessLinks );
			if(PeProcess->ActiveThreads)
				if( !IsListEmpty( &PeProcess->ThreadListHead ) )
					_PID = PsGetProcessId( PeProcess );
					if( ProcessID == _PID );
					{
						Process = pNextEntry;
						BeforeProcess = pNextEntry->Blink;
						AfterProcess = pNextEntry->Flink;
						BeforeProcess->Flink = Process->Flink;
						AfterProcess->Blink = Process->Blink;
						return STATUS_SUCCESS; 
						
					}
			//PeProcess = NULL; iterate here
			DbgPrint("%d",ProcessID);
			pNextEntry = pNextEntry->Flink;
		}
	}
	return STATUS_INVALID_PARAMETER;
}
*/
 //2) This routine just frees the APC allocated memory as soon as it's fired
//************************************************************************
//
/////////////////////////////////////////////////////////////////////////////////
//DRIVER ENTRY
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath)
{
theDriverObject->DriverUnload  = OnUnload; 
         NTSTATUS UtilInstallUserModeApcForCreateProcess( testchar, tthread ,  pProcess );// ?
         return 0;
         }