- Timestamp:
- Oct 7, 2014 8:29:34 AM (10 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/nt/nt.h
r52955 r52969 1974 1974 NTSYSAPI NTSTATUS NTAPI NtWaitForSingleObject(HANDLE, BOOLEAN PLARGE_INTEGER); 1975 1975 #endif 1976 typedef NTSYSAPI NTSTATUS (NTAPI *PFNNTWAITFORSINGLEOBJECT)(HANDLE, BOOLEAN, PLARGE_INTEGER); 1976 1977 typedef enum _OBJECT_WAIT_TYPE { WaitAllObjects = 0, WaitAnyObject = 1, ObjectWaitTypeHack = 0x7fffffff } OBJECT_WAIT_TYPE; 1977 1978 NTSYSAPI NTSTATUS NTAPI NtWaitForMultipleObjects(ULONG, PHANDLE, OBJECT_WAIT_TYPE, BOOLEAN, PLARGE_INTEGER); … … 1990 1991 NTSYSAPI NTSTATUS NTAPI NtCreateEvent(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, EVENT_TYPE, BOOLEAN); 1991 1992 NTSYSAPI NTSTATUS NTAPI NtOpenEvent(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); 1993 typedef NTSYSAPI NTSTATUS (NTAPI *PFNNTCLEAREVENT)(HANDLE); 1992 1994 NTSYSAPI NTSTATUS NTAPI NtClearEvent(HANDLE); 1993 1995 NTSYSAPI NTSTATUS NTAPI NtResetEvent(HANDLE, PULONG); 1994 1996 NTSYSAPI NTSTATUS NTAPI NtSetEvent(HANDLE, PULONG); 1997 typedef NTSYSAPI NTSTATUS (NTAPI *PFNNTSETEVENT)(HANDLE, PULONG); 1995 1998 typedef enum _EVENT_INFORMATION_CLASS 1996 1999 { -
trunk/src/VBox/HostDrivers/Support/SUPLibInternal.h
r52967 r52969 448 448 DECLHIDDEN(void) supR3HardenedWinInitVersion(void); 449 449 DECLHIDDEN(void) supR3HardenedWinInitImports(void); 450 # ifdef ___iprt_nt_nt_h___ 451 DECLHIDDEN(void) supR3HardenedWinGetVeryEarlyImports(uintptr_t uNtDllAddr, 452 PFNNTWAITFORSINGLEOBJECT *ppfnNtWaitForSingleObject, 453 PFNNTSETEVENT *ppfnNtSetEvent); 454 # endif 450 455 DECLHIDDEN(void) supR3HardenedWinInitImportsEarly(uintptr_t uNtDllAddr); 451 456 DECLHIDDEN(void) supR3HardenedWinInitSyscalls(bool fReportErrors); -
trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
r52968 r52969 175 175 176 176 /** 177 * VM process parameters. 177 * Child requests. 178 */ 179 typedef 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. 178 193 */ 179 194 typedef struct SUPR3WINPROCPARAMS 180 195 { 181 196 /** The event semaphore the child will be waiting on. */ 182 HANDLE hEvtChild;197 HANDLE hEvtChild; 183 198 /** 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; 189 207 /** The last status. */ 190 int32_t rc;208 int32_t rc; 191 209 /** The init operation the error relates to if message, kSupInitOp_Invalid if 192 210 * not message. */ 193 SUPINITOP enmWhat;211 SUPINITOP enmWhat; 194 212 /** Where if message. */ 195 char szWhere[80];213 char szWhere[80]; 196 214 /** Error message / path name string space. */ 197 char szErrorMsg[4096];215 char szErrorMsg[4096]; 198 216 } SUPR3WINPROCPARAMS; 217 218 219 /** 220 * Child process data structure for use during child process init setup and 221 * purification. 222 */ 223 typedef 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. */ 251 typedef SUPR3HARDNTCHILD *PSUPR3HARDNTCHILD; 199 252 200 253 … … 204 257 /** Process parameters. Specified by parent if VM process, see 205 258 * supR3HardenedVmProcessInit. */ 206 static SUPR3WINPROCPARAMS g_ProcParams = { NULL, NULL, 0, 0 };259 static SUPR3WINPROCPARAMS g_ProcParams = { NULL, NULL, 0, (SUPR3WINCHILDREQ)0, 0 }; 207 260 /** Set if supR3HardenedEarlyProcessInit was invoked. */ 208 261 bool g_fSupEarlyProcessInit = false; … … 2128 2181 2129 2182 2130 #ifdef RT_ARCH_AMD642131 /**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 uStart2137 * if @a iDirection is negative, and higher if2138 * 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 else2158 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_FREE2178 && 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 > 02191 ? (uintptr_t)pvMem >= uStart2192 && (uintptr_t)pvMem <= uEnd2193 : (uintptr_t)pvMem >= uEnd2194 && (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 > 02203 ? uNext <= uCur2204 || uNext > uEnd2205 || uNext - (uintptr_t)MemInfo.BaseAddress > MemInfo.RegionSize2206 || MemInfo.RegionSize - (uNext - (uintptr_t)MemInfo.BaseAddress) < cbAlloc2207 : uNext >= uCur2208 || uNext < uEnd2209 || 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 else2226 {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 #endif2238 2239 2240 2183 static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue) 2241 2184 { … … 2679 2622 2680 2623 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 */ 2989 2635 2990 2636 … … 3166 2812 3167 2813 2814 3168 2815 /* 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 */ 2833 static 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 */ 2878 static 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)); 3214 2936 #endif 3215 2937 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 */ 2993 DECLINLINE(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 */ 3022 static 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 */ 3117 static 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 */ 3180 static 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); 3221 3191 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 */ 3253 static 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 */ 3293 3329 else 3294 3330 { 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); 3297 3335 } 3298 NtClose(hSection);3299 3336 } 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 */ 3371 static 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; 3304 3388 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 } 3478 3402 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 */ 3422 static void supR3HardNtChildSanitizePeb(PSUPR3HARDNTCHILD pThis) 3487 3423 { 3488 3424 /* … … 3531 3467 NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem); 3532 3468 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 */ 3480 static 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 */ 3558 static 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 */ 3665 static 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 */ 3698 static 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 */ 3722 static void supR3HardNtChildFindNtdll(PSUPR3HARDNTCHILD pThis) 3540 3723 { 3541 3724 /* … … 3606 3789 } 3607 3790 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 */ 3800 static 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); 3608 3826 #ifdef DEBUG 3609 supR3HardenedFatal("%s: ntdll.dll not found in child.", __FUNCTION__);3827 SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt); 3610 3828 #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); 3632 3858 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); 3900 3866 } 3901 3867 … … 3907 3873 * @param iWhich Which respawn we're to check for, 1 being the 3908 3874 * first one, and 2 the second and final. 3909 * 3910 * @todo Split up this function. 3911 */ 3912 static int supR3HardenedWinDoReSpawn(int iWhich) 3875 */ 3876 static void supR3HardenedWinDoReSpawn(int iWhich) 3913 3877 { 3914 3878 NTSTATUS rcNt; … … 3919 3883 3920 3884 /* 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 3923 3892 OBJECT_ATTRIBUTES ObjAttrs; 3924 HANDLEhEvtChild = NULL;3893 This.hEvtChild = NULL; 3925 3894 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 HANDLEhEvtParent = NULL;3895 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE)); 3896 3897 This.hEvtParent = NULL; 3929 3898 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)); 3931 3900 3932 3901 /* … … 3935 3904 SECURITY_ATTRIBUTES ProcessSecAttrs; 3936 3905 MYSECURITYCLEANUP ProcessSecAttrsCleanup; 3937 supR3Hard enedInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);3906 supR3HardNtChildInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/); 3938 3907 3939 3908 SECURITY_ATTRIBUTES ThreadSecAttrs; 3940 3909 MYSECURITYCLEANUP ThreadSecAttrsCleanup; 3941 supR3Hard enedInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);3910 supR3HardNtChildInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/); 3942 3911 3943 3912 #if 1 … … 3969 3938 * Construct the command line and launch the process. 3970 3939 */ 3971 PRTUTF16 pwszCmdLine = supR3Hard enedWinConstructCmdLine(NULL, iWhich);3940 PRTUTF16 pwszCmdLine = supR3HardNtChildConstructCmdLine(NULL, iWhich); 3972 3941 3973 3942 supR3HardenedWinEnableThreadCreation(); … … 3991 3960 SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [kernel32].\n", 3992 3961 iWhich, ProcessInfoW32.dwProcessId, ProcessInfoW32.dwThreadId)); 3993 HANDLEhProcess = ProcessInfoW32.hProcess;3994 HANDLEhThread = ProcessInfoW32.hThread;3962 This.hProcess = ProcessInfoW32.hProcess; 3963 This.hThread = ProcessInfoW32.hThread; 3995 3964 3996 3965 #else … … 4005 3974 4006 3975 UNICODE_STRING CmdLine; 4007 supR3Hard enedWinConstructCmdLine(&CmdLine, iWhich);3976 supR3HardNtChildConstructCmdLine(&CmdLine, iWhich); 4008 3977 4009 3978 PRTL_USER_PROCESS_PARAMETERS pProcParams = NULL; … … 4048 4017 RtlDestroyProcessParameters(pProcParams); 4049 4018 4050 HANDLEhProcess = ProcessInfoNt.ProcessHandle;4051 HANDLEhThread = ProcessInfoNt.ThreadHandle;4019 This.hProcess = ProcessInfoNt.ProcessHandle; 4020 This.hThread = ProcessInfoNt.ThreadHandle; 4052 4021 #endif 4053 4022 … … 4057 4026 * supR3HardenedWinInit.) 4058 4027 */ 4059 rcNt = NtSetInformationThread( hThread, ThreadHideFromDebugger, NULL, 0);4028 rcNt = NtSetInformationThread(This.hThread, ThreadHideFromDebugger, NULL, 0); 4060 4029 if (!NT_SUCCESS(rcNt)) 4061 supR3HardenedWinKillChild( hProcess, "supR3HardenedWinReSpawn", rcNt,4030 supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", rcNt, 4062 4031 "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt); 4063 4032 #endif 4064 4033 4065 4034 /* 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); 4079 4040 4080 4041 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); 4082 4060 4083 4061 /* … … 4086 4064 * away with the process handle returned by CreateProcess. 4087 4065 */ 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); 4109 4073 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, 4122 4075 "NtSetEvent failed on child process handle: %#x\n", rcNt); 4123 4076 … … 4134 4087 4135 4088 /* 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! */ 4199 4103 } 4200 4104 … … 4545 4449 * Respawn the process with kernel protection for the new process. 4546 4450 */ 4547 return supR3HardenedWinDoReSpawn(iWhich); 4451 supR3HardenedWinDoReSpawn(iWhich); 4452 SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */ 4453 return RTEXITCODE_FAILURE; 4548 4454 } 4549 4455 … … 5339 5245 { 5340 5246 SUPR3HARDENED_ASSERT(g_fSupEarlyProcessInit); 5247 5248 g_ProcParams.enmRequest = kSupR3WinChildReq_CloseEvents; 5341 5249 NtSetEvent(g_ProcParams.hEvtParent, NULL); 5250 5342 5251 NtClose(g_ProcParams.hEvtParent); 5343 5252 NtClose(g_ProcParams.hEvtChild); … … 5460 5369 g_ProcParams.enmWhat = enmWhat; 5461 5370 g_ProcParams.rc = RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR_2 : rc; 5462 5371 g_ProcParams.enmRequest = kSupR3WinChildReq_Error; 5372 5373 NtClearEvent(g_ProcParams.hEvtChild); 5463 5374 NTSTATUS rcNt = NtSetEvent(g_ProcParams.hEvtParent, NULL); 5464 5375 if (NT_SUCCESS(rcNt)) … … 5467 5378 Timeout.QuadPart = -300000000; /* 30 second */ 5468 5379 NTSTATUS rcNt = NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout); 5469 NtClearEvent(g_ProcParams.hEvtChild);5470 5380 } 5471 5381 } … … 5489 5399 { 5490 5400 /* 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) 5496 5414 { 5497 5415 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; 5501 5469 5502 5470 /* … … 5504 5472 * process has been initialized. 5505 5473 */ 5506 supR3HardenedWinInitImportsEarly( g_ProcParams.uNtDllAddr);5474 supR3HardenedWinInitImportsEarly(uNtDllAddr); 5507 5475 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED; 5508 5476 … … 5511 5479 */ 5512 5480 supR3HardenedWinInitVersion(); 5513 5514 /*5515 * Wait on the parent process to dispose of the full access process and5516 * 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 }5528 5481 5529 5482 /* … … 5535 5488 char **papszArgs = suplibCommandLineToArgvWStub(pCmdLineStr->Buffer, pCmdLineStr->Length / sizeof(WCHAR), &cArgs); 5536 5489 supR3HardenedOpenLog(&cArgs, papszArgs); 5537 SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p\n", g_ProcParams.uNtDllAddr));5490 SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p\n", uNtDllAddr)); 5538 5491 5539 5492 /* … … 5592 5545 * normally when we return. 5593 5546 */ 5594 SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrIn tiailizeThunk...\n"));5547 SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrInitializeThunk...\n")); 5595 5548 PSUPHNTLDRCACHEENTRY pLdrEntry; 5596 5549 int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry); … … 5599 5552 5600 5553 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*/); 5602 5555 if (RT_FAILURE(rc)) 5603 5556 supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc); 5604 5557 5605 5558 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); 5607 5560 if (RT_FAILURE(rc)) 5608 5561 supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc); … … 5610 5563 PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue; 5611 5564 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); 5613 5566 SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ)); 5614 5567 5615 SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrIn tiailizeThunk...\n"));5568 SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrInitializeThunk...\n")); 5616 5569 return (uintptr_t)pvLdrInitThunk; 5617 5570 } -
trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp
r52967 r52969 588 588 589 589 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 */ 597 DECLHIDDEN(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 590 632 591 633 /**
Note:
See TracChangeset
for help on using the changeset viewer.