Вообщем пофиксил основную часть, вывод в логе. Трабл есть с WsGet который сканит память модулей что залезли в ПО. Нужно ресерчить еще, а так годнота --- Сообщение объединено, Sep 24, 2024 --- для фикса WsGet нужно увеличить Local Mm[2048]:ULONG
из длл полет нормальный, тестовая структура для вывода --- Сообщение объединено, Sep 24, 2024 --- на таймерах вылетает без анхуков разных под скиллахайдом ,дальше не тестил еще --- Сообщение объединено, Sep 24, 2024 --- но код бодрый, под мультипоток замутить будет пушка
под дебуггером валится на Log data, item 2 Address = 1000262B Message = Access violation when writing to [000000D0] - passed to application
там валится должно да,главное под продакшен чтобы не мешало --- Сообщение объединено, Sep 24, 2024 --- по критериям подойдет опрос структуры раз в минуту думаю,кому как нрав. всевдокод Code (ASM): ; cb.asm ; Добавьте следующие глобальные переменные и функции .data gDebugLog DEBUG_LOG <?> gDebugLogMutex HANDLE ? gUpdateThreadHandle HANDLE ? gStopUpdateThread BOOL FALSE .code UpdateDebugLogThread proc .while !gStopUpdateThread ; Захват мьютекса invoke WaitForSingleObject, gDebugLogMutex, INFINITE ; Обновление gDebugLog ; ... (код для обновления различных полей gDebugLog) inc gDebugLog.UpdateCounter ; Освобождение мьютекса invoke ReleaseMutex, gDebugLogMutex ; Ожидание перед следующим обновлением invoke Sleep, 100 ; Обновление каждые 100 мс .endw ret UpdateDebugLogThread endp StartDebugLogUpdate proc ; Создание мьютекса invoke CreateMutex, NULL, FALSE, NULL mov gDebugLogMutex, eax ; Создание потока обновления invoke CreateThread, NULL, 0, addr UpdateDebugLogThread, NULL, 0, NULL mov gUpdateThreadHandle, eax ret StartDebugLogUpdate endp StopDebugLogUpdate proc ; Установка флага остановки mov gStopUpdateThread, TRUE ; Ожидание завершения потока invoke WaitForSingleObject, gUpdateThreadHandle, INFINITE ; Закрытие хэндла потока invoke CloseHandle, gUpdateThreadHandle ; Закрытие мьютекса invoke CloseHandle, gDebugLogMutex ret StopDebugLogUpdate endp GetDebugLog proc pDebugLog:PTR DEBUG_LOG ; Захват мьютекса invoke WaitForSingleObject, gDebugLogMutex, INFINITE ; Копирование данных invoke CopyMemoryInternal, pDebugLog, addr gDebugLog, sizeof DEBUG_LOG ; Освобождение мьютекса invoke ReleaseMutex, gDebugLogMutex ret GetDebugLog endp ; ... (остальной код остается без изменений) Code (C++): #include "cblib.h" #include <iostream> #include <iomanip> #include <thread> #include <atomic> #include <chrono> std::atomic<bool> g_stopThread(false); DEBUG_LOG g_latestDebugLog; void UpdateDebugLogThread() { while (!g_stopThread) { GetDebugLog(&g_latestDebugLog); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } void PrintDebugLog(const DEBUG_LOG& debugLog) { // ... (код для вывода содержимого DEBUG_LOG) } bool DetectAnomaly(const DEBUG_LOG& debugLog) { // Пример простой проверки на аномалию if (debugLog.IsDebuggerPresent || debugLog.SysexitFlags != 0) { return true; } return false; } int main() { // Запуск обновления DEBUG_LOG в DLL StartDebugLogUpdate(); // Создание потока для обновления g_latestDebugLog std::thread updateThread(UpdateDebugLogThread); // Основной цикл программы int anomalyCounter = 0; while (true) { if (DetectAnomaly(g_latestDebugLog)) { anomalyCounter++; std::cout << "Warning: Debug detected! (" << anomalyCounter << " times)" << std::endl; } // Вывод текущего состояния DEBUG_LOG каждые 5 секунд if (g_latestDebugLog.UpdateCounter % 50 == 0) { PrintDebugLog(g_latestDebugLog); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Пример условия выхода из цикла if (anomalyCounter > 10) { std::cout << "Too many anomalies detected. Exiting." << std::endl; break; } } // Остановка потока обновления и очистка ресурсов g_stopThread = true; updateThread.join(); StopDebugLogUpdate(); return 0; } --- Сообщение объединено, Sep 24, 2024 --- Вот мультипоточный с реализацией кода выше
сорцы рабочей либы + пример как в нативе юзать StkTrapResult: 0x00000000 TrIsDebug: 0x00000000 это в отладке если StkTrapResult: 0xc0000353 TrIsDebug: 0xc0000353 то не отлаживают
Привет. Это отлаживалось на старом крэклабе и там в конце была проблема с анстабом связанная с планировщиком(сброс кэша для стека и есчо что то). GtCall & RtCall - реализация этого. Перед вызовом загружается шифрованный указатель на код возврата и адрес который прочитает ядро, без выборки из юзер. При вызове состояние треда восстанавливается, в частности сбрасывается тф если до вызова небыло трассировки. Сохраненное состояние в юзер не прочитать, вызовы мб рекурсивны(цепь): SvMsgBox сохраняет контекст и ключ, возвращая управление. RtMsgBox читает ключ и восстанавливает контекст, передавая управление на шифрованный адрес. SvGetTicks формирует цепь адресов возврата, RtGetTicks возвращает управление проходя цепь. Ws* это проверка рабочего набора, если память прочитать(например просмотрев отладчиком) он изменится. По антидебагу интересное вот это было, в частности что бы пином не крутили, из того что помню
По CFG: Нелинейный буфер. сохраню для нубисов ФайлЧто происходитGp.asm- Подготовка и применение патчей к коду - Манипуляции с памятью для создания динамических патчей - Модификация потока выполнения программы - Использование недокументированных структур NTScan.asm- Инициализация и настройка окружения для сканирования - Использование SList для сканирования памяти без генерации исключений - Обнаружение отладки через подсчет квантований потока - Попытки обхода точек останова на чтение памяти - Проверка на наличие строки в памяти (возможно, сигнатуры)Swap.asm- Настройка механизма для измерения времени выполнения - Использование SList для обнаружения планирования потоков - Реализация механизма "swap" для обхода анализа потока выполнения - Измерение количества переключений контекста за определенное времяSelf.asm- Использование FPU для определения текущего адреса выполнения - Нарушение предполагаемого потока выполнения для обнаружения DBI - Манипуляции со стеком для обхода анализа - Проверка на возможность доступа к нижней границе стека без его расширения
На скрине неточно, как понял это работает так: Code (ASM): GtCall proc C push ebp push fs:[TEB.NtTib.ExceptionList] push esp ; s: stack ; NtUserEnumDisplayMonitors(arg1, arg2, *lpfnEnum, s) Call @f ; *lpfnEnum ; lpfnEnum(3args, s): mov esp, DWORD PTR[esp][4*4] ; s pop fs:[TEB.NtTib.ExceptionList] pop ebp retn ; return @@: push 0 ; arg1 & 2 push 0 %ENVR Eax, Gid ; GateID" = Service ID. %IGATE ; syscall NtUserEnumDisplayMonitors -> lpfnEnum ; caller -> GtCall -> syscall -> (push STATE) -> lpfnEnum -> ret GtpRet: .if Esi ; fetch? add ebx, eax ; +[k]: read from km. .else add ebx, fs:[DKEY] ; +k .endif %TRT ; restack jmp Ebx ; go RtCall proc C %TST ; save stack. %ENVR Edx, Gsz ; Narg mov ecx, ebx ; fetch? Int 2BH ; (pop STATE) -> GtpRet %CBR macro K mov ebx,K ; k or *k jmp RtCall %CBC macro F mov esi, F ; fetch? Call GtCall Там рядом был эксперимент - если тормознуть тред, он остановится не в начале кванта. Значит если исполнять что либо на начале кванта, это не будет видно в отладчике при остановке.
galenkane, Это так не работает. У вас серия NtCallbackReturn() возвращающих ошибку разумеется, так как калбэки не загружаются(нет CbCall()). Серия загружает ядерные состояния: Code (Text): CbCall(CbThunk3) CbCall(CbThunk2) CbCall(CbThunk1) затем они восстанавливаются по RtCall(): RtCall -> CbThunk1 -> RtCall -> CbThunk2 -> RtCall -> CbThunk3 -> ... Соответственно куда будет передано управление по RtCall: NtCallbackReturn в юзермоде не узнать, причём со сбросом TF вроде бы. CbCall() асм стаб должен быть, так как по syscall восстанавливается стек и нельзя вернуться(ret). --- Сообщение объединено, Feb 26, 2025 --- - обрыв трассы на RtCall:syscall, так как не трассировался CbCall(). При трассировке CbCall() трассируется и возврат, адрес ветвления затемнён" ядром.
galenkane, Не правильно всё. Вызов NtUserEnumDisplayMonitors приводит к вызову калбэка, из него возврат напрямую в вызывающий код, таким образом ядерное состояние сохраняется. Восстановление состояния происходит далее по NtCallbackReturn. Это похоже на NtContinue, только контекст хранится в ядре в виде цепочки. Прямой вызов syscall нужен что бы небыло выборок в стек(те не звать апи по call). Для syscall номер сервиса в rax.