ring0 APC DLL injection crash target process on win7

4

I am trying to implement a ring0 dll injector driver and implement by APC injection. the code work perfectly on win XP. by on win7, it keeps crashing the target process.

here is the code:

NTSTATUS InjectDll( IN ULONG ulProcessId, HANDLE hEvent, IN TCHAR * pszDllPath )
{
    NTSTATUS            ntStatus;
    HANDLE              hProcess = NULL;
    OBJECT_ATTRIBUTES   ObjectAttributes;
    CLIENT_ID           ClientId;
    PKEVENT             pevtWait;
    PKPROCESS           pPcb = NULL;
    PVOID               pArg = NULL;
    ULONG               ulSize = 0;
    KAPC_STATE          ApcState;
    PKAPC               pApc;
    PVOID               pTcb;
    PVOID               pfnLoadLibrary;
    KPROCESSOR_MODE     PreviousMode = ExGetPreviousMode();
    LARGE_INTEGER       nTimeout;

    nTimeout.QuadPart = (LONGLONG) -300000000;

    do
    {
        InitializeObjectAttributes( 
            &ObjectAttributes,
            NULL,
            0,
            NULL,
            NULL)

        ClientId.UniqueProcess = (HANDLE)ulProcessId;
        ClientId.UniqueThread = 0;

        ntStatus = ZwOpenProcess(
            &hProcess,
            PROCESS_ALL_ACCESS,
            &ObjectAttributes,
            &ClientId
        );
        if ( NT_ERROR(ntStatus) )
        {
            DbgPrint("Open process %d failed - error 0x%X\r\n", ulProcessId, ntStatus);
            break;
        }

        ulSize = MAX_FILENAME_LEN * sizeof(WCHAR);
        ntStatus = ZwAllocateVirtualMemory(
            hProcess,
            &pArg,
            0,
            &ulSize,
            MEM_RESERVE|MEM_COMMIT,
            PAGE_READWRITE);
        if ( NT_ERROR(ntStatus) )
        {
            DbgPrint("ZwAllocateVirtualMemory failed - error 0x%X\r\n", ntStatus);
            // thus, make it leak in target process space!
            break;
        }

        ntStatus = ObReferenceObjectByHandle( 
            hProcess,
            PROCESS_ALL_ACCESS,
            0,//should be PsProcessType,
            PreviousMode,
            (PVOID*)&pPcb,
            NULL);
        if ( NT_ERROR(ntStatus) )
        {
            DbgPrint("ObReferenceObjectByHandle 0x%X failed - error 0x%X\r\n", hProcess, ntStatus);
            break;
        }

        //enter target process space
        KeStackAttachProcess(pPcb,&ApcState);

        pfnLoadLibrary = GetLoadLibraryAddress(pPcb);
        if (!pfnLoadLibrary)
        {
            DbgPrint("Failed to get address of LoadLibrary\r\n");
            // leave target process space
            KeUnstackDetachProcess(&ApcState);
            break;
        }
        else
        {
            DbgPrint("Get address of LoadLibrary : 0x%X\r\n", pfnLoadLibrary);
        }

        RtlCopyMemory( pArg, pszDllPath, MAX_FILENAME_LEN * sizeof(TCHAR));

        // leave target process space
        KeUnstackDetachProcess(&ApcState);

        // get target thread
        pTcb = GetThreadByProcess(pPcb);
        if (!pTcb)
        {
            DbgPrint("Get thread failed!\r\n");
            ntStatus = STATUS_UNSUCCESSFUL;
            break;
        }

        // start inject
        pApc = (PKAPC)ExAllocatePoolWithTag(NonPagedPool,sizeof(KAPC),POOL_TAG);
        if (!pApc)
        {
            DbgPrint("Failed to allocate memory for the APC structure\r\n");
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        KeInitializeApc(
            pApc,
            (PETHREAD)pTcb,
            OriginalApcEnvironment,
            &ApcKernelRoutine,
            NULL,
            (PKNORMAL_ROUTINE)pfnLoadLibrary,
            UserMode,
            pArg);


        if (!KeInsertQueueApc(pApc,NULL,NULL,0))
        {
            DbgPrint("Failed to insert APC\r\n");
            ntStatus = STATUS_UNSUCCESSFUL;
            ExFreePool(pApc);
            break;
        }

    } while (0);


    if ( pPcb )
    {
        ObDereferenceObject(pPcb);
    }

    if ( hProcess)
    {
        ZwClose(hProcess);
    }

    DbgPrint("InjectDll end - status = 0x%X\r\n", ntStatus);
    return ntStatus;
}

injectDll is called by DeviceIoControl serivce routine.

    DRIVER_DISPATCH InjectorDeviceControl;
NTSTATUS NTAPI InjectorDeviceControl( 
    IN PDEVICE_OBJECT pDeviceObject, 
    PIRP pIrp)
{
    PIO_STACK_LOCATION pStack;
    PINJECT_DLL_PARAM pParam;
    NTSTATUS ntStatus;

    DbgPrint("call InjectorDeviceControl...\r\n");

    /* Get the stack location and parameters */
    pStack = IoGetCurrentIrpStackLocation(pIrp);
    pParam = (PINJECT_DLL_PARAM)pIrp->AssociatedIrp.SystemBuffer;


    if (pStack->Parameters.DeviceIoControl.IoControlCode != IOCTL_INJECTDLL)
    {
        /* Unsupported command */
        ntStatus = STATUS_NOT_IMPLEMENTED;
    }
    else
    {
        /* Validate the input buffer length */
        if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(INJECT_DLL_PARAM))
        {
            /* Invalid buffer */
            ntStatus = STATUS_INVALID_PARAMETER;
        }
        else
        {
            /* Inject dll */
            ntStatus = InjectDll(pParam->ulProcessId, pParam->hEvent, pParam->DllName);
        }
    }

    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return ntStatus;
} 

here is the crash stacktrace of target process.

FAULTING_IP: 
ntdll!RtlActivateActivationContextUnsafeFast+9c
7770f59a 8933            mov     dword ptr [ebx],esi

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 7770f59a (ntdll!RtlActivateActivationContextUnsafeFast+0x0000009c)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000001
   Parameter[1]: 00000000
Attempt to write to address 00000000

DEFAULT_BUCKET_ID:  NULL_POINTER_WRITE

PROCESS_NAME:  QQProtect.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx

EXCEPTION_PARAMETER1:  00000001

EXCEPTION_PARAMETER2:  00000000

WRITE_ADDRESS:  00000000 

FOLLOWUP_IP: 
ntdll!RtlActivateActivationContextUnsafeFast+9c
7770f59a 8933            mov     dword ptr [ebx],esi

NTGLOBALFLAG:  0

FAULTING_THREAD:  00000300

PRIMARY_PROBLEM_CLASS:  NULL_POINTER_WRITE

BUGCHECK_STR:  APPLICATION_FAULT_NULL_POINTER_WRITE

LAST_CONTROL_TRANSFER:  from 77740baa to 7770f59a

STACK_TEXT:  
021df8a0 77740baa 75655a2c 00000000 02790000 ntdll!RtlActivateActivationContextUnsafeFast+0x9c
021df91c 77740461 002b5cd0 021dfab8 756559bc ntdll!LdrpProcessStaticImports+0x1b8
021dfa8c 7774232c 021dfaec 021dfab8 00000000 ntdll!LdrpLoadDll+0x314
021dfac0 759088ee 0026f074 021dfb00 021dfaec ntdll!LdrLoadDll+0x92
021dfaf8 75dc3c12 00000000 00000000 00000001 KERNELBASE!LoadLibraryExW+0x15a
021dfb0c 77726f7d 02790000 00000000 00000000 kernel32!LoadLibraryW+0x11
021dff88 75dc3c45 00000000 021dffd4 777437f5 ntdll!KiUserApcDispatcher+0x25
021dff94 777437f5 00295fe8 75655ce4 00000000 kernel32!BaseThreadInitThunk+0xe
021dffd4 777437c8 7770fd0f 00295fe8 00000000 ntdll!__RtlUserThreadStart+0x70
021dffec 00000000 7770fd0f 00295fe8 00000000 ntdll!_RtlUserThreadStart+0x1b


STACK_COMMAND:  ~1s; .ecxr ; kb

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  ntdll!RtlActivateActivationContextUnsafeFast+9c

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: ntdll

IMAGE_NAME:  ntdll.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  4ce7b96e

FAILURE_BUCKET_ID:  NULL_POINTER_WRITE_c0000005_ntdll.dll!RtlActivateActivationContextUnsafeFast

BUCKET_ID:  APPLICATION_FAULT_NULL_POINTER_WRITE_ntdll!RtlActivateActivationContextUnsafeFast+9c

WATSON_STAGEONE_URL:  http://watson.microsoft.com/StageOne/QQProtect_exe/3_0_1_3629/50a06369/ntdll_dll/6_1_7601_17514/4ce7b96e/c0000005/0002f59a.htm?Retriage=1

Followup: MachineOwner

any idea?

crash
driver
apc
dll-injection
asked on Stack Overflow Nov 18, 2012 by eeelin

2 Answers

1

I've being working on some dll-injetion by apc and got this exact same problem.
After performing some reverse-engeenering, I found why it occured.

This is caused when the current thread's TEB does not have a ActivationContextStackPointer member:

ntdll!_TEB
   +0x000 NtTib            : _NT_TIB 
   +0x01c EnvironmentPointer : (null) 
   +0x020 ClientId         : _CLIENT_ID
   +0x028 ActiveRpcHandle  : (null) 
   +0x02c ThreadLocalStoragePointer : (null) 
   +0x030 ProcessEnvironmentBlock : 0x7ffd8000 _PEB
   +0x034 LastErrorValue   : 0
   +0x038 CountOfOwnedCriticalSections : 0
   +0x03c CsrClientThread  : (null) 
   +0x040 Win32ThreadInfo  : (null) 
   +0x044 User32Reserved   : [26] 0
   +0x0ac UserReserved     : [5] 0
   +0x0c0 WOW32Reserved    : (null) 
   +0x0c4 CurrentLocale    : 0x804
   +0x0c8 FpSoftwareStatusRegister : 0
   +0x0cc SystemReserved1  : [54] (null) 
   +0x1a4 ExceptionCode    : 0n0
   +0x1a8 ActivationContextStackPointer : (null) <---it's null

The RtlActivateActivationContextUnsafeFast function tries to insert a node in the ActivationContext list, but the ActivationContextStackPointer is null, so an ACCESS_VIOLATION is raised.

    mov     dword ptr [ebx],esi  //ebx = teb->ActivationContextStackPointer  which is 000000000  esi = node

The ActivationContext is kinda related to the manifest of DLL.

You can get more information about this in the Microsoft help.

When loading a dll with a manifest file, the RtlActivateActivationContextUnsafeFast is called.

In case of that, I've solved the problem by disabling the generation of my dll's manifest file:

linker - > (/MANIFEST:NO)

or you can try to enforce the system to allocate a ActivationContextStack for target thread by making the target thread to call a ActivateActCtx function (I'm not sure if it is possible?).

Hope this helps you.

answered on Stack Overflow Aug 17, 2018 by 王云路 • edited Aug 17, 2018 by CKE
0

I stumbled upon the same problem, with a slightly different crash on Windows 10 (invalid address access, but not NULL pointer dereference). I didn't like 王云路's solution since I wanted to be able to load any DLL. What worked for me is calling NtQueueApcThread directly instead of using QueueUserAPC. In this case, RtlDispatchAPC is bypassed and the activation functions are not called.

Reference:
https://repnz.github.io/posts/apc/user-apc/#queueuserapc-kernelbase-dll-layer

answered on Stack Overflow Sep 18, 2020 by Paul

User contributions licensed under CC BY-SA 3.0