Привет. Нашлась публикация по детекту кода через шедулер. Closing the Execution Gap: Hardware-Backed Telemetry for Detecting Out-Of-Context Execution Метод древний, интересно другое. Была тема про обход таких детектов. Системный профайлер такой код не видит, с контекстом тоже не выйдет. Дальше поверх хайд контекста в ntcallbackreturn. Такой буфер шедулер не видит. Как дополнить детектор, что бы работал ?
Ну вот нейронка помыслила. Мб поможет. Через markdown viewer открывать через https://github.com/Wasdubya/x64dbgMCP можно посмотреть позже) мб тула позволит)
На анализ берется текущий контекст. Нагрузка выполняется после завершения контекстного переключения и прерываний, что бы вероятность свапконтекста была минимальной. Детектор должен каким то образом выйти на ветвь с нагрузкой, симулировать или транслировать. В emet был simexecflow что бы ловить подобное.
На то исполнение внеконтекстное", потому что шедулер себя выдает. Странные тексты выше, чепуха ботов многословная Профайлер устроен крайне просто - частота прерываний повышается(перестройка таймера апик вроде), из них профайлер сохраняет в лог context.ip Разумеется он не получит управления сразу как отдал его. i-cet это простейший хард cfi. Можно закрутить последовательности, где будет баланс call-ret. Простая цепь rop что бы метод обкатать профайлерами, не суть важно что там крутится. --- Сообщение объединено, 23 окт 2025 --- Тут можно много интереснлго найти. Тут картировалось, тем по шедулеру навалом.
Когда окно переднего плана активно (перетаскивание мыши), Windows автоматически повышает частоту системного таймера для обеспечения плавности GUI. Это не квантование потоков, а именно частота clock interrupt. Код (C++): #include <windows.h> #include <winternl.h> #include <iostream> #include <iomanip> #include <vector> #include <cmath> #pragma comment(lib, "ntdll.lib") typedef LONG NTSTATUS; typedef NTSTATUS(NTAPI* pNtQueryTimerResolution)( PULONG MinimumResolution, PULONG MaximumResolution, PULONG CurrentResolution ); typedef NTSTATUS(NTAPI* pNtQuerySystemInformation)( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); #define SystemInterruptInformation 23 class TimerMonitor { private: pNtQueryTimerResolution NtQueryTimerResolution; pNtQuerySystemInformation NtQuerySystemInformation; LARGE_INTEGER perfFreq; struct Snapshot { ULONG resolution; ULONG dpcCount; LARGE_INTEGER perfCounter; double estimatedIPS; }; Snapshot lastSnapshot; public: TimerMonitor() { HMODULE ntdll = GetModuleHandleA("ntdll.dll"); NtQueryTimerResolution = (pNtQueryTimerResolution)GetProcAddress(ntdll, "NtQueryTimerResolution"); NtQuerySystemInformation = (pNtQuerySystemInformation)GetProcAddress(ntdll, "NtQuerySystemInformation"); QueryPerformanceFrequency(&perfFreq); QueryPerformanceCounter(&lastSnapshot.perfCounter); ULONG min, max, cur; NtQueryTimerResolution(&min, &max, &cur); lastSnapshot.resolution = cur; lastSnapshot.dpcCount = 0; lastSnapshot.estimatedIPS = 0; } void PrintHeader() { ULONG minRes, maxRes, curRes; NtQueryTimerResolution(&minRes, &maxRes, &curRes); std::cout << "=== System Timer Frequency Monitor ===" << std::endl; std::cout << "Range: " << maxRes / 10000.0 << " - " << minRes / 10000.0 << " ms" << std::endl; std::cout << "Frequencies: " << (int)(10000000.0 / minRes) << " - " << (int)( 10000000.0 / maxRes) << " Hz" << std::endl; std::cout << std::endl; std::cout << "INSTRUCTIONS:" << std::endl; std::cout << "1. Run the program" << std::endl; std::cout << "2. Drag another application's window (for example, File Explorer)" << std::endl; std::cout << "3. Observe changes in Resolution and Frequency" << std::endl; std::cout << std::endl; std::cout << std::setw(10) << "Time(s)" << std::setw(18) << "Resolution(ms)" << std::setw(15) << "Frequency(Hz)" << std::setw(18) << "Change" << std::endl; std::cout << std::string(65, '-') << std::endl; } void Update() { ULONG minRes, maxRes, curRes; NtQueryTimerResolution(&minRes, &maxRes, &curRes); LARGE_INTEGER now; QueryPerformanceCounter(&now); double elapsed = (double)(now.QuadPart - lastSnapshot.perfCounter.QuadPart) / perfFreq.QuadPart; double currentFreq = 10000000.0 / curRes; // Detect if resolution changed std::string change = ""; if (curRes < lastSnapshot.resolution) { change = "↑ INCREASE!"; } else if (curRes > lastSnapshot.resolution) { change = "↓ decrease"; } static DWORD startTime = GetTickCount(); double runTime = (GetTickCount() - startTime) / 1000.0; std::cout << std::fixed << std::setprecision(1) << std::setw(10) << runTime << std::setw(18) << curRes / 10000.0 << std::setw(15) << (int)currentFreq << std::setw(18) << change << std::endl; lastSnapshot.resolution = curRes; lastSnapshot.perfCounter = now; } }; int main() { TimerMonitor monitor; monitor.PrintHeader(); // Monitor for 5 minutes for (int i = 0; i < 300; i++) { Sleep(1000); monitor.Update(); } std::cout << "\nMonitoring finished." << std::endl; std::cout << "\nAnalysis: If the Resolution decreased during window dragging" << std::endl; std::cout << "(and Frequency increased), then the hypothesis is confirmed!" << std::endl; return 0; } Код (C++): #include <windows.h> #include <winternl.h> #include <psapi.h> #include <tlhelp32.h> #include <iostream> #include <iomanip> #include <vector> #include <string> #include <map> #include <set> #include <algorithm> #pragma comment(lib, "ntdll.lib") #pragma comment(lib, "psapi.lib") typedef LONG NTSTATUS; typedef NTSTATUS(NTAPI* pNtQueryTimerResolution)( PULONG MinimumResolution, PULONG MaximumResolution, PULONG CurrentResolution ); typedef NTSTATUS(NTAPI* pNtQuerySystemInformation)( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); // Структуры для получения информации о процессах // SYSTEM_PROCESS_INFORMATION is already defined in winternl.h #define SystemProcessInformation 5 class ProcessMonitor { private: struct ProcessInfo { DWORD pid; std::string name; DWORD lastSeen; }; std::map<DWORD, ProcessInfo> activeProcesses; std::set<DWORD> suspiciousProcesses; public: std::string GetProcessName(DWORD processID) { char processName[MAX_PATH] = "<unknown>"; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); if (hProcess != NULL) { HMODULE hMod; DWORD cbNeeded; if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { GetModuleBaseNameA(hProcess, hMod, processName, sizeof(processName)); } CloseHandle(hProcess); } return std::string(processName); } void ScanActiveProcesses() { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { return; } PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); DWORD currentTime = GetTickCount(); if (Process32First(hSnapshot, &pe32)) { do { ProcessInfo info; info.pid = pe32.th32ProcessID; // Convert WCHAR to std::string char processName[MAX_PATH]; WideCharToMultiByte(CP_UTF8, 0, pe32.szExeFile, -1, processName, MAX_PATH, NULL, NULL); info.name = processName; info.lastSeen = currentTime; activeProcesses[pe32.th32ProcessID] = info; } while (Process32Next(hSnapshot, &pe32)); } CloseHandle(hSnapshot); } void MarkSuspicious(DWORD pid) { suspiciousProcesses.insert(pid); } std::vector<ProcessInfo> GetRecentlyActive(DWORD withinMs = 2000) { std::vector<ProcessInfo> result; DWORD currentTime = GetTickCount(); for (const auto& pair : activeProcesses) { if (currentTime - pair.second.lastSeen <= withinMs) { result.push_back(pair.second); } } return result; } void PrintSuspiciousProcesses() { if (suspiciousProcesses.empty()) { std::cout << "\nNo suspicious processes detected." << std::endl; return; } std::cout << "\n=== Processes that likely changed timer resolution ===" << std::endl; std::cout << std::setw(10) << "PID" << " " << "Process Name" << std::endl; std::cout << std::string(50, '-') << std::endl; for (DWORD pid : suspiciousProcesses) { auto it = activeProcesses.find(pid); if (it != activeProcesses.end()) { std::cout << std::setw(10) << pid << " " << it->second.name << std::endl; } } } }; class TimerMonitor { private: pNtQueryTimerResolution NtQueryTimerResolution; LARGE_INTEGER perfFreq; ProcessMonitor processMonitor; struct Snapshot { ULONG resolution; LARGE_INTEGER perfCounter; std::vector<DWORD> activePIDs; }; Snapshot lastSnapshot; std::map<ULONG, std::set<DWORD>> resolutionChanges; // resolution -> PIDs public: TimerMonitor() { HMODULE ntdll = GetModuleHandleA("ntdll.dll"); NtQueryTimerResolution = (pNtQueryTimerResolution)GetProcAddress(ntdll, "NtQueryTimerResolution"); QueryPerformanceFrequency(&perfFreq); QueryPerformanceCounter(&lastSnapshot.perfCounter); ULONG min, max, cur; NtQueryTimerResolution(&min, &max, &cur); lastSnapshot.resolution = cur; } void PrintHeader() { ULONG minRes, maxRes, curRes; NtQueryTimerResolution(&minRes, &maxRes, &curRes); std::cout << "=== System Timer Resolution Monitor with Process Tracking ===" << std::endl; std::cout << "Range: " << maxRes / 10000.0 << " - " << minRes / 10000.0 << " ms" << std::endl; std::cout << "Frequencies: " << (int)(10000000.0 / minRes) << " - " << (int)(10000000.0 / maxRes) << " Hz" << std::endl; std::cout << std::endl; std::cout << "INSTRUCTIONS:" << std::endl; std::cout << "1. Run the program" << std::endl; std::cout << "2. Drag another application's window" << std::endl; std::cout << "3. Watch for Resolution changes and suspect processes" << std::endl; std::cout << "4. Press Ctrl+C to exit and see summary" << std::endl; std::cout << std::endl; std::cout << std::setw(10) << "Time(s)" << std::setw(15) << "Res(ms)" << std::setw(12) << "Freq(Hz)" << std::setw(15) << "Status" << " Suspect Processes" << std::endl; std::cout << std::string(80, '=') << std::endl; } void Update() { // Scan processes before checking resolution processMonitor.ScanActiveProcesses(); ULONG minRes, maxRes, curRes; NtQueryTimerResolution(&minRes, &maxRes, &curRes); LARGE_INTEGER now; QueryPerformanceCounter(&now); double currentFreq = 10000000.0 / curRes; // Detect if resolution changed std::string status = ""; std::string suspectInfo = ""; if (curRes < lastSnapshot.resolution) { status = "** INCREASED **"; // Get recently active processes auto suspects = processMonitor.GetRecentlyActive(1500); // Build suspect list if (!suspects.empty()) { suspectInfo = "PIDs: "; size_t maxSuspects = (std::min)(suspects.size(), size_t(3)); for (size_t i = 0; i < maxSuspects; i++) { suspectInfo += std::to_string(suspects[i].pid) + "(" + suspects[i].name + ")"; if (i < maxSuspects - 1) { suspectInfo += ", "; } processMonitor.MarkSuspicious(suspects[i].pid); resolutionChanges[curRes].insert(suspects[i].pid); } } } else if (curRes > lastSnapshot.resolution) { status = "decreased"; } else { status = "-"; } static DWORD startTime = GetTickCount(); double runTime = (GetTickCount() - startTime) / 1000.0; std::cout << std::fixed << std::setprecision(1) << std::setw(10) << runTime << std::setw(15) << curRes / 10000.0 << std::setw(12) << (int)currentFreq << std::setw(15) << status; if (!suspectInfo.empty()) { std::cout << " " << suspectInfo; } std::cout << std::endl; lastSnapshot.resolution = curRes; lastSnapshot.perfCounter = now; } void PrintSummary() { std::cout << "\n" << std::string(80, '=') << std::endl; std::cout << "=== MONITORING SUMMARY ===" << std::endl; std::cout << std::string(80, '=') << std::endl; processMonitor.PrintSuspiciousProcesses(); if (!resolutionChanges.empty()) { std::cout << "\n=== Resolution Changes Log ===" << std::endl; for (const auto& pair : resolutionChanges) { std::cout << "\nResolution changed to " << pair.first / 10000.0 << " ms (" << (int)(10000000.0 / pair.first) << " Hz)" << std::endl; std::cout << "Suspect PIDs: "; for (DWORD pid : pair.second) { std::cout << pid << " "; } std::cout << std::endl; } } std::cout << "\n=== Analysis ===" << std::endl; std::cout << "Common processes that change timer resolution:" << std::endl; std::cout << "- dwm.exe (Desktop Window Manager - during window dragging)" << std::endl; std::cout << "- audiodg.exe (Audio Engine - during audio playback)" << std::endl; std::cout << "- chrome.exe / firefox.exe (Browsers - for smooth animations)" << std::endl; std::cout << "- Discord.exe, Spotify.exe (Media apps)" << std::endl; std::cout << "- explorer.exe (File Explorer - during drag operations)" << std::endl; } }; int main() { TimerMonitor monitor; monitor.PrintHeader(); // Monitor for 5 minutes (or until Ctrl+C) std::cout << "Monitoring started... (will run for 5 minutes)\n" << std::endl; for (int i = 0; i < 300; i++) { Sleep(1000); monitor.Update(); } monitor.PrintSummary(); std::cout << "\n=== Hypothesis Check ===" << std::endl; std::cout << "If you saw resolution decrease (frequency increase) during window" << std::endl; std::cout << "dragging, and the suspect processes include dwm.exe or explorer.exe," << std::endl; std::cout << "then the hypothesis is CONFIRMED!" << std::endl; std::cout << "\nWindows automatically increases timer frequency for GUI responsiveness." << std::endl; return 0; }
galenkane Предполагалось что железные таймеры не перестраиваются. Там на скрине 2&3 столбцы - isr/s без изменений. Эффект пропадает когда апик на макс частоте(последние столбцы). Откуда еще прерываниям взяться не известно, только если они генерятся софтверно.
Подсчет числа прерываний, секунда абсолютна. Такие частоты только профайлер может поднять, но никак не окна. Вы измеряете переменные ядра, а не характеристики железных таймеров Откуда они сыпались не знаю, в то время ядерный монитор этого дела собирать не хотелось, чесно говоря от синих экранов какой то рефлекс не хороший. Как начнет что то падать и при этом инфу с дампов не получить и не разобрать, такое желательно избегать