VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use