VirtualBox

Changeset 52969 in vbox for trunk


Ignore:
Timestamp:
Oct 7, 2014 8:29:34 AM (10 years ago)
Author:
vboxsync
Message:

SUP: Restructured the respawn + child purification code to use the primary thread to trigger the initial image load events since more recent windows versions only seems to trigger these on the primary thread.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/nt/nt.h

    r52955 r52969  
    19741974NTSYSAPI NTSTATUS NTAPI NtWaitForSingleObject(HANDLE, BOOLEAN PLARGE_INTEGER);
    19751975#endif
     1976typedef NTSYSAPI NTSTATUS (NTAPI *PFNNTWAITFORSINGLEOBJECT)(HANDLE, BOOLEAN, PLARGE_INTEGER);
    19761977typedef enum _OBJECT_WAIT_TYPE { WaitAllObjects = 0, WaitAnyObject = 1, ObjectWaitTypeHack = 0x7fffffff } OBJECT_WAIT_TYPE;
    19771978NTSYSAPI NTSTATUS NTAPI NtWaitForMultipleObjects(ULONG, PHANDLE, OBJECT_WAIT_TYPE, BOOLEAN, PLARGE_INTEGER);
     
    19901991NTSYSAPI NTSTATUS NTAPI NtCreateEvent(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, EVENT_TYPE, BOOLEAN);
    19911992NTSYSAPI NTSTATUS NTAPI NtOpenEvent(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
     1993typedef NTSYSAPI NTSTATUS (NTAPI *PFNNTCLEAREVENT)(HANDLE);
    19921994NTSYSAPI NTSTATUS NTAPI NtClearEvent(HANDLE);
    19931995NTSYSAPI NTSTATUS NTAPI NtResetEvent(HANDLE, PULONG);
    19941996NTSYSAPI NTSTATUS NTAPI NtSetEvent(HANDLE, PULONG);
     1997typedef NTSYSAPI NTSTATUS (NTAPI *PFNNTSETEVENT)(HANDLE, PULONG);
    19951998typedef enum _EVENT_INFORMATION_CLASS
    19961999{
  • trunk/src/VBox/HostDrivers/Support/SUPLibInternal.h

    r52967 r52969  
    448448DECLHIDDEN(void)    supR3HardenedWinInitVersion(void);
    449449DECLHIDDEN(void)    supR3HardenedWinInitImports(void);
     450# ifdef ___iprt_nt_nt_h___
     451DECLHIDDEN(void)    supR3HardenedWinGetVeryEarlyImports(uintptr_t uNtDllAddr,
     452                                                        PFNNTWAITFORSINGLEOBJECT *ppfnNtWaitForSingleObject,
     453                                                        PFNNTSETEVENT *ppfnNtSetEvent);
     454# endif
    450455DECLHIDDEN(void)    supR3HardenedWinInitImportsEarly(uintptr_t uNtDllAddr);
    451456DECLHIDDEN(void)    supR3HardenedWinInitSyscalls(bool fReportErrors);
  • trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp

    r52968 r52969  
    175175
    176176/**
    177  * VM process parameters.
     177 * Child requests.
     178 */
     179typedef enum SUPR3WINCHILDREQ
     180{
     181    /** Perform child purification and close full access handles (must be zero). */
     182    kSupR3WinChildReq_PurifyChildAndCloseHandles = 0,
     183    /** Close the events, we're good on our own from here on.  */
     184    kSupR3WinChildReq_CloseEvents,
     185    /** Reporting error. */
     186    kSupR3WinChildReq_Error,
     187    /** End of valid requests. */
     188    kSupR3WinChildReq_End
     189} SUPR3WINCHILDREQ;
     190
     191/**
     192 * Child process parameters.
    178193 */
    179194typedef struct SUPR3WINPROCPARAMS
    180195{
    181196    /** The event semaphore the child will be waiting on. */
    182     HANDLE          hEvtChild;
     197    HANDLE                      hEvtChild;
    183198    /** The event semaphore the parent will be waiting on. */
    184     HANDLE          hEvtParent;
    185 
    186     /** The address of the NTDLL. */
    187     uintptr_t       uNtDllAddr;
    188 
     199    HANDLE                      hEvtParent;
     200
     201    /** The address of the NTDLL. This is only valid during the very early
     202     * initialization as we abuse for thread creation protection. */
     203    uintptr_t                   uNtDllAddr;
     204
     205    /** The requested operation (set by the child). */
     206    SUPR3WINCHILDREQ            enmRequest;
    189207    /** The last status. */
    190     int32_t         rc;
     208    int32_t                     rc;
    191209    /** The init operation the error relates to if message, kSupInitOp_Invalid if
    192210     *  not message. */
    193     SUPINITOP       enmWhat;
     211    SUPINITOP                   enmWhat;
    194212    /** Where if message. */
    195     char            szWhere[80];
     213    char                        szWhere[80];
    196214    /** Error message / path name string space. */
    197     char            szErrorMsg[4096];
     215    char                        szErrorMsg[4096];
    198216} SUPR3WINPROCPARAMS;
     217
     218
     219/**
     220 * Child process data structure for use during child process init setup and
     221 * purification.
     222 */
     223typedef struct SUPR3HARDNTCHILD
     224{
     225    /** Process handle. */
     226    HANDLE                      hProcess;
     227    /** Primary thread handle. */
     228    HANDLE                      hThread;
     229    /** Handle to the parent process, if we're the middle (stub) process. */
     230    HANDLE                      hParent;
     231    /** The event semaphore the child will be waiting on. */
     232    HANDLE                      hEvtChild;
     233    /** The event semaphore the parent will be waiting on. */
     234    HANDLE                      hEvtParent;
     235    /** The address of NTDLL in the child. */
     236    uintptr_t                   uNtDllAddr;
     237    /** The address of NTDLL in this process. */
     238    uintptr_t                   uNtDllParentAddr;
     239    /** Which respawn number this is (1 = stub, 2 = VM). */
     240    int                         iWhich;
     241    /** The basic process info. */
     242    PROCESS_BASIC_INFORMATION   BasicInfo;
     243    /** The probable size of the PEB. */
     244    size_t                      cbPeb;
     245    /** The pristine process environment block. */
     246    PEB                         Peb;
     247    /** The child process parameters. */
     248    SUPR3WINPROCPARAMS          ProcParams;
     249} SUPR3HARDNTCHILD;
     250/** Pointer to a child process data structure. */
     251typedef SUPR3HARDNTCHILD *PSUPR3HARDNTCHILD;
    199252
    200253
     
    204257/** Process parameters.  Specified by parent if VM process, see
    205258 *  supR3HardenedVmProcessInit. */
    206 static SUPR3WINPROCPARAMS   g_ProcParams = { NULL, NULL, 0, 0 };
     259static SUPR3WINPROCPARAMS   g_ProcParams = { NULL, NULL, 0, (SUPR3WINCHILDREQ)0, 0 };
    207260/** Set if supR3HardenedEarlyProcessInit was invoked. */
    208261bool                        g_fSupEarlyProcessInit = false;
     
    21282181
    21292182
    2130 #ifdef RT_ARCH_AMD64
    2131 /**
    2132  * Tries to allocate memory between @a uStart and @a uEnd.
    2133  *
    2134  * @returns Pointer to the memory on success.  NULL on failure.
    2135  * @param   uStart              The start address.
    2136  * @param   uEnd                The end address.  This is lower than @a uStart
    2137  *                              if @a iDirection is negative, and higher if
    2138  *                              positive.
    2139  * @param   iDirection          The search direction.
    2140  * @param   cbAlloc             The number of bytes to allocate.
    2141  */
    2142 static void *supR3HardenedWinAllocHookMemory(uintptr_t uStart, uintptr_t uEnd, intptr_t iDirection, size_t cbAlloc)
    2143 {
    2144     size_t const cbAllocGranularity    = _64K;
    2145     size_t const uAllocGranularityMask = ~(cbAllocGranularity - 1);
    2146     HANDLE const hProc                 = NtCurrentProcess();
    2147 
    2148     /*
    2149      * Make uEnd the last valid return address.
    2150      */
    2151     if (iDirection > 0)
    2152     {
    2153         SUPR3HARDENED_ASSERT(uEnd > cbAlloc);
    2154         uEnd -= cbAlloc;
    2155         uEnd &= uAllocGranularityMask;
    2156     }
    2157     else
    2158         uEnd = RT_ALIGN_Z(uEnd, cbAllocGranularity);
    2159 
    2160     /*
    2161      * Search for free memory.
    2162      */
    2163     uintptr_t uCur = uStart & uAllocGranularityMask;
    2164     for (;;)
    2165     {
    2166         /*
    2167          * Examine the memory at this address, if it's free, try make the allocation here.
    2168          */
    2169         SIZE_T                      cbIgn;
    2170         MEMORY_BASIC_INFORMATION    MemInfo;
    2171         SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryVirtualMemory(hProc,
    2172                                                              (void *)uCur,
    2173                                                              MemoryBasicInformation,
    2174                                                              &MemInfo,
    2175                                                              sizeof(MemInfo),
    2176                                                              &cbIgn));
    2177         if (   MemInfo.State      == MEM_FREE
    2178             && MemInfo.RegionSize >= cbAlloc)
    2179         {
    2180             for (;;)
    2181             {
    2182                 SUPR3HARDENED_ASSERT((uintptr_t)MemInfo.BaseAddress <= uCur);
    2183 
    2184                 PVOID    pvMem = (PVOID)uCur;
    2185                 SIZE_T   cbMem = cbAlloc;
    2186                 NTSTATUS rcNt = NtAllocateVirtualMemory(hProc, &pvMem, 0 /*ZeroBits*/, &cbAlloc,
    2187                                                         MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    2188                 if (NT_SUCCESS(rcNt))
    2189                 {
    2190                     if (  iDirection > 0
    2191                         ?    (uintptr_t)pvMem >= uStart
    2192                           && (uintptr_t)pvMem <= uEnd
    2193                         :    (uintptr_t)pvMem >= uEnd
    2194                           && (uintptr_t)pvMem <= uStart)
    2195                         return pvMem;
    2196                     NtFreeVirtualMemory(hProc, &pvMem, &cbMem, MEM_RELEASE);
    2197                 }
    2198 
    2199                 /* Advance within the free area and try again? */
    2200                 uintptr_t uNext = iDirection > 0 ? uCur + cbAllocGranularity : uCur - cbAllocGranularity;
    2201                 uNext &= uAllocGranularityMask;
    2202                 if (  iDirection > 0
    2203                     ?    uNext <= uCur
    2204                       || uNext >  uEnd
    2205                       || uNext - (uintptr_t)MemInfo.BaseAddress > MemInfo.RegionSize
    2206                       || MemInfo.RegionSize - (uNext - (uintptr_t)MemInfo.BaseAddress) < cbAlloc
    2207                     :    uNext >= uCur
    2208                       || uNext <  uEnd
    2209                       || uNext <  (uintptr_t)MemInfo.BaseAddress)
    2210                     break;
    2211                 uCur = uNext;
    2212             }
    2213         }
    2214 
    2215         /*
    2216          * Advance to the next memory region.
    2217          */
    2218         if (iDirection > 0)
    2219         {
    2220             uCur = (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize;
    2221             uCur = RT_ALIGN_Z(uCur, cbAllocGranularity);
    2222             if (uCur >= uEnd)
    2223                 break;
    2224         }
    2225         else
    2226         {
    2227             uCur = (uintptr_t)(MemInfo.AllocationBase ? MemInfo.AllocationBase : MemInfo.BaseAddress);
    2228             if (uCur > uEnd)
    2229                 uCur -= cbAlloc;
    2230             uCur &= uAllocGranularityMask;
    2231             if (uCur < uEnd)
    2232                 break;
    2233         }
    2234     }
    2235     return NULL;
    2236 }
    2237 #endif
    2238 
    2239 
    22402183static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue)
    22412184{
     
    26792622
    26802623
    2681 /**
    2682  * Gets the SID of the user associated with the process.
    2683  *
    2684  * @returns @c true if we've got a login SID, @c false if not.
    2685  * @param   pSidUser            Where to return the user SID.
    2686  * @param   cbSidUser           The size of the user SID buffer.
    2687  * @param   pSidLogin           Where to return the login SID.
    2688  * @param   cbSidLogin          The size of the login SID buffer.
    2689  */
    2690 static bool supR3HardenedGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
    2691 {
    2692     HANDLE hToken;
    2693     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));
    2694     union
    2695     {
    2696         TOKEN_USER      UserInfo;
    2697         TOKEN_GROUPS    Groups;
    2698         uint8_t         abPadding[4096];
    2699     } uBuf;
    2700     ULONG cbRet = 0;
    2701     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
    2702     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
    2703 
    2704     bool fLoginSid = false;
    2705     NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
    2706     if (NT_SUCCESS(rcNt))
    2707     {
    2708         for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
    2709             if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
    2710             {
    2711                 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
    2712                 fLoginSid = true;
    2713                 break;
    2714             }
    2715     }
    2716 
    2717     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
    2718 
    2719     return fLoginSid;
    2720 }
    2721 
    2722 
    2723 /**
    2724  * Build security attributes for the process or the primary thread (@a fProcess)
    2725  *
    2726  * Process DACLs can be bypassed using the SeDebugPrivilege (generally available
    2727  * to admins, i.e. normal windows users), or by taking ownership and/or
    2728  * modifying the DACL.  However, it restricts
    2729  *
    2730  * @param   pSecAttrs           Where to return the security attributes.
    2731  * @param   pCleanup            Cleanup record.
    2732  * @param   fProcess            Set if it's for the process, clear if it's for
    2733  *                              the primary thread.
    2734  */
    2735 static void supR3HardenedInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
    2736 {
    2737     /*
    2738      * Safe return values.
    2739      */
    2740     suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
    2741 
    2742     pSecAttrs->nLength              = sizeof(*pSecAttrs);
    2743     pSecAttrs->bInheritHandle       = FALSE;
    2744     pSecAttrs->lpSecurityDescriptor = NULL;
    2745 
    2746 /** @todo This isn't at all complete, just sketches... */
    2747 
    2748     /*
    2749      * Create an ACL detailing the access of the above groups.
    2750      */
    2751     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
    2752 
    2753     ULONG fDeny  = DELETE | WRITE_DAC | WRITE_OWNER;
    2754     ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
    2755     ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
    2756     if (fProcess)
    2757     {
    2758         fDeny       |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
    2759                     |  PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
    2760                     |  PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
    2761         fAllow      |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
    2762         fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
    2763         if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
    2764         {
    2765             fAllow      |= PROCESS_QUERY_LIMITED_INFORMATION;
    2766             fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
    2767         }
    2768         if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
    2769             fAllow  |= PROCESS_SET_LIMITED_INFORMATION;
    2770     }
    2771     else
    2772     {
    2773         fDeny       |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
    2774                     |  THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
    2775         fAllow      |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
    2776         fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
    2777         if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
    2778         {
    2779             fAllow      |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
    2780             fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
    2781         }
    2782 
    2783     }
    2784     fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
    2785 
    2786     /* Deny everyone access to bad bits. */
    2787 #if 1
    2788     SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    2789     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
    2790     *RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
    2791     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
    2792                                                           fDeny, &pCleanup->Everyone.Sid));
    2793 #endif
    2794 
    2795 #if 0
    2796     /* Grant some access to the owner - doesn't work. */
    2797     SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
    2798     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
    2799     *RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
    2800 
    2801     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
    2802                                                           fDeny, &pCleanup->Owner.Sid));
    2803     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
    2804                                                            fAllow, &pCleanup->Owner.Sid));
    2805 #endif
    2806 
    2807 #if 1
    2808     bool fHasLoginSid = supR3HardenedGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
    2809                                                        &pCleanup->Login.Sid, sizeof(pCleanup->Login));
    2810 
    2811 # if 1
    2812     /* Grant minimal access to the user. */
    2813     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
    2814                                                           fDeny, &pCleanup->User.Sid));
    2815     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
    2816                                                            fAllow, &pCleanup->User.Sid));
    2817 # endif
    2818 
    2819 # if 1
    2820     /* Grant very limited access to the login sid. */
    2821     if (fHasLoginSid)
    2822     {
    2823         SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
    2824                                                                fAllowLogin, &pCleanup->Login.Sid));
    2825     }
    2826 # endif
    2827 
    2828 #endif
    2829 
    2830     /*
    2831      * Create a security descriptor with the above ACL.
    2832      */
    2833     PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
    2834     pCleanup->pSecDesc = pSecDesc;
    2835 
    2836     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
    2837     SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
    2838                                                                  FALSE /*fDaclDefaulted*/));
    2839     pSecAttrs->lpSecurityDescriptor = pSecDesc;
    2840 }
    2841 
    2842 
    2843 /**
    2844  * Predicate function which tests whether @a ch is a argument separator
    2845  * character.
    2846  *
    2847  * @returns True/false.
    2848  * @param   ch                  The character to examine.
    2849  */
    2850 DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
    2851 {
    2852     return ch == ' '
    2853         || ch == '\t'
    2854         || ch == '\n'
    2855         || ch == '\r';
    2856 }
    2857 
    2858 
    2859 /**
    2860  * Construct the new command line.
    2861  *
    2862  * Since argc/argv are both derived from GetCommandLineW (see
    2863  * suplibHardenedWindowsMain), we skip the argument by argument UTF-8 -> UTF-16
    2864  * conversion and quoting by going to the original source.
    2865  *
    2866  * The executable name, though, is replaced in case it's not a fullly
    2867  * qualified path.
    2868  *
    2869  * The re-spawn indicator is added immediately after the executable name
    2870  * so that we don't get tripped up missing close quote chars in the last
    2871  * argument.
    2872  *
    2873  * @returns Pointer to a command line string (heap).
    2874  * @param   pUniStr         Unicode string structure to initialize to the
    2875  *                          command line. Optional.
    2876  * @param   iWhich          Which respawn we're to check for, 1 being the first
    2877  *                          one, and 2 the second and final.
    2878  */
    2879 static PRTUTF16 supR3HardenedWinConstructCmdLine(PUNICODE_STRING pString, int iWhich)
    2880 {
    2881     SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
    2882 
    2883     /*
    2884      * Get the command line and skip the executable name.
    2885      */
    2886     PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
    2887     PCRTUTF16 pawcArgs = pCmdLineStr->Buffer;
    2888     uint32_t  cwcArgs  = pCmdLineStr->Length / sizeof(WCHAR);
    2889 
    2890     /* Skip leading space (shouldn't be any, but whatever). */
    2891     while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs) )
    2892         cwcArgs--, pawcArgs++;
    2893     SUPR3HARDENED_ASSERT(cwcArgs > 0 && *pawcArgs != '\0');
    2894 
    2895     /* Walk to the end of it. */
    2896     int fQuoted = false;
    2897     do
    2898     {
    2899         if (*pawcArgs == '"')
    2900         {
    2901             fQuoted = !fQuoted;
    2902             cwcArgs--; pawcArgs++;
    2903         }
    2904         else if (*pawcArgs != '\\' || (pawcArgs[1] != '\\' && pawcArgs[1] != '"'))
    2905             cwcArgs--, pawcArgs++;
    2906         else
    2907         {
    2908             unsigned cSlashes = 0;
    2909             do
    2910             {
    2911                 cSlashes++;
    2912                 cwcArgs--;
    2913                 pawcArgs++;
    2914             }
    2915             while (cwcArgs > 0 && *pawcArgs == '\\');
    2916             if (cwcArgs > 0 && *pawcArgs == '"' && (cSlashes & 1))
    2917                 cwcArgs--, pawcArgs++; /* odd number of slashes == escaped quote */
    2918         }
    2919     } while (cwcArgs > 0 && (fQuoted || !suplibCommandLineIsArgSeparator(*pawcArgs)));
    2920 
    2921     /* Skip trailing spaces. */
    2922     while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs))
    2923         cwcArgs--, pawcArgs++;
    2924 
    2925     /*
    2926      * Allocate a new buffer.
    2927      */
    2928     AssertCompile(sizeof(SUPR3_RESPAWN_1_ARG0) == sizeof(SUPR3_RESPAWN_2_ARG0));
    2929     size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_1_ARG0) - 1) / sizeof(SUPR3_RESPAWN_1_ARG0[0]) /* Respawn exe name. */
    2930                       + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
    2931     if (cwcCmdLine * sizeof(WCHAR) >= 0xfff0)
    2932         supR3HardenedFatalMsg("supR3HardenedWinConstructCmdLine", kSupInitOp_Misc, VERR_OUT_OF_RANGE,
    2933                               "Command line is too long (%u chars)!", cwcCmdLine);
    2934 
    2935     PRTUTF16 pwszCmdLine = (PRTUTF16)RTMemAlloc((cwcCmdLine + 1) * sizeof(RTUTF16));
    2936     SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
    2937 
    2938     /*
    2939      * Construct the new command line.
    2940      */
    2941     PRTUTF16 pwszDst = pwszCmdLine;
    2942     for (const char *pszSrc = iWhich == 1 ? SUPR3_RESPAWN_1_ARG0 : SUPR3_RESPAWN_2_ARG0; *pszSrc; pszSrc++)
    2943         *pwszDst++ = *pszSrc;
    2944 
    2945     if (cwcArgs)
    2946     {
    2947         *pwszDst++ = ' ';
    2948         suplibHardenedMemCopy(pwszDst, pawcArgs, cwcArgs * sizeof(RTUTF16));
    2949         pwszDst += cwcArgs;
    2950     }
    2951 
    2952     *pwszDst = '\0';
    2953     SUPR3HARDENED_ASSERT(pwszDst - pwszCmdLine == cwcCmdLine);
    2954 
    2955     if (pString)
    2956     {
    2957         pString->Buffer = pwszCmdLine;
    2958         pString->Length = (USHORT)(cwcCmdLine * sizeof(WCHAR));
    2959         pString->MaximumLength = pString->Length + sizeof(WCHAR);
    2960     }
    2961     return pwszCmdLine;
    2962 }
    2963 
    2964 
    2965 /**
    2966  * Check if the zero terminated NT unicode string is the path to the given
    2967  * system32 DLL.
    2968  *
    2969  * @returns true if it is, false if not.
    2970  * @param   pUniStr             The zero terminated NT unicode string path.
    2971  * @param   pszName             The name of the system32 DLL.
    2972  */
    2973 static bool supR3HardNtIsNamedSystem32Dll(PUNICODE_STRING pUniStr, const char *pszName)
    2974 {
    2975     if (pUniStr->Length > g_System32NtPath.UniStr.Length)
    2976     {
    2977         if (memcmp(pUniStr->Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
    2978         {
    2979             if (pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
    2980             {
    2981                 if (RTUtf16ICmpAscii(&pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR) + 1], pszName) == 0)
    2982                     return true;
    2983             }
    2984         }
    2985     }
    2986 
    2987     return false;
    2988 }
     2624
     2625
     2626
     2627
     2628/*
     2629 *
     2630 * T h r e a d   c r e a t i o n   c o n t r o l
     2631 * T h r e a d   c r e a t i o n   c o n t r o l
     2632 * T h r e a d   c r e a t i o n   c o n t r o l
     2633 *
     2634 */
    29892635
    29902636
     
    31662812
    31672813
     2814
    31682815/*
    3169  * Child-Process Purification - release it from dubious influences.
    3170  * 
    3171  * AV software and other things injecting themselves into the embryonic
    3172  * and budding process to intercept API calls and what not.  Unfortunately
    3173  * this is also the behavior of viruses, malware and other unfriendly
    3174  * software, so we won't stand for it.  AV software can scan our image
    3175  * as they are loaded via kernel hooks, that's sufficient.  No need for
    3176  * matching half of NTDLL or messing with the import table of the
    3177  * process executable.
    3178  */
    3179 
    3180 typedef struct SUPR3HARDNTPUCH
    3181 {
    3182     /** Process handle. */
    3183     HANDLE                      hProcess;
    3184     /** Primary thread handle. */
    3185     HANDLE                      hThread;
    3186     /** Error buffer. */
    3187     PRTERRINFO                  pErrInfo;
    3188     /** The address of NTDLL in the child. */
    3189     uintptr_t                   uNtDllAddr;
    3190     /** The address of NTDLL in this process. */
    3191     uintptr_t                   uNtDllParentAddr;
    3192     /** The basic process info. */
    3193     PROCESS_BASIC_INFORMATION   BasicInfo;
    3194     /** The probable size of the PEB. */
    3195     size_t                      cbPeb;
    3196     /** The pristine process environment block. */
    3197     PEB                         Peb;
    3198 } SUPR3HARDNTPUCH;
    3199 typedef SUPR3HARDNTPUCH *PSUPR3HARDNTPUCH;
    3200 
    3201 
    3202 static int supR3HardNtPuChScrewUpPebForInitialImageEvents(PSUPR3HARDNTPUCH pThis)
    3203 {
    3204     /*
    3205      * Not sure if any of the cracker software uses the PEB at this point, but
    3206      * just in case they do make some of the PEB fields a little less useful.
    3207      */
    3208     PEB Peb = pThis->Peb;
    3209 
    3210     /* Make ImageBaseAddress useless. */
    3211     Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress ^ UINT32_C(0x5f139000));
    3212 #ifdef RT_ARCH_AMD64
    3213     Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress | UINT64_C(0x0313000000000000));
     2816 *
     2817 * R e s p a w n
     2818 * R e s p a w n
     2819 * R e s p a w n
     2820 *
     2821 */
     2822
     2823
     2824/**
     2825 * Gets the SID of the user associated with the process.
     2826 *
     2827 * @returns @c true if we've got a login SID, @c false if not.
     2828 * @param   pSidUser            Where to return the user SID.
     2829 * @param   cbSidUser           The size of the user SID buffer.
     2830 * @param   pSidLogin           Where to return the login SID.
     2831 * @param   cbSidLogin          The size of the login SID buffer.
     2832 */
     2833static bool supR3HardNtChildGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
     2834{
     2835    HANDLE hToken;
     2836    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));
     2837    union
     2838    {
     2839        TOKEN_USER      UserInfo;
     2840        TOKEN_GROUPS    Groups;
     2841        uint8_t         abPadding[4096];
     2842    } uBuf;
     2843    ULONG cbRet = 0;
     2844    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
     2845    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
     2846
     2847    bool fLoginSid = false;
     2848    NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
     2849    if (NT_SUCCESS(rcNt))
     2850    {
     2851        for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
     2852            if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
     2853            {
     2854                SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
     2855                fLoginSid = true;
     2856                break;
     2857            }
     2858    }
     2859
     2860    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
     2861
     2862    return fLoginSid;
     2863}
     2864
     2865
     2866/**
     2867 * Build security attributes for the process or the primary thread (@a fProcess)
     2868 *
     2869 * Process DACLs can be bypassed using the SeDebugPrivilege (generally available
     2870 * to admins, i.e. normal windows users), or by taking ownership and/or
     2871 * modifying the DACL.  However, it restricts
     2872 *
     2873 * @param   pSecAttrs           Where to return the security attributes.
     2874 * @param   pCleanup            Cleanup record.
     2875 * @param   fProcess            Set if it's for the process, clear if it's for
     2876 *                              the primary thread.
     2877 */
     2878static void supR3HardNtChildInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
     2879{
     2880    /*
     2881     * Safe return values.
     2882     */
     2883    suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
     2884
     2885    pSecAttrs->nLength              = sizeof(*pSecAttrs);
     2886    pSecAttrs->bInheritHandle       = FALSE;
     2887    pSecAttrs->lpSecurityDescriptor = NULL;
     2888
     2889/** @todo This isn't at all complete, just sketches... */
     2890
     2891    /*
     2892     * Create an ACL detailing the access of the above groups.
     2893     */
     2894    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
     2895
     2896    ULONG fDeny  = DELETE | WRITE_DAC | WRITE_OWNER;
     2897    ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
     2898    ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
     2899    if (fProcess)
     2900    {
     2901        fDeny       |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
     2902                    |  PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
     2903                    |  PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
     2904        fAllow      |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
     2905        fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
     2906        if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
     2907        {
     2908            fAllow      |= PROCESS_QUERY_LIMITED_INFORMATION;
     2909            fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
     2910        }
     2911        if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
     2912            fAllow  |= PROCESS_SET_LIMITED_INFORMATION;
     2913    }
     2914    else
     2915    {
     2916        fDeny       |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
     2917                    |  THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
     2918        fAllow      |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
     2919        fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
     2920        if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
     2921        {
     2922            fAllow      |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
     2923            fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
     2924        }
     2925
     2926    }
     2927    fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
     2928
     2929    /* Deny everyone access to bad bits. */
     2930#if 1
     2931    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
     2932    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
     2933    *RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
     2934    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
     2935                                                          fDeny, &pCleanup->Everyone.Sid));
    32142936#endif
    32152937
    3216     /*
    3217      * Write the PEB.
    3218      */
    3219     SIZE_T cbActualMem = pThis->cbPeb;
    3220     NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
     2938#if 0
     2939    /* Grant some access to the owner - doesn't work. */
     2940    SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
     2941    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
     2942    *RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
     2943
     2944    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
     2945                                                          fDeny, &pCleanup->Owner.Sid));
     2946    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
     2947                                                           fAllow, &pCleanup->Owner.Sid));
     2948#endif
     2949
     2950#if 1
     2951    bool fHasLoginSid = supR3HardNtChildGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
     2952                                                          &pCleanup->Login.Sid, sizeof(pCleanup->Login));
     2953
     2954# if 1
     2955    /* Grant minimal access to the user. */
     2956    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
     2957                                                          fDeny, &pCleanup->User.Sid));
     2958    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
     2959                                                           fAllow, &pCleanup->User.Sid));
     2960# endif
     2961
     2962# if 1
     2963    /* Grant very limited access to the login sid. */
     2964    if (fHasLoginSid)
     2965    {
     2966        SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
     2967                                                               fAllowLogin, &pCleanup->Login.Sid));
     2968    }
     2969# endif
     2970
     2971#endif
     2972
     2973    /*
     2974     * Create a security descriptor with the above ACL.
     2975     */
     2976    PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
     2977    pCleanup->pSecDesc = pSecDesc;
     2978
     2979    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
     2980    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
     2981                                                                 FALSE /*fDaclDefaulted*/));
     2982    pSecAttrs->lpSecurityDescriptor = pSecDesc;
     2983}
     2984
     2985
     2986/**
     2987 * Predicate function which tests whether @a ch is a argument separator
     2988 * character.
     2989 *
     2990 * @returns True/false.
     2991 * @param   ch                  The character to examine.
     2992 */
     2993DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
     2994{
     2995    return ch == ' '
     2996        || ch == '\t'
     2997        || ch == '\n'
     2998        || ch == '\r';
     2999}
     3000
     3001
     3002/**
     3003 * Construct the new command line.
     3004 *
     3005 * Since argc/argv are both derived from GetCommandLineW (see
     3006 * suplibHardenedWindowsMain), we skip the argument by argument UTF-8 -> UTF-16
     3007 * conversion and quoting by going to the original source.
     3008 *
     3009 * The executable name, though, is replaced in case it's not a fullly
     3010 * qualified path.
     3011 *
     3012 * The re-spawn indicator is added immediately after the executable name
     3013 * so that we don't get tripped up missing close quote chars in the last
     3014 * argument.
     3015 *
     3016 * @returns Pointer to a command line string (heap).
     3017 * @param   pUniStr         Unicode string structure to initialize to the
     3018 *                          command line. Optional.
     3019 * @param   iWhich          Which respawn we're to check for, 1 being the first
     3020 *                          one, and 2 the second and final.
     3021 */
     3022static PRTUTF16 supR3HardNtChildConstructCmdLine(PUNICODE_STRING pString, int iWhich)
     3023{
     3024    SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
     3025
     3026    /*
     3027     * Get the command line and skip the executable name.
     3028     */
     3029    PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
     3030    PCRTUTF16 pawcArgs = pCmdLineStr->Buffer;
     3031    uint32_t  cwcArgs  = pCmdLineStr->Length / sizeof(WCHAR);
     3032
     3033    /* Skip leading space (shouldn't be any, but whatever). */
     3034    while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs) )
     3035        cwcArgs--, pawcArgs++;
     3036    SUPR3HARDENED_ASSERT(cwcArgs > 0 && *pawcArgs != '\0');
     3037
     3038    /* Walk to the end of it. */
     3039    int fQuoted = false;
     3040    do
     3041    {
     3042        if (*pawcArgs == '"')
     3043        {
     3044            fQuoted = !fQuoted;
     3045            cwcArgs--; pawcArgs++;
     3046        }
     3047        else if (*pawcArgs != '\\' || (pawcArgs[1] != '\\' && pawcArgs[1] != '"'))
     3048            cwcArgs--, pawcArgs++;
     3049        else
     3050        {
     3051            unsigned cSlashes = 0;
     3052            do
     3053            {
     3054                cSlashes++;
     3055                cwcArgs--;
     3056                pawcArgs++;
     3057            }
     3058            while (cwcArgs > 0 && *pawcArgs == '\\');
     3059            if (cwcArgs > 0 && *pawcArgs == '"' && (cSlashes & 1))
     3060                cwcArgs--, pawcArgs++; /* odd number of slashes == escaped quote */
     3061        }
     3062    } while (cwcArgs > 0 && (fQuoted || !suplibCommandLineIsArgSeparator(*pawcArgs)));
     3063
     3064    /* Skip trailing spaces. */
     3065    while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs))
     3066        cwcArgs--, pawcArgs++;
     3067
     3068    /*
     3069     * Allocate a new buffer.
     3070     */
     3071    AssertCompile(sizeof(SUPR3_RESPAWN_1_ARG0) == sizeof(SUPR3_RESPAWN_2_ARG0));
     3072    size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_1_ARG0) - 1) / sizeof(SUPR3_RESPAWN_1_ARG0[0]) /* Respawn exe name. */
     3073                      + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
     3074    if (cwcCmdLine * sizeof(WCHAR) >= 0xfff0)
     3075        supR3HardenedFatalMsg("supR3HardNtChildConstructCmdLine", kSupInitOp_Misc, VERR_OUT_OF_RANGE,
     3076                              "Command line is too long (%u chars)!", cwcCmdLine);
     3077
     3078    PRTUTF16 pwszCmdLine = (PRTUTF16)RTMemAlloc((cwcCmdLine + 1) * sizeof(RTUTF16));
     3079    SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
     3080
     3081    /*
     3082     * Construct the new command line.
     3083     */
     3084    PRTUTF16 pwszDst = pwszCmdLine;
     3085    for (const char *pszSrc = iWhich == 1 ? SUPR3_RESPAWN_1_ARG0 : SUPR3_RESPAWN_2_ARG0; *pszSrc; pszSrc++)
     3086        *pwszDst++ = *pszSrc;
     3087
     3088    if (cwcArgs)
     3089    {
     3090        *pwszDst++ = ' ';
     3091        suplibHardenedMemCopy(pwszDst, pawcArgs, cwcArgs * sizeof(RTUTF16));
     3092        pwszDst += cwcArgs;
     3093    }
     3094
     3095    *pwszDst = '\0';
     3096    SUPR3HARDENED_ASSERT(pwszDst - pwszCmdLine == cwcCmdLine);
     3097
     3098    if (pString)
     3099    {
     3100        pString->Buffer = pwszCmdLine;
     3101        pString->Length = (USHORT)(cwcCmdLine * sizeof(WCHAR));
     3102        pString->MaximumLength = pString->Length + sizeof(WCHAR);
     3103    }
     3104    return pwszCmdLine;
     3105}
     3106
     3107
     3108/**
     3109 * Terminates the child process.
     3110 *
     3111 * @param   hProcess            The process handle.
     3112 * @param   pszWhere            Who's having child rasing troubles.
     3113 * @param   rc                  The status code to report.
     3114 * @param   pszFormat           The message format string.
     3115 * @param   ...                 Message format arguments.
     3116 */
     3117static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
     3118{
     3119    /*
     3120     * Terminate the process ASAP and display error.
     3121     */
     3122    NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
     3123
     3124    va_list va;
     3125    va_start(va, pszFormat);
     3126    supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
     3127    va_end(va);
     3128
     3129    /*
     3130     * Wait for the process to really go away.
     3131     */
     3132    PROCESS_BASIC_INFORMATION BasicInfo;
     3133    NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
     3134    bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
     3135    if (!fExitOk)
     3136    {
     3137        NTSTATUS rcNtWait;
     3138        uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
     3139        do
     3140        {
     3141            NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
     3142
     3143            LARGE_INTEGER Timeout;
     3144            Timeout.QuadPart = -20000000; /* 2 second */
     3145            rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
     3146
     3147            rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
     3148            fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
     3149        } while (   !fExitOk
     3150                 && (   rcNtWait == STATUS_TIMEOUT
     3151                     || rcNtWait == STATUS_USER_APC
     3152                     || rcNtWait == STATUS_ALERTED)
     3153                 && supR3HardenedWinGetMilliTS() - uMsTsStart < 60 * 1000);
     3154        if (fExitOk)
     3155            supR3HardenedError(rc, false /*fFatal*/,
     3156                               "NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
     3157                               rc, rc, rcNtWait, hProcess);
     3158    }
     3159
     3160    /*
     3161     * Final error message.
     3162     */
     3163    va_start(va, pszFormat);
     3164    supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
     3165    va_end(va);
     3166}
     3167
     3168
     3169/**
     3170 * Checks the child process when hEvtParent is signalled.
     3171 *
     3172 * This will read the request data from the child and check it against expected
     3173 * request.  If an error is signalled, we'll raise it and make sure the child
     3174 * terminates before terminating the calling process.
     3175 *
     3176 * @param   pThis               The child process data structure.
     3177 * @param   enmExpectedRequest  The expected child request.
     3178 * @param   pszWhat             What we're waiting for.
     3179 */
     3180static void supR3HardNtChildProcessRequest(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, const char *pszWhat)
     3181{
     3182    /*
     3183     * Read the process parameters from the child.
     3184     */
     3185    uintptr_t           uChildAddr = (uintptr_t)pThis->Peb.ImageBaseAddress
     3186                                   + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
     3187    SIZE_T              cbIgnored  = 0;
     3188    RT_ZERO(pThis->ProcParams);
     3189    NTSTATUS rcNt = NtReadVirtualMemory(pThis->hProcess, (PVOID)uChildAddr,
     3190                                        &pThis->ProcParams, sizeof(pThis->ProcParams), &cbIgnored);
    32213191    if (!NT_SUCCESS(rcNt))
    3222         return RTErrInfoSetF(pThis->pErrInfo, VERR_GENERAL_FAILURE, "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
    3223     return VINF_SUCCESS;
    3224 }
    3225 
    3226 
    3227 /**
    3228  * Unmaps a DLL from the child process that was previously mapped by
    3229  * supR3HardNtPuChMapDllIntoChild.
    3230  *
    3231  * @returns Pointer to the DLL mapping on success, NULL on failure.
    3232  * @param   pThis               The child purification instance data.
    3233  * @param   pvBase              The base address of the mapping.  Nothing done
    3234  *                              if NULL.
    3235  * @param   pszShort            The short name (for logging).
    3236  */
    3237 static void supR3HardNtPuChUnmapDllFromChild(PSUPR3HARDNTPUCH pThis, PVOID pvBase, const char *pszShort)
    3238 {
    3239     if (pvBase)
    3240     {
    3241         /*SUP_DPRINTF(("supR3HardNtPuChUnmapDllFromChild: Calling NtUnmapViewOfSection on %p / %s\n", pvBase, pszShort));*/
    3242         NTSTATUS rcNt = NtUnmapViewOfSection(pThis->hProcess, pvBase);
    3243         if (!NT_SUCCESS(!rcNt))
    3244             SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: NtUnmapViewOfSection failed on %s: %#x (%p)\n",
    3245                          pszShort, rcNt, pvBase));
    3246     }
    3247 }
    3248 
    3249 
    3250 /**
    3251  * Maps a DLL into the child process.
    3252  *
    3253  * @returns Pointer to the DLL mapping on success, NULL on failure.
    3254  * @param   pThis               The child purification instance data.
    3255  * @param   pNtName             The path to the DLL.
    3256  * @param   pszShort            The short name (for logging).
    3257  */
    3258 static PVOID supR3HardNtPuChMapDllIntoChild(PSUPR3HARDNTPUCH pThis, PUNICODE_STRING pNtName, const char *pszShort)
    3259 {
    3260     HANDLE              hFile  = RTNT_INVALID_HANDLE_VALUE;
    3261     IO_STATUS_BLOCK     Ios    = RTNT_IO_STATUS_BLOCK_INITIALIZER;
    3262     OBJECT_ATTRIBUTES   ObjAttr;
    3263     InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
    3264     NTSTATUS rcNt = NtCreateFile(&hFile,
    3265                                  GENERIC_READ | GENERIC_EXECUTE,
    3266                                  &ObjAttr,
    3267                                  &Ios,
    3268                                  NULL /* Allocation Size*/,
    3269                                  FILE_ATTRIBUTE_NORMAL,
    3270                                  FILE_SHARE_READ,
    3271                                  FILE_OPEN,
    3272                                  FILE_NON_DIRECTORY_FILE,
    3273                                  NULL /*EaBuffer*/,
    3274                                  0 /*EaLength*/);
    3275     if (NT_SUCCESS(rcNt))
    3276         rcNt = Ios.Status;
    3277     PVOID pvRet = NULL;
    3278     if (NT_SUCCESS(rcNt))
    3279     {
    3280         HANDLE hSection = RTNT_INVALID_HANDLE_VALUE;
    3281         rcNt = NtCreateSection(&hSection,
    3282                                SECTION_MAP_EXECUTE | SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_QUERY,
    3283                                NULL /* pObjAttr*/, NULL /*pMaxSize*/,
    3284                                PAGE_EXECUTE, SEC_IMAGE, hFile);
    3285         if (NT_SUCCESS(rcNt))
    3286         {
    3287             SIZE_T cbView = 0;
    3288             SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: mapping view of %s\n", pszShort)); /* For SEP. */
    3289             rcNt = NtMapViewOfSection(hSection, pThis->hProcess, &pvRet, 0 /*ZeroBits*/, 0 /*CommitSize*/,
    3290                                       NULL /*pOffSect*/, &cbView, ViewShare, 0 /*AllocationType*/, PAGE_READWRITE);
    3291             if (NT_SUCCESS(rcNt))
    3292                 SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: %s mapped at %p LB %#x\n", pszShort, pvRet, cbView));
     3192        supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt,
     3193                                  "NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
     3194
     3195    /*
     3196     * Is it the expected request?
     3197     */
     3198    if (pThis->ProcParams.enmRequest == enmExpectedRequest)
     3199        return;
     3200
     3201    /*
     3202     * No, not the expected request. If it's an error request, tell the child
     3203     * to terminate itself, otherwise we'll have to terminate it.
     3204     */
     3205    pThis->ProcParams.szErrorMsg[sizeof(pThis->ProcParams.szErrorMsg) - 1] = '\0';
     3206    pThis->ProcParams.szWhere[sizeof(pThis->ProcParams.szWhere) - 1] = '\0';
     3207    SUP_DPRINTF(("supR3HardenedWinCheckChild: enmRequest=%d rc=%d enmWhat=%d %s: %s\n",
     3208                 pThis->ProcParams.enmRequest, pThis->ProcParams.rc, pThis->ProcParams.enmWhat,
     3209                 pThis->ProcParams.szWhere, pThis->ProcParams.szErrorMsg));
     3210
     3211    if (pThis->ProcParams.enmRequest != kSupR3WinChildReq_Error)
     3212        supR3HardenedWinKillChild(pThis, "supR3HardenedWinCheckChild", VERR_INVALID_PARAMETER,
     3213                                  "Unexpected child request #%d. Was expecting #%d (%s).\n",
     3214                                  pThis->ProcParams.enmRequest, enmExpectedRequest, pszWhat);
     3215
     3216    rcNt = NtSetEvent(pThis->hEvtChild, NULL);
     3217    if (!NT_SUCCESS(rcNt))
     3218        supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt, "NtSetEvent failed: %#x\n", rcNt);
     3219
     3220    /* Wait for it to terminate. */
     3221    LARGE_INTEGER Timeout;
     3222    Timeout.QuadPart = -50000000; /* 5 seconds */
     3223    rcNt = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, &Timeout);
     3224    if (rcNt != STATUS_WAIT_0)
     3225    {
     3226        SUP_DPRINTF(("supR3HardNtChildProcessRequest: Child is taking too long to quit (rcWait=%#x), killing it...\n", rcNt));
     3227        NtTerminateProcess(pThis->hProcess, DBG_TERMINATE_PROCESS);
     3228    }
     3229
     3230    /*
     3231     * Report the error in the same way as it occured in the guest.
     3232     */
     3233    if (pThis->ProcParams.enmWhat == kSupInitOp_Invalid)
     3234        supR3HardenedFatalMsg("supR3HardenedWinCheckChild", kSupInitOp_Misc, pThis->ProcParams.rc,
     3235                              "%s", pThis->ProcParams.szErrorMsg);
     3236    else
     3237        supR3HardenedFatalMsg(pThis->ProcParams.szWhere, pThis->ProcParams.enmWhat, pThis->ProcParams.rc,
     3238                              "%s", pThis->ProcParams.szErrorMsg);
     3239}
     3240
     3241
     3242/**
     3243 * Waits for the child to make a certain request or terminate.
     3244 *
     3245 * The stub process will also wait on it's parent to terminate.
     3246 * This call will only return if the child made the expected request.
     3247 *
     3248 * @param   pThis               The child process data structure.
     3249 * @param   enmExpectedRequest  The child request to wait for.
     3250 * @param   cMsTimeout          The number of milliseconds to wait (at least).
     3251 * @param   pszWhat             What we're waiting for.
     3252 */
     3253static void supR3HardNtChildWaitFor(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, RTMSINTERVAL cMsTimeout,
     3254                                    const char *pszWhat)
     3255{
     3256    /*
     3257     * The wait loop.
     3258     * Will return when the expected request arrives.
     3259     * Will break out when one of the processes terminates.
     3260     */
     3261    NTSTATUS      rcNtWait;
     3262    LARGE_INTEGER Timeout;
     3263    uint64_t      uMsTsStart = supR3HardenedWinGetMilliTS();
     3264    uint64_t      cMsElapsed = 0;
     3265    for (;;)
     3266    {
     3267        /*
     3268         * Assemble handles to wait for.
     3269         */
     3270        ULONG  cHandles = 1;
     3271        HANDLE ahHandles[3];
     3272        ahHandles[0] = pThis->hProcess;
     3273        if (pThis->hEvtParent)
     3274            ahHandles[cHandles++] = pThis->hEvtParent;
     3275        if (pThis->hParent)
     3276            ahHandles[cHandles++] = pThis->hParent;
     3277
     3278        /*
     3279         * Do the waiting according to the callers wishes.
     3280         */
     3281        if (   enmExpectedRequest == kSupR3WinChildReq_End
     3282            || cMsTimeout  == RT_INDEFINITE_WAIT)
     3283            rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*Timeout*/);
     3284        else
     3285        {
     3286            Timeout.QuadPart = -(int64_t)(cMsTimeout - cMsElapsed) * 10000;
     3287            rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, &Timeout);
     3288        }
     3289
     3290        /*
     3291         * Process child request.
     3292         */
     3293        if (rcNtWait == STATUS_WAIT_0 + 1 && pThis->hEvtParent != NULL)
     3294        {
     3295            supR3HardNtChildProcessRequest(pThis, enmExpectedRequest, pszWhat);
     3296            SUP_DPRINTF(("supR3HardNtChildWaitFor: Found expected request %d (%s) after %llu ms.\n",
     3297                         enmExpectedRequest, pszWhat, supR3HardenedWinGetMilliTS() - uMsTsStart));
     3298            return; /* Expected request received. */
     3299        }
     3300
     3301        /*
     3302         * Process termination?
     3303         */
     3304        if (   (ULONG)rcNtWait - (ULONG)STATUS_WAIT_0           < cHandles
     3305            || (ULONG)rcNtWait - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
     3306            break;
     3307
     3308        /*
     3309         * Check sanity.
     3310         */
     3311        if (   rcNtWait != STATUS_TIMEOUT
     3312            && rcNtWait != STATUS_USER_APC
     3313            && rcNtWait != STATUS_ALERTED)
     3314            supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
     3315                                      "NtWaitForMultipleObjects returned %#x waiting for #%d (%s)\n",
     3316                                      rcNtWait, enmExpectedRequest, pszWhat);
     3317
     3318        /*
     3319         * Calc elapsed time for the next timeout calculation, checking to see
     3320         * if we've timed out already.
     3321         */
     3322        cMsElapsed = supR3HardenedWinGetMilliTS() - uMsTsStart;
     3323        if (   cMsElapsed > cMsTimeout
     3324            && cMsTimeout != RT_INDEFINITE_WAIT
     3325            && enmExpectedRequest != kSupR3WinChildReq_End)
     3326        {
     3327            if (rcNtWait == STATUS_USER_APC || rcNtWait == STATUS_ALERTED)
     3328                cMsElapsed = cMsTimeout - 1; /* try again */
    32933329            else
    32943330            {
    3295                 SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: NtMapViewOfSection failed on %s: %#x\n", pszShort, rcNt));
    3296                 pvRet = NULL;
     3331                /* We timed out. */
     3332                supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
     3333                                          "Timed out after %llu ms waiting for child request #%d (%s).\n",
     3334                                          cMsElapsed, enmExpectedRequest, pszWhat);
    32973335            }
    3298             NtClose(hSection);
    32993336        }
    3300         else
    3301             SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: NtCreateSection failed on %s: %#x\n", pszShort, rcNt));
    3302         NtClose(hFile);
    3303     }
     3337    }
     3338
     3339    /*
     3340     * Proxy the termination code of the child, if it exited already.
     3341     */
     3342    PROCESS_BASIC_INFORMATION BasicInfo;
     3343    NTSTATUS rcNt1 = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
     3344    NTSTATUS rcNt2 = STATUS_PENDING;
     3345    NTSTATUS rcNt3 = STATUS_PENDING;
     3346    if (   !NT_SUCCESS(rcNt1)
     3347        || BasicInfo.ExitStatus == STATUS_PENDING)
     3348    {
     3349        rcNt2 = NtTerminateProcess(pThis->hProcess, RTEXITCODE_FAILURE);
     3350        Timeout.QuadPart = NT_SUCCESS(rcNt2) ? -20000000 /* 2 sec */ : -1280000 /* 128 ms */;
     3351        rcNt3 = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, NULL /*Timeout*/);
     3352        BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
     3353    }
     3354
     3355    SUP_DPRINTF(("supR3HardNtChildWaitFor[%d]: Quitting: ExitCode=%#x (rcNtWait=%#x, rcNt1=%#x, rcNt2=%#x, rcNt3=%#x, %llu ms, %s);\n",
     3356                 pThis->iWhich, BasicInfo.ExitStatus, rcNtWait, rcNt1, rcNt2, rcNt3,
     3357                 supR3HardenedWinGetMilliTS() - uMsTsStart, pszWhat));
     3358    suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
     3359}
     3360
     3361
     3362/**
     3363 * Closes full access child thread and process handles, making a harmless
     3364 * duplicate of the process handle first.
     3365 *
     3366 * The hProcess member of the child process data structure will be change to the
     3367 * harmless handle, while the hThread will be set to NULL.
     3368 *
     3369 * @param   pThis               The child process data structure.
     3370 */
     3371static void supR3HardNtChildCloseFullAccessHandles(PSUPR3HARDNTCHILD pThis)
     3372{
     3373    /*
     3374     * The thread handle.
     3375     */
     3376    NTSTATUS rcNt = NtClose(pThis->hThread);
     3377    if (!NT_SUCCESS(rcNt))
     3378        supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt, "NtClose(hThread) failed: %#x", rcNt);
     3379    pThis->hThread = NULL;
     3380
     3381    /*
     3382     * Duplicate the process handle into a harmless one.
     3383     */
     3384    HANDLE hProcWait;
     3385    ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_VM_READ;
     3386    if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
     3387        fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
    33043388    else
    3305         SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: Error opening %s: %#x\n", pszShort, rcNt));
    3306     return pvRet;
    3307 }
    3308 
    3309 
    3310 /**
    3311  * Trigger the initial image events without actually initializing the process.
    3312  *
    3313  * This is a trick to force sysplant.sys to call its hand by tripping the image
    3314  * loaded event for the main executable and ntdll images.  This will happen when
    3315  * the first thread in a process starts executing in PspUserThreadStartup.  We
    3316  * create a second thread that quits immediately by means of temporarily
    3317  * replacing ntdll!LdrInitializeThunk by a NtTerminateThread call.
    3318  * (LdrInitializeThunk is called by way of an APC queued the thread is created,
    3319  * thus NtSetContextThread is of no use.)
    3320  *
    3321  * @returns VBox status code.
    3322  * @param   pThis               The child cleanup
    3323  * @param   pErrInfo            For extended error information.
    3324  */
    3325 static int supR3HardNtPuChTriggerInitialImageEvents(PSUPR3HARDNTPUCH pThis)
    3326 {
    3327     /*
    3328      * Use the on-disk image for the ntdll entrypoints here.
    3329      */
    3330     PSUPHNTLDRCACHEENTRY pLdrEntry;
    3331     int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
    3332     if (RT_FAILURE(rc))
    3333         return RTErrInfoSetF(pThis->pErrInfo, rc, "supHardNtLdrCacheOpen failed on NTDLL: %Rrc", rc);
    3334 
    3335     RTLDRADDR uLdrInitThunk;
    3336     rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pLdrEntry->pbBits, pThis->uNtDllAddr, UINT32_MAX,
    3337                           "LdrInitializeThunk", &uLdrInitThunk);
    3338     if (RT_FAILURE(rc))
    3339         return RTErrInfoSetF(pThis->pErrInfo, rc, "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
    3340     PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
    3341 
    3342     RTLDRADDR uNtTerminateThread;
    3343     rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pLdrEntry->pbBits, pThis->uNtDllAddr, UINT32_MAX,
    3344                           "NtTerminateThread", &uNtTerminateThread);
    3345     if (RT_FAILURE(rc))
    3346         return RTErrInfoSetF(pThis->pErrInfo, rc, "Error locating NtTerminateThread in NTDLL: %Rrc", rc);
    3347 
    3348     SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: uLdrInitThunk=%p uNtTerminateThread=%p\n",
    3349                  (uintptr_t)uLdrInitThunk, (uintptr_t)uNtTerminateThread));
    3350 
    3351     /*
    3352      * Patch the child's LdrInitializeThunk to exit the thread immediately.
    3353      */
    3354     uint8_t abBackup[16];
    3355     rc = supR3HardNtDisableThreadCreationEx(pThis->hProcess, pvLdrInitThunk, (void *)(uintptr_t)uNtTerminateThread,
    3356                                             abBackup, sizeof(abBackup), pThis->pErrInfo);
    3357     if (RT_FAILURE(rc))
    3358         return rc;
    3359 
    3360     /*
    3361      * To further muddle the waters, we map the executable image and ntdll.dll
    3362      * a 2nd time into the process before we actually start executing the thread
    3363      * and trigger the genuine image load events.
    3364      *
    3365      * Update #1 (after 4.3.15 build 7):
    3366      *      Turns out Symantec Endpoint Protection deadlocks when we map the
    3367      *      executable into the process like this.  The system only works
    3368      *      halfways after that Powerbutton, impossible to shutdown without
    3369      *      using the power or reset button. The order of the two mappings
    3370      *      below doesn't matter. Haven't had time to look at stack yet.
    3371      *      Observed on W7/64, SEP v12.1.4112.4156.
    3372      *
    3373      * Update #2 (after 4.3.16):
    3374      *      Some avast! users complain about a deadlock mapping ntdll.dll
    3375      *      as well.  Unfortunately not reproducible, so there may possibly be
    3376      *      some other cause.  Sad as it's really a serious bug in whichever
    3377      *      software it is that is causing it, and we'd like to report it to
    3378      *      the responsible party.
    3379      */
    3380 #if 0
    3381     PVOID pvExe2 = supR3HardNtPuChMapDllIntoChild(pThis, &g_SupLibHardenedExeNtPath.UniStr, "executable[2nd]");
    3382 #else
    3383     PVOID pvExe2 = NULL;
    3384 #endif
    3385 #if 0
    3386     UNICODE_STRING NtName1 = RTNT_CONSTANT_UNISTR(L"\\SystemRoot\\System32\\ntdll.dll");
    3387     PVOID pvNtDll2 = supR3HardNtPuChMapDllIntoChild(pThis, &NtName1, "ntdll.dll[2nd]");
    3388 #else
    3389     PVOID pvNtDll2 = NULL;
    3390 #endif
    3391 
    3392     /*
    3393      * Create the thread, waiting 10 seconds for it to complete.
    3394      */
    3395     CLIENT_ID Thread2Id;
    3396     HANDLE hThread2;
    3397     NTSTATUS rcNt = RtlCreateUserThread(pThis->hProcess,
    3398                                         NULL /* SecurityAttribs */,
    3399                                         FALSE /* CreateSuspended */,
    3400                                         0 /* ZeroBits */,
    3401                                         0 /* MaximumStackSize */,
    3402                                         0 /* CommittedStackSize */,
    3403                                         (PFNRT)2 /* StartAddress */,
    3404                                         NULL /*Parameter*/ ,
    3405                                         &hThread2,
    3406                                         &Thread2Id);
    3407     if (NT_SUCCESS(rcNt))
    3408     {
    3409         LARGE_INTEGER Timeout;
    3410         Timeout.QuadPart = -10 * 10000000; /* 10 seconds */
    3411         NtWaitForSingleObject(hThread2, FALSE /* Alertable */, &Timeout);
    3412         NtTerminateThread(hThread2, DBG_TERMINATE_THREAD);
    3413         NtClose(hThread2);
    3414     }
    3415 
    3416 #if 0
    3417     /*
    3418      * Map kernel32.dll and kernelbase.dll (if applicable) into the process.
    3419      * This triggers should image load events that may set of AV activities
    3420      * that we'd rather see early than later.
    3421      */
    3422     UNICODE_STRING NtName2 = RTNT_CONSTANT_UNISTR(L"\\SystemRoot\\System32\\kernel32.dll");
    3423     PVOID pvKernel32 = supR3HardNtPuChMapDllIntoChild(pThis, &NtName2, "kernel32.dll");
    3424 
    3425     UNICODE_STRING NtName3 = RTNT_CONSTANT_UNISTR(L"\\SystemRoot\\System32\\KernelBase.dll");
    3426     PVOID pvKernelBase = g_uNtVerCombined >= SUP_NT_VER_VISTA
    3427                        ? supR3HardNtPuChMapDllIntoChild(pThis, &NtName3, "KernelBase.dll")
    3428                        : NULL;
    3429 #endif
    3430 
    3431     /*
    3432      * Fudge factor for letting kernel threads get a chance to mess up our
    3433      * process asynchronously.
    3434      */
    3435     uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
    3436     uint32_t cMsKludge = (g_fSupAdversaries & SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT) ? 256 : g_fSupAdversaries ? 64 : 16;
    3437 cMsKludge = 1024;
    3438     do
    3439     {
    3440         NtYieldExecution();
    3441         LARGE_INTEGER Time;
    3442         Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
    3443         NtDelayExecution(FALSE, &Time);
    3444     } while (supR3HardenedWinGetMilliTS() - uMsTsStart < cMsKludge);
    3445     SUP_DPRINTF(("supR3HardNtPuChTriggerInitialImageEvents: Startup delay kludge #1: %u ms\n",
    3446                  supR3HardenedWinGetMilliTS() - uMsTsStart));
    3447 
    3448 #if 0
    3449     /*
    3450      * Unmap the image we mapped into the guest above.
    3451      */
    3452     supR3HardNtPuChUnmapDllFromChild(pThis, pvKernel32, "kernel32.dll");
    3453     supR3HardNtPuChUnmapDllFromChild(pThis, pvKernelBase, "KernelBase.dll");
    3454     supR3HardNtPuChUnmapDllFromChild(pThis, pvNtDll2, "ntdll.dll[2nd]");
    3455     supR3HardNtPuChUnmapDllFromChild(pThis, pvExe2, "executable[2nd]");
    3456 #endif
    3457 
    3458     /*
    3459      * Restore the original thunk code and protection.
    3460      * We do this after waiting as anyone trying to kick of threads in the
    3461      * process will get nothing done as long as our patch is in place.
    3462      */
    3463     rc = supR3HardNtEnableThreadCreationEx(pThis->hProcess, pvLdrInitThunk, abBackup, sizeof(abBackup), pThis->pErrInfo);
    3464     if (RT_FAILURE(rc))
    3465         return rc;
    3466 
    3467     return VINF_SUCCESS;
    3468 }
    3469 
    3470 #if 0
    3471 static int supR3HardenedWinScratchChildMemory(HANDLE hProcess, void *pv, size_t cb, const char *pszWhat, PRTERRINFO pErrInfo)
    3472 {
    3473     SUP_DPRINTF(("supR3HardenedWinScratchChildMemory: %p %#x\n", pv, cb));
    3474 
    3475     PVOID  pvCopy = pv;
    3476     SIZE_T cbCopy = cb;
    3477     NTSTATUS rcNt = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
     3389        fRights |= PROCESS_QUERY_INFORMATION;
     3390    rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
     3391                             NtCurrentProcess(), &hProcWait,
     3392                             fRights, 0 /*HandleAttributes*/, 0);
     3393    if (rcNt == STATUS_ACCESS_DENIED)
     3394    {
     3395        supR3HardenedError(rcNt, false /*fFatal*/,
     3396                           "supR3HardenedWinDoReSpawn: NtDuplicateObject(,,,,%#x,,) -> %#x, retrying with only %#x...\n",
     3397                           fRights, rcNt, SYNCHRONIZE);
     3398        rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
     3399                                 NtCurrentProcess(), &hProcWait,
     3400                                 SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
     3401    }
    34783402    if (!NT_SUCCESS(rcNt))
    3479         return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "NtProtectVirtualMemory/%s (%p LB %#zx) failed: %#x",
    3480                              pszWhat, pv, cb, rcNt);
    3481     return VINF_SUCCESS;
    3482 }
    3483 #endif
    3484 
    3485 
    3486 static int supR3HardNtPuChSanitizePeb(PSUPR3HARDNTPUCH pThis)
     3403        supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt,
     3404                                  "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
     3405    /*
     3406     * Close the process handle and replace it with the harmless one.
     3407     */
     3408    rcNt = NtClose(pThis->hProcess);
     3409    pThis->hProcess = hProcWait;
     3410    if (!NT_SUCCESS(rcNt))
     3411        supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
     3412                                  "NtClose failed on child process handle: %#x\n", rcNt);
     3413}
     3414
     3415
     3416/**
     3417 * This restores the child PEB and tweaks a couple of fields before we do the
     3418 * child purification and let the process run normally.
     3419 *
     3420 * @param   pThis               The child process data structure.
     3421 */
     3422static void supR3HardNtChildSanitizePeb(PSUPR3HARDNTCHILD pThis)
    34873423{
    34883424    /*
     
    35313467    NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
    35323468    if (!NT_SUCCESS(rcNt))
    3533         return RTErrInfoSetF(pThis->pErrInfo, VERR_GENERAL_FAILURE, "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
    3534 
    3535     return VINF_SUCCESS;
    3536 }
    3537 
    3538 
    3539 static void supR3HardNtPuChFindNtdll(PSUPR3HARDNTPUCH pThis)
     3469        supR3HardenedWinKillChild(pThis, "supR3HardNtChildSanitizePeb", rcNt,
     3470                                  "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
     3471
     3472}
     3473
     3474
     3475/**
     3476 * Purifies the child process after very early init has been performed.
     3477 *
     3478 * @param   pThis               The child process data structure.
     3479 */
     3480static void supR3HardNtChildPurify(PSUPR3HARDNTCHILD pThis)
     3481{
     3482    /*
     3483     * We loop until we no longer make any fixes.  This is similar to what
     3484     * we do (or used to do, really) in the fAvastKludge case of
     3485     * supR3HardenedWinInit.  We might be up against asynchronous changes,
     3486     * which we fudge by waiting a short while before earch purification. This
     3487     * is arguably a fragile technique, but it's currently the best we've got.
     3488     * Fortunately, most AVs seems to either favor immediate action on initial
     3489     * load events or (much better for us) later events like kernel32.
     3490     */
     3491    uint32_t cMsFudge = g_fSupAdversaries ? 512 : 256;
     3492    uint32_t cFixes;
     3493    for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
     3494    {
     3495        /*
     3496         * Delay.
     3497         */
     3498        uint32_t cSleeps = 0;
     3499        uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
     3500        do
     3501        {
     3502            NtYieldExecution();
     3503            LARGE_INTEGER Time;
     3504            Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
     3505            NtDelayExecution(FALSE, &Time);
     3506            cSleeps++;
     3507        } while (   supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
     3508                 || cSleeps < 8);
     3509        SUP_DPRINTF(("supR3HardenedWinInit: Startup delay kludge #2/%u: %u ms, %u sleeps\n",
     3510                     iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
     3511
     3512        /*
     3513         * Purify.
     3514         */
     3515        cFixes = 0;
     3516        int rc = supHardenedWinVerifyProcess(pThis->hProcess, pThis->hThread, SUPHARDNTVPKIND_CHILD_PURIFICATION,
     3517                                             &cFixes, RTErrInfoInitStatic(&g_ErrInfoStatic));
     3518        if (RT_FAILURE(rc))
     3519            supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", rc,
     3520                                      "supHardenedWinVerifyProcess failed with %Rrc: %s", g_ErrInfoStatic.szMsg);
     3521        if (cFixes == 0)
     3522            return; /* We're probably good. */
     3523
     3524        if (!g_fSupAdversaries)
     3525            g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
     3526        cMsFudge = 512;
     3527
     3528        /*
     3529         * Log the KiOpPrefetchPatchCount value if available, hoping it might
     3530         * sched some light on spider38's case.
     3531         */
     3532        ULONG cPatchCount = 0;
     3533        NTSTATUS rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
     3534                                                 &cPatchCount, sizeof(cPatchCount), NULL);
     3535        if (NT_SUCCESS(rcNt))
     3536            SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
     3537                         cFixes, g_fSupAdversaries, cPatchCount));
     3538        else
     3539            SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
     3540    }
     3541
     3542    /*
     3543     * We've given up fixing the child process.  Probably fighting someone
     3544     * that monitors their patches or/and our activities.
     3545     */
     3546    supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", VERR_TRY_AGAIN,
     3547                              "Unable to purify child process! After 16 tries, we still %u fix(es) in the last pass.",
     3548                              cFixes);
     3549}
     3550
     3551
     3552
     3553/**
     3554 * Sets up the early process init.
     3555 *
     3556 * @param   pThis               The child process data structure.
     3557 */
     3558static void supR3HardNtChildSetUpChildInit(PSUPR3HARDNTCHILD pThis)
     3559{
     3560    uintptr_t const uChildExeAddr = (uintptr_t)pThis->Peb.ImageBaseAddress;
     3561
     3562    /*
     3563     * Plant the process parameters.  This ASSUMES the handle inheritance is
     3564     * performed when creating the child process.
     3565     */
     3566    RT_ZERO(pThis->ProcParams);
     3567    pThis->ProcParams.hEvtChild  = pThis->hEvtChild;
     3568    pThis->ProcParams.hEvtParent = pThis->hEvtParent;
     3569    pThis->ProcParams.uNtDllAddr = pThis->uNtDllAddr;
     3570    pThis->ProcParams.enmRequest = kSupR3WinChildReq_Error;
     3571    pThis->ProcParams.rc         = VINF_SUCCESS;
     3572
     3573    uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
     3574    SIZE_T    cbIgnored;
     3575    NTSTATUS  rcNt = NtWriteVirtualMemory(pThis->hProcess, (PVOID)uChildAddr, &pThis->ProcParams,
     3576                                          sizeof(pThis->ProcParams), &cbIgnored);
     3577    if (!NT_SUCCESS(rcNt))
     3578        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
     3579                                  "NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
     3580
     3581    /*
     3582     * Locate the LdrInitializeThunk address in the child as well as pristine
     3583     * code bits for it.
     3584     */
     3585    PSUPHNTLDRCACHEENTRY pLdrEntry;
     3586    int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
     3587    if (RT_FAILURE(rc))
     3588        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
     3589                                  "supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
     3590
     3591    uint8_t *pbChildNtDllBits;
     3592    rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, pThis->uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
     3593    if (RT_FAILURE(rc))
     3594        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
     3595                                  "supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
     3596
     3597    RTLDRADDR uLdrInitThunk;
     3598    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pThis->uNtDllAddr, UINT32_MAX,
     3599                          "LdrInitializeThunk", &uLdrInitThunk);
     3600    if (RT_FAILURE(rc))
     3601        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
     3602                                  "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
     3603    PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
     3604    SUP_DPRINTF(("supR3HardenedWinSetupChildInit: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
     3605
     3606    /*
     3607     * Calculate the address of our code in the child process.
     3608     */
     3609    uintptr_t uEarlyProcInitEP = uChildExeAddr + (  (uintptr_t)&supR3HardenedEarlyProcessInitThunk
     3610                                                  - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
     3611
     3612    /*
     3613     * Compose the LdrInitializeThunk replacement bytes.
     3614     * Note! The amount of code we replace here must be less or equal to what
     3615     *       the process verification code ignores.
     3616     */
     3617    uint8_t abNew[16];
     3618    memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - pThis->uNtDllAddr), sizeof(abNew));
     3619#ifdef RT_ARCH_AMD64
     3620    abNew[0] = 0xff;
     3621    abNew[1] = 0x25;
     3622    *(uint32_t *)&abNew[2] = 0;
     3623    *(uint64_t *)&abNew[6] = uEarlyProcInitEP;
     3624#elif defined(RT_ARCH_X86)
     3625    abNew[0] = 0xe9;
     3626    *(uint32_t *)&abNew[1] = uEarlyProcInitEP - ((uint32_t)uLdrInitThunk + 5);
     3627#else
     3628# error "Unsupported arch."
     3629#endif
     3630
     3631    /*
     3632     * Install the LdrInitializeThunk replacement code in the child process.
     3633     */
     3634    PVOID   pvProt = pvLdrInitThunk;
     3635    SIZE_T  cbProt = sizeof(abNew);
     3636    ULONG   fOldProt;
     3637    rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
     3638    if (!NT_SUCCESS(rcNt))
     3639        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
     3640                                  "NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
     3641
     3642    rcNt = NtWriteVirtualMemory(pThis->hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
     3643    if (!NT_SUCCESS(rcNt))
     3644        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
     3645                                  "NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
     3646
     3647    pvProt = pvLdrInitThunk;
     3648    cbProt = sizeof(abNew);
     3649    rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
     3650    if (!NT_SUCCESS(rcNt))
     3651        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
     3652                                  "NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
     3653
     3654    /* Caller starts child execution. */
     3655    SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Start child.\n"));
     3656}
     3657
     3658
     3659
     3660/**
     3661 * This messes with the child PEB before we trigger the initial image events.
     3662 *
     3663 * @param   pThis               The child process data structure.
     3664 */
     3665static void supR3HardNtChildScrewUpPebForInitialImageEvents(PSUPR3HARDNTCHILD pThis)
     3666{
     3667    /*
     3668     * Not sure if any of the cracker software uses the PEB at this point, but
     3669     * just in case they do make some of the PEB fields a little less useful.
     3670     */
     3671    PEB Peb = pThis->Peb;
     3672
     3673    /* Make ImageBaseAddress useless. */
     3674    Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress ^ UINT32_C(0x5f139000));
     3675#ifdef RT_ARCH_AMD64
     3676    Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress | UINT64_C(0x0313000000000000));
     3677#endif
     3678
     3679    /*
     3680     * Write the PEB.
     3681     */
     3682    SIZE_T cbActualMem = pThis->cbPeb;
     3683    NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
     3684    if (!NT_SUCCESS(rcNt))
     3685        supR3HardenedWinKillChild(pThis, "supR3HardNtChildScrewUpPebForInitialImageEvents", rcNt,
     3686                                  "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
     3687}
     3688
     3689
     3690/**
     3691 * Check if the zero terminated NT unicode string is the path to the given
     3692 * system32 DLL.
     3693 *
     3694 * @returns true if it is, false if not.
     3695 * @param   pUniStr             The zero terminated NT unicode string path.
     3696 * @param   pszName             The name of the system32 DLL.
     3697 */
     3698static bool supR3HardNtIsNamedSystem32Dll(PUNICODE_STRING pUniStr, const char *pszName)
     3699{
     3700    if (pUniStr->Length > g_System32NtPath.UniStr.Length)
     3701    {
     3702        if (memcmp(pUniStr->Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
     3703        {
     3704            if (pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
     3705            {
     3706                if (RTUtf16ICmpAscii(&pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR) + 1], pszName) == 0)
     3707                    return true;
     3708            }
     3709        }
     3710    }
     3711
     3712    return false;
     3713}
     3714
     3715
     3716/**
     3717 * Worker for supR3HardNtChildGatherData that locates NTDLL in the child
     3718 * process.
     3719 *
     3720 * @param   pThis               The child process data structure.
     3721 */
     3722static void supR3HardNtChildFindNtdll(PSUPR3HARDNTCHILD pThis)
    35403723{
    35413724    /*
     
    36063789    }
    36073790
     3791    supR3HardenedWinKillChild(pThis, "supR3HardNtChildFindNtdll", VERR_MODULE_NOT_FOUND, "ntdll.dll not found in child process.");
     3792}
     3793
     3794
     3795/**
     3796 * Gather child data.
     3797 *
     3798 * @param   This                The child process data structure.
     3799 */
     3800static void supR3HardNtChildGatherData(PSUPR3HARDNTCHILD pThis)
     3801{
     3802    /*
     3803     * Basic info.
     3804     */
     3805    ULONG cbActual = 0;
     3806    NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation,
     3807                                              &pThis->BasicInfo, sizeof(pThis->BasicInfo), &cbActual);
     3808    if (!NT_SUCCESS(rcNt))
     3809        supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
     3810                                  "NtQueryInformationProcess/ProcessBasicInformation failed: %#x", rcNt);
     3811
     3812    /*
     3813     * If this is the middle (stub) process, we wish to wait for both child
     3814     * and parent.  So open the parent process.  Not fatal if we cannnot.
     3815     */
     3816    if (pThis->iWhich > 1)
     3817    {
     3818        OBJECT_ATTRIBUTES ObjAttr;
     3819        InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
     3820
     3821        CLIENT_ID ClientId;
     3822        ClientId.UniqueProcess = (HANDLE)pThis->BasicInfo.InheritedFromUniqueProcessId;
     3823        ClientId.UniqueThread  = NULL;
     3824
     3825        rcNt = NtOpenProcess(&pThis->hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
    36083826#ifdef DEBUG
    3609     supR3HardenedFatal("%s: ntdll.dll not found in child.", __FUNCTION__);
     3827        SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
    36103828#endif
    3611 }
    3612 
    3613 
    3614 
    3615 static int supR3HardenedWinPurifyChild(HANDLE hProcess, HANDLE hThread, uintptr_t *puChildNtDllAddr, uintptr_t *puChildExeAddr,
    3616                                        PRTERRINFO pErrInfo)
    3617 {
    3618     *puChildNtDllAddr = 0;
    3619     *puChildExeAddr = 0;
    3620 
    3621     /*
    3622      * Initialize the purifier instance data.
    3623      */
    3624     SUPR3HARDNTPUCH This;
    3625     This.hProcess = hProcess;
    3626     This.hThread  = hThread;
    3627     This.pErrInfo = pErrInfo;
    3628 
    3629     ULONG cbActual = 0;
    3630     NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessBasicInformation,
    3631                                               &This.BasicInfo, sizeof(This.BasicInfo), &cbActual);
     3829        if (!NT_SUCCESS(rcNt))
     3830        {
     3831            pThis->hParent = NULL;
     3832            SUP_DPRINTF(("supR3HardNtChildGatherData: Failed to open parent process (%#p): %#x\n", ClientId.UniqueProcess, rcNt));
     3833        }
     3834    }
     3835
     3836    /*
     3837     * Process environment block.
     3838     */
     3839    if (g_uNtVerCombined < SUP_NT_VER_W2K3)
     3840        pThis->cbPeb = PEB_SIZE_W51;
     3841    else if (g_uNtVerCombined < SUP_NT_VER_VISTA)
     3842        pThis->cbPeb = PEB_SIZE_W52;
     3843    else if (g_uNtVerCombined < SUP_NT_VER_W70)
     3844        pThis->cbPeb = PEB_SIZE_W6;
     3845    else if (g_uNtVerCombined < SUP_NT_VER_W80)
     3846        pThis->cbPeb = PEB_SIZE_W7;
     3847    else if (g_uNtVerCombined < SUP_NT_VER_W81)
     3848        pThis->cbPeb = PEB_SIZE_W80;
     3849    else
     3850        pThis->cbPeb = PEB_SIZE_W81;
     3851
     3852    SUP_DPRINTF(("supR3HardNtChildGatherData: PebBaseAddress=%p cbPeb=%#x\n",
     3853                 pThis->BasicInfo.PebBaseAddress, pThis->cbPeb));
     3854
     3855    SIZE_T cbActualMem;
     3856    RT_ZERO(pThis->Peb);
     3857    rcNt = NtReadVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &pThis->Peb, sizeof(pThis->Peb), &cbActualMem);
    36323858    if (!NT_SUCCESS(rcNt))
    3633         return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
    3634                              "NtQueryInformationProcess/ProcessBasicInformation failed: %#x", rcNt);
    3635 
    3636     if (g_uNtVerCombined < SUP_NT_VER_W2K3)
    3637         This.cbPeb = PEB_SIZE_W51;
    3638     else if (g_uNtVerCombined < SUP_NT_VER_VISTA)
    3639         This.cbPeb = PEB_SIZE_W52;
    3640     else if (g_uNtVerCombined < SUP_NT_VER_W70)
    3641         This.cbPeb = PEB_SIZE_W6;
    3642     else if (g_uNtVerCombined < SUP_NT_VER_W80)
    3643         This.cbPeb = PEB_SIZE_W7;
    3644     else if (g_uNtVerCombined < SUP_NT_VER_W81)
    3645         This.cbPeb = PEB_SIZE_W80;
    3646     else
    3647         This.cbPeb = PEB_SIZE_W81;
    3648 
    3649     SUP_DPRINTF(("supR3HardenedWinPurifyChild: PebBaseAddress=%p cbPeb=%#x\n", This.BasicInfo.PebBaseAddress, This.cbPeb));
    3650 
    3651     SIZE_T cbActualMem;
    3652     RT_ZERO(This.Peb);
    3653     rcNt = NtReadVirtualMemory(hProcess, This.BasicInfo.PebBaseAddress, &This.Peb, sizeof(This.Peb), &cbActualMem);
    3654     if (!NT_SUCCESS(rcNt))
    3655         return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "NtReadVirtualMemory/Peb failed: %#x", rcNt);
    3656 
    3657     supR3HardNtPuChFindNtdll(&This);
    3658 
    3659     *puChildNtDllAddr = This.uNtDllAddr;
    3660     *puChildExeAddr   = (uintptr_t)This.Peb.ImageBaseAddress;
    3661 
    3662     /*
    3663      * Do the work, the last bit we tag along with the process verfication code.
    3664      */
    3665     int rc = supR3HardNtPuChScrewUpPebForInitialImageEvents(&This);
    3666     if (RT_SUCCESS(rc))
    3667         rc = supR3HardNtPuChTriggerInitialImageEvents(&This);
    3668     if (RT_SUCCESS(rc))
    3669         rc = supR3HardNtPuChSanitizePeb(&This);
    3670     if (RT_SUCCESS(rc))
    3671         rc = supHardenedWinVerifyProcess(hProcess, hThread, SUPHARDNTVPKIND_CHILD_PURIFICATION, NULL /*pcFixes*/, pErrInfo);
    3672 
    3673     return rc;
    3674 }
    3675 
    3676 
    3677 /**
    3678  * Terminates the child process.
    3679  *
    3680  * @param   hProcess            The process handle.
    3681  * @param   pszWhere            Who's having child rasing troubles.
    3682  * @param   rc                  The status code to report.
    3683  * @param   pszFormat           The message format string.
    3684  * @param   ...                 Message format arguments.
    3685  */
    3686 static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
    3687 {
    3688     /*
    3689      * Terminate the process ASAP and display error.
    3690      */
    3691     NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
    3692 
    3693     va_list va;
    3694     va_start(va, pszFormat);
    3695     supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
    3696     va_end(va);
    3697 
    3698     /*
    3699      * Wait for the process to really go away.
    3700      */
    3701     PROCESS_BASIC_INFORMATION BasicInfo;
    3702     NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
    3703     bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
    3704     if (!fExitOk)
    3705     {
    3706         NTSTATUS rcNtWait;
    3707         uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
    3708         do
    3709         {
    3710             NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
    3711 
    3712             LARGE_INTEGER Timeout;
    3713             Timeout.QuadPart = -20000000; /* 2 second */
    3714             rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
    3715 
    3716             rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
    3717             fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
    3718         } while (   !fExitOk
    3719                  && (   rcNtWait == STATUS_TIMEOUT
    3720                      || rcNtWait == STATUS_USER_APC
    3721                      || rcNtWait == STATUS_ALERTED)
    3722                  && supR3HardenedWinGetMilliTS() - uMsTsStart < 60 * 1000);
    3723         if (fExitOk)
    3724             supR3HardenedError(rc, false /*fFatal*/,
    3725                                "NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
    3726                                rc, rc, rcNtWait, hProcess);
    3727     }
    3728 
    3729     /*
    3730      * Final error message.
    3731      */
    3732     va_start(va, pszFormat);
    3733     supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
    3734     va_end(va);
    3735 }
    3736 
    3737 
    3738 /**
    3739  * Checks the child process for error when the parent event semaphore is
    3740  * signaled.
    3741  *
    3742  * If there is an error pending, this function will not return.
    3743  *
    3744  * @param   hProcWait       The child process handle.
    3745  * @param   uChildExeAddr   The address of the executable in the child process.
    3746  * @param   phEvtParent     Pointer to the parent event semaphore handle.  We
    3747  *                          may close the event semaphore and set it to NULL.
    3748  * @param   phEvtChild      Pointer to the child event semaphore handle.  We may
    3749  *                          close the event semaphore and set it to NULL.
    3750  */
    3751 static void supR3HardenedWinCheckChild(HANDLE hProcWait, uintptr_t uChildExeAddr, HANDLE *phEvtParent, HANDLE *phEvtChild)
    3752 {
    3753     /*
    3754      * Read the process parameters from the child.
    3755      */
    3756     uintptr_t           uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
    3757     SIZE_T              cbIgnored = 0;
    3758     SUPR3WINPROCPARAMS  ChildProcParams;
    3759     RT_ZERO(ChildProcParams);
    3760     NTSTATUS rcNt = NtReadVirtualMemory(hProcWait, (PVOID)uChildAddr, &ChildProcParams, sizeof(ChildProcParams), &cbIgnored);
    3761     if (!NT_SUCCESS(rcNt))
    3762         supR3HardenedWinKillChild(hProcWait, "supR3HardenedWinCheckChild", rcNt,
    3763                                   "NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
    3764 
    3765     /*
    3766      * Signal the child to get on with whatever it's doing.
    3767      */
    3768     rcNt = NtSetEvent(*phEvtChild, NULL);
    3769     if (!NT_SUCCESS(rcNt))
    3770         supR3HardenedWinKillChild(hProcWait, "supR3HardenedWinCheckChild", rcNt, "NtSetEvent failed: %#x\n", rcNt);
    3771 
    3772     /*
    3773      * Close the event semaphore handles.
    3774      */
    3775     rcNt = NtClose(*phEvtParent);
    3776     if (NT_SUCCESS(rcNt))
    3777         rcNt = NtClose(*phEvtChild);
    3778     if (!NT_SUCCESS(rcNt))
    3779         supR3HardenedWinKillChild(hProcWait, "supR3HardenedWinCheckChild", rcNt, "NtClose failed on event sem: %#x\n", rcNt);
    3780     *phEvtChild = NULL;
    3781     *phEvtParent = NULL;
    3782 
    3783     /*
    3784      * Process the information we read.
    3785      */
    3786     if (ChildProcParams.rc == VINF_SUCCESS)
    3787         return;
    3788 
    3789     /* An error occurred, report it. */
    3790     ChildProcParams.szErrorMsg[sizeof(ChildProcParams.szErrorMsg) - 1] = '\0';
    3791     ChildProcParams.szWhere[sizeof(ChildProcParams.szWhere) - 1] = '\0';
    3792     SUP_DPRINTF(("supR3HardenedWinCheckChild: rc=%d enmWhat=%d %s: %s\n",
    3793                  ChildProcParams.rc, ChildProcParams.enmWhat, ChildProcParams.szWhere, ChildProcParams.szErrorMsg));
    3794 
    3795     if (ChildProcParams.enmWhat == kSupInitOp_Invalid)
    3796         supR3HardenedFatalMsg("supR3HardenedWinCheckChild", kSupInitOp_Misc, ChildProcParams.rc,
    3797                               "%s", ChildProcParams.szErrorMsg);
    3798     else
    3799         supR3HardenedFatalMsg(ChildProcParams.szWhere, ChildProcParams.enmWhat, ChildProcParams.rc,
    3800                               "%s", ChildProcParams.szErrorMsg);
    3801 }
    3802 
    3803 
    3804 static void supR3HardenedWinSetupChildInit(HANDLE hProcess, HANDLE hThread, uintptr_t uChildNtDllAddr, uintptr_t uChildExeAddr,
    3805                                            HANDLE hEvtChild, HANDLE hEvtParent)
    3806 {
    3807     /*
    3808      * Plant the process parameters.  This ASSUMES the handle inheritance is
    3809      * performed when creating the child process.
    3810      */
    3811     SUPR3WINPROCPARAMS ChildProcParams;
    3812     RT_ZERO(ChildProcParams);
    3813     ChildProcParams.hEvtChild  = hEvtChild;
    3814     ChildProcParams.hEvtParent = hEvtParent;
    3815     ChildProcParams.uNtDllAddr = uChildNtDllAddr;
    3816     ChildProcParams.rc         = VINF_SUCCESS;
    3817 
    3818     uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
    3819     SIZE_T    cbIgnored;
    3820     NTSTATUS  rcNt = NtWriteVirtualMemory(hProcess, (PVOID)uChildAddr, &ChildProcParams, sizeof(ChildProcParams), &cbIgnored);
    3821     if (!NT_SUCCESS(rcNt))
    3822         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rcNt,
    3823                                   "NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
    3824 
    3825     /*
    3826      * Locate the LdrInitializeThunk address in the child as well as pristine
    3827      * code bits for it.
    3828      */
    3829     PSUPHNTLDRCACHEENTRY pLdrEntry;
    3830     int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
    3831     if (RT_FAILURE(rc))
    3832         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rc,
    3833                                   "supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
    3834 
    3835     uint8_t *pbChildNtDllBits;
    3836     rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, uChildNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
    3837     if (RT_FAILURE(rc))
    3838         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rc,
    3839                                   "supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
    3840 
    3841     RTLDRADDR uLdrInitThunk;
    3842     rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, uChildNtDllAddr, UINT32_MAX,
    3843                           "LdrInitializeThunk", &uLdrInitThunk);
    3844     if (RT_FAILURE(rc))
    3845         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rc,
    3846                                   "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
    3847     PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
    3848     SUP_DPRINTF(("supR3HardenedWinSetupChildInit: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
    3849 
    3850     /*
    3851      * Calculate the address of our code in the child process.
    3852      */
    3853     uintptr_t uEarlyProcInitEP = uChildExeAddr + (  (uintptr_t)&supR3HardenedEarlyProcessInitThunk
    3854                                                   - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
    3855 
    3856     /*
    3857      * Compose the LdrInitializeThunk replacement bytes.
    3858      * Note! The amount of code we replace here must be less or equal to what
    3859      *       the process verification code ignores.
    3860      */
    3861     uint8_t abNew[16];
    3862     memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - uChildNtDllAddr), sizeof(abNew));
    3863 #ifdef RT_ARCH_AMD64
    3864     abNew[0] = 0xff;
    3865     abNew[1] = 0x25;
    3866     *(uint32_t *)&abNew[2] = 0;
    3867     *(uint64_t *)&abNew[6] = uEarlyProcInitEP;
    3868 #elif defined(RT_ARCH_X86)
    3869     abNew[0] = 0xe9;
    3870     *(uint32_t *)&abNew[1] = uEarlyProcInitEP - ((uint32_t)uLdrInitThunk + 5);
    3871 #else
    3872 # error "Unsupported arch."
    3873 #endif
    3874 
    3875     /*
    3876      * Install the LdrInitializeThunk replacement code in the child process.
    3877      */
    3878     PVOID   pvProt = pvLdrInitThunk;
    3879     SIZE_T  cbProt = sizeof(abNew);
    3880     ULONG   fOldProt;
    3881     rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
    3882     if (!NT_SUCCESS(rcNt))
    3883         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rcNt,
    3884                                   "NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
    3885 
    3886     rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
    3887     if (!NT_SUCCESS(rcNt))
    3888         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rcNt,
    3889                                   "NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
    3890 
    3891     pvProt = pvLdrInitThunk;
    3892     cbProt = sizeof(abNew);
    3893     rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
    3894     if (!NT_SUCCESS(rcNt))
    3895         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinSetupChildInit", rcNt,
    3896                                   "NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
    3897 
    3898     /* Caller starts child execution. */
    3899     SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Start child.\n"));
     3859        supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
     3860                                  "NtReadVirtualMemory/Peb failed: %#x", rcNt);
     3861
     3862    /*
     3863     * Locate NtDll.
     3864     */
     3865    supR3HardNtChildFindNtdll(pThis);
    39003866}
    39013867
     
    39073873 * @param   iWhich              Which respawn we're to check for, 1 being the
    39083874 *                              first one, and 2 the second and final.
    3909  *
    3910  * @todo    Split up this function.
    3911  */
    3912 static int supR3HardenedWinDoReSpawn(int iWhich)
     3875 */
     3876static void supR3HardenedWinDoReSpawn(int iWhich)
    39133877{
    39143878    NTSTATUS                        rcNt;
     
    39193883
    39203884    /*
    3921      * Set up VM child communication event semaphores.
    3922      */
     3885     * Init the child process data structure, creating the child communication
     3886     * event sempahores.
     3887     */
     3888    SUPR3HARDNTCHILD This;
     3889    RT_ZERO(This);
     3890    This.iWhich = iWhich;
     3891
    39233892    OBJECT_ATTRIBUTES ObjAttrs;
    3924     HANDLE hEvtChild = NULL;
     3893    This.hEvtChild = NULL;
    39253894    InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
    3926     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, NotificationEvent, FALSE));
    3927 
    3928     HANDLE hEvtParent = NULL;
     3895    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
     3896
     3897    This.hEvtParent = NULL;
    39293898    InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
    3930     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, NotificationEvent, FALSE));
     3899    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
    39313900
    39323901    /*
     
    39353904    SECURITY_ATTRIBUTES ProcessSecAttrs;
    39363905    MYSECURITYCLEANUP   ProcessSecAttrsCleanup;
    3937     supR3HardenedInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
     3906    supR3HardNtChildInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
    39383907
    39393908    SECURITY_ATTRIBUTES ThreadSecAttrs;
    39403909    MYSECURITYCLEANUP   ThreadSecAttrsCleanup;
    3941     supR3HardenedInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
     3910    supR3HardNtChildInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
    39423911
    39433912#if 1
     
    39693938     * Construct the command line and launch the process.
    39703939     */
    3971     PRTUTF16 pwszCmdLine = supR3HardenedWinConstructCmdLine(NULL, iWhich);
     3940    PRTUTF16 pwszCmdLine = supR3HardNtChildConstructCmdLine(NULL, iWhich);
    39723941
    39733942    supR3HardenedWinEnableThreadCreation();
     
    39913960    SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [kernel32].\n",
    39923961                 iWhich, ProcessInfoW32.dwProcessId, ProcessInfoW32.dwThreadId));
    3993     HANDLE hProcess = ProcessInfoW32.hProcess;
    3994     HANDLE hThread  = ProcessInfoW32.hThread;
     3962    This.hProcess = ProcessInfoW32.hProcess;
     3963    This.hThread  = ProcessInfoW32.hThread;
    39953964
    39963965#else
     
    40053974
    40063975    UNICODE_STRING CmdLine;
    4007     supR3HardenedWinConstructCmdLine(&CmdLine, iWhich);
     3976    supR3HardNtChildConstructCmdLine(&CmdLine, iWhich);
    40083977
    40093978    PRTL_USER_PROCESS_PARAMETERS pProcParams = NULL;
     
    40484017    RtlDestroyProcessParameters(pProcParams);
    40494018
    4050     HANDLE hProcess = ProcessInfoNt.ProcessHandle;
    4051     HANDLE hThread  = ProcessInfoNt.ThreadHandle;
     4019    This.hProcess = ProcessInfoNt.ProcessHandle;
     4020    This.hThread  = ProcessInfoNt.ThreadHandle;
    40524021#endif
    40534022
     
    40574026     * supR3HardenedWinInit.)
    40584027     */
    4059     rcNt = NtSetInformationThread(hThread, ThreadHideFromDebugger, NULL, 0);
     4028    rcNt = NtSetInformationThread(This.hThread, ThreadHideFromDebugger, NULL, 0);
    40604029    if (!NT_SUCCESS(rcNt))
    4061         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", rcNt,
     4030        supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", rcNt,
    40624031                                  "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
    40634032#endif
    40644033
    40654034    /*
    4066      * Clean up the process.
    4067      */
    4068     uintptr_t uChildNtDllAddr;
    4069     uintptr_t uChildExeAddr;
    4070     int rc = supR3HardenedWinPurifyChild(hProcess, hThread, &uChildNtDllAddr, &uChildExeAddr,
    4071                                          RTErrInfoInitStatic(&g_ErrInfoStatic));
    4072     if (RT_FAILURE(rc))
    4073         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", rc, "%s", g_ErrInfoStatic.szMsg);
    4074 
    4075     /*
    4076      * Start the process execution.
    4077      */
    4078     supR3HardenedWinSetupChildInit(hProcess, hThread, uChildNtDllAddr, uChildExeAddr, hEvtChild, hEvtParent);
     4035     * Perform very early child initialization.
     4036     */
     4037    supR3HardNtChildGatherData(&This);
     4038    supR3HardNtChildScrewUpPebForInitialImageEvents(&This);
     4039    supR3HardNtChildSetUpChildInit(&This);
    40794040
    40804041    ULONG cSuspendCount = 0;
    4081     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtResumeThread(hThread, &cSuspendCount));
     4042    rcNt = NtResumeThread(This.hThread, &cSuspendCount);
     4043    if (!NT_SUCCESS(rcNt))
     4044        supR3HardenedWinKillChild(&This, "supR3HardenedWinDoReSpawn", rcNt, "NtResumeThread failed: %#x", rcNt);
     4045
     4046    /*
     4047     * Santizie the pre-NTDLL child when it's ready.
     4048     *
     4049     * AV software and other things injecting themselves into the embryonic
     4050     * and budding process to intercept API calls and what not.  Unfortunately
     4051     * this is also the behavior of viruses, malware and other unfriendly
     4052     * software, so we won't stand for it.  AV software can scan our image
     4053     * as they are loaded via kernel hooks, that's sufficient.  No need for
     4054     * patching half of NTDLL or messing with the import table of the
     4055     * process executable.
     4056     */
     4057    supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_PurifyChildAndCloseHandles, 2000 /*ms*/, "PurifyChildAndCloseHandles");
     4058    supR3HardNtChildPurify(&This);
     4059    supR3HardNtChildSanitizePeb(&This);
    40824060
    40834061    /*
     
    40864064     * away with the process handle returned by CreateProcess.
    40874065     */
    4088     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hThread));
    4089 
    4090     PROCESS_BASIC_INFORMATION BasicInfo;
    4091     HANDLE hProcWait;
    4092     ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_VM_READ;
    4093     if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
    4094         fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
    4095     else
    4096         fRights |= PROCESS_QUERY_INFORMATION;
    4097     rcNt = NtDuplicateObject(NtCurrentProcess(), hProcess,
    4098                              NtCurrentProcess(), &hProcWait,
    4099                              fRights, 0 /*HandleAttributes*/, 0);
    4100     if (rcNt == STATUS_ACCESS_DENIED)
    4101     {
    4102         supR3HardenedError(rcNt, false /*fFatal*/,
    4103                            "supR3HardenedWinDoReSpawn: NtDuplicateObject(,,,,%#x,,) -> %#x, retrying with only %#x...\n",
    4104                            fRights, rcNt, SYNCHRONIZE);
    4105         rcNt = NtDuplicateObject(NtCurrentProcess(), hProcess,
    4106                                  NtCurrentProcess(), &hProcWait,
    4107                                  SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
    4108     }
     4066    supR3HardNtChildCloseFullAccessHandles(&This);
     4067
     4068    /*
     4069     * Signal the child that we've closed the unrestricted handles and it can
     4070     * safely try open the driver.
     4071     */
     4072    rcNt = NtSetEvent(This.hEvtChild, NULL);
    41094073    if (!NT_SUCCESS(rcNt))
    4110         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
    4111                                   "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
    4112 
    4113     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hProcess));
    4114     hProcess = NULL;
    4115 
    4116     /*
    4117      * Signal the child that we've closed the unrestricted handles.
    4118      */
    4119     rcNt = NtSetEvent(hEvtChild, NULL);
    4120     if (!NT_SUCCESS(rcNt))
    4121         supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
     4074        supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
    41224075                                  "NtSetEvent failed on child process handle: %#x\n", rcNt);
    41234076
     
    41344087
    41354088    /*
    4136      * If this is the middle process, wait for both parent and child to quit.
    4137      */
    4138     HANDLE hParent = NULL;
    4139     if (iWhich > 1)
    4140     {
    4141         rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
    4142         if (NT_SUCCESS(rcNt))
    4143         {
    4144             OBJECT_ATTRIBUTES ObjAttr;
    4145             InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
    4146 
    4147             CLIENT_ID ClientId;
    4148             ClientId.UniqueProcess = (HANDLE)BasicInfo.InheritedFromUniqueProcessId;
    4149             ClientId.UniqueThread  = NULL;
    4150 
    4151             rcNt = NtOpenProcess(&hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
    4152         }
    4153 #ifdef DEBUG
    4154         SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
    4155 #endif
    4156     }
    4157 
    4158     for (;;)
    4159     {
    4160         HANDLE ahHandles[3];
    4161         ULONG  cHandles = 1;
    4162         ahHandles[0] = hProcWait;
    4163         if (hEvtParent != NULL)
    4164             ahHandles[cHandles++] = hEvtParent;
    4165         if (hParent != NULL)
    4166             ahHandles[cHandles++] = hParent;
    4167 
    4168         rcNt = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*pTimeout*/);
    4169         if (rcNt == STATUS_WAIT_0 + 1 && hEvtParent != NULL)
    4170             supR3HardenedWinCheckChild(hProcWait, uChildExeAddr, &hEvtParent, &hEvtChild);
    4171         else if (   (ULONG)rcNt - (ULONG)STATUS_WAIT_0           < cHandles
    4172                  || (ULONG)rcNt - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
    4173             break;
    4174         else if (   rcNt != STATUS_TIMEOUT
    4175                  && rcNt != STATUS_USER_APC
    4176                  && rcNt != STATUS_ALERTED)
    4177             supR3HardenedFatal("NtWaitForMultipleObjects returned %#x\n", rcNt);
    4178     }
    4179 
    4180     if (hParent != NULL)
    4181         NtClose(hParent);
    4182 
    4183     /*
    4184      * Proxy the termination code of the child, if it exited already.
    4185      */
    4186     NTSTATUS rcNt2 = NtQueryInformationProcess(hProcWait, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
    4187     if (!NT_SUCCESS(rcNt2))
    4188         BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
    4189     else if (BasicInfo.ExitStatus == STATUS_PENDING)
    4190     {
    4191         if (hEvtParent)
    4192             NtTerminateProcess(hProcWait, RTEXITCODE_FAILURE);
    4193         BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
    4194     }
    4195 
    4196     NtClose(hProcWait);
    4197     SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): Quitting: ExitCode=%#x rcNt=%#x\n", iWhich, BasicInfo.ExitStatus, rcNt));
    4198     suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
     4089     * Wait for the child to get to suplibHardenedWindowsMain so we can close the handles.
     4090     */
     4091    supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_CloseEvents, 60000 /*ms*/, "CloseEvents");
     4092
     4093    NtClose(This.hEvtChild);
     4094    NtClose(This.hEvtParent);
     4095    This.hEvtChild  = NULL;
     4096    This.hEvtParent = NULL;
     4097
     4098    /*
     4099     * Wait for the process to terminate.
     4100     */
     4101    supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_End, RT_INDEFINITE_WAIT, "the end");
     4102    SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */
    41994103}
    42004104
     
    45454449     * Respawn the process with kernel protection for the new process.
    45464450     */
    4547     return supR3HardenedWinDoReSpawn(iWhich);
     4451    supR3HardenedWinDoReSpawn(iWhich);
     4452    SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */
     4453    return RTEXITCODE_FAILURE;
    45484454}
    45494455
     
    53395245    {
    53405246        SUPR3HARDENED_ASSERT(g_fSupEarlyProcessInit);
     5247
     5248        g_ProcParams.enmRequest = kSupR3WinChildReq_CloseEvents;
    53415249        NtSetEvent(g_ProcParams.hEvtParent, NULL);
     5250
    53425251        NtClose(g_ProcParams.hEvtParent);
    53435252        NtClose(g_ProcParams.hEvtChild);
     
    54605369    g_ProcParams.enmWhat = enmWhat;
    54615370    g_ProcParams.rc      = RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR_2 : rc;
    5462 
     5371    g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
     5372
     5373    NtClearEvent(g_ProcParams.hEvtChild);
    54635374    NTSTATUS rcNt = NtSetEvent(g_ProcParams.hEvtParent, NULL);
    54645375    if (NT_SUCCESS(rcNt))
     
    54675378        Timeout.QuadPart = -300000000; /* 30 second */
    54685379        NTSTATUS rcNt = NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout);
    5469         NtClearEvent(g_ProcParams.hEvtChild);
    54705380    }
    54715381}
     
    54895399{
    54905400    /*
    5491      * Only let the first thread thru.
    5492      */
    5493     if (!ASMAtomicCmpXchgU32((uint32_t volatile *)&g_enmSupR3HardenedMainState,
    5494                              SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED,
    5495                              SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED))
     5401     * When the first thread gets here we wait for the parent to continue with
     5402     * the process purifications.  The primary thread must execute for image
     5403     * load notifications to trigger, at least in more recent windows versions.
     5404     * The old trick of starting a different thread that terminates immediately
     5405     * thus doesn't work.
     5406     *
     5407     * We are not allowed to modify any data at this point because it will be
     5408     * reset by the child process purification the parent does when we stop. To
     5409     * sabotage thread creation during purification, and to avoid unnecessary
     5410     * work for the parent, we reset g_ProcParams before signalling the parent
     5411     * here.
     5412     */
     5413    if (g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
    54965414    {
    54975415        NtTerminateThread(0, 0);
    5498         return 0x22;  /* crash */
    5499     }
    5500     g_fSupEarlyProcessInit = true;
     5416        return 0x22; /* crash */
     5417    }
     5418
     5419    /* Retrieve the data we need. */
     5420    uintptr_t uNtDllAddr = ASMAtomicXchgPtrT(&g_ProcParams.uNtDllAddr, 0, uintptr_t);
     5421    if (!RT_VALID_PTR(uNtDllAddr))
     5422    {
     5423        NtTerminateThread(0, 0);
     5424        return 0x23; /* crash */
     5425    }
     5426
     5427    HANDLE hEvtChild  = g_ProcParams.hEvtChild;
     5428    HANDLE hEvtParent = g_ProcParams.hEvtParent;
     5429    if (   hEvtChild  == NULL
     5430        || hEvtChild  == RTNT_INVALID_HANDLE_VALUE
     5431        || hEvtParent == NULL
     5432        || hEvtParent == RTNT_INVALID_HANDLE_VALUE)
     5433    {
     5434        NtTerminateThread(0, 0);
     5435        return 0x24; /* crash */
     5436    }
     5437
     5438    /* Resolve the APIs we need. */
     5439    PFNNTWAITFORSINGLEOBJECT    pfnNtWaitForSingleObject;
     5440    PFNNTSETEVENT               pfnNtSetEvent;
     5441    supR3HardenedWinGetVeryEarlyImports(uNtDllAddr, &pfnNtWaitForSingleObject, &pfnNtSetEvent);
     5442
     5443    /* Signal the parent that we're ready for purification. */
     5444    RT_ZERO(g_ProcParams);
     5445    g_ProcParams.enmRequest = kSupR3WinChildReq_PurifyChildAndCloseHandles;
     5446    NTSTATUS rcNt = pfnNtSetEvent(hEvtParent, NULL);
     5447    if (rcNt != STATUS_SUCCESS)
     5448        return 0x33; /* crash */
     5449
     5450    /* Wait up to 2 mins for the parent to exorcise evil. */
     5451    LARGE_INTEGER Timeout;
     5452    Timeout.QuadPart = -1200000000; /* 120 second */
     5453    rcNt = pfnNtWaitForSingleObject(hEvtChild, FALSE /*Alertable*/, &Timeout);
     5454    if (rcNt != STATUS_SUCCESS)
     5455        return 0x34; /* crash */
     5456
     5457    /*
     5458     * We're good to go, work global state and restore process parameters.
     5459     * Note that we will not restore uNtDllAddr since that is our first defence
     5460     * against unwanted threads (see above).
     5461     */
     5462    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED;
     5463    g_fSupEarlyProcessInit      = true;
     5464
     5465    g_ProcParams.hEvtChild      = hEvtChild;
     5466    g_ProcParams.hEvtParent     = hEvtParent;
     5467    g_ProcParams.enmRequest     = kSupR3WinChildReq_Error;
     5468    g_ProcParams.rc             = VINF_SUCCESS;
    55015469
    55025470    /*
     
    55045472     * process has been initialized.
    55055473     */
    5506     supR3HardenedWinInitImportsEarly(g_ProcParams.uNtDllAddr);
     5474    supR3HardenedWinInitImportsEarly(uNtDllAddr);
    55075475    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED;
    55085476
     
    55115479     */
    55125480    supR3HardenedWinInitVersion();
    5513 
    5514     /*
    5515      * Wait on the parent process to dispose of the full access process and
    5516      * thread handles.
    5517      */
    5518     LARGE_INTEGER Timeout;
    5519     Timeout.QuadPart = -600000000; /* 60 second */
    5520     NTSTATUS rcNt = NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout);
    5521     if (rcNt != STATUS_SUCCESS)
    5522         rcNt = NtClearEvent(g_ProcParams.hEvtChild);
    5523     if (!NT_SUCCESS(rcNt))
    5524     {
    5525         NtTerminateProcess(NtCurrentProcess(), 0x42);
    5526         return 0x42; /* crash */
    5527     }
    55285481
    55295482    /*
     
    55355488    char **papszArgs = suplibCommandLineToArgvWStub(pCmdLineStr->Buffer, pCmdLineStr->Length / sizeof(WCHAR), &cArgs);
    55365489    supR3HardenedOpenLog(&cArgs, papszArgs);
    5537     SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p\n", g_ProcParams.uNtDllAddr));
     5490    SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p\n", uNtDllAddr));
    55385491
    55395492    /*
     
    55925545     * normally when we return.
    55935546     */
    5594     SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrIntiailizeThunk...\n"));
     5547    SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrInitializeThunk...\n"));
    55955548    PSUPHNTLDRCACHEENTRY pLdrEntry;
    55965549    int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
     
    55995552
    56005553    uint8_t *pbBits;
    5601     rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, g_ProcParams.uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
     5554    rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
    56025555    if (RT_FAILURE(rc))
    56035556        supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
    56045557
    56055558    RTLDRADDR uValue;
    5606     rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, g_ProcParams.uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
     5559    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
    56075560    if (RT_FAILURE(rc))
    56085561        supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc);
     
    56105563    PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue;
    56115564    SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READWRITE));
    5612     memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - g_ProcParams.uNtDllAddr), 16);
     5565    memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - uNtDllAddr), 16);
    56135566    SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ));
    56145567
    5615     SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrIntiailizeThunk...\n"));
     5568    SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrInitializeThunk...\n"));
    56165569    return (uintptr_t)pvLdrInitThunk;
    56175570}
  • trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp

    r52967 r52969  
    588588
    589589
     590/**
     591 * Resolves a few NtDll functions we need before child purification is executed.
     592 *
     593 * We must not permanently modify any global data here.
     594 *
     595 * @param   uNtDllAddr          The address of the NTDLL.
     596 */
     597DECLHIDDEN(void) supR3HardenedWinGetVeryEarlyImports(uintptr_t uNtDllAddr,
     598                                                     PFNNTWAITFORSINGLEOBJECT *ppfnNtWaitForSingleObject,
     599                                                     PFNNTSETEVENT *ppfnNtSetEvent)
     600{
     601    /*
     602     * NTDLL is the first entry in the list.  Save it and do the parsing.
     603     */
     604    SUPHNTIMPDLL SavedDllEntry = g_aSupNtImpDlls[0];
     605
     606    g_aSupNtImpDlls[0].pbImageBase = (uint8_t const *)uNtDllAddr;
     607    supR3HardenedParseModule(&g_aSupNtImpDlls[0]);
     608
     609    /*
     610     * Create a temporary import table for the requested APIs and resolve them.
     611     */
     612    SUPHNTIMPFUNC aImports[] =
     613    {
     614        { "NtWaitForSingleObject",  (PFNRT *)ppfnNtWaitForSingleObject,     NULL, false },
     615        { "NtSetEvent",             (PFNRT *)ppfnNtSetEvent,                NULL, false },
     616    };
     617
     618    for (uint32_t i = 0; i < RT_ELEMENTS(aImports); i++)
     619        {
     620            const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &aImports[i], false);
     621            if (pszForwarder)
     622                SUPHNTIMP_ERROR(false, 31, "supR3HardenedWinGetVeryEarlyImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
     623                                "ntdll: Failed to resolve forwarder '%s'.", pszForwarder);
     624        }
     625
     626    /*
     627     * Restore the NtDll entry.
     628     */
     629    g_aSupNtImpDlls[0] = SavedDllEntry;
     630}
     631
    590632
    591633/**
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette