@@ -307,6 +307,10 @@ void IntegrityCheckBypass::ignore_application_entries() {
307
307
Hooks::get ()->ignore_application_entry (0x00c0ab9309584734 );
308
308
Hooks::get ()->ignore_application_entry (0xa474f1d3a294e6a4 );
309
309
#endif
310
+ #if TDB_VER >= 74
311
+ Hooks::get ()->ignore_application_entry (0x00ec4793097cd833 );
312
+ Hooks::get ()->ignore_application_entry (0x00d85893096c4c0c );
313
+ #endif
310
314
}
311
315
312
316
void IntegrityCheckBypass::immediate_patch_re8 () {
@@ -519,6 +523,110 @@ void* IntegrityCheckBypass::renderer_create_blas_hook(void* a1, void* a2, void*
519
523
return s_renderer_create_blas_hook->get_original <decltype (renderer_create_blas_hook)>()(a1, a2, a3, a4, a5);
520
524
}
521
525
526
+ // This is used to nuke the heap allocated code that causes crashes
527
+ // when debuggers are attached and other integrity checks.
528
+ // They happen to be in the same (heap allocated) executable section, so we can just
529
+ // replace every byte with a RET instruction.
530
+ void IntegrityCheckBypass::nuke_heap_allocated_code (uintptr_t addr) {
531
+ // Get the base of the memory region.
532
+ MEMORY_BASIC_INFORMATION mbi{};
533
+ if (VirtualQuery ((LPCVOID)addr, &mbi, sizeof (mbi)) == 0 ) {
534
+ spdlog::error (" [IntegrityCheckBypass]: VirtualQuery failed!" );
535
+ return ;
536
+ }
537
+
538
+ // Get the end of the memory region.
539
+ const auto start = (uintptr_t )mbi.BaseAddress ;
540
+ const auto end = (uintptr_t )mbi.BaseAddress + mbi.RegionSize ;
541
+
542
+ spdlog::info (" [IntegrityCheckBypass]: Nuking heap allocated code at 0x{:X} - 0x{:X}" , start, end);
543
+
544
+ // Fix the protection of the memory region.
545
+ ProtectionOverride _{(void *)start, mbi.RegionSize , PAGE_EXECUTE_READWRITE};
546
+
547
+ // Replace every single byte with a RET (C3) instruction.
548
+ std::memset ((void *)start, 0xC3 , mbi.RegionSize );
549
+
550
+ spdlog::info (" [IntegrityCheckBypass]: Nuked heap allocated code at 0x{:X}" , start);
551
+ }
552
+
553
+ void IntegrityCheckBypass::anti_debug_watcher () try {
554
+ static const auto ntdll = GetModuleHandleW (L" ntdll.dll" );
555
+ static const auto dbg_ui_remote_breakin = ntdll != nullptr ? GetProcAddress (ntdll, " DbgUiRemoteBreakin" ) : nullptr ;
556
+ static auto original_dbg_ui_remote_breakin_bytes = dbg_ui_remote_breakin != nullptr ? utility::get_original_bytes (dbg_ui_remote_breakin) : std::optional<std::vector<uint8_t >>{};
557
+
558
+ if (dbg_ui_remote_breakin == nullptr ) {
559
+ return ;
560
+ }
561
+
562
+ // We can generally assume it's not hooked at this point if the original bytes are empty.
563
+ if (!original_dbg_ui_remote_breakin_bytes || original_dbg_ui_remote_breakin_bytes->empty ()) {
564
+ spdlog::info (" [IntegrityCheckBypass]: Manually copying original bytes for DbgUiRemoteBreakin." );
565
+ if (!original_dbg_ui_remote_breakin_bytes) {
566
+ original_dbg_ui_remote_breakin_bytes = std::vector<uint8_t >{};
567
+ }
568
+
569
+ if (original_dbg_ui_remote_breakin_bytes->size () < 32 ) {
570
+ std::copy_n ((uint8_t *)dbg_ui_remote_breakin + original_dbg_ui_remote_breakin_bytes->size (), 32 - original_dbg_ui_remote_breakin_bytes->size (), std::back_inserter (*original_dbg_ui_remote_breakin_bytes));
571
+ }
572
+ }
573
+
574
+ const uint64_t * first_8_bytes = (uint64_t *)dbg_ui_remote_breakin;
575
+ const uint8_t * first_8_bytes_ptr = (uint8_t *)dbg_ui_remote_breakin;
576
+
577
+ if (*(uint64_t *)original_dbg_ui_remote_breakin_bytes->data () != *first_8_bytes) {
578
+ spdlog::info (" [IntegrityCheckBypass]: DbgUiRemoteBreakin was hooked, restoring original bytes." );
579
+
580
+ if (first_8_bytes_ptr[0 ] == 0xE9 ) {
581
+ spdlog::info (" [IntegrityCheckBypass]: DbgUiRemoteBreakin was directly hooked, resolving..." );
582
+ const auto resolved_jmp = utility::calculate_absolute ((uintptr_t )dbg_ui_remote_breakin + 1 );
583
+ const auto is_heap_allocated = utility::get_module_within (resolved_jmp).value_or (nullptr ) == nullptr ;
584
+
585
+ if (is_heap_allocated && !IsBadReadPtr ((void *)resolved_jmp, 32 )) {
586
+ spdlog::info (" [IntegrityCheckBypass]: Nuking heap allocated code at 0x{:X}" , resolved_jmp);
587
+ nuke_heap_allocated_code (resolved_jmp);
588
+ }
589
+ } else if (first_8_bytes_ptr[0 ] == 0xFF && first_8_bytes_ptr[1 ] == 0x25 ) {
590
+ spdlog::info (" [IntegrityCheckBypass]: DbgUiRemoteBreakin was indirectly hooked, resolving..." );
591
+ const auto resolved_ptr = utility::calculate_absolute ((uintptr_t )dbg_ui_remote_breakin + 2 );
592
+ const auto resolved_jmp = *(uintptr_t *)resolved_ptr;
593
+ const auto is_heap_allocated = utility::get_module_within (resolved_jmp).value_or (nullptr ) == nullptr ;
594
+
595
+ if (is_heap_allocated && !IsBadReadPtr ((void *)resolved_jmp, 32 )) {
596
+ spdlog::info (" [IntegrityCheckBypass]: Nuking heap allocated code at 0x{:X}" , resolved_jmp);
597
+ nuke_heap_allocated_code (resolved_jmp);
598
+ }
599
+ }
600
+
601
+ ProtectionOverride _{dbg_ui_remote_breakin, original_dbg_ui_remote_breakin_bytes->size (), PAGE_EXECUTE_READWRITE};
602
+ std::copy (original_dbg_ui_remote_breakin_bytes->begin (), original_dbg_ui_remote_breakin_bytes->end (), (uint8_t *)dbg_ui_remote_breakin);
603
+
604
+ spdlog::info (" [IntegrityCheckBypass]: Restored DbgUiRemoteBreakin." );
605
+ }
606
+ } catch (const std::exception & e) {
607
+ spdlog::error (" [IntegrityCheckBypass]: Exception in anti_debug_watcher: {}" , e.what ());
608
+ } catch (...) {
609
+ spdlog::error (" [IntegrityCheckBypass]: Unknown exception in anti_debug_watcher!" );
610
+ }
611
+
612
+ void IntegrityCheckBypass::init_anti_debug_watcher () {
613
+ if (s_anti_anti_debug_thread != nullptr ) {
614
+ return ;
615
+ }
616
+
617
+ // Run the original watcher once so we get it at least without creating a thread first.
618
+ anti_debug_watcher ();
619
+
620
+ s_anti_anti_debug_thread = std::make_unique<std::jthread>([](std::stop_token stop_token) {
621
+ spdlog::info (" [IntegrityCheckBypass]: Hello from anti_debug_watcher!" );
622
+
623
+ while (!stop_token.stop_requested ()) {
624
+ anti_debug_watcher ();
625
+ std::this_thread::sleep_for (std::chrono::milliseconds (500 ));
626
+ }
627
+ });
628
+ }
629
+
522
630
void IntegrityCheckBypass::immediate_patch_dd2 () {
523
631
// Just like RE4, this deals with the scans that are done every frame on the game's memory.
524
632
// The scans are still performed, but the crash will be avoided.
@@ -533,6 +641,8 @@ void IntegrityCheckBypass::immediate_patch_dd2() {
533
641
const auto game = utility::get_executable ();
534
642
535
643
#if TDB_VER >= 74
644
+ init_anti_debug_watcher ();
645
+
536
646
const auto query_performance_frequency = &QueryPerformanceFrequency;
537
647
const auto query_performance_counter = &QueryPerformanceCounter;
538
648
0 commit comments