VirtualBox

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

Last change on this file since 73156 was 70137, checked in by vboxsync, 7 years ago

SUPR3HardenedMainImports-win.cpp: not where there is similar code in iprt.

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