@@ -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+
428428LPVOID 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