VirtualBox

source: vbox/trunk/src/VBox/Installer/win/MsiHack/MsiHack.cpp@ 94991

Last change on this file since 94991 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.1 KB
Line 
1/* $Id: MsiHack.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * MsiHack - Exterimental DLL that intercept small ReadFile calls from
4 * MSI, CABINET and WINTEROP, buffering them using memory mapped files.
5 *
6 * @remarks Doesn't save as much as hoped on fast disks.
7 */
8
9/*
10 * Copyright (C) 2016-2022 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#include <iprt/win/windows.h>
26#include <iprt/string.h>
27#include <iprt/asm.h>
28#include <stdio.h>
29#include <wchar.h>
30
31
32/*********************************************************************************************************************************
33* Defined Constants And Macros *
34*********************************************************************************************************************************/
35/** @MSI_HACK_HANDLE_TO_INDEX
36 * Handle to g_papHandles index.
37 */
38#if ARCH_BITS == 64
39# define MSI_HACK_HANDLE_TO_INDEX(hHandle) (((uintptr_t)hHandle & ~UINT64_C(0x80000000)) >> 3)
40#elif ARCH_BITS == 32
41# define MSI_HACK_HANDLE_TO_INDEX(hHandle) (((uintptr_t)hHandle & ~UINT32_C(0x80000000)) >> 2)
42#else
43# error "Unsupported or missing ARCH_BITS!"
44#endif
45
46/** Generic assertion macro. */
47#define MSIHACK_ASSERT(a_Expr) \
48 do { \
49 if (!!(a_Expr)) { /* likely */ } \
50 else MsiHackErrorF("Assertion failed at line " RT_STR(__LINE__) ": " #a_Expr "\n"); \
51 } while (0)
52
53/** Assertion macro that returns if expression is false. */
54#define MSIHACK_ASSERT_RETURN(a_Expr, a_fRc) \
55 do { \
56 if (!!(a_Expr)) { /* likely */ } \
57 else \
58 { \
59 MsiHackErrorF("Assertion failed at line " RT_STR(__LINE__) ": " #a_Expr "\n"); \
60 return (a_fRc); \
61 } \
62 } while (0)
63
64/** Assertion macro that executes a statemtn when false. */
65#define MSIHACK_ASSERT_STMT(a_Expr, a_Stmt) \
66 do { \
67 if (!!(a_Expr)) { /* likely */ } \
68 else \
69 { \
70 MsiHackErrorF("Assertion failed at line " RT_STR(__LINE__) ": " #a_Expr "\n"); \
71 a_Stmt; \
72 } \
73 } while (0)
74
75/** Assertion macro that executes a statemtn when false. */
76#define MSIHACK_ASSERT_MSG(a_Expr, a_Msg) \
77 do { \
78 if (!!(a_Expr)) { /* likely */ } \
79 else \
80 { \
81 MsiHackErrorF("Assertion failed at line " RT_STR(__LINE__) ": " #a_Expr "\n"); \
82 MsiHackErrorF a_Msg; \
83 } \
84 } while (0)
85
86
87/*********************************************************************************************************************************
88* Structures and Typedefs *
89*********************************************************************************************************************************/
90/**
91 * Intercepted handle data.
92 */
93typedef struct MSIHACKHANDLE
94{
95 /** The actual handle value. */
96 HANDLE hHandle;
97 /** The buffer. */
98 uint8_t *pbBuffer;
99 /** Valid buffer size. */
100 size_t cbBuffer;
101 /** The allocated buffer size. */
102 size_t cbBufferAlloc;
103 /** The file offset of the buffer. */
104 uint64_t offFileBuffer;
105 /** The file size. */
106 uint64_t cbFile;
107 /** The current file offset. */
108 uint64_t offFile;
109 /** Whether pbBuffer is a memory mapping of hHandle. */
110 bool fMemoryMapped;
111 /** We only try caching a file onece. */
112 bool fDontTryAgain;
113 /** Reference counter. */
114 int32_t volatile cRefs;
115 /** Critical section protecting the handle. */
116 CRITICAL_SECTION CritSect;
117} MSIHACKHANDLE;
118/** Pointer to an intercepted handle. */
119typedef MSIHACKHANDLE *PMSIHACKHANDLE;
120
121
122/**
123 * Replacement function entry.
124 */
125typedef struct MSIHACKREPLACEMENT
126{
127 /** The function name. */
128 const char *pszFunction;
129 /** The length of the function name. */
130 size_t cchFunction;
131 /** The module name (optional). */
132 const char *pszModule;
133 /** The replacement function or data address. */
134 uintptr_t pfnReplacement;
135} MSIHACKREPLACEMENT;
136/** Pointer to a replacement function entry */
137typedef MSIHACKREPLACEMENT const *PCMSIHACKREPLACEMENT;
138
139
140/*********************************************************************************************************************************
141* Global Variables *
142*********************************************************************************************************************************/
143/** Critical section protecting the handle table. */
144static CRITICAL_SECTION g_CritSect;
145/** Size of the handle table. */
146static size_t g_cHandles;
147/** The handle table. */
148static PMSIHACKHANDLE *g_papHandles;
149
150
151
152void MsiHackErrorF(const char *pszFormat, ...)
153{
154 fprintf(stderr, "MsiHack: error: ");
155 va_list va;
156 va_start(va, pszFormat);
157 vfprintf(stderr, pszFormat, va);
158 va_end(va);
159}
160
161
162void MsiHackDebugF(const char *pszFormat, ...)
163{
164 if (1)
165 {
166 fprintf(stderr, "MsiHack: debug: ");
167 va_list va;
168 va_start(va, pszFormat);
169 vfprintf(stderr, pszFormat, va);
170 va_end(va);
171 }
172}
173
174
175/**
176 * Destroys a handle.
177 */
178DECL_NO_INLINE(static, void) MsiHackHandleDestroy(PMSIHACKHANDLE pHandle)
179{
180 /* The handle value should always be invalid at this point! */
181 MSIHACK_ASSERT(pHandle->hHandle == INVALID_HANDLE_VALUE);
182
183 if (pHandle->fMemoryMapped)
184 UnmapViewOfFile(pHandle->pbBuffer);
185 else
186 free(pHandle->pbBuffer);
187 pHandle->pbBuffer = NULL;
188
189 DeleteCriticalSection(&pHandle->CritSect);
190 free(pHandle);
191}
192
193
194/**
195 * Releases a handle reference.
196 * @param pHandle The handle to release.
197 */
198DECLINLINE(void) MsiHackHandleRelease(PMSIHACKHANDLE pHandle)
199{
200 if (ASMAtomicDecS32(&pHandle->cRefs) != 0)
201 return;
202 MsiHackHandleDestroy(pHandle);
203}
204
205
206/**
207 * Get and retain handle.
208 *
209 * @returns Pointer to a reference handle or NULL if not our handle.
210 * @param hHandle The handle.
211 */
212DECLINLINE(PMSIHACKHANDLE) MsiHackHandleRetain(HANDLE hHandle)
213{
214 uintptr_t const idxHandle = MSI_HACK_HANDLE_TO_INDEX(hHandle);
215 EnterCriticalSection(&g_CritSect);
216 if (idxHandle < g_cHandles)
217 {
218 PMSIHACKHANDLE pHandle = g_papHandles[idxHandle];
219 if (pHandle)
220 {
221 ASMAtomicIncS32(&pHandle->cRefs);
222 LeaveCriticalSection(&g_CritSect);
223 return pHandle;
224 }
225 }
226 LeaveCriticalSection(&g_CritSect);
227 return NULL;
228}
229
230
231/**
232 * Enters @a pHandle into the handle table under @a hHandle.
233 *
234 * @returns true on succes, false on error.
235 * @param pHandle The handle to enter.
236 * @param hHandle The handle value to enter it under.
237 */
238static bool MsiHackHandleEnter(PMSIHACKHANDLE pHandle, HANDLE hHandle)
239{
240 uintptr_t const idxHandle = MSI_HACK_HANDLE_TO_INDEX(hHandle);
241 EnterCriticalSection(&g_CritSect);
242
243 /*
244 * Make sure there is room in the handle table.
245 */
246 bool fOkay = idxHandle < g_cHandles;
247 if (fOkay)
248 { /* typical */ }
249 else if (idxHandle < _1M)
250 {
251 size_t cNew = g_cHandles * 2;
252 while (cNew < idxHandle)
253 cNew *= 2;
254
255 void *pvNew = realloc(g_papHandles, cNew * sizeof(g_papHandles[0]));
256 if (pvNew)
257 {
258 g_papHandles = (PMSIHACKHANDLE *)pvNew;
259 memset(&g_papHandles[g_cHandles], 0, (cNew - g_cHandles) * sizeof(g_papHandles[0]));
260 g_cHandles = cNew;
261 fOkay = true;
262 }
263 else
264 MsiHackErrorF("Failed to grow handle table from %p to %p entries!\n", g_cHandles, cNew);
265 }
266 else
267 MsiHackErrorF("Handle %p (0x%p) is above the max handle table size limit!\n", hHandle, idxHandle);
268 if (fOkay)
269 {
270 /*
271 * Insert it into the table if the entry is empty.
272 */
273 if (g_papHandles[idxHandle] == NULL)
274 {
275 g_papHandles[idxHandle] = pHandle;
276 LeaveCriticalSection(&g_CritSect);
277 return true;
278 }
279 MsiHackErrorF("Handle table entry 0x%p (%p) is already busy with %p! Cannot replace with %p.\n",
280 hHandle, idxHandle, g_papHandles[idxHandle], pHandle);
281 }
282
283 LeaveCriticalSection(&g_CritSect);
284 return false;
285}
286
287
288/**
289 * Prepares a file for potential caching.
290 *
291 * If successful, the handled is entered into the handle table.
292 *
293 * @param hFile Handle to the file to cache.
294 */
295static void MsiHackFilePrepare(HANDLE hFile)
296{
297 DWORD const dwErrSaved = GetLastError();
298
299 LARGE_INTEGER cbFile;
300 if (GetFileSizeEx(hFile, &cbFile))
301 {
302 PMSIHACKHANDLE pHandle = (PMSIHACKHANDLE)calloc(1, sizeof(*pHandle));
303 if (pHandle)
304 {
305 pHandle->cbFile = cbFile.QuadPart;
306 pHandle->pbBuffer = NULL;
307 pHandle->cbBuffer = 0;
308 pHandle->cbBufferAlloc = 0;
309 pHandle->offFileBuffer = 0;
310 pHandle->fMemoryMapped = true;
311 pHandle->cRefs = 1;
312 InitializeCriticalSection(&pHandle->CritSect);
313 if (MsiHackHandleEnter(pHandle, hFile))
314 {
315 SetLastError(dwErrSaved);
316 return;
317 }
318
319 free(pHandle);
320 }
321 }
322
323 SetLastError(dwErrSaved);
324}
325
326
327/**
328 * Worker for MsiHackFileSetupCache
329 *
330 * @returns True if sucessfully cached, False if not.
331 * @param pHandle The file.
332 * @param hFile The current valid handle.
333 */
334static bool MsiHackFileSetupCache(PMSIHACKHANDLE pHandle, HANDLE hFile)
335{
336 DWORD const dwErrSaved = GetLastError();
337 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
338 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
339 if (hMapping != NULL)
340 {
341 pHandle->pbBuffer = (uint8_t *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/,
342 (SIZE_T)pHandle->cbFile);
343 if (pHandle->pbBuffer)
344 {
345 pHandle->cbBuffer = (size_t)pHandle->cbFile;
346 pHandle->cbBufferAlloc = (size_t)pHandle->cbFile;
347 pHandle->offFileBuffer = 0;
348 pHandle->fMemoryMapped = true;
349 CloseHandle(hMapping);
350
351 SetLastError(dwErrSaved);
352 return true;
353 }
354 CloseHandle(hMapping);
355 }
356 SetLastError(dwErrSaved);
357 pHandle->fDontTryAgain = true;
358 return false;
359}
360
361
362/**
363 * This is called to check if the file is cached and try cache it.
364 *
365 * We delay the actually caching till the file is read, so we don't waste time
366 * mapping it into memory when all that is wanted is the file size or something
367 * like that.
368 *
369 * @returns True if cached, False if not.
370 * @param pHandle The file.
371 * @param hFile The current valid handle.
372 */
373DECLINLINE(bool) MsiHackFileIsCached(PMSIHACKHANDLE pHandle, HANDLE hFile)
374{
375 if (pHandle->pbBuffer)
376 return true;
377 if (!pHandle->fDontTryAgain)
378 return MsiHackFileSetupCache(pHandle, hFile);
379 return false;
380}
381
382
383/** Kernel32 - CreateFileA */
384static HANDLE WINAPI MsiHack_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
385 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
386 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
387{
388 /*
389 * If this is read-only access to the file, try cache it.
390 */
391 if (dwCreationDisposition == OPEN_EXISTING)
392 {
393 if ( dwDesiredAccess == GENERIC_READ
394 || dwDesiredAccess == FILE_GENERIC_READ)
395 {
396 if (dwShareMode & FILE_SHARE_READ)
397 {
398 if ( !pSecAttrs
399 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
400 && pSecAttrs->lpSecurityDescriptor == NULL ) )
401 {
402 HANDLE hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
403 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
404 if (hFile != INVALID_HANDLE_VALUE && hFile != NULL)
405 {
406 MsiHackDebugF("CreateFileA: %s\n", pszFilename );
407 MsiHackFilePrepare(hFile);
408 }
409 return hFile;
410 }
411 }
412 }
413 }
414
415 /*
416 * Don't intercept it.
417 */
418 return CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
419 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
420}
421
422
423/** Kernel32 - CreateFileW */
424static HANDLE WINAPI MsiHack_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
425 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
426 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
427{
428 /*
429 * If this is read-only access to the file, try cache it.
430 */
431 if (dwCreationDisposition == OPEN_EXISTING)
432 {
433 if ( dwDesiredAccess == GENERIC_READ
434 || dwDesiredAccess == FILE_GENERIC_READ)
435 {
436 if (dwShareMode & FILE_SHARE_READ)
437 {
438 if ( !pSecAttrs
439 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
440 && pSecAttrs->lpSecurityDescriptor == NULL ) )
441 {
442 HANDLE hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
443 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
444 if (hFile != INVALID_HANDLE_VALUE && hFile != NULL)
445 {
446 MsiHackDebugF("CreateFileW: %ls\n", pwszFilename);
447 MsiHackFilePrepare(hFile);
448 }
449 return hFile;
450 }
451 }
452 }
453 }
454
455 /*
456 * Don't intercept it.
457 */
458 return CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
459 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
460}
461
462
463/** Kernel32 - SetFilePointer */
464static DWORD WINAPI MsiHack_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
465{
466 /*
467 * If intercepted handle, deal with it.
468 */
469 PMSIHACKHANDLE pHandle = MsiHackHandleRetain(hFile);
470 if (pHandle)
471 {
472 if (MsiHackFileIsCached(pHandle, hFile))
473 {
474 int64_t offMove = pcbMoveHi ? ((int64_t)*pcbMoveHi << 32) | cbMove : cbMove;
475
476 EnterCriticalSection(&pHandle->CritSect);
477 switch (dwMoveMethod)
478 {
479 case FILE_BEGIN:
480 break;
481 case FILE_CURRENT:
482 offMove += pHandle->offFile;
483 break;
484 case FILE_END:
485 offMove += pHandle->cbFile;
486 break;
487 default:
488 LeaveCriticalSection(&pHandle->CritSect);
489 MsiHackHandleRelease(pHandle);
490
491 MsiHackErrorF("SetFilePointer(%p) - invalid method!\n", hFile);
492 SetLastError(ERROR_INVALID_PARAMETER);
493 return INVALID_SET_FILE_POINTER;
494 }
495
496 if (offMove >= 0)
497 {
498 /* Seeking beyond the end isn't useful, so just clamp it. */
499 if (offMove >= (int64_t)pHandle->cbFile)
500 offMove = (int64_t)pHandle->cbFile;
501 pHandle->offFile = (uint64_t)offMove;
502 }
503 else
504 {
505 LeaveCriticalSection(&pHandle->CritSect);
506 MsiHackHandleRelease(pHandle);
507
508 MsiHackErrorF("SetFilePointer(%p) - negative seek!\n", hFile);
509 SetLastError(ERROR_NEGATIVE_SEEK);
510 return INVALID_SET_FILE_POINTER;
511 }
512
513 LeaveCriticalSection(&pHandle->CritSect);
514 MsiHackHandleRelease(pHandle);
515
516 if (pcbMoveHi)
517 *pcbMoveHi = (uint64_t)offMove >> 32;
518 SetLastError(NO_ERROR);
519 return (uint32_t)offMove;
520 }
521 MsiHackHandleRelease(pHandle);
522 }
523
524 /*
525 * Not one of ours.
526 */
527 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
528}
529
530
531/** Kernel32 - SetFilePointerEx */
532static BOOL WINAPI MsiHack_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
533 DWORD dwMoveMethod)
534{
535 /*
536 * If intercepted handle, deal with it.
537 */
538 PMSIHACKHANDLE pHandle = MsiHackHandleRetain(hFile);
539 if (pHandle)
540 {
541 if (MsiHackFileIsCached(pHandle, hFile))
542 {
543 int64_t offMyMove = offMove.QuadPart;
544
545 EnterCriticalSection(&pHandle->CritSect);
546 switch (dwMoveMethod)
547 {
548 case FILE_BEGIN:
549 break;
550 case FILE_CURRENT:
551 offMyMove += pHandle->offFile;
552 break;
553 case FILE_END:
554 offMyMove += pHandle->cbFile;
555 break;
556 default:
557 LeaveCriticalSection(&pHandle->CritSect);
558 MsiHackHandleRelease(pHandle);
559
560 MsiHackErrorF("SetFilePointerEx(%p) - invalid method!\n", hFile);
561 SetLastError(ERROR_INVALID_PARAMETER);
562 return INVALID_SET_FILE_POINTER;
563 }
564 if (offMyMove >= 0)
565 {
566 /* Seeking beyond the end isn't useful, so just clamp it. */
567 if (offMyMove >= (int64_t)pHandle->cbFile)
568 offMyMove = (int64_t)pHandle->cbFile;
569 pHandle->offFile = (uint64_t)offMyMove;
570 }
571 else
572 {
573 LeaveCriticalSection(&pHandle->CritSect);
574 MsiHackHandleRelease(pHandle);
575
576 MsiHackErrorF("SetFilePointerEx(%p) - negative seek!\n", hFile);
577 SetLastError(ERROR_NEGATIVE_SEEK);
578 return INVALID_SET_FILE_POINTER;
579 }
580
581 LeaveCriticalSection(&pHandle->CritSect);
582 MsiHackHandleRelease(pHandle);
583
584 if (poffNew)
585 poffNew->QuadPart = offMyMove;
586 return TRUE;
587 }
588 MsiHackHandleRelease(pHandle);
589 }
590
591 /*
592 * Not one of ours.
593 */
594 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
595}
596
597
598/** Kernel32 - ReadFile */
599static BOOL WINAPI MsiHack_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
600 LPOVERLAPPED pOverlapped)
601{
602 /*
603 * If intercepted handle, deal with it.
604 */
605 PMSIHACKHANDLE pHandle = MsiHackHandleRetain(hFile);
606 if (pHandle)
607 {
608 if (MsiHackFileIsCached(pHandle, hFile))
609 {
610 EnterCriticalSection(&pHandle->CritSect);
611 uint32_t cbActually = pHandle->cbFile - pHandle->offFile;
612 if (cbActually > cbToRead)
613 cbActually = cbToRead;
614
615 memcpy(pvBuffer, &pHandle->pbBuffer[pHandle->offFile], cbActually);
616 pHandle->offFile += cbActually;
617
618 LeaveCriticalSection(&pHandle->CritSect);
619 MsiHackHandleRelease(pHandle);
620
621 MSIHACK_ASSERT(!pOverlapped); MSIHACK_ASSERT(pcbActuallyRead);
622 *pcbActuallyRead = cbActually;
623
624 return TRUE;
625 }
626 MsiHackHandleRelease(pHandle);
627 }
628
629 /*
630 * Not one of ours.
631 */
632 return ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
633}
634
635
636/** Kernel32 - ReadFileEx */
637static BOOL WINAPI MsiHack_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
638 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
639{
640 /*
641 * If intercepted handle, deal with it.
642 */
643 PMSIHACKHANDLE pHandle = MsiHackHandleRetain(hFile);
644 if (pHandle)
645 {
646 MsiHackHandleRelease(pHandle);
647
648 MsiHackErrorF("Unexpected ReadFileEx call!\n");
649 SetLastError(ERROR_INVALID_FUNCTION);
650 return FALSE;
651 }
652
653 /*
654 * Not one of ours.
655 */
656 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
657}
658
659
660/** Kernel32 - DuplicateHandle */
661static BOOL WINAPI MsiHack_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
662 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
663{
664 /*
665 * We must catch our handles being duplicated.
666 */
667 if ( hSrcProc == GetCurrentProcess()
668 && hDstProc == hSrcProc)
669 {
670 PMSIHACKHANDLE pSrcHandle = MsiHackHandleRetain(hSrcProc);
671 if (pSrcHandle)
672 {
673 if (dwOptions & DUPLICATE_CLOSE_SOURCE)
674 MsiHackErrorF("DUPLICATE_CLOSE_SOURCE is not implemented!\n");
675 BOOL fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
676 if (fRet)
677 {
678 if (MsiHackHandleEnter(pSrcHandle, *phNew))
679 return fRet; /* don't release reference. */
680
681 CloseHandle(*phNew);
682 *phNew = INVALID_HANDLE_VALUE;
683 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
684 fRet = FALSE;
685 }
686 MsiHackHandleRelease(pSrcHandle);
687 return fRet;
688 }
689 }
690
691 /*
692 * Not one of ours.
693 */
694 return DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
695}
696
697
698/** Kernel32 - CloseHandle */
699static BOOL WINAPI MsiHack_Kernel32_CloseHandle(HANDLE hObject)
700{
701 /*
702 * If intercepted handle, remove it from the table.
703 */
704 uintptr_t const idxHandle = MSI_HACK_HANDLE_TO_INDEX(hObject);
705 EnterCriticalSection(&g_CritSect);
706 if (idxHandle < g_cHandles)
707 {
708 PMSIHACKHANDLE pHandle = g_papHandles[idxHandle];
709 if (pHandle)
710 {
711 g_papHandles[idxHandle] = NULL;
712 LeaveCriticalSection(&g_CritSect);
713
714 /*
715 * Then close the handle.
716 */
717 EnterCriticalSection(&pHandle->CritSect);
718 BOOL fRet = CloseHandle(hObject);
719 pHandle->hHandle = INVALID_HANDLE_VALUE;
720 DWORD dwErr = GetLastError();
721 LeaveCriticalSection(&pHandle->CritSect);
722
723 /*
724 * And finally release the reference held by the handle table.
725 */
726 MsiHackHandleRelease(pHandle);
727 SetLastError(dwErr);
728 return fRet;
729 }
730 }
731
732 /*
733 * Not one of ours.
734 */
735 LeaveCriticalSection(&g_CritSect);
736 return CloseHandle(hObject);
737}
738
739
740
741
742/** Replacement functions. */
743static const MSIHACKREPLACEMENT g_aReplaceFunctions[] =
744{
745 { RT_STR_TUPLE("CreateFileA"), NULL, (uintptr_t)MsiHack_Kernel32_CreateFileA },
746 { RT_STR_TUPLE("CreateFileW"), NULL, (uintptr_t)MsiHack_Kernel32_CreateFileW },
747 { RT_STR_TUPLE("ReadFile"), NULL, (uintptr_t)MsiHack_Kernel32_ReadFile },
748 { RT_STR_TUPLE("ReadFileEx"), NULL, (uintptr_t)MsiHack_Kernel32_ReadFileEx },
749 { RT_STR_TUPLE("SetFilePointer"), NULL, (uintptr_t)MsiHack_Kernel32_SetFilePointer },
750 { RT_STR_TUPLE("SetFilePointerEx"), NULL, (uintptr_t)MsiHack_Kernel32_SetFilePointerEx },
751 { RT_STR_TUPLE("DuplicateHandle"), NULL, (uintptr_t)MsiHack_Kernel32_DuplicateHandle },
752 { RT_STR_TUPLE("CloseHandle"), NULL, (uintptr_t)MsiHack_Kernel32_CloseHandle },
753};
754
755
756
757/**
758 * Patches the import table of the given DLL.
759 *
760 * @returns true on success, false on failure.
761 * @param hmod .
762 */
763__declspec(dllexport) /* kBuild workaround */
764bool MsiHackPatchDll(HMODULE hmod)
765{
766 uint8_t const * const pbImage = (uint8_t const *)hmod;
767
768 /*
769 * Locate the import descriptors.
770 */
771 /* MZ header and PE headers. */
772 IMAGE_NT_HEADERS const *pNtHdrs;
773 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
774 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
775 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
776 else
777 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
778
779 /* Check PE header. */
780 MSIHACK_ASSERT_RETURN(pNtHdrs->Signature == IMAGE_NT_SIGNATURE, false);
781 MSIHACK_ASSERT_RETURN(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader), false);
782 uint32_t const cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
783
784 /* Locate the import descriptor array. */
785 IMAGE_DATA_DIRECTORY const *pDirEnt;
786 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
787 if ( pDirEnt->Size > 0
788 && pDirEnt->VirtualAddress != 0)
789 {
790 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
791 uint32_t cLeft = pDirEnt->Size / sizeof(*pImpDesc);
792 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
793 uint8_t *pbProtRange = NULL;
794 SIZE_T cbProtRange = 0;
795 DWORD fOldProt = 0;
796 uint32_t const cbPage = 0x1000;
797 BOOL fRc;
798
799 MSIHACK_ASSERT_RETURN(pDirEnt->VirtualAddress < cbImage, false);
800 MSIHACK_ASSERT_RETURN(pDirEnt->Size < cbImage, false);
801 MSIHACK_ASSERT_RETURN(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage, false);
802
803 /*
804 * Walk the import descriptor array.
805 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
806 */
807 while ( cLeft-- > 0
808 && pImpDesc->Name > 0
809 && pImpDesc->FirstThunk > 0)
810 {
811 uint32_t iThunk;
812 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
813 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
814 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
815 MSIHACK_ASSERT_RETURN(pImpDesc->Name < cbImage, false);
816 MSIHACK_ASSERT_RETURN(pImpDesc->FirstThunk < cbImage, false);
817 MSIHACK_ASSERT_RETURN(pImpDesc->OriginalFirstThunk < cbImage, false);
818 MSIHACK_ASSERT_RETURN(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk, false);
819 MSIHACK_ASSERT_RETURN(pImpDesc->OriginalFirstThunk, false);
820
821 /* Iterate the thunks. */
822 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
823 {
824 uintptr_t const off = paOrgThunks[iThunk].u1.Function;
825 MSIHACK_ASSERT_RETURN(off < cbImage, false);
826 if (!IMAGE_SNAP_BY_ORDINAL(off))
827 {
828 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
829 size_t const cchSymbol = strlen((const char *)&pName->Name[0]);
830 uint32_t i = RT_ELEMENTS(g_aReplaceFunctions);
831 while (i-- > 0)
832 if ( g_aReplaceFunctions[i].cchFunction == cchSymbol
833 && memcmp(g_aReplaceFunctions[i].pszFunction, pName->Name, cchSymbol) == 0)
834 {
835 if ( !g_aReplaceFunctions[i].pszModule
836 || stricmp(g_aReplaceFunctions[i].pszModule, pszImport) == 0)
837 {
838 MsiHackDebugF("Replacing %s!%s\n", pszImport, pName->Name);
839
840 /* The .rdata section is normally read-only, so we need to make it writable first. */
841 if ((uintptr_t)&paThunks[iThunk] - (uintptr_t)pbProtRange >= cbPage)
842 {
843 /* Restore previous .rdata page. */
844 if (fOldProt)
845 {
846 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
847 MSIHACK_ASSERT(fRc);
848 fOldProt = 0;
849 }
850
851 /* Query attributes for the current .rdata page. */
852 pbProtRange = (uint8_t *)((uintptr_t)&paThunks[iThunk] & ~(uintptr_t)(cbPage - 1));
853 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
854 MSIHACK_ASSERT(cbProtRange);
855 if (cbProtRange)
856 {
857 switch (ProtInfo.Protect)
858 {
859 case PAGE_READWRITE:
860 case PAGE_WRITECOPY:
861 case PAGE_EXECUTE_READWRITE:
862 case PAGE_EXECUTE_WRITECOPY:
863 /* Already writable, nothing to do. */
864 fRc = TRUE;
865 break;
866
867 default:
868 MSIHACK_ASSERT_MSG(false, ("%#x\n", ProtInfo.Protect));
869 case PAGE_READONLY:
870 cbProtRange = cbPage;
871 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
872 break;
873
874 case PAGE_EXECUTE:
875 case PAGE_EXECUTE_READ:
876 cbProtRange = cbPage;
877 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
878 break;
879 }
880 MSIHACK_ASSERT_STMT(fRc, fOldProt = 0);
881 }
882 }
883
884 paThunks[iThunk].u1.AddressOfData = g_aReplaceFunctions[i].pfnReplacement;
885 break;
886 }
887 }
888 }
889 }
890
891
892 /* Next import descriptor. */
893 pImpDesc++;
894 }
895
896
897 if (fOldProt)
898 {
899 DWORD fIgnore = 0;
900 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
901 MSIHACK_ASSERT_MSG(fRc, ("%u\n", GetLastError())); NOREF(fRc);
902 }
903 return true;
904 }
905 MsiHackErrorF("No imports in target DLL!\n");
906 return false;
907}
908
909
910/**
911 * The Dll main entry point.
912 */
913BOOL __stdcall DllMain(HANDLE hModule, DWORD dwReason, PVOID pvReserved)
914{
915 RT_NOREF_PV(pvReserved);
916
917 switch (dwReason)
918 {
919 case DLL_PROCESS_ATTACH:
920 {
921 /*
922 * Make sure we cannot be unloaded. This saves us the bother of ever
923 * having to unpatch MSI.DLL
924 */
925 WCHAR wszName[MAX_PATH*2];
926 SetLastError(NO_ERROR);
927 if ( GetModuleFileNameW((HMODULE)hModule, wszName, RT_ELEMENTS(wszName)) > 0
928 && GetLastError() == NO_ERROR)
929 {
930 int cExtraLoads = 32;
931 while (cExtraLoads-- > 0)
932 LoadLibraryW(wszName);
933 }
934
935 /*
936 * Initialize globals.
937 */
938 InitializeCriticalSection(&g_CritSect);
939 g_papHandles = (PMSIHACKHANDLE *)calloc(sizeof(g_papHandles[0]), 8192);
940 if (g_papHandles)
941 g_cHandles = 8192;
942 else
943 {
944 MsiHackErrorF("Failed to allocate handle table!\n");
945 return FALSE;
946 }
947
948 /*
949 * Find MSI and patch it.
950 */
951 static struct
952 {
953 const wchar_t *pwszName; /**< Dll name. */
954 bool fSystem; /**< Set if system, clear if wix. */
955 } s_aDlls[] =
956 {
957 { L"MSI.DLL", true },
958 { L"CABINET.DLL", true },
959 { L"WINTEROP.DLL", false },
960 };
961
962 for (unsigned i = 0; i < RT_ELEMENTS(s_aDlls); i++)
963 {
964 HMODULE hmodTarget = GetModuleHandleW(s_aDlls[i].pwszName);
965 if (!hmodTarget)
966 {
967 UINT cwc;
968 if (s_aDlls[i].fSystem)
969 cwc = GetSystemDirectoryW(wszName, RT_ELEMENTS(wszName) - 16);
970 else
971 {
972 cwc = GetModuleFileNameW(GetModuleHandleW(NULL), wszName, RT_ELEMENTS(wszName) - 16);
973 while (cwc > 0 && (wszName[cwc - 1] != '\\' && wszName[cwc - 1] != '/'))
974 wszName[--cwc] = '\0';
975 }
976 wszName[cwc++] = '\\';
977 wcscpy(&wszName[cwc], s_aDlls[i].pwszName);
978 hmodTarget = LoadLibraryW(wszName);
979 if (!hmodTarget)
980 {
981 MsiHackErrorF("%ls could not be found nor loaded (%ls): %u\n", &wszName[cwc], wszName, GetLastError());
982 return FALSE;
983 }
984 }
985
986 if (MsiHackPatchDll(hmodTarget))
987 MsiHackDebugF("MsiHackPatchDll returned successfully for %ls.\n", s_aDlls[i].pwszName);
988 else
989 MsiHackErrorF("MsiHackPatchDll failed for %ls!\n", s_aDlls[i].pwszName);
990 }
991 break;
992 }
993
994 case DLL_PROCESS_DETACH:
995 case DLL_THREAD_ATTACH:
996 case DLL_THREAD_DETACH:
997 default:
998 /* ignore */
999 break;
1000 }
1001 return TRUE;
1002}
1003
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