VirtualBox

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

Last change on this file since 56743 was 56732, checked in by vboxsync, 9 years ago

Added SUPHARNT_IMPORT_STDCALL_OPTIONAL (currently unused).

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