1 | /* $Id: SUPR3HardenedMainImports-win.cpp 106893 2024-11-08 15:54:01Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VirtualBox Support Library - Hardened Main, Windows Import Trickery.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2006-2024 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * The contents of this file may alternatively be used under the terms
|
---|
26 | * of the Common Development and Distribution License Version 1.0
|
---|
27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
29 | * CDDL are applicable instead of those of the GPL.
|
---|
30 | *
|
---|
31 | * You may elect to license modified versions of this file under the
|
---|
32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
33 | *
|
---|
34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
35 | */
|
---|
36 |
|
---|
37 |
|
---|
38 | /*********************************************************************************************************************************
|
---|
39 | * Header Files *
|
---|
40 | *********************************************************************************************************************************/
|
---|
41 | #include <iprt/nt/nt-and-windows.h>
|
---|
42 |
|
---|
43 | #include <VBox/sup.h>
|
---|
44 | #include <VBox/err.h>
|
---|
45 | #include <iprt/ctype.h>
|
---|
46 | #include <iprt/initterm.h>
|
---|
47 | #include <iprt/param.h>
|
---|
48 | #include <iprt/string.h>
|
---|
49 | #include <iprt/utf16.h>
|
---|
50 | #ifdef RT_ARCH_ARM64
|
---|
51 | # include <iprt/armv8.h>
|
---|
52 | #endif
|
---|
53 |
|
---|
54 | #include "SUPLibInternal.h"
|
---|
55 | #include "SUPHardenedVerify-win.h"
|
---|
56 |
|
---|
57 |
|
---|
58 | /*********************************************************************************************************************************
|
---|
59 | * Defined Constants And Macros *
|
---|
60 | *********************************************************************************************************************************/
|
---|
61 | #define SUPHARNT_COMMENT(a_Blah) /* nothing */
|
---|
62 |
|
---|
63 | #define VBOX_HARDENED_STUB_WITHOUT_IMPORTS
|
---|
64 | #ifdef VBOX_HARDENED_STUB_WITHOUT_IMPORTS
|
---|
65 | # define SUPHNTIMP_ERROR(a_fReportErrors, a_id, a_szWhere, a_enmOp, a_rc, ...) \
|
---|
66 | do { \
|
---|
67 | if (a_fReportErrors) supR3HardenedFatalMsg(a_szWhere, a_enmOp, a_rc, __VA_ARGS__); \
|
---|
68 | else { static const char s_szWhere[] = a_szWhere; *(char *)(uintptr_t)(a_id) += 1; __debugbreak(); } \
|
---|
69 | } while (0)
|
---|
70 | #else
|
---|
71 | # define SUPHNTIMP_ERROR(a_fReportErrors, a_id, a_szWhere, a_enmOp, a_rc, ...) \
|
---|
72 | supR3HardenedFatalMsg(a_szWhere, a_enmOp, a_rc, __VA_ARGS__)
|
---|
73 |
|
---|
74 | #endif
|
---|
75 |
|
---|
76 |
|
---|
77 | /*********************************************************************************************************************************
|
---|
78 | * Defined Constants And Macros *
|
---|
79 | *********************************************************************************************************************************/
|
---|
80 | /**
|
---|
81 | * Import function entry.
|
---|
82 | */
|
---|
83 | typedef struct SUPHNTIMPFUNC
|
---|
84 | {
|
---|
85 | /** The name of the function we're importing. */
|
---|
86 | const char *pszName;
|
---|
87 | /** Where to store the function address (think __imp_ApiName). */
|
---|
88 | PFNRT *ppfnImport;
|
---|
89 | /** Pointer to an early dummy function for imports that aren't available
|
---|
90 | * during early process initialization. */
|
---|
91 | PFNRT pfnEarlyDummy;
|
---|
92 | /** Indicates whether this is an optional import and failure to locate it
|
---|
93 | * should set it to NULL instead of freaking out. */
|
---|
94 | bool fOptional;
|
---|
95 | } SUPHNTIMPFUNC;
|
---|
96 | /** Pointer to an import table entry. */
|
---|
97 | typedef SUPHNTIMPFUNC const *PCSUPHNTIMPFUNC;
|
---|
98 |
|
---|
99 | /**
|
---|
100 | * Information for constructing a direct system call.
|
---|
101 | */
|
---|
102 | typedef struct SUPHNTIMPSYSCALL
|
---|
103 | {
|
---|
104 | /** Where to store the system call number.
|
---|
105 | * NULL if this import doesn't stupport direct system call. */
|
---|
106 | uint32_t *puApiNo;
|
---|
107 | /** Assembly system call routine, type 1. */
|
---|
108 | PFNRT pfnType1;
|
---|
109 | #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
|
---|
110 | /** Assembly system call routine, type 2. */
|
---|
111 | PFNRT pfnType2;
|
---|
112 | #endif
|
---|
113 | #ifdef RT_ARCH_X86
|
---|
114 | /** The parameter size in bytes for a standard call. */
|
---|
115 | uint32_t cbParams;
|
---|
116 | #endif
|
---|
117 | } SUPHNTIMPSYSCALL;
|
---|
118 | /** Pointer to a system call entry. */
|
---|
119 | typedef SUPHNTIMPSYSCALL const *PCSUPHNTIMPSYSCALL;
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * Import DLL.
|
---|
123 | *
|
---|
124 | * This contains both static (like name & imports) and runtime information (like
|
---|
125 | * load and export table locations).
|
---|
126 | *
|
---|
127 | * @sa RTDBGNTKRNLMODINFO
|
---|
128 | */
|
---|
129 | typedef struct SUPHNTIMPDLL
|
---|
130 | {
|
---|
131 | /** @name Static data.
|
---|
132 | * @{ */
|
---|
133 | const wchar_t *pwszName;
|
---|
134 | const char *pszName;
|
---|
135 | size_t cImports;
|
---|
136 | PCSUPHNTIMPFUNC paImports;
|
---|
137 | /** Array running parallel to paImports if present. */
|
---|
138 | PCSUPHNTIMPSYSCALL paSyscalls;
|
---|
139 | /** @} */
|
---|
140 |
|
---|
141 |
|
---|
142 | /** The image base. */
|
---|
143 | uint8_t const *pbImageBase;
|
---|
144 | /** The NT headers. */
|
---|
145 | PIMAGE_NT_HEADERS pNtHdrs;
|
---|
146 | /** The NT header offset/RVA. */
|
---|
147 | uint32_t offNtHdrs;
|
---|
148 | /** The end of the section headers. */
|
---|
149 | uint32_t offEndSectHdrs;
|
---|
150 | /** The end of the image. */
|
---|
151 | uint32_t cbImage;
|
---|
152 | /** Offset of the export directory. */
|
---|
153 | uint32_t offExportDir;
|
---|
154 | /** Size of the export directory. */
|
---|
155 | uint32_t cbExportDir;
|
---|
156 |
|
---|
157 | /** Exported functions and data by ordinal (RVAs). */
|
---|
158 | uint32_t const *paoffExports;
|
---|
159 | /** The number of exports. */
|
---|
160 | uint32_t cExports;
|
---|
161 | /** The number of exported names. */
|
---|
162 | uint32_t cNamedExports;
|
---|
163 | /** Pointer to the array of exported names (RVAs to strings). */
|
---|
164 | uint32_t const *paoffNamedExports;
|
---|
165 | /** Array parallel to paoffNamedExports with the corresponding ordinals
|
---|
166 | * (indexes into paoffExports). */
|
---|
167 | uint16_t const *pau16NameOrdinals;
|
---|
168 |
|
---|
169 | /** Number of patched export table entries. */
|
---|
170 | uint32_t cPatchedExports;
|
---|
171 |
|
---|
172 | } SUPHNTIMPDLL;
|
---|
173 | /** Pointer to an import DLL entry. */
|
---|
174 | typedef SUPHNTIMPDLL *PSUPHNTIMPDLL;
|
---|
175 |
|
---|
176 |
|
---|
177 |
|
---|
178 | /*
|
---|
179 | * Declare assembly symbols.
|
---|
180 | */
|
---|
181 | #define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
|
---|
182 | extern PFNRT RT_CONCAT(g_pfn, a_Name);
|
---|
183 | #define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86)
|
---|
184 | #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
|
---|
185 | # define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
|
---|
186 | SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
|
---|
187 | extern uint32_t RT_CONCAT(g_uApiNo, a_Name); \
|
---|
188 | extern FNRT RT_CONCAT(a_Name, _SyscallType1); \
|
---|
189 | extern FNRT RT_CONCAT(a_Name, _SyscallType2);
|
---|
190 | #else
|
---|
191 | # define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
|
---|
192 | SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
|
---|
193 | extern uint32_t RT_CONCAT(g_uApiNo, a_Name); \
|
---|
194 | extern FNRT RT_CONCAT(a_Name, _Syscall);
|
---|
195 | #endif
|
---|
196 | #define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
|
---|
197 | extern PFNRT RT_CONCAT(g_pfn, a_Name); \
|
---|
198 | extern FNRT RT_CONCAT(a_Name, _Early);
|
---|
199 | #define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
|
---|
200 |
|
---|
201 | RT_C_DECLS_BEGIN
|
---|
202 | #include "import-template-ntdll.h"
|
---|
203 | #include "import-template-kernel32.h"
|
---|
204 | RT_C_DECLS_END
|
---|
205 |
|
---|
206 | /*
|
---|
207 | * Import functions.
|
---|
208 | */
|
---|
209 | #undef SUPHARNT_IMPORT_SYSCALL
|
---|
210 | #undef SUPHARNT_IMPORT_STDCALL_EARLY
|
---|
211 | #undef SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL
|
---|
212 | #undef SUPHARNT_IMPORT_STDCALL
|
---|
213 | #undef SUPHARNT_IMPORT_STDCALL_OPTIONAL
|
---|
214 | #define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
|
---|
215 | { #a_Name, &RT_CONCAT(g_pfn, a_Name), NULL, false },
|
---|
216 | #define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) \
|
---|
217 | { #a_Name, &RT_CONCAT(g_pfn, a_Name), NULL, false },
|
---|
218 | #define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) \
|
---|
219 | { #a_Name, &RT_CONCAT(g_pfn, a_Name), NULL, true },
|
---|
220 | #define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
|
---|
221 | { #a_Name, &RT_CONCAT(g_pfn, a_Name), RT_CONCAT(a_Name,_Early), false },
|
---|
222 | #define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) \
|
---|
223 | { #a_Name, &RT_CONCAT(g_pfn, a_Name), RT_CONCAT(a_Name,_Early), true },
|
---|
224 | static const SUPHNTIMPFUNC g_aSupNtImpNtDllFunctions[] =
|
---|
225 | {
|
---|
226 | #include "import-template-ntdll.h"
|
---|
227 | };
|
---|
228 |
|
---|
229 | static const SUPHNTIMPFUNC g_aSupNtImpKernel32Functions[] =
|
---|
230 | {
|
---|
231 | #include "import-template-kernel32.h"
|
---|
232 | };
|
---|
233 |
|
---|
234 |
|
---|
235 |
|
---|
236 | /*
|
---|
237 | * Syscalls in ntdll.
|
---|
238 | */
|
---|
239 | #undef SUPHARNT_IMPORT_SYSCALL
|
---|
240 | #undef SUPHARNT_IMPORT_STDCALL_EARLY
|
---|
241 | #undef SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL
|
---|
242 | #undef SUPHARNT_IMPORT_STDCALL
|
---|
243 | #undef SUPHARNT_IMPORT_STDCALL_OPTIONAL
|
---|
244 | #ifdef RT_ARCH_AMD64
|
---|
245 | # define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
|
---|
246 | { NULL, NULL },
|
---|
247 | # define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
|
---|
248 | { &RT_CONCAT(g_uApiNo, a_Name), &RT_CONCAT(a_Name, _SyscallType1), &RT_CONCAT(a_Name, _SyscallType2) },
|
---|
249 |
|
---|
250 | #elif defined(RT_ARCH_ARM64)
|
---|
251 | # define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
|
---|
252 | { NULL, NULL },
|
---|
253 | # define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
|
---|
254 | { &RT_CONCAT(g_uApiNo, a_Name), &RT_CONCAT(a_Name, _Syscall) },
|
---|
255 |
|
---|
256 | #elif defined(RT_ARCH_X86)
|
---|
257 | # define SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86) \
|
---|
258 | { NULL, NULL, NULL, 0 },
|
---|
259 | # define SUPHARNT_IMPORT_SYSCALL(a_Name, a_cbParamsX86) \
|
---|
260 | { &RT_CONCAT(g_uApiNo, a_Name), &RT_CONCAT(a_Name,_SyscallType1), &RT_CONCAT(a_Name, _SyscallType2), a_cbParamsX86 },
|
---|
261 |
|
---|
262 | #else
|
---|
263 | # error "port me"
|
---|
264 | #endif
|
---|
265 | #define SUPHARNT_IMPORT_STDCALL_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
|
---|
266 | #define SUPHARNT_IMPORT_STDCALL_EARLY(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
|
---|
267 | #define SUPHARNT_IMPORT_STDCALL_EARLY_OPTIONAL(a_Name, a_cbParamsX86) SUPHARNT_IMPORT_STDCALL(a_Name, a_cbParamsX86)
|
---|
268 | static const SUPHNTIMPSYSCALL g_aSupNtImpNtDllSyscalls[] =
|
---|
269 | {
|
---|
270 | #include "import-template-ntdll.h"
|
---|
271 | };
|
---|
272 |
|
---|
273 |
|
---|
274 | /**
|
---|
275 | * All the DLLs we import from.
|
---|
276 | * @remarks Code ASSUMES that ntdll is the first entry.
|
---|
277 | */
|
---|
278 | static SUPHNTIMPDLL g_aSupNtImpDlls[] =
|
---|
279 | {
|
---|
280 | { L"ntdll.dll", "ntdll.dll", RT_ELEMENTS(g_aSupNtImpNtDllFunctions), g_aSupNtImpNtDllFunctions, g_aSupNtImpNtDllSyscalls },
|
---|
281 | { L"kernelbase.dll", "kernelbase.dll", 0 /* optional module, forwarders only */, NULL, NULL },
|
---|
282 | { L"kernel32.dll", "kernel32.dll", RT_ELEMENTS(g_aSupNtImpKernel32Functions), g_aSupNtImpKernel32Functions, NULL },
|
---|
283 | };
|
---|
284 |
|
---|
285 |
|
---|
286 | static void supR3HardenedFindOrLoadModule(PSUPHNTIMPDLL pDll)
|
---|
287 | {
|
---|
288 | #ifdef VBOX_HARDENED_STUB_WITHOUT_IMPORTS
|
---|
289 | uint32_t const cbName = (uint32_t)RTUtf16Len(pDll->pwszName) * sizeof(WCHAR);
|
---|
290 | PPEB_LDR_DATA pLdrData = NtCurrentPeb()->Ldr;
|
---|
291 | LIST_ENTRY *pList = &pLdrData->InMemoryOrderModuleList;
|
---|
292 | LIST_ENTRY *pListEntry = pList->Flink;
|
---|
293 | uint32_t cLoops = 0;
|
---|
294 | while (pListEntry != pList && cLoops < 1024)
|
---|
295 | {
|
---|
296 | PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
---|
297 |
|
---|
298 | if ( pLdrEntry->FullDllName.Length > cbName + sizeof(WCHAR)
|
---|
299 | && ( pLdrEntry->FullDllName.Buffer[(pLdrEntry->FullDllName.Length - cbName) / sizeof(WCHAR) - 1] == '\\'
|
---|
300 | || pLdrEntry->FullDllName.Buffer[(pLdrEntry->FullDllName.Length - cbName) / sizeof(WCHAR) - 1] == '/')
|
---|
301 | && RTUtf16ICmpAscii(&pLdrEntry->FullDllName.Buffer[(pLdrEntry->FullDllName.Length - cbName) / sizeof(WCHAR)],
|
---|
302 | pDll->pszName) == 0)
|
---|
303 | {
|
---|
304 | pDll->pbImageBase = (uint8_t *)pLdrEntry->DllBase;
|
---|
305 | return;
|
---|
306 | }
|
---|
307 |
|
---|
308 | pListEntry = pListEntry->Flink;
|
---|
309 | cLoops++;
|
---|
310 | }
|
---|
311 |
|
---|
312 | if (!pDll->cImports)
|
---|
313 | pDll->pbImageBase = NULL; /* optional */
|
---|
314 | else
|
---|
315 | SUPHNTIMP_ERROR(false, 1, "supR3HardenedFindOrLoadModule", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
|
---|
316 | "Failed to locate %ls", pDll->pwszName);
|
---|
317 | #else
|
---|
318 | HMODULE hmod = GetModuleHandleW(pDll->pwszName);
|
---|
319 | if (RT_UNLIKELY(!hmod && pDll->cImports))
|
---|
320 | SUPHNTIMP_ERROR(true, 1, "supR3HardenedWinInitImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
|
---|
321 | "Failed to locate %ls", pDll->pwszName);
|
---|
322 | pDll->pbImageBase = (uint8_t *)hmod;
|
---|
323 | #endif
|
---|
324 | }
|
---|
325 |
|
---|
326 |
|
---|
327 | /** @sa rtR0DbgKrnlNtParseModule */
|
---|
328 | static void supR3HardenedParseModule(PSUPHNTIMPDLL pDll)
|
---|
329 | {
|
---|
330 | /*
|
---|
331 | * Locate the PE header, do some basic validations.
|
---|
332 | */
|
---|
333 | IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pDll->pbImageBase;
|
---|
334 | uint32_t offNtHdrs = 0;
|
---|
335 | PIMAGE_NT_HEADERS pNtHdrs;
|
---|
336 | if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
|
---|
337 | {
|
---|
338 | offNtHdrs = pMzHdr->e_lfanew;
|
---|
339 | if (offNtHdrs > _2K)
|
---|
340 | SUPHNTIMP_ERROR(false, 2, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
|
---|
341 | "%ls: e_lfanew=%#x, expected a lower value", pDll->pwszName, offNtHdrs);
|
---|
342 | }
|
---|
343 | pDll->pNtHdrs = pNtHdrs = (PIMAGE_NT_HEADERS)&pDll->pbImageBase[offNtHdrs];
|
---|
344 |
|
---|
345 | if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
|
---|
346 | SUPHNTIMP_ERROR(false, 3, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
347 | "%ls: Invalid PE signature: %#x", pDll->pwszName, pNtHdrs->Signature);
|
---|
348 | if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader))
|
---|
349 | SUPHNTIMP_ERROR(false, 4, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
350 | "%ls: Unexpected optional header size: %#x", pDll->pwszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
|
---|
351 | if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
|
---|
352 | SUPHNTIMP_ERROR(false, 5, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
353 | "%ls: Unexpected optional header magic: %#x", pDll->pwszName, pNtHdrs->OptionalHeader.Magic);
|
---|
354 | if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
|
---|
355 | SUPHNTIMP_ERROR(false, 6, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
356 | "%ls: Unexpected number of RVA and sizes: %#x", pDll->pwszName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
|
---|
357 |
|
---|
358 | pDll->offNtHdrs = offNtHdrs;
|
---|
359 | pDll->offEndSectHdrs = offNtHdrs
|
---|
360 | + sizeof(*pNtHdrs)
|
---|
361 | + pNtHdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
|
---|
362 | pDll->cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
|
---|
363 |
|
---|
364 | /*
|
---|
365 | * Find the export directory.
|
---|
366 | */
|
---|
367 | IMAGE_DATA_DIRECTORY ExpDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
---|
368 | if ( ExpDir.Size < sizeof(IMAGE_EXPORT_DIRECTORY)
|
---|
369 | || ExpDir.VirtualAddress < pDll->offEndSectHdrs
|
---|
370 | || ExpDir.VirtualAddress >= pNtHdrs->OptionalHeader.SizeOfImage
|
---|
371 | || ExpDir.VirtualAddress + ExpDir.Size > pNtHdrs->OptionalHeader.SizeOfImage)
|
---|
372 | SUPHNTIMP_ERROR(false, 7, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
373 | "%ls: Missing or invalid export directory: %#lx LB %#x", pDll->pwszName, ExpDir.VirtualAddress, ExpDir.Size);
|
---|
374 | pDll->offExportDir = ExpDir.VirtualAddress;
|
---|
375 | pDll->cbExportDir = ExpDir.Size;
|
---|
376 |
|
---|
377 | IMAGE_EXPORT_DIRECTORY const *pExpDir = (IMAGE_EXPORT_DIRECTORY const *)&pDll->pbImageBase[ExpDir.VirtualAddress];
|
---|
378 |
|
---|
379 | if ( pExpDir->NumberOfFunctions >= _1M
|
---|
380 | || pExpDir->NumberOfFunctions < 1
|
---|
381 | || pExpDir->NumberOfNames >= _1M
|
---|
382 | || pExpDir->NumberOfNames < 1)
|
---|
383 | SUPHNTIMP_ERROR(false, 8, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
384 | "%ls: NumberOfNames or/and NumberOfFunctions are outside the expected range: nof=%#x non=%#x\n",
|
---|
385 | pDll->pwszName, pExpDir->NumberOfFunctions, pExpDir->NumberOfNames);
|
---|
386 | pDll->cNamedExports = pExpDir->NumberOfNames;
|
---|
387 | pDll->cExports = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
|
---|
388 |
|
---|
389 | if ( pExpDir->AddressOfFunctions < pDll->offEndSectHdrs
|
---|
390 | || pExpDir->AddressOfFunctions >= pNtHdrs->OptionalHeader.SizeOfImage
|
---|
391 | || pExpDir->AddressOfFunctions + pDll->cExports * sizeof(uint32_t) > pNtHdrs->OptionalHeader.SizeOfImage)
|
---|
392 | SUPHNTIMP_ERROR(false, 9, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
393 | "%ls: Bad AddressOfFunctions: %#x\n", pDll->pwszName, pExpDir->AddressOfFunctions);
|
---|
394 | pDll->paoffExports = (uint32_t const *)&pDll->pbImageBase[pExpDir->AddressOfFunctions];
|
---|
395 |
|
---|
396 | if ( pExpDir->AddressOfNames < pDll->offEndSectHdrs
|
---|
397 | || pExpDir->AddressOfNames >= pNtHdrs->OptionalHeader.SizeOfImage
|
---|
398 | || pExpDir->AddressOfNames + pExpDir->NumberOfNames * sizeof(uint32_t) > pNtHdrs->OptionalHeader.SizeOfImage)
|
---|
399 | SUPHNTIMP_ERROR(false, 10, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
400 | "%ls: Bad AddressOfNames: %#x\n", pDll->pwszName, pExpDir->AddressOfNames);
|
---|
401 | pDll->paoffNamedExports = (uint32_t const *)&pDll->pbImageBase[pExpDir->AddressOfNames];
|
---|
402 |
|
---|
403 | if ( pExpDir->AddressOfNameOrdinals < pDll->offEndSectHdrs
|
---|
404 | || pExpDir->AddressOfNameOrdinals >= pNtHdrs->OptionalHeader.SizeOfImage
|
---|
405 | || pExpDir->AddressOfNameOrdinals + pExpDir->NumberOfNames * sizeof(uint32_t) > pNtHdrs->OptionalHeader.SizeOfImage)
|
---|
406 | SUPHNTIMP_ERROR(false, 11, "supR3HardenedParseModule", kSupInitOp_Misc, VERR_INVALID_EXE_SIGNATURE,
|
---|
407 | "%ls: Bad AddressOfNameOrdinals: %#x\n", pDll->pwszName, pExpDir->AddressOfNameOrdinals);
|
---|
408 | pDll->pau16NameOrdinals = (uint16_t const *)&pDll->pbImageBase[pExpDir->AddressOfNameOrdinals];
|
---|
409 | }
|
---|
410 |
|
---|
411 |
|
---|
412 | /** @sa rtR0DbgKrnlInfoLookupSymbol */
|
---|
413 | static const char *supR3HardenedResolveImport(PSUPHNTIMPDLL pDll, PCSUPHNTIMPFUNC pImport, bool fReportErrors)
|
---|
414 | {
|
---|
415 | /*
|
---|
416 | * Binary search.
|
---|
417 | */
|
---|
418 | uint32_t iStart = 0;
|
---|
419 | uint32_t iEnd = pDll->cNamedExports;
|
---|
420 | while (iStart < iEnd)
|
---|
421 | {
|
---|
422 | uint32_t iCur = iStart + (iEnd - iStart) / 2;
|
---|
423 | uint32_t offExpName = pDll->paoffNamedExports[iCur];
|
---|
424 | if (RT_UNLIKELY(offExpName < pDll->offEndSectHdrs || offExpName >= pDll->cbImage))
|
---|
425 | SUPHNTIMP_ERROR(fReportErrors, 12, "supR3HardenedResolveImport", kSupInitOp_Misc, VERR_SYMBOL_NOT_FOUND,
|
---|
426 | "%ls: Bad export name entry: %#x (iCur=%#x)", pDll->pwszName, offExpName, iCur);
|
---|
427 |
|
---|
428 | const char *pszExpName = (const char *)&pDll->pbImageBase[offExpName];
|
---|
429 | int iDiff = strcmp(pszExpName, pImport->pszName);
|
---|
430 | if (iDiff > 0) /* pszExpName > pszSymbol: search chunck before i */
|
---|
431 | iEnd = iCur;
|
---|
432 | else if (iDiff < 0) /* pszExpName < pszSymbol: search chunk after i */
|
---|
433 | iStart = iCur + 1;
|
---|
434 | else /* pszExpName == pszSymbol */
|
---|
435 | {
|
---|
436 | uint16_t iExpOrdinal = pDll->pau16NameOrdinals[iCur];
|
---|
437 | if (iExpOrdinal < pDll->cExports)
|
---|
438 | {
|
---|
439 | uint32_t offExport = pDll->paoffExports[iExpOrdinal];
|
---|
440 |
|
---|
441 | /* detect export table patching. */
|
---|
442 | if (offExport >= pDll->cbImage)
|
---|
443 | pDll->cPatchedExports++;
|
---|
444 |
|
---|
445 | if (offExport - pDll->offExportDir >= pDll->cbExportDir)
|
---|
446 | {
|
---|
447 | *pImport->ppfnImport = (PFNRT)&pDll->pbImageBase[offExport];
|
---|
448 | return NULL;
|
---|
449 | }
|
---|
450 |
|
---|
451 | /* Forwarder. */
|
---|
452 | return (const char *)&pDll->pbImageBase[offExport];
|
---|
453 | }
|
---|
454 | SUPHNTIMP_ERROR(fReportErrors, 14, "supR3HardenedResolveImport", kSupInitOp_Misc, VERR_BAD_EXE_FORMAT,
|
---|
455 | "%ls: Name ordinal for '%s' is out of bounds: %#x (max %#x)",
|
---|
456 | pDll->pwszName, iExpOrdinal, pDll->cExports);
|
---|
457 | return NULL;
|
---|
458 | }
|
---|
459 | }
|
---|
460 |
|
---|
461 | if (!pImport->fOptional)
|
---|
462 | SUPHNTIMP_ERROR(fReportErrors, 15, "supR3HardenedResolveImport", kSupInitOp_Misc, VERR_SYMBOL_NOT_FOUND,
|
---|
463 | "%ls: Failed to resolve '%s'.", pDll->pwszName, pImport->pszName);
|
---|
464 | *pImport->ppfnImport = NULL;
|
---|
465 | return NULL;
|
---|
466 | }
|
---|
467 |
|
---|
468 |
|
---|
469 | static void supR3HardenedDirectSyscall(PSUPHNTIMPDLL pDll, PCSUPHNTIMPFUNC pImport, PCSUPHNTIMPSYSCALL pSyscall,
|
---|
470 | PSUPHNTLDRCACHEENTRY pLdrEntry, uint8_t *pbBits, bool fReportErrors)
|
---|
471 | {
|
---|
472 | /*
|
---|
473 | * Skip non-syscall entries.
|
---|
474 | */
|
---|
475 | if (!pSyscall->puApiNo)
|
---|
476 | return;
|
---|
477 |
|
---|
478 | /*
|
---|
479 | * Locate the virgin bits.
|
---|
480 | */
|
---|
481 | RTLDRADDR uValue;
|
---|
482 | int rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, (uintptr_t)pDll->pbImageBase, UINT32_MAX, pImport->pszName, &uValue);
|
---|
483 | if (RT_FAILURE(rc))
|
---|
484 | {
|
---|
485 | SUPHNTIMP_ERROR(fReportErrors, 16, "supR3HardenedDirectSyscall", kSupInitOp_Misc, rc,
|
---|
486 | "%s: RTLdrGetSymbolEx failed on %s: %Rrc", pDll->pszName, pImport->pszName, rc);
|
---|
487 | return;
|
---|
488 | }
|
---|
489 | uintptr_t offSymbol = (uintptr_t)uValue - (uintptr_t)pDll->pbImageBase;
|
---|
490 | uint8_t const * const pbFunction = &pbBits[offSymbol];
|
---|
491 |
|
---|
492 | /*
|
---|
493 | * Parse the code and extract the API call number.
|
---|
494 | */
|
---|
495 | #ifdef RT_ARCH_AMD64
|
---|
496 | /* Pattern #1: XP64/W2K3-64 thru Windows 10 build 10240.
|
---|
497 | 0:000> u ntdll!NtCreateSection
|
---|
498 | ntdll!NtCreateSection:
|
---|
499 | 00000000`779f1750 4c8bd1 mov r10,rcx
|
---|
500 | 00000000`779f1753 b847000000 mov eax,47h
|
---|
501 | 00000000`779f1758 0f05 syscall
|
---|
502 | 00000000`779f175a c3 ret
|
---|
503 | 00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
|
---|
504 |
|
---|
505 | Pattern #2: Windows 10 build 10525+.
|
---|
506 | 0:000> u ntdll_7ffc26300000!NtCreateSection
|
---|
507 | ntdll_7ffc26300000!ZwCreateSection:
|
---|
508 | 00007ffc`263943e0 4c8bd1 mov r10,rcx
|
---|
509 | 00007ffc`263943e3 b84a000000 mov eax,4Ah
|
---|
510 | 00007ffc`263943e8 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
|
---|
511 | 00007ffc`263943f0 7503 jne ntdll_7ffc26300000!ZwCreateSection+0x15 (00007ffc`263943f5)
|
---|
512 | 00007ffc`263943f2 0f05 syscall
|
---|
513 | 00007ffc`263943f4 c3 ret
|
---|
514 | 00007ffc`263943f5 cd2e int 2Eh
|
---|
515 | 00007ffc`263943f7 c3 ret
|
---|
516 | */
|
---|
517 | if ( pbFunction[ 0] == 0x4c /* mov r10, rcx */
|
---|
518 | && pbFunction[ 1] == 0x8b
|
---|
519 | && pbFunction[ 2] == 0xd1
|
---|
520 | && pbFunction[ 3] == 0xb8 /* mov eax, 0000yyzzh */
|
---|
521 | //&& pbFunction[ 4] == 0xZZ
|
---|
522 | //&& pbFunction[ 5] == 0xYY
|
---|
523 | && pbFunction[ 6] == 0x00
|
---|
524 | && pbFunction[ 7] == 0x00)
|
---|
525 | {
|
---|
526 | if ( pbFunction[ 8] == 0x0f /* syscall */
|
---|
527 | && pbFunction[ 9] == 0x05
|
---|
528 | && pbFunction[10] == 0xc3 /* ret */ )
|
---|
529 | {
|
---|
530 | *pSyscall->puApiNo = RT_MAKE_U16(pbFunction[4], pbFunction[5]);
|
---|
531 | *pImport->ppfnImport = pSyscall->pfnType1;
|
---|
532 | return;
|
---|
533 | }
|
---|
534 | if ( pbFunction[ 8] == 0xf6 /* test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 */
|
---|
535 | && pbFunction[ 9] == 0x04
|
---|
536 | && pbFunction[10] == 0x25
|
---|
537 | && pbFunction[11] == 0x08
|
---|
538 | && pbFunction[12] == 0x03
|
---|
539 | && pbFunction[13] == 0xfe
|
---|
540 | && pbFunction[14] == 0x7f
|
---|
541 | && pbFunction[15] == 0x01
|
---|
542 | && pbFunction[16] == 0x75 /* jnz +3 */
|
---|
543 | && pbFunction[17] == 0x03
|
---|
544 | && pbFunction[18] == 0x0f /* syscall*/
|
---|
545 | && pbFunction[19] == 0x05
|
---|
546 | && pbFunction[20] == 0xc3 /* ret */
|
---|
547 | && pbFunction[21] == 0xcd /* int 2eh */
|
---|
548 | && pbFunction[22] == 0x2e
|
---|
549 | && pbFunction[23] == 0xc3 /* ret */ )
|
---|
550 | {
|
---|
551 | *pSyscall->puApiNo = RT_MAKE_U16(pbFunction[4], pbFunction[5]);
|
---|
552 | *pImport->ppfnImport = pSyscall->pfnType2;
|
---|
553 | return;
|
---|
554 | }
|
---|
555 | }
|
---|
556 |
|
---|
557 | #elif defined(RT_ARCH_X86)
|
---|
558 | /* Pattern #1: XP thru Windows 7
|
---|
559 | kd> u ntdll!NtCreateSection
|
---|
560 | ntdll!NtCreateSection:
|
---|
561 | 7c90d160 b832000000 mov eax,32h
|
---|
562 | 7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
|
---|
563 | 7c90d16a ff12 call dword ptr [edx]
|
---|
564 | 7c90d16c c21c00 ret 1Ch
|
---|
565 | 7c90d16f 90 nop
|
---|
566 | The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
|
---|
567 |
|
---|
568 | Pattern #2: Windows 8.1
|
---|
569 | 0:000:x86> u ntdll_6a0f0000!NtCreateSection
|
---|
570 | ntdll_6a0f0000!NtCreateSection:
|
---|
571 | 6a15eabc b854010000 mov eax,154h
|
---|
572 | 6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
|
---|
573 | 6a15eac6 c21c00 ret 1Ch
|
---|
574 | 6a15eac9 8bd4 mov edx,esp
|
---|
575 | 6a15eacb 0f34 sysenter
|
---|
576 | 6a15eacd c3 ret
|
---|
577 | The variable bit is the value loaded into eax: W81=154h
|
---|
578 | Note! One nice thing here is that we can share code pattern #1. */
|
---|
579 |
|
---|
580 | if ( pbFunction[ 0] == 0xb8 /* mov eax, 0000yyzzh*/
|
---|
581 | //&& pbFunction[ 1] <= 0xZZ
|
---|
582 | //&& pbFunction[ 2] <= 0xYY
|
---|
583 | && pbFunction[ 3] == 0x00
|
---|
584 | && pbFunction[ 4] == 0x00)
|
---|
585 | {
|
---|
586 | *pSyscall->puApiNo = RT_MAKE_U16(pbFunction[1], pbFunction[2]);
|
---|
587 | if ( pbFunction[5] == 0xba /* mov edx, offset SharedUserData!SystemCallStub */
|
---|
588 | && pbFunction[ 6] == 0x00
|
---|
589 | && pbFunction[ 7] == 0x03
|
---|
590 | && pbFunction[ 8] == 0xfe
|
---|
591 | && pbFunction[ 9] == 0x7f
|
---|
592 | && pbFunction[10] == 0xff /* call [edx] */
|
---|
593 | && pbFunction[11] == 0x12
|
---|
594 | && ( ( pbFunction[12] == 0xc2 /* ret 1ch */
|
---|
595 | && pbFunction[13] == pSyscall->cbParams
|
---|
596 | && pbFunction[14] == 0x00)
|
---|
597 | || ( pbFunction[12] == 0xc3 /* ret */
|
---|
598 | && pSyscall->cbParams == 0)
|
---|
599 | )
|
---|
600 | )
|
---|
601 | {
|
---|
602 | *pImport->ppfnImport = pSyscall->pfnType1;
|
---|
603 | return;
|
---|
604 | }
|
---|
605 |
|
---|
606 | if ( pbFunction[ 5] == 0xe8 /* call [$+3] */
|
---|
607 | && RT_ABS(*(int32_t *)&pbFunction[6]) < 0x10
|
---|
608 | && ( ( pbFunction[10] == 0xc2 /* ret 1ch */
|
---|
609 | && pbFunction[11] == pSyscall->cbParams
|
---|
610 | && pbFunction[12] == 0x00)
|
---|
611 | || ( pbFunction[10] == 0xc3 /* ret */
|
---|
612 | && pSyscall->cbParams == 0)
|
---|
613 | )
|
---|
614 | )
|
---|
615 | {
|
---|
616 | *pImport->ppfnImport = pSyscall->pfnType2;
|
---|
617 | return;
|
---|
618 | }
|
---|
619 | }
|
---|
620 |
|
---|
621 | #elif defined(RT_ARCH_ARM64)
|
---|
622 | /* Only pattern (W10-1709):
|
---|
623 | 0000000180022950 <ZwCreateSection>:
|
---|
624 | 180022950: d4000941 svc #0x4a
|
---|
625 | 180022954: d65f03c0 ret
|
---|
626 | 180022958: 00000000 udf #0x0
|
---|
627 | 18002295c: 00000000 udf #0x0 */
|
---|
628 | uint32_t const * const pu32Function = (uint32_t const *)pbFunction;
|
---|
629 | if ( pu32Function[1] == ARMV8_A64_INSTR_RET
|
---|
630 | && (pu32Function[0] & ~(UINT32_C(0xffff) << 5)) == UINT32_C(0xd0000001))
|
---|
631 | {
|
---|
632 | *pSyscall->puApiNo = (pu32Function[0] >> 5) & UINT32_C(0xffff);
|
---|
633 | *pImport->ppfnImport = pSyscall->pfnType1;
|
---|
634 | return;
|
---|
635 | }
|
---|
636 |
|
---|
637 | #else
|
---|
638 | # error "port me"
|
---|
639 | #endif
|
---|
640 |
|
---|
641 | /*
|
---|
642 | * Failed to parse it.
|
---|
643 | */
|
---|
644 | volatile uint8_t abCopy[16];
|
---|
645 | memcpy((void *)&abCopy[0], pbFunction, sizeof(abCopy));
|
---|
646 | SUPHNTIMP_ERROR(fReportErrors, 17, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
|
---|
647 | "%ls: failed to parse syscall: '%s': %.16Rhxs",
|
---|
648 | pDll->pwszName, pImport->pszName, &abCopy[0]);
|
---|
649 | }
|
---|
650 |
|
---|
651 |
|
---|
652 | /**
|
---|
653 | * Check out system calls and do the directly instead of via NtDll.
|
---|
654 | *
|
---|
655 | * We need to have access to the on disk NTDLL.DLL file as we do not trust the
|
---|
656 | * stuff we find in memory. Too early to verify signatures though.
|
---|
657 | *
|
---|
658 | * @param fReportErrors Whether we've got the machinery for reporting
|
---|
659 | * errors going already.
|
---|
660 | * @param pErrInfo Buffer for gathering additional error info. This
|
---|
661 | * is mainly to avoid consuming lots of stacks with
|
---|
662 | * RTERRINFOSTATIC structures.
|
---|
663 | */
|
---|
664 | DECLHIDDEN(void) supR3HardenedWinInitSyscalls(bool fReportErrors, PRTERRINFO pErrInfo)
|
---|
665 | {
|
---|
666 | for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
|
---|
667 | if (g_aSupNtImpDlls[iDll].paSyscalls)
|
---|
668 | {
|
---|
669 | PSUPHNTLDRCACHEENTRY pLdrEntry;
|
---|
670 | int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry, pErrInfo);
|
---|
671 | if (RT_SUCCESS(rc))
|
---|
672 | {
|
---|
673 | uint8_t *pbBits;
|
---|
674 | rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase,
|
---|
675 | NULL, NULL, pErrInfo);
|
---|
676 | if (RT_SUCCESS(rc))
|
---|
677 | {
|
---|
678 | for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
|
---|
679 | supR3HardenedDirectSyscall(&g_aSupNtImpDlls[iDll], &g_aSupNtImpDlls[iDll].paImports[i],
|
---|
680 | &g_aSupNtImpDlls[iDll].paSyscalls[i], pLdrEntry, pbBits, fReportErrors);
|
---|
681 | }
|
---|
682 | else
|
---|
683 | SUPHNTIMP_ERROR(fReportErrors, 20, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
|
---|
684 | "%ls: supHardNtLdrCacheEntryGetBits failed: %Rrc %s",
|
---|
685 | g_aSupNtImpDlls[iDll].pwszName, rc, pErrInfo ? pErrInfo->pszMsg : "");
|
---|
686 | }
|
---|
687 | else
|
---|
688 | SUPHNTIMP_ERROR(fReportErrors, 21, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
|
---|
689 | "%ls: supHardNtLdrCacheOpen failed: %Rrc %s",
|
---|
690 | g_aSupNtImpDlls[iDll].pwszName, rc, pErrInfo ? pErrInfo->pszMsg : "");
|
---|
691 | }
|
---|
692 | }
|
---|
693 |
|
---|
694 |
|
---|
695 | /**
|
---|
696 | * Resolves a few NtDll functions we need before child purification is executed.
|
---|
697 | *
|
---|
698 | * We must not permanently modify any global data here.
|
---|
699 | *
|
---|
700 | * @param uNtDllAddr The address of the NTDLL.
|
---|
701 | * @param ppfnNtWaitForSingleObject Where to store the NtWaitForSingleObject
|
---|
702 | * address.
|
---|
703 | * @param ppfnNtSetEvent Where to store the NtSetEvent address.
|
---|
704 | */
|
---|
705 | DECLHIDDEN(void) supR3HardenedWinGetVeryEarlyImports(uintptr_t uNtDllAddr,
|
---|
706 | PFNNTWAITFORSINGLEOBJECT *ppfnNtWaitForSingleObject,
|
---|
707 | PFNNTSETEVENT *ppfnNtSetEvent)
|
---|
708 | {
|
---|
709 | /*
|
---|
710 | * NTDLL is the first entry in the list. Save it and do the parsing.
|
---|
711 | */
|
---|
712 | SUPHNTIMPDLL SavedDllEntry = g_aSupNtImpDlls[0];
|
---|
713 |
|
---|
714 | g_aSupNtImpDlls[0].pbImageBase = (uint8_t const *)uNtDllAddr;
|
---|
715 | supR3HardenedParseModule(&g_aSupNtImpDlls[0]);
|
---|
716 |
|
---|
717 | /*
|
---|
718 | * Create a temporary import table for the requested APIs and resolve them.
|
---|
719 | */
|
---|
720 | SUPHNTIMPFUNC aImports[] =
|
---|
721 | {
|
---|
722 | { "NtWaitForSingleObject", (PFNRT *)ppfnNtWaitForSingleObject, NULL, false },
|
---|
723 | { "NtSetEvent", (PFNRT *)ppfnNtSetEvent, NULL, false },
|
---|
724 | };
|
---|
725 |
|
---|
726 | for (uint32_t i = 0; i < RT_ELEMENTS(aImports); i++)
|
---|
727 | {
|
---|
728 | const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &aImports[i], false);
|
---|
729 | if (pszForwarder)
|
---|
730 | SUPHNTIMP_ERROR(false, 31, "supR3HardenedWinGetVeryEarlyImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
|
---|
731 | "ntdll: Failed to resolve forwarder '%s'.", pszForwarder);
|
---|
732 | }
|
---|
733 |
|
---|
734 | /*
|
---|
735 | * Restore the NtDll entry.
|
---|
736 | */
|
---|
737 | g_aSupNtImpDlls[0] = SavedDllEntry;
|
---|
738 | }
|
---|
739 |
|
---|
740 |
|
---|
741 | /**
|
---|
742 | * Resolves NtDll functions we can trust calling before process init.
|
---|
743 | *
|
---|
744 | * @param uNtDllAddr The address of the NTDLL.
|
---|
745 | */
|
---|
746 | DECLHIDDEN(void) supR3HardenedWinInitImportsEarly(uintptr_t uNtDllAddr)
|
---|
747 | {
|
---|
748 | /*
|
---|
749 | * NTDLL is the first entry in the list.
|
---|
750 | */
|
---|
751 | g_aSupNtImpDlls[0].pbImageBase = (uint8_t const *)uNtDllAddr;
|
---|
752 | supR3HardenedParseModule(&g_aSupNtImpDlls[0]);
|
---|
753 | for (uint32_t i = 0; i < g_aSupNtImpDlls[0].cImports; i++)
|
---|
754 | if (!g_aSupNtImpDlls[0].paImports[i].pfnEarlyDummy)
|
---|
755 | {
|
---|
756 | const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &g_aSupNtImpDlls[0].paImports[i], false);
|
---|
757 | if (pszForwarder)
|
---|
758 | SUPHNTIMP_ERROR(false, 32, "supR3HardenedWinInitImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
|
---|
759 | "ntdll: Failed to resolve forwarder '%s'.", pszForwarder);
|
---|
760 | }
|
---|
761 | else
|
---|
762 | *g_aSupNtImpDlls[0].paImports[i].ppfnImport = g_aSupNtImpDlls[0].paImports[i].pfnEarlyDummy;
|
---|
763 |
|
---|
764 | /*
|
---|
765 | * Point the other imports at the early init stubs.
|
---|
766 | */
|
---|
767 | for (uint32_t iDll = 1; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
|
---|
768 | for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
|
---|
769 | if (!g_aSupNtImpDlls[iDll].paImports[i].fOptional)
|
---|
770 | *g_aSupNtImpDlls[iDll].paImports[i].ppfnImport = g_aSupNtImpDlls[iDll].paImports[i].pfnEarlyDummy;
|
---|
771 | else
|
---|
772 | *g_aSupNtImpDlls[iDll].paImports[i].ppfnImport = NULL;
|
---|
773 | }
|
---|
774 |
|
---|
775 |
|
---|
776 | /**
|
---|
777 | * Resolves imported functions, esp. system calls from NTDLL.
|
---|
778 | *
|
---|
779 | * This crap is necessary because there are sandboxing products out there that
|
---|
780 | * will mess with system calls we make, just like any other wannabe userland
|
---|
781 | * rootkit. Kudos to microsoft for not providing a generic system call hook API
|
---|
782 | * in the kernel mode, which I guess is what forcing these kind of products to
|
---|
783 | * do ugly userland hacks that doesn't really hold water.
|
---|
784 | */
|
---|
785 | DECLHIDDEN(void) supR3HardenedWinInitImports(void)
|
---|
786 | {
|
---|
787 | RTERRINFOSTATIC ErrInfo;
|
---|
788 |
|
---|
789 | /*
|
---|
790 | * Find the DLLs we will be needing first (forwarders).
|
---|
791 | */
|
---|
792 | for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
|
---|
793 | {
|
---|
794 | supR3HardenedFindOrLoadModule(&g_aSupNtImpDlls[iDll]);
|
---|
795 | if (g_aSupNtImpDlls[iDll].pbImageBase)
|
---|
796 | supR3HardenedParseModule(&g_aSupNtImpDlls[iDll]);
|
---|
797 | }
|
---|
798 |
|
---|
799 | /*
|
---|
800 | * Resolve the functions.
|
---|
801 | */
|
---|
802 | for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
|
---|
803 | for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
|
---|
804 | {
|
---|
805 | const char *pszForwarder = supR3HardenedResolveImport(&g_aSupNtImpDlls[iDll], &g_aSupNtImpDlls[iDll].paImports[i],
|
---|
806 | false);
|
---|
807 | if (pszForwarder)
|
---|
808 | {
|
---|
809 | const char *pszDot = strchr(pszForwarder, '.');
|
---|
810 | size_t cchDllName = pszDot - pszForwarder;
|
---|
811 | SUPHNTIMPFUNC Tmp = g_aSupNtImpDlls[iDll].paImports[i];
|
---|
812 | Tmp.pszName = pszDot + 1;
|
---|
813 | if (cchDllName == sizeof("ntdll") - 1 && RTStrNICmp(pszForwarder, RT_STR_TUPLE("ntdll")) == 0)
|
---|
814 | supR3HardenedResolveImport(&g_aSupNtImpDlls[0], &Tmp, false);
|
---|
815 | else if (cchDllName == sizeof("kernelbase") - 1 && RTStrNICmp(pszForwarder, RT_STR_TUPLE("kernelbase")) == 0)
|
---|
816 | supR3HardenedResolveImport(&g_aSupNtImpDlls[1], &Tmp, false);
|
---|
817 | else
|
---|
818 | SUPHNTIMP_ERROR(false, 18, "supR3HardenedWinInitImports", kSupInitOp_Misc, VERR_MODULE_NOT_FOUND,
|
---|
819 | "%ls: Failed to resolve forwarder '%s'.", g_aSupNtImpDlls[iDll].pwszName, pszForwarder);
|
---|
820 | }
|
---|
821 | }
|
---|
822 |
|
---|
823 | /*
|
---|
824 | * Do system calls directly.
|
---|
825 | */
|
---|
826 | supR3HardenedWinInitSyscalls(false, RTErrInfoInitStatic(&ErrInfo));
|
---|
827 |
|
---|
828 | /*
|
---|
829 | * Use the on disk image to avoid export table patching. Currently
|
---|
830 | * ignoring errors here as can live normally without this step.
|
---|
831 | */
|
---|
832 | for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
|
---|
833 | if (g_aSupNtImpDlls[iDll].cPatchedExports > 0)
|
---|
834 | {
|
---|
835 | PSUPHNTLDRCACHEENTRY pLdrEntry;
|
---|
836 | int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry, RTErrInfoInitStatic(&ErrInfo));
|
---|
837 | if (RT_SUCCESS(rc))
|
---|
838 | {
|
---|
839 | uint8_t *pbBits;
|
---|
840 | rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
|
---|
841 | RTErrInfoInitStatic(&ErrInfo));
|
---|
842 | if (RT_SUCCESS(rc))
|
---|
843 | for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
|
---|
844 | {
|
---|
845 | RTLDRADDR uValue;
|
---|
846 | rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase,
|
---|
847 | UINT32_MAX, g_aSupNtImpDlls[iDll].paImports[i].pszName, &uValue);
|
---|
848 | if (RT_SUCCESS(rc))
|
---|
849 | *g_aSupNtImpDlls[iDll].paImports[i].ppfnImport = (PFNRT)(uintptr_t)uValue;
|
---|
850 | }
|
---|
851 | }
|
---|
852 | }
|
---|
853 |
|
---|
854 |
|
---|
855 | #if 0 /* Win7/32 ntdll!LdrpDebugFlags. */
|
---|
856 | *(uint8_t *)&g_aSupNtImpDlls[0].pbImageBase[0xdd770] = 0x3;
|
---|
857 | #endif
|
---|
858 | }
|
---|
859 |
|
---|
860 |
|
---|
861 | /**
|
---|
862 | * Gets the address of a procedure in a DLL, ignoring our own syscall
|
---|
863 | * implementations.
|
---|
864 | *
|
---|
865 | * Currently restricted to NTDLL and KERNEL32
|
---|
866 | *
|
---|
867 | * @returns The procedure address.
|
---|
868 | * @param pszDll The DLL name.
|
---|
869 | * @param pszProcedure The procedure name.
|
---|
870 | */
|
---|
871 | DECLHIDDEN(PFNRT) supR3HardenedWinGetRealDllSymbol(const char *pszDll, const char *pszProcedure)
|
---|
872 | {
|
---|
873 | RTERRINFOSTATIC ErrInfo;
|
---|
874 |
|
---|
875 | /*
|
---|
876 | * Look the DLL up in the import DLL table.
|
---|
877 | */
|
---|
878 | for (uint32_t iDll = 0; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
|
---|
879 | if (RTStrICmp(g_aSupNtImpDlls[iDll].pszName, pszDll) == 0)
|
---|
880 | {
|
---|
881 |
|
---|
882 | PSUPHNTLDRCACHEENTRY pLdrEntry;
|
---|
883 | int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry, RTErrInfoInitStatic(&ErrInfo));
|
---|
884 | if (RT_SUCCESS(rc))
|
---|
885 | {
|
---|
886 | uint8_t *pbBits;
|
---|
887 | rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
|
---|
888 | RTErrInfoInitStatic(&ErrInfo));
|
---|
889 | if (RT_SUCCESS(rc))
|
---|
890 | {
|
---|
891 | RTLDRADDR uValue;
|
---|
892 | rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase,
|
---|
893 | UINT32_MAX, pszProcedure, &uValue);
|
---|
894 | if (RT_SUCCESS(rc))
|
---|
895 | return (PFNRT)(uintptr_t)uValue;
|
---|
896 | SUP_DPRINTF(("supR3HardenedWinGetRealDllSymbol: Error getting %s in %s -> %Rrc\n", pszProcedure, pszDll, rc));
|
---|
897 | }
|
---|
898 | else
|
---|
899 | SUP_DPRINTF(("supR3HardenedWinGetRealDllSymbol: supHardNtLdrCacheEntryAllocBits failed on %s: %Rrc %s\n",
|
---|
900 | pszDll, rc, ErrInfo.Core.pszMsg));
|
---|
901 | }
|
---|
902 | else
|
---|
903 | SUP_DPRINTF(("supR3HardenedWinGetRealDllSymbol: supHardNtLdrCacheOpen failed on %s: %Rrc %s\n",
|
---|
904 | pszDll, rc, ErrInfo.Core.pszMsg));
|
---|
905 |
|
---|
906 | /* Complications, just call GetProcAddress. */
|
---|
907 | if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
|
---|
908 | return (PFNRT)GetProcAddress(GetModuleHandleW(g_aSupNtImpDlls[iDll].pwszName), pszProcedure);
|
---|
909 | return NULL;
|
---|
910 | }
|
---|
911 |
|
---|
912 | supR3HardenedFatal("supR3HardenedWinGetRealDllSymbol: Unknown DLL %s (proc: %s)\n", pszDll, pszProcedure);
|
---|
913 | /* not reached */
|
---|
914 | }
|
---|
915 |
|
---|