Skip to content

Commit 6000883

Browse files
Fix AV in FindRealCode and increment version number (#8)
* Fix AV in FindRealCode and increment version number * Switch back to Release in the setup file * Addressed CR comments
1 parent ea676dc commit 6000883

3 files changed

Lines changed: 174 additions & 106 deletions

File tree

setup/version.h

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

2-
#define VLDVERSION L"2.5.6"
3-
#define VERSION_NUMBER 2,5,6,0
4-
#define VERSION_STRING "2.5.6.0"
2+
#define VLDVERSION L"2.5.7"
3+
#define VERSION_NUMBER 2,5,7,0
4+
#define VERSION_STRING "2.5.7.0"
55
#define VERSION_COPYRIGHT "Copyright (C) 2005-2020"
66

77
#ifndef __FILE__
8-
!define VLD_VERSION "2.5.6" // NSIS Script
8+
!define VLD_VERSION "2.5.7" // 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.6"
5+
#define MyAppVersion "2.5.7"
66
#define MyAppPublisher "VLD Team"
77
#define MyAppURL "http://vld.codeplex.com/"
88
#define MyAppRegKey "Software\Visual Leak Detector"

src/utility.cpp

Lines changed: 169 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -423,38 +423,81 @@ BOOL IsModulePatched (HMODULE importmodule, moduleentry_t patchtable [], UINT ta
423423

424424
// No patches listed in the patch table were found in the import module.
425425
return FALSE;
426-
}
427-
426+
}
427+
428428
LPVOID FindRealCode(LPVOID pCode)
429-
{
430-
LPVOID result = pCode;
429+
{
430+
LPVOID result;
431431
if (pCode != NULL)
432432
{
433-
if (*(WORD *)pCode == 0x25ff) // JMP r/m32
434-
{
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))
436+
{
437+
if (*(WORD*)pCode == 0x25ff) // JMP r/m32
438+
{
435439
#ifdef _WIN64
436-
LONG offset = *((LONG *)((ULONG_PTR)pCode + 2));
437-
// RIP relative addressing
438-
PBYTE pNextInst = (PBYTE)((ULONG_PTR)pCode + 6);
439-
pCode = *(LPVOID*)(pNextInst + offset);
440-
return pCode;
440+
LONG offset = *((LONG*)((ULONG_PTR)pCode + 2));
441+
// RIP relative addressing
442+
PBYTE pNextInst = (PBYTE)((ULONG_PTR)pCode + 6);
443+
444+
// now that we got the offset, make sure we can read the code at the offset
445+
DWORD old_protect_2;
446+
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);
451+
}
452+
else
453+
{
454+
result = NULL;
455+
}
441456
#else
442-
DWORD addr = *((DWORD *)((ULONG_PTR)pCode + 2));
443-
pCode = *(LPVOID*)(addr);
444-
return FindRealCode(pCode);
457+
DWORD addr = *((DWORD*)((ULONG_PTR)pCode + 2));
458+
// 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))
461+
{
462+
pCode = *(LPVOID*)(addr);
463+
result = FindRealCode(pCode);
464+
(void)VirtualProtect((LPVOID*)addr, sizeof(LPVOID), old_protect_2, &old_protect_2);
465+
}
466+
else
467+
{
468+
result = NULL;
469+
}
445470
#endif
471+
}
472+
else if (*(BYTE*)pCode == 0xE9) // JMP rel32
473+
{
474+
// Relative next instruction
475+
PBYTE pNextInst = (PBYTE)((ULONG_PTR)pCode + 5);
476+
LONG offset = *((LONG*)((ULONG_PTR)pCode + 1));
477+
pCode = (LPVOID*)(pNextInst + offset);
478+
result = FindRealCode(pCode);
479+
}
480+
else
481+
{
482+
result = pCode;
483+
}
484+
485+
// restore the page protection state
486+
(void)VirtualProtect(pCode, sizeof(ULONG_PTR) * 3, old_protect, &old_protect);
487+
}
488+
else
489+
{
490+
result = NULL;
446491
}
447-
if (*(BYTE *)pCode == 0xE9) // JMP rel32
448-
{
449-
// Relative next instruction
450-
PBYTE pNextInst = (PBYTE)((ULONG_PTR)pCode + 5);
451-
LONG offset = *((LONG *)((ULONG_PTR)pCode + 1));
452-
pCode = (LPVOID*)(pNextInst + offset);
453-
return FindRealCode(pCode);
454-
}
455-
}
492+
}
493+
else
494+
{
495+
result = NULL;
496+
}
456497
return result;
457498
}
499+
500+
#define MAX_PATCH_ENTRY_COUNT 128
458501

459502
// PatchImport - Patches all future calls to an imported function, or references
460503
// to an imported variable, through to a replacement function or variable.
@@ -515,106 +558,131 @@ BOOL PatchImport (HMODULE importmodule, moduleentry_t *patchModule)
515558
LPSTR pszBuffer = cwBuffer;
516559
DWORD dwMaxChars = _countof(cwBuffer);
517560
DWORD dwLength = ::GetModuleFileNameA(importmodule, pszBuffer, dwMaxChars);
518-
#endif
561+
#endif
562+
563+
// have a stack local array of the addresses, don't want to use malloc for this
564+
// The reason to precompute and cache these is because VirtualProtect is expensive
565+
// Thus first we compute *only* once the real addresses for the export module
566+
LPVOID realAddresses[MAX_PATCH_ENTRY_COUNT];
567+
patchentry_t* patchEntry = patchModule->patchTable;
568+
int i = 0;
569+
while (patchEntry->importName)
570+
{
571+
LPCSTR importname = patchEntry->importName;
572+
573+
// Get the *real* address of the import. If we find this address in the IAT,
574+
// then we've found the entry that needs to be patched.
575+
LPVOID import = VisualLeakDetector::_RGetProcAddress(exportmodule, importname);
576+
if (!import)
577+
import = GetProcAddress(exportmodule, importname);
578+
import = FindRealCode(import);
579+
580+
realAddresses[i] = import;
581+
582+
patchEntry++;
583+
i++;
584+
585+
if (i >= MAX_PATCH_ENTRY_COUNT)
586+
{
587+
// we only process MAX_PATCH_ENTRY_COUNT, if we exceed it crash
588+
// if this abort is ever hit, it means that MAX_PATCH_ENTRY_COUNT should be bumped up
589+
Report(L"MAX_PATCH_ENTRY_COUNT is set to %zu, but has been exceeded, MAX_PATCH_ENTRY_COUNT needs to be increased.\n", MAX_PATCH_ENTRY_COUNT);
590+
abort();
591+
break;
592+
}
593+
}
519594

520595
int result = 0;
521596
while (idte->FirstThunk != 0x0) {
522597
PCHAR importdllname = (PCHAR)R2VA(importmodule, idte->Name);
523598
UNREFERENCED_PARAMETER(importdllname);
524599

525-
patchentry_t *patchEntry = patchModule->patchTable;
526-
int i = 0;
527-
while(patchEntry->importName)
528-
{
529-
LPCSTR importname = patchEntry->importName;
530-
LPCVOID replacement = patchEntry->replacement;
531-
532-
// Get the *real* address of the import. If we find this address in the IAT,
533-
// then we've found the entry that needs to be patched.
534-
LPVOID import = VisualLeakDetector::_RGetProcAddress(exportmodule, importname);
535-
if ( !import)
536-
import = GetProcAddress(exportmodule, importname);
537-
import = FindRealCode(import);
538-
539-
if (import == NULL) // Perhaps the named export module does not actually export the named import?
540-
{
541-
patchEntry++; i++;
542-
continue;
543-
}
544-
545-
// Locate the import's IAT entry.
546-
IMAGE_THUNK_DATA *thunk = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);
547-
IMAGE_THUNK_DATA *origThunk = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->OriginalFirstThunk);
548-
for (; origThunk->u1.Function != NULL;
549-
origThunk++, thunk++)
550-
{
551-
LPVOID func = FindRealCode((LPVOID)thunk->u1.Function);
552-
if (((DWORD_PTR)func == (DWORD_PTR)import))
600+
// Locate the import's IAT entry.
601+
IMAGE_THUNK_DATA *thunk = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);
602+
IMAGE_THUNK_DATA *origThunk = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->OriginalFirstThunk);
603+
for (; origThunk->u1.Function != NULL;
604+
origThunk++, thunk++)
605+
{
606+
LPVOID func = FindRealCode((LPVOID)thunk->u1.Function);
607+
608+
patchEntry = patchModule->patchTable;
609+
i = 0;
610+
while (patchEntry->importName)
611+
{
612+
LPVOID import = realAddresses[i];
613+
if (import != NULL)
553614
{
554-
// Found the IAT entry. Overwrite the address stored in the IAT
555-
// entry with the address of the replacement. Note that the IAT
556-
// entry may be write-protected, so we must first ensure that it is
557-
// writable.
558-
if (import != replacement)
615+
LPCVOID replacement = patchEntry->replacement;
616+
617+
if (((DWORD_PTR)func == (DWORD_PTR)import))
559618
{
560-
if (patchEntry->original != NULL)
561-
*patchEntry->original = func;
619+
// Found the IAT entry. Overwrite the address stored in the IAT
620+
// entry with the address of the replacement. Note that the IAT
621+
// entry may be write-protected, so we must first ensure that it is
622+
// writable.
623+
if (import != replacement)
624+
{
625+
if (patchEntry->original != NULL)
626+
*patchEntry->original = func;
562627

563-
DWORD protect;
564-
if (VirtualProtect(&thunk->u1.Function, sizeof(thunk->u1.Function), PAGE_EXECUTE_READWRITE, &protect)) {
565-
thunk->u1.Function = (DWORD_PTR)replacement;
566-
if (VirtualProtect(&thunk->u1.Function, sizeof(thunk->u1.Function), protect, &protect)) {
628+
DWORD protect;
629+
if (VirtualProtect(&thunk->u1.Function, sizeof(thunk->u1.Function), PAGE_EXECUTE_READWRITE, &protect)) {
630+
thunk->u1.Function = (DWORD_PTR)replacement;
631+
if (VirtualProtect(&thunk->u1.Function, sizeof(thunk->u1.Function), protect, &protect)) {
567632
#ifdef PRINTHOOKINFO
568-
if (!IS_ORDINAL(importname)) {
569-
DbgReport(L"Hook dll \"%S\" import %S!%S()\n",
570-
strrchr(pszBuffer, '\\') + 1, patchModule->exportModuleName, importname);
571-
} else {
572-
DbgReport(L"Hook dll \"%S\" import %S!%zu()\n",
573-
strrchr(pszBuffer, '\\') + 1, patchModule->exportModuleName, importname);
574-
}
633+
if (!IS_ORDINAL(importname)) {
634+
DbgReport(L"Hook dll \"%S\" import %S!%S()\n",
635+
strrchr(pszBuffer, '\\') + 1, patchModule->exportModuleName, importname);
636+
}
637+
else {
638+
DbgReport(L"Hook dll \"%S\" import %S!%zu()\n",
639+
strrchr(pszBuffer, '\\') + 1, patchModule->exportModuleName, importname);
640+
}
575641
#endif
642+
}
576643
}
577644
}
645+
// The patch has been installed in the import module.
646+
result++;
647+
break;
578648
}
579-
// The patch has been installed in the import module.
580-
result++;
581-
break;
582-
}
583649
#ifdef PRINTHOOKINFO
584-
PIMAGE_IMPORT_BY_NAME funcEntry = (PIMAGE_IMPORT_BY_NAME)
585-
R2VA(importmodule, origThunk->u1.AddressOfData);
586-
if (stricmp(importdllname, patchModule->exportModuleName) == 0)
587-
{
588-
if (!(origThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) && !IS_ORDINAL(importname) &&
589-
strcmp(reinterpret_cast<const char*>(funcEntry->Name), importname) == 0)
650+
PIMAGE_IMPORT_BY_NAME funcEntry = (PIMAGE_IMPORT_BY_NAME)
651+
R2VA(importmodule, origThunk->u1.AddressOfData);
652+
if (stricmp(importdllname, patchModule->exportModuleName) == 0)
590653
{
591-
if (!dllNamePrinted)
654+
if (!(origThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) && !IS_ORDINAL(importname) &&
655+
strcmp(reinterpret_cast<const char*>(funcEntry->Name), importname) == 0)
592656
{
593-
dllNamePrinted = true;
594-
DbgReport(L"Hook dll \"%S\":\n",
595-
strrchr(pszBuffer, '\\') + 1);
657+
if (!dllNamePrinted)
658+
{
659+
dllNamePrinted = true;
660+
DbgReport(L"Hook dll \"%S\":\n",
661+
strrchr(pszBuffer, '\\') + 1);
662+
}
663+
DbgReport(L"Import found %S(\"%S\") for dll \"%S\".\n",
664+
importname, patchModule->exportModuleName, importdllname);
665+
break;
596666
}
597-
DbgReport(L"Import found %S(\"%S\") for dll \"%S\".\n",
598-
importname, patchModule->exportModuleName, importdllname);
599-
break;
600-
}
601-
if ((origThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) && IS_ORDINAL(importname) &&
602-
(IMAGE_ORDINAL(origThunk->u1.Ordinal) == (UINT_PTR)importname))
603-
{
604-
if (!dllNamePrinted)
667+
if ((origThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) && IS_ORDINAL(importname) &&
668+
(IMAGE_ORDINAL(origThunk->u1.Ordinal) == (UINT_PTR)importname))
605669
{
606-
dllNamePrinted = true;
607-
DbgReport(L"Hook dll \"%S\":\n",
608-
strrchr(pszBuffer, '\\') + 1);
670+
if (!dllNamePrinted)
671+
{
672+
dllNamePrinted = true;
673+
DbgReport(L"Hook dll \"%S\":\n",
674+
strrchr(pszBuffer, '\\') + 1);
675+
}
676+
DbgReport(L"Import found %zu(\"%S\") for dll \"%S\".\n",
677+
importname, patchModule->exportModuleName, importdllname);
678+
break;
609679
}
610-
DbgReport(L"Import found %zu(\"%S\") for dll \"%S\".\n",
611-
importname, patchModule->exportModuleName, importdllname);
612-
break;
613680
}
614-
}
615-
#endif
681+
#endif
682+
}
683+
684+
patchEntry++; i++;
616685
}
617-
patchEntry++; i++;
618686
}
619687

620688
idte++;

0 commit comments

Comments
 (0)