VirtualBox

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

Last change on this file since 78351 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

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