Skip to content

Commit c1967cc

Browse files
Fix bad restore of the protection flags for memory pages when finding real code for hooks (#10)
* Add some logging for virtual protect failures * More test code * More hacks * Have a smarter VirtualProtect * Fix 64 biit path to call recursively into FindRealCode * Adjust protected area * Address CR comments * Bump version number and update changelog
1 parent 30111f7 commit c1967cc

4 files changed

Lines changed: 126 additions & 22 deletions

File tree

CHANGES.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Visual Leak Detector (VLD) Version 2.5.7
33

44
Change Log / Release Notes
55

6+
2.5.8 (1 December 2021)
7+
----------------------------
8+
Bugs Fixed:
9+
+ Fix AV in FindRealCode due to incorrect restore of page protection flags
10+
11+
612
2.5.7 (10 March 2021)
713
----------------------------
814
Bugs Fixed:

setup/version.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

2-
#define VLDVERSION L"2.5.7"
3-
#define VERSION_NUMBER 2,5,7,0
4-
#define VERSION_STRING "2.5.7.0"
5-
#define VERSION_COPYRIGHT "Copyright (C) 2005-2020"
2+
#define VLDVERSION L"2.5.8"
3+
#define VERSION_NUMBER 2,5,8,0
4+
#define VERSION_STRING "2.5.8.0"
5+
#define VERSION_COPYRIGHT "Copyright (C) 2005-2021"
66

77
#ifndef __FILE__
8-
!define VLD_VERSION "2.5.7" // NSIS Script
8+
!define VLD_VERSION "2.5.8" // NSIS Script
99
#endif

setup/vld-setup.iss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
33

44
#define MyAppName "Visual Leak Detector"
5-
#define MyAppVersion "2.5.7"
5+
#define MyAppVersion "2.5.8"
66
#define MyAppPublisher "VLD Team"
77
#define MyAppURL "http://vld.codeplex.com/"
88
#define MyAppRegKey "Software\Visual Leak Detector"

src/utility.cpp

Lines changed: 114 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -425,14 +425,99 @@ BOOL IsModulePatched (HMODULE importmodule, moduleentry_t patchtable [], UINT ta
425425
return FALSE;
426426
}
427427

428+
#define MAX_PAGES_PROTECT 2
429+
430+
typedef struct PROTECT_INSTANCE_TAG
431+
{
432+
DWORD old_protect[MAX_PAGES_PROTECT];
433+
LPVOID address;
434+
SIZE_T size;
435+
} PROTECT_INSTANCE;
436+
437+
typedef struct PROTECT_INSTANCE_TAG* PROTECT_HANDLE;
438+
439+
#ifdef _WIN64
440+
#define PAGE_MASK 0xFFFFFFFFFFFFF000
441+
#else
442+
#define PAGE_MASK 0xFFFFF000
443+
#endif
444+
445+
#define PAGE_SIZE 0x1000
446+
447+
static BOOL VLDVirtualProtect(PROTECT_HANDLE protect_handle, LPVOID address, SIZE_T size, DWORD protect)
448+
{
449+
BOOL result = TRUE;
450+
size_t page_count = 0;
451+
452+
// save the address and size so that we can restore in the same way
453+
protect_handle->address = address;
454+
protect_handle->size = size;
455+
456+
uintptr_t current_address = (uintptr_t)address;
457+
458+
// walk all pages while we still have size > 0
459+
while ((size > 0) && (page_count < MAX_PAGES_PROTECT))
460+
{
461+
uintptr_t page_address = current_address & PAGE_MASK;
462+
SIZE_T size_in_page = page_address + PAGE_SIZE - current_address;
463+
SIZE_T size_to_protect = (size_in_page < size) ? size_in_page : size;
464+
465+
result = VirtualProtect((LPVOID)current_address, size_to_protect, protect, &protect_handle->old_protect[page_count]);
466+
if (!result)
467+
{
468+
Report(L"%zu: !!! VirtualProtect FAILED when protecting for address=%p, size=%zu, with GetLastError()=%lu, protect_handle->address=%p, protect_handle->size=%zu",
469+
__LINE__, current_address, size_to_protect,
470+
GetLastError(),
471+
protect_handle->address, protect_handle->size);
472+
}
473+
page_count++;
474+
current_address += size_to_protect;
475+
size -= size_to_protect;
476+
}
477+
478+
return result;
479+
}
480+
481+
static void VLDVirtualRestore(PROTECT_HANDLE protect_handle)
482+
{
483+
size_t page_count = 0;
484+
SIZE_T size = protect_handle->size;
485+
uintptr_t current_address = (uintptr_t)protect_handle->address;
486+
487+
// walk all pages while we still have size > 0
488+
while ((size > 0) && (page_count < MAX_PAGES_PROTECT))
489+
{
490+
uintptr_t page_address = current_address & PAGE_MASK;
491+
SIZE_T size_in_page = page_address + PAGE_SIZE - current_address;
492+
SIZE_T size_to_protect = (size_in_page < size) ? size_in_page : size;
493+
494+
DWORD dont_care;
495+
if (!VirtualProtect((LPVOID)current_address, size_to_protect, protect_handle->old_protect[page_count], &dont_care))
496+
{
497+
static volatile DWORD lastError = GetLastError();
498+
Report(L"%zu: !!! VirtualProtect FAILED when restoring for address=%p, size=%zu, with GetLastError()=%lu, protect_handle->old_protect[page_count]=%lu, protect_handle->address=%p, protect_handle->size=%zu",
499+
__LINE__, current_address, size_to_protect,
500+
GetLastError(), protect_handle->old_protect[page_count],
501+
protect_handle->address, protect_handle->size);
502+
abort();
503+
}
504+
505+
page_count++;
506+
current_address += size_to_protect;
507+
size -= size_to_protect;
508+
}
509+
510+
}
511+
428512
LPVOID FindRealCode(LPVOID pCode)
429513
{
430514
LPVOID result;
431515
if (pCode != NULL)
432516
{
433-
// we need to make sure we can read the first 3 ULONG_PTRs
434-
DWORD old_protect;
435-
if (VirtualProtect(pCode, sizeof(ULONG_PTR) * 3, PAGE_EXECUTE_READ, &old_protect))
517+
// we need to make sure we can read the first 7 ULONG_PTRs
518+
PROTECT_INSTANCE protect_1;
519+
520+
if (VLDVirtualProtect(&protect_1, pCode, sizeof(ULONG_PTR) * 7, PAGE_EXECUTE_READ))
436521
{
437522
if (*(WORD*)pCode == 0x25ff) // JMP r/m32
438523
{
@@ -442,12 +527,14 @@ LPVOID FindRealCode(LPVOID pCode)
442527
PBYTE pNextInst = (PBYTE)((ULONG_PTR)pCode + 6);
443528

444529
// now that we got the offset, make sure we can read the code at the offset
445-
DWORD old_protect_2;
530+
PROTECT_INSTANCE protect_2;
446531
PBYTE addr = pNextInst + offset;
447-
if (VirtualProtect(addr, sizeof(LPVOID), PAGE_EXECUTE_READ, &old_protect_2))
448-
{
449-
result = *(LPVOID*)(addr);
450-
(void)VirtualProtect(addr, sizeof(LPVOID), old_protect_2, &old_protect_2);
532+
533+
if (VLDVirtualProtect(&protect_2, (LPVOID*)addr, sizeof(LPVOID), PAGE_EXECUTE_READ))
534+
{
535+
pCode = *(LPVOID*)(addr);
536+
result = FindRealCode(pCode);
537+
VLDVirtualRestore(&protect_2);
451538
}
452539
else
453540
{
@@ -456,12 +543,12 @@ LPVOID FindRealCode(LPVOID pCode)
456543
#else
457544
DWORD addr = *((DWORD*)((ULONG_PTR)pCode + 2));
458545
// now that we got the address to read, make sure we can read the code at the offset
459-
DWORD old_protect_2;
460-
if (VirtualProtect((LPVOID*)addr, sizeof(LPVOID), PAGE_EXECUTE_READ, &old_protect_2))
546+
PROTECT_INSTANCE protect_2;
547+
if (VLDVirtualProtect(&protect_2, (LPVOID)addr, sizeof(LPVOID), PAGE_EXECUTE_READ))
461548
{
462549
pCode = *(LPVOID*)(addr);
463550
result = FindRealCode(pCode);
464-
(void)VirtualProtect((LPVOID*)addr, sizeof(LPVOID), old_protect_2, &old_protect_2);
551+
VLDVirtualRestore(&protect_2);
465552
}
466553
else
467554
{
@@ -471,19 +558,28 @@ LPVOID FindRealCode(LPVOID pCode)
471558
}
472559
else if (*(BYTE*)pCode == 0xE9) // JMP rel32
473560
{
474-
// Relative next instruction
561+
// Relative next instruction
562+
PROTECT_INSTANCE protect_2;
475563
PBYTE pNextInst = (PBYTE)((ULONG_PTR)pCode + 5);
476564
LONG offset = *((LONG*)((ULONG_PTR)pCode + 1));
477-
pCode = (LPVOID*)(pNextInst + offset);
478-
result = FindRealCode(pCode);
565+
if (VLDVirtualProtect(&protect_2, pNextInst + offset, sizeof(LPVOID), PAGE_EXECUTE_READ))
566+
{
567+
pCode = (LPVOID*)(pNextInst + offset);
568+
result = FindRealCode(pCode);
569+
VLDVirtualRestore(&protect_2);
570+
}
571+
else
572+
{
573+
result = NULL;
574+
}
479575
}
480576
else
481577
{
482578
result = pCode;
483579
}
484580

485581
// restore the page protection state
486-
(void)VirtualProtect(pCode, sizeof(ULONG_PTR) * 3, old_protect, &old_protect);
582+
VLDVirtualRestore(&protect_1);
487583
}
488584
else
489585
{
@@ -595,7 +691,9 @@ BOOL PatchImport (HMODULE importmodule, moduleentry_t *patchModule)
595691
int result = 0;
596692
while (idte->FirstThunk != 0x0) {
597693
PCHAR importdllname = (PCHAR)R2VA(importmodule, idte->Name);
598-
UNREFERENCED_PARAMETER(importdllname);
694+
UNREFERENCED_PARAMETER(importdllname);
695+
HMODULE importdllbaseaddress = GetModuleHandleA(importdllname);
696+
UNREFERENCED_PARAMETER(importdllbaseaddress);
599697

600698
// Locate the import's IAT entry.
601699
IMAGE_THUNK_DATA *thunk = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);

0 commit comments

Comments
 (0)