VirtualBox

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

Last change on this file since 54890 was 52969, checked in by vboxsync, 10 years ago

SUP: Restructured the respawn + child purification code to use the primary thread to trigger the initial image load events since more recent windows versions only seems to trigger these on the primary thread.

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