Пример #1
0
/*
 * @implemented
 */
DWORD
WINAPI
SignalObjectAndWait(IN HANDLE hObjectToSignal,
                    IN HANDLE hObjectToWaitOn,
                    IN DWORD dwMilliseconds,
                    IN BOOL bAlertable)
{
    PLARGE_INTEGER TimePtr;
    LARGE_INTEGER Time;
    NTSTATUS Status;
    RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;

    /* APCs must execute with the default activation context */
    if (bAlertable)
    {
        /* Setup the frame */
        RtlZeroMemory(&ActCtx, sizeof(ActCtx));
        ActCtx.Size = sizeof(ActCtx);
        ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
        RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
    }

    /* Get real handle */
    hObjectToWaitOn = TranslateStdHandle(hObjectToWaitOn);

    /* Check for console handle */
    if ((IsConsoleHandle(hObjectToWaitOn)) &&
        (VerifyConsoleIoHandle(hObjectToWaitOn)))
    {
        /* Get the real wait handle */
        hObjectToWaitOn = GetConsoleInputWaitHandle();
    }

    /* Convert the timeout */
    TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);

    /* Start wait loop */
    do
    {
        /* Do the wait */
        Status = NtSignalAndWaitForSingleObject(hObjectToSignal,
                                                hObjectToWaitOn,
                                                (BOOLEAN)bAlertable,
                                                TimePtr);
        if (!NT_SUCCESS(Status))
        {
            /* The wait failed */
            BaseSetLastNTError(Status);
            Status = WAIT_FAILED;
        }
    } while ((Status == STATUS_ALERTED) && (bAlertable));

    /* Cleanup the activation context */
    if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);

    /* Return wait status */
    return Status;
}
Пример #2
0
/*
 * @implemented
 */
BOOL
WINAPI
GetQueuedCompletionStatus(IN HANDLE CompletionHandle,
                          IN LPDWORD lpNumberOfBytesTransferred,
                          OUT PULONG_PTR lpCompletionKey,
                          OUT LPOVERLAPPED *lpOverlapped,
                          IN DWORD dwMilliseconds)
{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatus;
    ULONG_PTR CompletionKey;
    LARGE_INTEGER Time;
    PLARGE_INTEGER TimePtr;

    /* Convert the timeout and then call the native API */
    TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);
    Status = NtRemoveIoCompletion(CompletionHandle,
                                  (PVOID*)&CompletionKey,
                                  (PVOID*)lpOverlapped,
                                  &IoStatus,
                                  TimePtr);
    if (!(NT_SUCCESS(Status)) || (Status == STATUS_TIMEOUT))
    {
        /* Clear out the overlapped output */
        *lpOverlapped = NULL;

        /* Check what kind of error we got */
        if (Status == STATUS_TIMEOUT)
        {
            /* Timeout error is set directly since there's no conversion */
            SetLastError(WAIT_TIMEOUT);
        }
        else
        {
            /* Any other error gets converted */
            BaseSetLastNTError(Status);
        }

        /* This is a failure case */
        return FALSE;
    }

    /* Write back the output parameters */
    *lpCompletionKey = CompletionKey;
    *lpNumberOfBytesTransferred = IoStatus.Information;

    /* Check for error */
    if (!NT_SUCCESS(IoStatus.Status))
    {
        /* Convert and fail */
        BaseSetLastNTError(IoStatus.Status);
        return FALSE;
    }

    /* Return success */
    return TRUE;
}
Пример #3
0
DWORD
WINAPI
SignalObjectAndWait(
    HANDLE hObjectToSignal,
    HANDLE hObjectToWaitOn,
    DWORD dwMilliseconds,
    BOOL bAlertable
)
{
    NTSTATUS Status;
    LARGE_INTEGER TimeOut;
    PLARGE_INTEGER pTimeOut;
    PPEB Peb;

    Peb = NtCurrentPeb();
    switch( (DWORD)hObjectToWaitOn ) {
    case STD_INPUT_HANDLE:
        hObjectToWaitOn = Peb->ProcessParameters->StandardInput;
        break;
    case STD_OUTPUT_HANDLE:
        hObjectToWaitOn = Peb->ProcessParameters->StandardOutput;
        break;
    case STD_ERROR_HANDLE:
        hObjectToWaitOn = Peb->ProcessParameters->StandardError;
        break;
    }

    if (CONSOLE_HANDLE(hObjectToWaitOn) && VerifyConsoleIoHandle(hObjectToWaitOn)) {
        hObjectToWaitOn = GetConsoleInputWaitHandle();
    }

    pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
rewait:
    Status = NtSignalAndWaitForSingleObject(
                 hObjectToSignal,
                 hObjectToWaitOn,
                 (BOOLEAN)bAlertable,
                 pTimeOut
             );

    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        Status = (NTSTATUS)0xffffffff;
    }
    else {
        if ( bAlertable && Status == STATUS_ALERTED ) {
            goto rewait;
        }
    }
    return (DWORD)Status;
}
Пример #4
0
/*
 * @implemented
 */
DWORD
WINAPI
SleepEx(IN DWORD dwMilliseconds,
        IN BOOL bAlertable)
{
    LARGE_INTEGER Time;
    PLARGE_INTEGER TimePtr;
    NTSTATUS errCode;
    RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;

    /* APCs must execute with the default activation context */
    if (bAlertable)
    {
        /* Setup the frame */
        RtlZeroMemory(&ActCtx, sizeof(ActCtx));
        ActCtx.Size = sizeof(ActCtx);
        ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
        RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
    }

    /* Convert the timeout */
    TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);
    if (!TimePtr)
    {
        /* Turn an infinite wait into a really long wait */
        Time.LowPart = 0;
        Time.HighPart = 0x80000000;
        TimePtr = &Time;
    }

    /* Loop the delay while APCs are alerting us */
    do
    {
        /* Do the delay */
        errCode = NtDelayExecution((BOOLEAN)bAlertable, TimePtr);
    }
    while ((bAlertable) && (errCode == STATUS_ALERTED));
    
    /* Cleanup the activation context */
    if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);

    /* Return the correct code */
    return (errCode == STATUS_USER_APC) ? WAIT_IO_COMPLETION : 0;
}
Пример #5
0
/*
 * @implemented
 */
DWORD
WINAPI
WaitForMultipleObjectsEx(IN DWORD nCount,
                         IN CONST HANDLE *lpHandles,
                         IN BOOL bWaitAll,
                         IN DWORD dwMilliseconds,
                         IN BOOL bAlertable)
{
    PLARGE_INTEGER TimePtr;
    LARGE_INTEGER Time;
    PHANDLE HandleBuffer;
    HANDLE Handle[8];
    DWORD i;
    NTSTATUS Status;
    RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;

    /* APCs must execute with the default activation context */
    if (bAlertable)
    {
        /* Setup the frame */
        RtlZeroMemory(&ActCtx, sizeof(ActCtx));
        ActCtx.Size = sizeof(ActCtx);
        ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
        RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
    }

    /* Check if we have more handles then we locally optimize */
    if (nCount > 8)
    {
        /* Allocate a buffer for them */
        HandleBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
                                       0,
                                       nCount * sizeof(HANDLE));
        if (!HandleBuffer)
        {
            /* No buffer, fail the wait */
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);
            return WAIT_FAILED;
        }
    }
    else
    {
        /* Otherwise, use our local buffer */
        HandleBuffer = Handle;
    }

    /* Copy the handles into our buffer and loop them all */
    RtlCopyMemory(HandleBuffer, (LPVOID)lpHandles, nCount * sizeof(HANDLE));
    for (i = 0; i < nCount; i++)
    {
        /* Check what kind of handle this is */
        HandleBuffer[i] = TranslateStdHandle(HandleBuffer[i]);

        /* Check for console handle */
        if ((IsConsoleHandle(HandleBuffer[i])) &&
            (VerifyConsoleIoHandle(HandleBuffer[i])))
        {
            /* Get the real wait handle */
            HandleBuffer[i] = GetConsoleInputWaitHandle();
        }
    }

    /* Convert the timeout */
    TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);

    /* Start wait loop */
    do
    {
        /* Do the wait */
        Status = NtWaitForMultipleObjects(nCount,
                                          HandleBuffer,
                                          bWaitAll ? WaitAll : WaitAny,
                                          (BOOLEAN)bAlertable,
                                          TimePtr);
        if (!NT_SUCCESS(Status))
        {
            /* Wait failed */
            BaseSetLastNTError(Status);
            Status = WAIT_FAILED;
        }
    } while ((Status == STATUS_ALERTED) && (bAlertable));

    /* Check if we didn't use our local buffer */
    if (HandleBuffer != Handle)
    {
        /* Free the allocated one */
        RtlFreeHeap(RtlGetProcessHeap(), 0, HandleBuffer);
    }

    /* Cleanup the activation context */
    if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);

    /* Return wait status */
    return Status;
}
Пример #6
0
DWORD
APIENTRY
SleepEx(
    DWORD dwMilliseconds,
    BOOL bAlertable
)

/*++

Routine Description:

    The execution of the current thread can be delayed for a specified
    interval of time with the SleepEx function.

    The SleepEx function causes the current thread to enter a waiting
    state until the specified interval of time has passed.

    If the bAlertable parameter is FALSE, the only way the SleepEx
    returns is when the specified time interval has passed.  If the
    bAlertable parameter is TRUE, then the SleepEx can return due to the
    expiration of the time interval (return value of 0), or because an
    I/O completion callback terminated the SleepEx early (return value
    of WAIT_IO_COMPLETION).

Arguments:

    dwMilliseconds - A time-out value that specifies the relative time,
        in milliseconds, over which the wait is to be completed.  A
        timeout value of 0 specified that the wait is to timeout
        immediately.  A timeout value of -1 specifies an infinite
        timeout period.

    bAlertable - Supplies a flag that controls whether or not the
        SleepEx may terminate early due to an I/O completion callback.
        A value of TRUE allows this API to complete early due to an I/O
        completion callback.  A value of FALSE will not allow I/O
        completion callbacks to terminate this call early.

Return Value:

    0 - The SleepEx terminated due to expiration of the time interval.

    WAIT_IO_COMPLETION - The SleepEx terminated due to one or more I/O
        completion callbacks.

--*/
{
    LARGE_INTEGER TimeOut;
    PLARGE_INTEGER pTimeOut;
    NTSTATUS Status;

    pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
    if (pTimeOut == NULL) {
        //
        // If Sleep( -1 ) then delay for the longest possible integer
        // relative to now.
        //

        TimeOut.LowPart = 0x0;
        TimeOut.HighPart = 0x80000000;
        pTimeOut = &TimeOut;
    }

rewait:
    Status = NtDelayExecution(
                 (BOOLEAN)bAlertable,
                 pTimeOut
             );
    if ( bAlertable && Status == STATUS_ALERTED ) {
        goto rewait;
    }
    return Status == STATUS_USER_APC ? WAIT_IO_COMPLETION : 0;
}
Пример #7
0
DWORD
APIENTRY
WaitForMultipleObjectsEx(
    DWORD nCount,
    CONST HANDLE *lpHandles,
    BOOL bWaitAll,
    DWORD dwMilliseconds,
    BOOL bAlertable
)

/*++

Routine Description:

    A wait operation on multiple waitable objects (up to
    MAXIMUM_WAIT_OBJECTS) is accomplished with the
    WaitForMultipleObjects function.

    This API can be used to wait on any of the specified objects to
    enter the signaled state, or all of the objects to enter the
    signaled state.

    If the bAlertable parameter is FALSE, the only way the wait
    terminates is because the specified timeout period expires, or
    because the specified objects entered the signaled state.  If the
    bAlertable parameter is TRUE, then the wait can return due to any one of
    the above wait termination conditions, or because an I/O completion
    callback terminated the wait early (return value of
    WAIT_IO_COMPLETION).

Arguments:

    nCount - A count of the number of objects that are to be waited on.

    lpHandles - An array of object handles.  Each handle must have
        SYNCHRONIZE access to the associated object.

    bWaitAll - A flag that supplies the wait type.  A value of TRUE
        indicates a "wait all".  A value of false indicates a "wait
        any".

    dwMilliseconds - A time-out value that specifies the relative time,
        in milliseconds, over which the wait is to be completed.  A
        timeout value of 0 specified that the wait is to timeout
        immediately.  This allows an application to test an object to
        determine if it is in the signaled state.  A timeout value of
        0xffffffff specifies an infinite timeout period.

    bAlertable - Supplies a flag that controls whether or not the
        wait may terminate early due to an I/O completion callback.
        A value of TRUE allows this API to complete early due to an I/O
        completion callback.  A value of FALSE will not allow I/O
        completion callbacks to terminate this call early.

Return Value:

    WAIT_TIME_OUT - indicates that the wait was terminated due to the
        TimeOut conditions.

    0 to MAXIMUM_WAIT_OBJECTS-1, indicates, in the case of wait for any
        object, the object number which satisfied the wait.  In the case
        of wait for all objects, the value only indicates that the wait
        was completed successfully.

    0xffffffff - The wait terminated due to an error. GetLastError may be
        used to get additional error information.

    WAIT_ABANDONED_0 to (WAIT_ABANDONED_0)+(MAXIMUM_WAIT_OBJECTS - 1),
        indicates, in the case of wait for any object, the object number
        which satisfied the event, and that the object which satisfied
        the event was abandoned.  In the case of wait for all objects,
        the value indicates that the wait was completed successfully and
        at least one of the objects was abandoned.

    WAIT_IO_COMPLETION - The wait terminated due to one or more I/O
        completion callbacks.

--*/
{
    NTSTATUS Status;
    LARGE_INTEGER TimeOut;
    PLARGE_INTEGER pTimeOut;
    DWORD i;
    LPHANDLE HandleArray;
    HANDLE Handles[ 8 ];
    PPEB Peb;

    if (nCount > 8) {
        HandleArray = (LPHANDLE) RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), nCount*sizeof(HANDLE));
        if (HandleArray == NULL) {
            BaseSetLastNTError(STATUS_NO_MEMORY);
            return 0xffffffff;
        }
    } else {
        HandleArray = Handles;
    }
    RtlCopyMemory(HandleArray,(LPVOID)lpHandles,nCount*sizeof(HANDLE));

    Peb = NtCurrentPeb();
    for (i=0; i<nCount; i++) {
        switch( (DWORD)HandleArray[i] ) {
        case STD_INPUT_HANDLE:
            HandleArray[i] = Peb->ProcessParameters->StandardInput;
            break;
        case STD_OUTPUT_HANDLE:
            HandleArray[i] = Peb->ProcessParameters->StandardOutput;
            break;
        case STD_ERROR_HANDLE:
            HandleArray[i] = Peb->ProcessParameters->StandardError;
            break;
        }

        if (CONSOLE_HANDLE(HandleArray[i]) && VerifyConsoleIoHandle(HandleArray[i])) {
            HandleArray[i] = GetConsoleInputWaitHandle();
        }
    }

    pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
rewait:
    Status = NtWaitForMultipleObjects(
                 (CHAR)nCount,
                 HandleArray,
                 bWaitAll ? WaitAll : WaitAny,
                 (BOOLEAN)bAlertable,
                 pTimeOut
             );
    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        Status = (NTSTATUS)0xffffffff;
    }
    else {
        if ( bAlertable && Status == STATUS_ALERTED ) {
            goto rewait;
        }
    }

    if (HandleArray != Handles) {
        RtlFreeHeap(RtlProcessHeap(), 0, HandleArray);
    }

    return (DWORD)Status;
}
Пример #8
0
DWORD
APIENTRY
WaitForSingleObjectEx(
    HANDLE hHandle,
    DWORD dwMilliseconds,
    BOOL bAlertable
)

/*++

Routine Description:

    A wait operation on a waitable object is accomplished with the
    WaitForSingleObjectEx function.

    Waiting on an object checks the current state of the object.  If the
    current state of the object allows continued execution, any
    adjustments to the object state are made (for example, decrementing
    the semaphore count for a semaphore object) and the thread continues
    execution.  If the current state of the object does not allow
    continued execution, the thread is placed into the wait state
    pending the change of the object's state or time-out.

    If the bAlertable parameter is FALSE, the only way the wait
    terminates is because the specified timeout period expires, or
    because the specified object entered the signaled state.  If the
    bAlertable parameter is TRUE, then the wait can return due to any
    one of the above wait termination conditions, or because an I/O
    completion callback terminated the wait early (return value of
    WAIT_IO_COMPLETION).

Arguments:

    hHandle - An open handle to a waitable object. The handle must have
        SYNCHRONIZE access to the object.

    dwMilliseconds - A time-out value that specifies the relative time,
        in milliseconds, over which the wait is to be completed.  A
        timeout value of 0 specified that the wait is to timeout
        immediately.  This allows an application to test an object to
        determine if it is in the signaled state.  A timeout value of
        0xffffffff specifies an infinite timeout period.

    bAlertable - Supplies a flag that controls whether or not the
        wait may terminate early due to an I/O completion callback.
        A value of TRUE allows this API to complete early due to an I/O
        completion callback.  A value of FALSE will not allow I/O
        completion callbacks to terminate this call early.

Return Value:

    WAIT_TIME_OUT - Indicates that the wait was terminated due to the
        TimeOut conditions.

    0 - indicates the specified object attained a Signaled
        state thus completing the wait.

    0xffffffff - The wait terminated due to an error. GetLastError may be
        used to get additional error information.

    WAIT_ABANDONED - indicates the specified object attained a Signaled
        state but was abandoned.

    WAIT_IO_COMPLETION - The wait terminated due to one or more I/O
        completion callbacks.

--*/
{
    NTSTATUS Status;
    LARGE_INTEGER TimeOut;
    PLARGE_INTEGER pTimeOut;
    PPEB Peb;

    Peb = NtCurrentPeb();
    switch( (DWORD)hHandle ) {
    case STD_INPUT_HANDLE:
        hHandle = Peb->ProcessParameters->StandardInput;
        break;
    case STD_OUTPUT_HANDLE:
        hHandle = Peb->ProcessParameters->StandardOutput;
        break;
    case STD_ERROR_HANDLE:
        hHandle = Peb->ProcessParameters->StandardError;
        break;
    }

    if (CONSOLE_HANDLE(hHandle) && VerifyConsoleIoHandle(hHandle)) {
        hHandle = GetConsoleInputWaitHandle();
    }

    pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
rewait:
    Status = NtWaitForSingleObject(hHandle,(BOOLEAN)bAlertable,pTimeOut);
    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        Status = (NTSTATUS)0xffffffff;
    }
    else {
        if ( bAlertable && Status == STATUS_ALERTED ) {
            goto rewait;
        }
    }
    return (DWORD)Status;
}
Пример #9
0
/*
 * @implemented
 */
BOOL
WINAPI
WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
                  IN DWORD dwMilliseconds)
{
    LARGE_INTEGER WaitTime;
    PLARGE_INTEGER Timeout;
    DBGUI_WAIT_STATE_CHANGE WaitStateChange;
    NTSTATUS Status;

    /* Convert to NT Timeout */
    Timeout = BaseFormatTimeOut(&WaitTime, dwMilliseconds);

    /* Loop while we keep getting interrupted */
    do
    {
        /* Call the native API */
        Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
    } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));

    /* Check if the wait failed */
    if (!(NT_SUCCESS(Status)) || (Status == DBG_UNABLE_TO_PROVIDE_HANDLE))
    {
        /* Set the error code and quit */
        BaseSetLastNTError(Status);
        return FALSE;
    }

    /* Check if we timed out */
    if (Status == STATUS_TIMEOUT)
    {
        /* Fail with a timeout error */
        SetLastError(ERROR_SEM_TIMEOUT);
        return FALSE;
    }

    /* Convert the structure */
    Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
    if (!NT_SUCCESS(Status))
    {
        /* Set the error code and quit */
        BaseSetLastNTError(Status);
        return FALSE;
    }

    /* Check what kind of event this was */
    switch (lpDebugEvent->dwDebugEventCode)
    {
        /* New thread was created */
        case CREATE_THREAD_DEBUG_EVENT:

            /* Setup the thread data */
            SaveThreadHandle(lpDebugEvent->dwProcessId,
                             lpDebugEvent->dwThreadId,
                             lpDebugEvent->u.CreateThread.hThread);
            break;

        /* New process was created */
        case CREATE_PROCESS_DEBUG_EVENT:

            /* Setup the process data */
            SaveProcessHandle(lpDebugEvent->dwProcessId,
                              lpDebugEvent->u.CreateProcessInfo.hProcess);

            /* Setup the thread data */
            SaveThreadHandle(lpDebugEvent->dwProcessId,
                             lpDebugEvent->dwThreadId,
                             lpDebugEvent->u.CreateProcessInfo.hThread);
            break;

        /* Process was exited */
        case EXIT_PROCESS_DEBUG_EVENT:

            /* Mark the thread data as such and fall through */
            MarkProcessHandle(lpDebugEvent->dwProcessId);

        /* Thread was exited */
        case EXIT_THREAD_DEBUG_EVENT:

            /* Mark the thread data */
            MarkThreadHandle(lpDebugEvent->dwThreadId);
            break;
    
        /* Nothing to do */
        case EXCEPTION_DEBUG_EVENT:
        case LOAD_DLL_DEBUG_EVENT:
        case UNLOAD_DLL_DEBUG_EVENT:
        case OUTPUT_DEBUG_STRING_EVENT:
        case RIP_EVENT:
            break;

        /* Fail anything else */
        default:
            return FALSE;
    }

    /* Return success */
    return TRUE;
}
Пример #10
0
NTSTATUS InjectSelfToRemoteProcess(HANDLE hProcess, HANDLE hThread)
{
    NTSTATUS    Status;
    PVOID       pvBuffer;
    DWORD       Length;
    WCHAR       szSelfPath[MAX_PATH];
    CONTEXT     ThreadContext;
    LARGE_INTEGER TimeOut;
    INJECT_DLL_CURRENT_THREAD inj;

    ThreadContext.ContextFlags = CONTEXT_CONTROL;
    Status = NtGetContextThread(hThread, &ThreadContext);
    if (!NT_SUCCESS(Status))
    {
//        BaseSetLastNTError(Status);
//        PrintError(RtlGetLastWin32Error());
        return Status;
    }

//    PrintConsoleW(L"Eip = %08X\n", ThreadContext.Eip);
//    getch();

    Length = Nt_GetExeDirectory(szSelfPath, countof(szSelfPath));
    if (Length == NULL)
        return STATUS_UNSUCCESSFUL;

    static WCHAR szDll[] = L"LocaleEmulator.dll";
    StrCopyW(szSelfPath + Length, szDll);
    Length += CONST_STRLEN(szDll);

    pvBuffer = NULL;
    Status = Nt_AllocateMemory(hProcess, &pvBuffer, MEMORY_PAGE_SIZE);
    if (!NT_SUCCESS(Status))
    {
//        BaseSetLastNTError(Status);
//        PrintError(RtlGetLastWin32Error());
        return Status;
    }

    Length *= sizeof(WCHAR);
    inj.pfLdrLoadDll = LdrLoadDll;
    inj.ReturnAddr   = ThreadContext.Eip;
    inj.ModuleFileName.Length = Length;
    inj.ModuleFileName.MaximumLength = Length + sizeof(WCHAR);
    inj.ModuleFileName.Buffer = (LPWSTR)((ULONG_PTR)pvBuffer + sizeof(inj));

    Status = STATUS_UNSUCCESSFUL;
    LOOP_ONCE
    {
        Status = Nt_WriteMemory(hProcess, pvBuffer, &inj, sizeof(inj));
        if (!NT_SUCCESS(Status))
            break;

        Length += sizeof(WCHAR);
        Status = Nt_WriteMemory(hProcess, (PVOID)((ULONG_PTR)pvBuffer + sizeof(inj)), szSelfPath, Length);
        if (!NT_SUCCESS(Status))
            break;

        ThreadContext.Eip = (DWORD)(PBYTE)pvBuffer + sizeof(inj) + Length;
        Status = Nt_WriteMemory(
                    hProcess,
                    (PVOID)ThreadContext.Eip,
                    LoadExternDll,
                    (ULONG_PTR)_LoadExternDllEnd - (ULONG_PTR)LoadExternDll
                 );
        if (!NT_SUCCESS(Status))
            break;

        Status = NtSetContextThread(hThread, &ThreadContext);
        if (!NT_SUCCESS(Status))
            break;

        Status = NtResumeThread(hThread, NULL);
        if (!NT_SUCCESS(Status))
            break;

        BaseFormatTimeOut(&TimeOut, 500);
        for (DWORD TryTimes = 30; TryTimes; --TryTimes)
        {
            DWORD Val;
            Status = Nt_ReadMemory(hProcess, pvBuffer, &Val, sizeof(Val));
            if (!NT_SUCCESS(Status))
                break;

            if (Val != 0)
            {
                NtDelayExecution(FALSE, &TimeOut);
                continue;
            }

            break;
        }

        if (!NT_SUCCESS(Status))
            break;

        NtDelayExecution(FALSE, &TimeOut);
        Status = NtGetContextThread(hThread, &ThreadContext);
        if (!NT_SUCCESS(Status))
            break;

        if ((ULONG_PTR)ThreadContext.Eip < (ULONG_PTR)pvBuffer ||
            (ULONG_PTR)ThreadContext.Eip > (ULONG_PTR)pvBuffer + MEMORY_PAGE_SIZE)
        {
            Status = STATUS_SUCCESS;
        }
        else
        {
            Status = STATUS_UNSUCCESSFUL;
        }
    }

//    BaseSetLastNTError(Status);
//    PrintError(RtlGetLastWin32Error());

//    Nt_FreeMemory(hProcess, pvBuffer);

    return Status;
}