Корректный ли это способ хука NtQueryPerformanceCounter, чтобы возвращать rdtsc напрямую?

Тема в разделе "WASM.NT.KERNEL", создана пользователем zky02, 24 май 2025.

  1. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    Привет! Это правильный способ заменить NtQueryPerformanceCounter на RDTSC? Спасибо.

    Код (Text):
    1. NTSTATUS HookedNtQueryPerformanceCounter3(
    2.     PLARGE_INTEGER PerformanceCounter,
    3.     PLARGE_INTEGER PerformanceFrequency)
    4. {
    5.     static bool initialized = false;
    6.  
    7.     if (!initialized) {
    8.         // Однократное исправление SharedUserData
    9.         __try {
    10.             // Сбросить флаг Qpc bias
    11.             *(BYTE*)(&SharedUserData->QpcData) = *(BYTE*)(&SharedUserData->QpcData) & 0xFFFE;
    12.  
    13.             // Установить новую частоту
    14.             *(LONGLONG*)(&SharedUserData->QpcFrequency) = rdtscfreq;
    15.  
    16.             // Очистить данные Qpc bias/virtualization
    17.             *(BYTE*)((BYTE*)SharedUserData + 0x3c7) = 0;
    18.             *(LONGLONG*)((BYTE*)SharedUserData + 0x3b8) = 0;
    19.  
    20.             initialized = true;
    21.         }
    22.         __except (EXCEPTION_EXECUTE_HANDLER) {
    23.             NOTHING;
    24.         }
    25.     }
    26.  
    27.     if (!PerformanceCounter)
    28.         return STATUS_ACCESS_VIOLATION;
    29.  
    30.     PerformanceCounter->QuadPart = __rdtsc();
    31.  
    32.     if (PerformanceFrequency)
    33.         PerformanceFrequency->QuadPart = rdtscfreq;
    34.  
    35.     return STATUS_SUCCESS;
    36. }
     
  2. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    модить SharedUserData надо?
     
  3. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    Я так думаю, потому что qpc нельзя масштабировать с помощью rdtsc
     
  4. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    Я нашёл, что работает только этот метод — напрямую использовать rdtsc у меня никогда не получалось, так как я не знаю, что именно управляет частотой 10 МГц

    Код (Text):
    1. static long long tsc_to_nanoseconds(unsigned long long tsc_count)
    2. {
    3.     long long scaled_ns = (tsc_count / rdtscfreq) * 10000000LL;
    4.  
    5.  
    6.     unsigned long long remainder = tsc_count % rdtscfreq;
    7.     scaled_ns += (remainder * 10000000LL) / rdtscfreq;
    8.  
    9.     return scaled_ns;
    10. }  
    11.  
    12. PerformanceCounter->QuadPart = tsc_to_nanoseconds(__rdtsc());
    13. if (PerformanceFrequency)
    14.      PerformanceFrequency->QuadPart = 10000000LL;
    --- Сообщение объединено, 26 май 2025 ---
    upload_2025-5-26_1-22-9.png
    преуспевать
     
  5. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    Код (C++):
    1. NTSTATUS HookedNtQueryPerformanceCounter(
    2.     PLARGE_INTEGER PerformanceCounter,
    3.     PLARGE_INTEGER PerformanceFrequency)
    4. {
    5.     if (!PerformanceCounter)
    6.         return STATUS_ACCESS_VIOLATION;
    7.  
    8.     // Get current TSC value
    9.     UINT64 tsc = __rdtsc();
    10.  
    11.     // Convert TSC to QPC units (10MHz base)
    12.     // This maintains compatibility with existing code
    13.     PerformanceCounter->QuadPart = (tsc * 10000000ULL) / rdtscfreq;
    14.  
    15.     if (PerformanceFrequency)
    16.         PerformanceFrequency->QuadPart = 10000000ULL; // 10 MHz standard
    17.  
    18.     return STATUS_SUCCESS;
    19. }
    --- Сообщение объединено, 26 май 2025 ---
    Intelligent QPC Hook with Auto-Calibration
    Код (C++):
    1. class SmartQPCHook {
    2. private:
    3.     static volatile bool initialized;
    4.     static UINT64 tsc_frequency;
    5.     static UINT64 qpc_baseline;
    6.     static UINT64 tsc_baseline;
    7.     static double scale_factor;
    8.    
    9.     static void calibrate_once() {
    10.         if (initialized) return;
    11.        
    12.         // Get real QPC values for calibration
    13.         LARGE_INTEGER real_qpc1, real_qpc2, freq;
    14.         UINT64 tsc1, tsc2;
    15.        
    16.         // Sample multiple times for accuracy
    17.         tsc1 = __rdtsc();
    18.         QueryPerformanceCounter(&real_qpc1);
    19.        
    20.         Sleep(10); // Short delay
    21.        
    22.         tsc2 = __rdtsc();
    23.         QueryPerformanceCounter(&real_qpc2);
    24.         QueryPerformanceFrequency(&freq);
    25.        
    26.         // Calculate precise scaling
    27.         UINT64 tsc_delta = tsc2 - tsc1;
    28.         UINT64 qpc_delta = real_qpc2.QuadPart - real_qpc1.QuadPart;
    29.        
    30.         scale_factor = (double)qpc_delta / tsc_delta;
    31.         tsc_baseline = tsc1;
    32.         qpc_baseline = real_qpc1.QuadPart;
    33.         tsc_frequency = freq.QuadPart;
    34.        
    35.         _InterlockedExchange8((char*)&initialized, 1);
    36.     }
    37.  
    38. public:
    39.     static NTSTATUS NTAPI HookedNtQueryPerformanceCounter(
    40.         PLARGE_INTEGER PerformanceCounter,
    41.         PLARGE_INTEGER PerformanceFrequency)
    42.     {
    43.         if (!PerformanceCounter)
    44.             return STATUS_ACCESS_VIOLATION;
    45.            
    46.         if (!initialized) {
    47.             calibrate_once();
    48.         }
    49.        
    50.         // Convert TSC to QPC using calibrated scaling
    51.         UINT64 current_tsc = __rdtsc();
    52.         UINT64 tsc_offset = current_tsc - tsc_baseline;
    53.        
    54.         PerformanceCounter->QuadPart = qpc_baseline +
    55.             (UINT64)(tsc_offset * scale_factor);
    56.        
    57.         if (PerformanceFrequency)
    58.             PerformanceFrequency->QuadPart = tsc_frequency;
    59.            
    60.         return STATUS_SUCCESS;
    61.     }
    62. };
    63.  
    64. // Static member definitions
    65. volatile bool SmartQPCHook::initialized = false;
    66. UINT64 SmartQPCHook::tsc_frequency = 0;
    67. UINT64 SmartQPCHook::qpc_baseline = 0;
    68. UINT64 SmartQPCHook::tsc_baseline = 0;
    69. double SmartQPCHook::scale_factor = 0.0;
    70.  
    Even Smarter: Runtime Adaptive Solution

    Код (C++):
    1. static NTSTATUS NTAPI AdaptiveQPCHook(
    2.     PLARGE_INTEGER PerformanceCounter,
    3.     PLARGE_INTEGER PerformanceFrequency)
    4. {
    5.     if (!PerformanceCounter)
    6.         return STATUS_ACCESS_VIOLATION;
    7.    
    8.     static thread_local bool tls_calibrated = false;
    9.     static thread_local UINT64 tls_scale_num = 0;
    10.     static thread_local UINT64 tls_scale_den = 0;
    11.     static thread_local UINT64 tls_qpc_freq = 0;
    12.    
    13.     // Per-thread calibration for maximum accuracy
    14.     if (!tls_calibrated) {
    15.         LARGE_INTEGER freq, qpc1, qpc2;
    16.         UINT64 tsc1, tsc2;
    17.        
    18.         // Quick calibration
    19.         QueryPerformanceFrequency(&freq);
    20.        
    21.         tsc1 = __rdtsc();
    22.         QueryPerformanceCounter(&qpc1);
    23.        
    24.         // Micro-delay using TSC
    25.         UINT64 target = tsc1 + 1000000; // ~1ms on modern CPUs
    26.         while (__rdtsc() < target);
    27.        
    28.         tsc2 = __rdtsc();
    29.         QueryPerformanceCounter(&qpc2);
    30.        
    31.         // Calculate integer scaling to avoid floating point
    32.         UINT64 tsc_delta = tsc2 - tsc1;
    33.         UINT64 qpc_delta = qpc2.QuadPart - qpc1.QuadPart;
    34.        
    35.         tls_scale_num = qpc_delta;
    36.         tls_scale_den = tsc_delta;
    37.         tls_qpc_freq = freq.QuadPart;
    38.        
    39.         tls_calibrated = true;
    40.     }
    41.    
    42.     // Fast TSC->QPC conversion using integer math
    43.     static UINT64 base_tsc = __rdtsc();
    44.     UINT64 current_tsc = __rdtsc();
    45.     UINT64 tsc_elapsed = current_tsc - base_tsc;
    46.    
    47.     // Scale TSC to QPC units
    48.     PerformanceCounter->QuadPart = (tsc_elapsed * tls_scale_num) / tls_scale_den;
    49.    
    50.     if (PerformanceFrequency)
    51.         PerformanceFrequency->QuadPart = tls_qpc_freq;
    52.        
    53.     return STATUS_SUCCESS;
    54. }
    55.  
    Ultra-Smart: VDSO-Style Fast Path
    Код (C++):
    1.  
    2. // Leverage existing system calibration
    3. static NTSTATUS NTAPI VDSOStyleQPCHook(
    4.     PLARGE_INTEGER PerformanceCounter,
    5.     PLARGE_INTEGER PerformanceFrequency)
    6. {
    7.     if (!PerformanceCounter)
    8.         return STATUS_ACCESS_VIOLATION;
    9.    
    10.     // Read system's own TSC calibration data
    11.     static UINT64 cached_mult = 0;
    12.     static UINT32 cached_shift = 0;
    13.     static UINT64 cached_freq = 0;
    14.    
    15.     if (!cached_mult) {
    16.         // Extract from SharedUserData if accessible
    17.         auto* sud = (KUSER_SHARED_DATA*)0x7FFE0000;
    18.         if (sud && sud->QpcFrequency.QuadPart) {
    19.             cached_freq = sud->QpcFrequency.QuadPart;
    20.            
    21.             // Use system's TSC scaling if available
    22.             if (sud->QpcData.QpcMultiplier) {
    23.                 cached_mult = sud->QpcData.QpcMultiplier;
    24.                 cached_shift = sud->QpcData.QpcShift;
    25.             } else {
    26.                 // Fallback: calculate our own
    27.                 cached_mult = (cached_freq << 32) / __rdtsc();
    28.                 cached_shift = 32;
    29.             }
    30.         }
    31.     }
    32.    
    33.     if (cached_mult) {
    34.         // Use system-compatible scaling
    35.         UINT64 tsc = __rdtsc();
    36.         PerformanceCounter->QuadPart = (tsc * cached_mult) >> cached_shift;
    37.     } else {
    38.         // Emergency fallback
    39.         PerformanceCounter->QuadPart = __rdtsc();
    40.     }
    41.    
    42.     if (PerformanceFrequency)
    43.         PerformanceFrequency->QuadPart = cached_freq;
    44.        
    45.     return STATUS_SUCCESS;
    46. }
     
  6. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29

    большое спасибо Моя цель заключалась в том, чтобы пропатчить 10МГц и использовать его напрямую. Для этого мне нужно было найти патч KeQueryPerformanceCounter внутри HalpPerformanceCounter.QueryCounter, чтобы он возвращал rdtsc, затем пропатчить структуру HAL - исправить каждые 10МГц внутри KeQueryPerformanceCounter по смещению 0xc0, чтобы он возвращал вашу реальную частоту. Получить её оттуда, а затем перехватить NtQueryPerformanceCounter, чтобы он возвращал rdtsc с частотой, полученной из смещения 0xc0.
    --- Сообщение объединено, 26 май 2025 ---
    Код (Text):
    1. PVOID FindHalpPerformanceCounter() {
    2.  
    3.     UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"KeQueryPerformanceCounter");
    4.     PVOID keQueryPerformanceCounter = MmGetSystemRoutineAddress(&routineName);
    5.  
    6.     if (!keQueryPerformanceCounter) {
    7.         return nullptr;
    8.     }
    9.  
    10.    
    11. // Template from your disassembly: mov rdi, qword ptr [nt!HalpPerformanceCounter]
    12. // Offset instruction 0x12: 48 8B 3D ?? ?? ?? ?? (REX.W mov rdi, [rip+offset])
    13.  
    14.     PUCHAR scanAddress = (PUCHAR)keQueryPerformanceCounter;
    15.  
    16.     for (ULONG i = 0; i < 0x100; i++) {
    17.         if (scanAddress[i] == 0x48 && scanAddress[i + 1] == 0x8B && scanAddress[i + 2] == 0x3D) {
    18.      
    19.             LONG relativeOffset = *(PLONG)(&scanAddress[i + 3]);
    20.             PVOID halpPerformanceCounterAddress = (PVOID)(&scanAddress[i + 7] + relativeOffset);
    21.  
    22.             return *(PVOID*)halpPerformanceCounterAddress;
    23.         }
    24.     }
    25.  
    26.     return nullptr;
    27. }
    28.  
    29. [code]UINT64 GetKernelQPCFrequency()
    30. {
    31.     static PVOID halpCounter = FindHalpPerformanceCounter();
    32.     if (!halpCounter)
    33.         return 0;
    34.     PUCHAR counterStruct = (PUCHAR)halpCounter;
    35.     UINT64 frequency = *(UINT64*)(counterStruct + 0xC0);
    36.     DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL,
    37.                "HalpPerformanceCounter: %p, Frequency: %llu\n",
    38.                halpCounter, frequency);
    39.     return frequency;
    40. }

    upload_2025-5-26_22-20-23.png
    --- Сообщение объединено, 27 май 2025 ---
    Код (Text):
    1. NTSTATUS HookedNtQueryPerformanceCounter(
    2.     PLARGE_INTEGER PerformanceCounter,
    3.     PLARGE_INTEGER PerformanceFrequency)
    4. {
    5.    
    6.     if (!PerformanceCounter)
    7.         return STATUS_ACCESS_VIOLATION;
    8.  
    9.  
    10.     static UINT64 cached_mult = 0;
    11.     static UINT32 cached_shift = 0;
    12.     static UINT64 cached_tsc_freq = 0;
    13.     static UINT64 cached_qpc_freq = 0;
    14.  
    15.    
    16.     if (!cached_mult) {
    17.         // Получаем частоту оригинального QPC (10 МГц)
    18.         auto* sud = (KUSER_SHARED_DATA*)0xFFFFF78000000000;
    19.         if (sud && sud->QpcFrequency) {
    20.             // Сохраняем исходную частоту QPC до изменения
    21.             cached_qpc_freq = 10000000;
    22.  
    23.             // Получаем частоту TSC от ядра
    24.             cached_tsc_freq = GetKernelTSCFrequency();
    25.  
    26.             if (cached_tsc_freq > 0) {
    27.                 cached_shift = 32;
    28.                 // Вычисляем множитель для преобразования значений
    29.                 cached_mult = (cached_tsc_freq << cached_shift) / cached_qpc_freq;
    30.             }
    31.         }
    32.     }
    33.  
    34.     // Получаем оригинальное значение QPC из ядра
    35.     LARGE_INTEGER original_qpc;
    36.     NTSTATUS status = OriginalNtQueryPerformanceCounter(&original_qpc, NULL);
    37.     if (!NT_SUCCESS(status)) {
    38.         return status;
    39.     }
    40.  
    41.     // Преобразуем значение QPC (в 10 МГц) в эквивалентное значение на частоте TSC
    42.     PerformanceCounter->QuadPart = (original_qpc.QuadPart * cached_mult) >> cached_shift;
    43.  
    44.  
    45.     if (PerformanceFrequency)
    46.         PerformanceFrequency->QuadPart = cached_tsc_freq;
    47.  
    48.     return STATUS_SUCCESS;
    49. }
     
  7. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    Код (Text):
    1. RDTSC: 104576556070602, QPC: 25113090688, FREQ: 10000000
    2. RDTSC: 104577051659995, QPC: 25114327194, FREQ: 10000000
    3. RDTSC: 104577482537159, QPC: 25115402236, FREQ: 10000000
    4. RDTSC: 104577918714364, QPC: 25116490501, FREQ: 10000000
    5. RDTSC: 104578358818373, QPC: 25117588566, FREQ: 10000000
    6. RDTSC: 104578797081227, QPC: 25118682036, FREQ: 10000000
    7. RDTSC: 104579235100407, QPC: 25119774899, FREQ: 10000000
    8. RDTSC: 104579675170783, QPC: 25120872878, FREQ: 10000000
    9. RDTSC: 104580111324371, QPC: 25121961084, FREQ: 10000000
    10. RDTSC: 104580549362612, QPC: 25123053994, FREQ: 10000000   104580549362612 - 104576556070602 = 3993292010  25123053994     - 25113090688     =    9963310  RDTSC / QPC = 3993292010 / 9963310 ≈ 400.799
    --- Сообщение объединено, 27 май 2025 ---
    преуспевать

    upload_2025-5-27_7-46-29.png
     
  8. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    Share source code :)
     
  9. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    Код (C++):
    1. static NTSTATUS QPCHook(
    2.  
    3.     PLARGE_INTEGER PerformanceCounter,
    4.     PLARGE_INTEGER PerformanceFrequency)
    5. {
    6.     if (!PerformanceCounter)
    7.         return STATUS_ACCESS_VIOLATION;
    8.  
    9.     static bool tls_calibrated = false;
    10.     static UINT64 tls_tsc_freq = 0;
    11.  
    12.     if (!tls_calibrated) {
    13.  
    14.         *(USHORT*)&SharedUserData->QpcData = (*(USHORT*)&SharedUserData->QpcData | 0x0001) & ~(0x0002 | 0x0080);
    15.         *(volatile ULONGLONG*)(&SharedUserData->QpcBias) = 0;
    16.  
    17.  
    18.         tls_tsc_freq = GetKernelTSCFrequency();
    19.  
    20.         *(LONGLONG*)(&SharedUserData->QpcFrequency) = tls_tsc_freq;
    21.  
    22.         tls_calibrated = true;
    23.     }
    24.  
    25.     UINT64 current_tsc = __rdtsc();
    26.  
    27.     PerformanceCounter->QuadPart = current_tsc;
    28.  
    29.     if (PerformanceFrequency)
    30.         PerformanceFrequency->QuadPart = tls_tsc_freq;
    31.  
    32.     return STATUS_SUCCESS;
    33. }
    Код (Text):
    1. RDTSC: 226281083690684, QPC: 226281083691523, FREQ: 4008003775
    2.  
    3. RDTSC: 226281501712621, QPC: 226281501715577, FREQ: 4008003775
    4. RDTSC: 226281938115703, QPC: 226281938119206, FREQ: 4008003775
    5. RDTSC: 226282378197316, QPC: 226282378200047, FREQ: 4008003775
    6. RDTSC: 226282816344965, QPC: 226282816347556, FREQ: 4008003775
    7. RDTSC: 226283252826076, QPC: 226283252829052, FREQ: 4008003775
    8. RDTSC: 226283690934045, QPC: 226283690936975, FREQ: 4008003775
    9. RDTSC: 226284129232575, QPC: 226284129235396, FREQ: 4008003775
    10. RDTSC: 226284567444364, QPC: 226284567482848, FREQ: 4008003775
    11. RDTSC: 226285005410383, QPC: 226285005413512, FREQ: 4008003775
    Значения QPC отличаются от RDTSC менее чем на 1000 тактов, но я не знаю, как уменьшить это расхождение ещё больше
     
  10. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    upload_2025-5-30_2-28-12.png совершенство
     
  11. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
  12. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    29
    Код (C++):
    1. static const ULONG_PTR QpcFrequencyAddress = 0x7FFE0300; // KUSER_SHARED_DATA->QpcFrequency
    2. static ULONGLONG origFreq = 0;
    3. static ULONGLONG newFreq = yourrdtscfreq;
    4.  
    5. PVOID mapped = NULL;
    6. PMDL mdl = NULL;
    7.  
    8. NTSTATUS PatchQpcFrequency(BOOLEAN restore)
    9. {
    10.     mdl = IoAllocateMdl((PVOID)QpcFrequencyAddress, sizeof(ULONGLONG), FALSE, FALSE, NULL);
    11.     if (!mdl)
    12.         return STATUS_INSUFFICIENT_RESOURCES;
    13.  
    14.     MmProbeAndLockPages(mdl, KernelMode, IoReadAccess);
    15.     MmProtectMdlSystemAddress(mdl, PAGE_READWRITE);
    16.     mapped = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmCached, NULL, FALSE, NormalPagePriority);
    17.     if (!mapped) {
    18.         MmUnlockPages(mdl);
    19.         IoFreeMdl(mdl);
    20.         return STATUS_UNSUCCESSFUL;
    21.     }
    22.  
    23.     if (!restore) {
    24.      
    25.         RtlCopyMemory(&origFreq, mapped, sizeof(origFreq));
    26.         RtlCopyMemory(mapped, &newFreq, sizeof(newFreq));
    27.     } else {
    28.         RtlCopyMemory(mapped, &origFreq, sizeof(origFreq));
    29.     }
    30.  
    31.     MmProtectMdlSystemAddress(mdl, PAGE_READONLY);
    32.     MmUnmapLockedPages(mapped, mdl);
    33.     MmUnlockPages(mdl);
    34.     IoFreeMdl(mdl);
    35.  
    36.     mapped = NULL;
    37.     mdl = NULL;
    38.     return STATUS_SUCCESS;
    39. }
    40.  
    41. VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
    42. {
    43.     UNREFERENCED_PARAMETER(DriverObject);
    44.     PatchQpcFrequency(TRUE);
    45. }
    46.  
    47. NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
    48. {
    49.     UNREFERENCED_PARAMETER(RegistryPath);
    50.  
    51.     DriverObject->DriverUnload = UnloadDriver;
    52.     return PatchQpcFrequency(FALSE);
    53. }
    dll
    Код (C++):
    1. typedef BOOL(WINAPI* PFN_QPC)(LARGE_INTEGER*);
    2. typedef BOOL(WINAPI* PFN_QPF)(LARGE_INTEGER*);
    3. static PFN_QPC g_OrigQpc = nullptr;
    4. static PFN_QPF g_OrigQpf = nullptr;
    5. static BOOL WINAPI MyQpc(LARGE_INTEGER* out) {
    6.     if (!out) return FALSE;
    7.     out->QuadPart = __rdtsc();
    8.     return TRUE;
    9. }
    10. static BOOL WINAPI MyQpf(LARGE_INTEGER* out) {
    11.     if (!out) return FALSE;
    12.     out->QuadPart = yourrdtscfreq;
    13.     return TRUE;
    14. }
    15. static void InstallHooks() {
    16.     MH_Initialize();
    17.     BOOL wow64 = FALSE;
    18.     IsWow64Process(GetCurrentProcess(), &wow64);
    19.     if (wow64) {
    20.         HMODULE hK32 = GetModuleHandleW(L"kernel32.dll");
    21.         if (hK32) {
    22.             auto pQpc = (PFN_QPC)GetProcAddress(hK32, "QueryPerformanceCounter");
    23.             if (pQpc) { MH_CreateHook(pQpc, MyQpc, (LPVOID*)&g_OrigQpc); MH_EnableHook(pQpc); }
    24.             auto pQpf = (PFN_QPF)GetProcAddress(hK32, "QueryPerformanceFrequency");
    25.             if (pQpf) { MH_CreateHook(pQpf, MyQpf, (LPVOID*)&g_OrigQpf); MH_EnableHook(pQpf); }
    26.         }
    27.     }
    28.     else {
    29.         HMODULE hKBase = GetModuleHandleW(L"KernelBase.dll");
    30.         if (hKBase) {
    31.             auto pQpc = (PFN_QPC)GetProcAddress(hKBase, "QueryPerformanceCounter");
    32.             if (pQpc) { MH_CreateHook(pQpc, MyQpc, (LPVOID*)&g_OrigQpc); MH_EnableHook(pQpc); }
    33.             auto pQpf = (PFN_QPF)GetProcAddress(hKBase, "QueryPerformanceFrequency");
    34.             if (pQpf) { MH_CreateHook(pQpf, MyQpf, (LPVOID*)&g_OrigQpf); MH_EnableHook(pQpf); }
    35.         }
    36.     }
    37. }
    38. static void RemoveHooks() {
    39.     MH_DisableHook(MH_ALL_HOOKS);
    40.     MH_Uninitialize();
    41. }
    42. DWORD WINAPI InitThread(LPVOID) {
    43.     InstallHooks();
    44.     return 0;
    45. }
    46.  
    47.  
    48. BOOL APIENTRY DllMain(HMODULE h, DWORD reason, LPVOID) {
    49.     if (reason == DLL_PROCESS_ATTACH) {
    50.         DisableThreadLibraryCalls(h);
    51.         CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)InitThread, nullptr, 0, nullptr));
    52.     }
    53.     else if (reason == DLL_PROCESS_DETACH) {
    54.         RemoveHooks();
    55.     }
    56.     return TRUE;
    57. }
     
    Research и galenkane нравится это.
  13. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    Твоя исходная идея в теме смешивает три разных уровня, и из-за этого часть решений выглядит рабочей только
    локально, но архитектурно некорректна:
    1. NtQueryPerformanceCounter и QueryPerformanceCounter это не источник истины.
    2. KUSER_SHARED_DATA это не источник истины, а usermode-зеркало fast path.
    3. Источник истины для QPC сидит в HAL timer provider, на текущих системах через HalpPerformanceCounter и
    связанный callback.
    Из этого следуют главные выводы.
    SharedUserData патчить само по себе недостаточно
    - Это меняет только пользовательское представление QpcFrequency/QpcBias/QpcData.
    - Kernel path продолжает жить по своим HAL-данным.
    - В итоге легко получить рассинхрон между user fast path, NtQueryPerformanceCounter,
    KeQueryPerformanceCounter и всем, что опирается на QPC внутри ядра.
    Подменять только NtQueryPerformanceCounter тоже некорректно
    - Ты меняешь лишь один API-слой.
    - Остальные потребители QPC могут брать данные не через него.
    - Если хочешь согласованную модель “QPC = raw TSC”, менять надо не syscall-обёртку, а сам provider и потом
    синхронно usermode-зеркало.
    Возвращать __rdtsc() напрямую можно только если вся система приведена к новой шкале
    - Надо, чтобы PerformanceCounter и PerformanceFrequency были согласованы.
    - Нельзя просто вернуть raw RDTSC, если остальной стек всё ещё считает, что частота и scaling у QPC старые.
    - Иначе получаются неверные интервалы, разный результат между fast path и syscall path, и ломается
    совместимость.
    Правильная логика такая
    - Если цель сохранить совместимость с Windows QPC, надо оставлять системную шкалу QPC и только корректно
    масштабировать raw counter в эту шкалу.
    - Если цель именно “QPC должен стать TSC-like counter”, то надо менять источник в HAL provider и затем
    синхронизировать KUSER_SHARED_DATA.
    - Патч одного QpcFrequency или одного NtQueryPerformanceCounter это полумера.
    Ещё важный нюанс:
    - “10 MHz” не универсален. На текущем ARM64 ядре, которое я проверил в WinDbg, QpcFrequency вообще
    24,000,000.
    - Значит жёстко пришивать 10,000,000 как “стандартный QPC” неправильно даже концептуально.

    --- Сообщение объединено, 25 мар 2026 ---
    Код (C++):
    1. #include <algorithm>
    2. #include <cstdint>
    3. #include <cstdio>
    4. #include <cstring>
    5.  
    6. #define WIN32_LEAN_AND_MEAN
    7. #define NOMINMAX
    8. #include <windows.h>
    9. #include <intrin.h>
    10.  
    11. struct QpcModel {
    12.     std::uint64_t frequency;
    13.     std::int64_t bias;
    14.     std::uint32_t counter_bits;
    15. };
    16.  
    17. struct QpcState {
    18.     std::uint64_t anchor;
    19.     bool initialized;
    20. };
    21.  
    22. struct QpcSample {
    23.     std::uint64_t counter;
    24.     std::uint64_t frequency;
    25. };
    26.  
    27. static inline std::uint64_t make_mask(std::uint32_t bits) {
    28.     if (bits == 0 || bits >= 64) {
    29.         return ~0ull;
    30.     }
    31.     return (1ull << bits) - 1;
    32. }
    33.  
    34. static inline std::uint64_t mask_counter(std::uint64_t value, std::uint32_t bits) {
    35.     return value & make_mask(bits);
    36. }
    37.  
    38. static inline std::uint64_t merge_with_epoch(std::uint64_t raw, std::uint64_t old_anchor, std::uint32_t bits) {
    39.     if (bits == 0 || bits >= 64) {
    40.         return raw;
    41.     }
    42.  
    43.     const std::uint64_t low_mask = make_mask(bits);
    44.     const std::uint64_t epoch_mask = ~low_mask;
    45.     const std::uint64_t sign_bit = 1ull << (bits - 1);
    46.     const bool sign_changed = ((raw ^ old_anchor) & sign_bit) != 0;
    47.  
    48.     if (!sign_changed) {
    49.         return (old_anchor & epoch_mask) | raw;
    50.     }
    51.  
    52.     const std::uint64_t base = old_anchor & epoch_mask;
    53.     const std::uint64_t merged = base | raw;
    54.     const std::uint64_t wrapped = merged + (1ull << bits);
    55.  
    56.     return (raw >= (old_anchor & low_mask)) ? merged : wrapped;
    57. }
    58.  
    59. static inline std::uint64_t update_monotonic_anchor(std::uint64_t raw_counter, const QpcModel& model, QpcState& state) {
    60.     const std::uint64_t raw = mask_counter(raw_counter, model.counter_bits);
    61.  
    62.     if (!state.initialized) {
    63.         state.anchor = raw;
    64.         state.initialized = true;
    65.         return state.anchor;
    66.     }
    67.  
    68.     std::uint64_t next = merge_with_epoch(raw, state.anchor, model.counter_bits);
    69.  
    70.     if (model.counter_bits == 0 || model.counter_bits >= 64) {
    71.         next = std::max(next, state.anchor);
    72.     } else if (next < state.anchor) {
    73.         next = state.anchor;
    74.     }
    75.  
    76.     state.anchor = next;
    77.     return state.anchor;
    78. }
    79.  
    80. static inline std::uint64_t apply_qpc_model(std::uint64_t monotonic_counter, const QpcModel& model) {
    81.     if (model.bias >= 0) {
    82.         return monotonic_counter + static_cast<std::uint64_t>(model.bias);
    83.     }
    84.  
    85.     return monotonic_counter - static_cast<std::uint64_t>(-model.bias);
    86. }
    87.  
    88. static inline QpcSample query_modeled_qpc(std::uint64_t raw_counter, const QpcModel& model, QpcState& state) {
    89.     const std::uint64_t monotonic_counter = update_monotonic_anchor(raw_counter, model, state);
    90.     return QpcSample{
    91.         apply_qpc_model(monotonic_counter, model),
    92.         model.frequency
    93.     };
    94. }
    95.  
    96. static inline double qpc_delta_seconds(const QpcSample& a, const QpcSample& b) {
    97.     if (b.counter < a.counter || a.frequency == 0 || a.frequency != b.frequency) {
    98.         return -1.0;
    99.     }
    100.  
    101.     const std::uint64_t delta = b.counter - a.counter;
    102.     return static_cast<double>(delta) / static_cast<double>(a.frequency);
    103. }
    104.  
    105. #if defined(_M_ARM64)
    106. #ifndef ARM64_SYSREG
    107. #define ARM64_SYSREG(op0, op1, crn, crm, op2) \
    108.     ((((op0) & 1) << 14) | (((op1) & 7) << 11) | (((crn) & 15) << 7) | (((crm) & 15) << 3) | ((op2) & 7))
    109. #endif
    110. static inline std::uint64_t read_raw_counter() {
    111.     return _ReadStatusReg(ARM64_SYSREG(3, 3, 14, 0, 2));
    112. }
    113. static constexpr const char* kRawSource = "CNTVCT_EL0";
    114. #elif defined(_M_X64)
    115. static inline std::uint64_t read_raw_counter() {
    116.     return __rdtsc();
    117. }
    118. static constexpr const char* kRawSource = "RDTSC";
    119. #else
    120. static inline std::uint64_t read_raw_counter() {
    121.     LARGE_INTEGER qpc{};
    122.     QueryPerformanceCounter(&qpc);
    123.     return static_cast<std::uint64_t>(qpc.QuadPart);
    124. }
    125. static constexpr const char* kRawSource = "QueryPerformanceCounter fallback";
    126. #endif
    127.  
    128. static void print_sample(int index, std::uint64_t raw, const QpcSample& current, const QpcSample* first, const QpcSample* previous) {
    129.     std::printf("sample[%d] raw = 0x%016llx\n", index, static_cast<unsigned long long>(raw));
    130.     std::printf("sample[%d] qpc = 0x%016llx\n", index, static_cast<unsigned long long>(current.counter));
    131.  
    132.     if (previous) {
    133.         std::printf("sample[%d] dt_prev  = %.9f sec\n", index, qpc_delta_seconds(*previous, current));
    134.     }
    135.     if (first) {
    136.         std::printf("sample[%d] dt_first = %.9f sec\n", index, qpc_delta_seconds(*first, current));
    137.     }
    138. }
    139.  
    140. static int run_test_mode(const QpcModel& model) {
    141.     QpcState state{};
    142.  
    143.     const std::uint64_t raw_samples[] = {
    144.         0x0000001234567000ull,
    145.         0x000000123458f000ull,
    146.         0x0000001234581000ull,
    147.         0x0000001234592000ull,
    148.     };
    149.  
    150.     QpcSample first{};
    151.     QpcSample previous{};
    152.  
    153.     for (int i = 0; i < 4; ++i) {
    154.         const QpcSample current = query_modeled_qpc(raw_samples[i], model, state);
    155.         print_sample(i, raw_samples[i], current, i == 0 ? nullptr : &first, i == 0 ? nullptr : &previous);
    156.         if (i == 0) {
    157.             first = current;
    158.         }
    159.         previous = current;
    160.     }
    161.  
    162.     return 0;
    163. }
    164.  
    165. static int run_live_mode(const QpcModel& model, int count, int sleep_ms) {
    166.     if (count <= 0) {
    167.         std::fprintf(stderr, "invalid sample count\n");
    168.         return 1;
    169.     }
    170.  
    171.     QpcState state{};
    172.     QpcSample first{};
    173.     QpcSample previous{};
    174.  
    175.     std::printf("raw source = %s\n", kRawSource);
    176.     std::printf("mode       = live\n");
    177.     std::printf("count      = %d\n", count);
    178.     std::printf("sleep_ms   = %d\n", sleep_ms);
    179.  
    180.     for (int i = 0; i < count; ++i) {
    181.         const std::uint64_t raw = read_raw_counter();
    182.         const QpcSample current = query_modeled_qpc(raw, model, state);
    183.         print_sample(i, raw, current, i == 0 ? nullptr : &first, i == 0 ? nullptr : &previous);
    184.         if (i == 0) {
    185.             first = current;
    186.         }
    187.         previous = current;
    188.         if (i + 1 < count && sleep_ms > 0) {
    189.             Sleep(static_cast<DWORD>(sleep_ms));
    190.         }
    191.     }
    192.  
    193.     return 0;
    194. }
    195.  
    196. static void print_usage(const char* exe) {
    197.     std::printf("usage: %s [--test] [--live [count] [sleep_ms]]\n", exe);
    198.     std::printf("  --test                 run injected samples\n");
    199.     std::printf("  --live [count] [ms]    read live raw counter values\n");
    200. }
    201.  
    202. int main(int argc, char** argv) {
    203.     const QpcModel model{
    204.         24000000ull,
    205.         static_cast<std::int64_t>(0xfffffffff471658aull),
    206.         64u
    207.     };
    208.  
    209.     if (argc <= 1 || std::strcmp(argv[1], "--test") == 0) {
    210.         const int rc = run_test_mode(model);
    211.         std::printf("freq = %llu\n", static_cast<unsigned long long>(model.frequency));
    212.         return rc;
    213.     }
    214.  
    215.     if (std::strcmp(argv[1], "--live") == 0) {
    216.         int count = 8;
    217.         int sleep_ms = 100;
    218.  
    219.         if (argc >= 3) {
    220.             count = std::atoi(argv[2]);
    221.         }
    222.         if (argc >= 4) {
    223.             sleep_ms = std::atoi(argv[3]);
    224.         }
    225.  
    226.         const int rc = run_live_mode(model, count, sleep_ms);
    227.         std::printf("freq = %llu\n", static_cast<unsigned long long>(model.frequency));
    228.         return rc;
    229.     }
    230.  
    231.     print_usage(argv[0]);
    232.     return 1;
    233. }
    234.  
    --- Сообщение объединено, 25 мар 2026 ---
    rd
     

    Вложения:

    • full.md.txt
      Размер файла:
      26,3 КБ
      Просмотров:
      66
    Последнее редактирование: 25 мар 2026
  14. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    602
    galenkane,

    Бот выводит чепуху:

    > SharedUserData патчить само по себе недостаточно

    Это невозможно, страница R. Что бы изменить выбираемые данные, нужно мониторить выборку(dbi).

    Зачем ты сюда бред бота цитируешь постоянно ?
     
  15. galenkane

    galenkane Active Member

    Публикаций:
    1
    Регистрация:
    13 янв 2017
    Сообщения:
    453
    Они предназначены для автора темы