5
Те, кому положено свои программы отлаживать, с удовольствием кормят нас забагованными Те, кому положено свои программы отлаживать, с удовольствием кормят нас забагованными релизами, которые начинают нормально работать только после третьего обновления. Зато те, релизами, которые начинают нормально работать только после третьего обновления. Зато те, кого отлаживать программы совсем не просят , с удовольствием в чужом творчестве ковыря- кого отлаживать программы совсем не просят , с удовольствием в чужом творчестве ковыря- ются. Вопросы антиотладки интересны не только исследователям малвари ( стоящим по обе ются. Вопросы антиотладки интересны не только исследователям малвари ( стоящим по обе стороны баррикад), но и кодерам, заботящимся о безопасности своих коммерческих поделок. стороны баррикад), но и кодерам, заботящимся о безопасности своих коммерческих поделок. БИБЛИОТЕКА АНТИОТЛАДЧИКА Кодинг 92 ХАКЕР 10 /177/ 2013 Классические приемы антиотладки, которые должен знать каждый Евгений Дроботун [email protected] и NtQueryInformationProcess, вызываемая с Pro- cessInformationClass = ProcessDebugPort (0x7): ... _asm { push 0 ... CheckRemoteDebuggerPresent(hProcess, &DbgDetect); if(DbgDetect) return true; else return false; СПЕЦИАЛЬНО ОБУЧЕННЫЕ API-ФУНКЦИИ В неисчерпаемых недрах Windows, помимо всем известной функции IsDebuggerPresent, есть еще парочка API, с помощью которых можно довольно просто определить факт запуска программы под отладчиком. Это CheckRemoteDebuggerPresent:

БИБЛИОТЕКА АНТИОТЛАДЧИКА

  • Upload
    others

  • View
    9

  • Download
    0

Embed Size (px)

Citation preview

Те, кому положено свои программы отлаживать, с удовольствием кормят нас забагованными Те, кому положено свои программы отлаживать, с удовольствием кормят нас забагованными релизами, которые начинают нормально работать только после третьего обновления. Зато те, релизами, которые начинают нормально работать только после третьего обновления. Зато те, кого отлаживать программы совсем не просят, с удовольствием в чужом творчестве ковыря-кого отлаживать программы совсем не просят, с удовольствием в чужом творчестве ковыря-ются. Вопросы антиотладки интересны не только исследователям малвари (стоящим по обе ются. Вопросы антиотладки интересны не только исследователям малвари (стоящим по обе стороны баррикад), но и кодерам, заботящимся о безопасности своих коммерческих поделок.стороны баррикад), но и кодерам, заботящимся о безопасности своих коммерческих поделок.

БИБЛИОТЕКА АНТИОТЛАДЧИКА

Кодинг92 ХАКЕР 10 /177/ 2013

Классические приемы антиотладки, которые должен знать каждый

Евгений Дроботун[email protected]

и NtQueryInformationProcess, вызываемая с Pro-cessInformationClass = ProcessDebugPort (0x7):

..._asm { push 0

...CheckRemoteDebuggerPresent(hProcess, &DbgDetect);if(DbgDetect) return true;else return false;

СПЕЦИАЛЬНО ОБУЧЕННЫЕ API-ФУНКЦИИВ неисчерпаемых недрах Windows, помимо всем известной функции IsDebuggerPresent, есть еще парочка API, с помощью которых можно довольно просто определить факт запуска программы под отладчиком.

Это CheckRemoteDebuggerPresent:

STATUS_BREAKPOINT 0x80000003

STATUS_SINGLE_STEP 0x80000004

DBG_PRINTEXCEPTION_C 0x40010006

DBG_RIPEXCEPTION 0x40010007

DBG_CONTROL_C 0x40010005

DBG_CONTROL_BREAK 0x40010008

DBG_COMMAND_EXCEPTION 0x40010009

EXCEPTION_WX86_SINGLE_STEP 0x4000001E

EXCEPTION_WX86_BREAKPOINT 0x4000001F

Способы определе-ния факта отладки

Значения для RaiseException, которые можно использовать для антиотладки

Нейтрализация CheckRemote-DebuggerPresent и NtQueryInforma-tionProcess в плагине Olly Advanced для OllyDbg

push 0x4 push FlagPointer // ProcessInformationClass = // ProcessDebugPort push 0x7 push ProcessID call NtQueryInformationProcess }if (Flag) return true;else return false;...

Однако к популярным отладчикам написано множество плагинов и скриптов, нейтрализующих действия этих API. В большинстве случаев это до-стигается их перехватом, поэтому перед приме-нением этих функций желательно проверить их код на целостность и отсутствие перехвата.

ОБРАБОТКА ИСКЛЮЧЕНИЙДостаточно известный и применяемый способ. Некоторые API-функции, команды или после-довательности команд процессора вызывают исключения, и, если программа не запущена под отладчиком, управление передается зара-нее установленному обработчику исключений. В то же время если запустить такую программу под отладчиком, то эти же самые функции, коман-ды или последовательности никаких исключений вызывать не будут.

Общий принцип применения способа:

...__try {

Общие приемы

Специальные API-функции Поиск процессов, окон, драйверов и других

характерных объектовПрограммы точки останова

Баги отладки

Аппаратные точки останова

Замер времени выполнения команд

SHE (VEH) обработчики

Особенности режима отладки

Манипуляции с PE-форматом

Флаги отладки

Приемы против конкретных отладчиков

Антиотладочные приемы

ХАКЕР 10 /177/ 2013 Библиотека антиотладчика 93

WARNING

Вся информация предо-ставлена исключительно

в ознакомительных целях. Ни редакция, ни автор не несут от-

ветственности за любой возможный вред, при-

чиненный материалами данной статьи.

... // В этом месте ну но написать // что-нибудь, что мо ет вызвать // ну ное нам искл чение ... return true; }__except(EXCEPTION_EXECUTE_HANDLER) { return false; }...

В качестве нужных команд или API сгодятся:• int 0x3 (одним байтом 0xCC);• int 0x3 (двумя байтами 0xCD, 0x03);• int 0x2d; • int 0x2c (работает только под Win Vista и выше); • так называемая точка заморозки (команда

с опкодом 0xf1); • API-функция DebugBreak (или DbgBreakPoint

из ntdll.dll); • API-функция RaiseException с некоторыми

входными значениями; • флаг трассировки (trap flag).

Код для trap flag:

...__asm { pushfd or word ptr[esp], 0x100 popfd nop }...

ЗАМЕР ВРЕМЕНИ ВЫПОЛНЕНИЯ КОМАНДКогда отладчик присутствует и выполняет по-шаговую трассировку, появляется существенная задержка между выполнением отдельных команд по сравнению с обычным выполнением.

В системе есть довольно много способов из-мерения временных промежутков. Вот некоторые из них:• команда RDTSC;• API-функция GetTickCount;• API-функция timeGetTime (из winmm.dll);• API-функция QueryPerformanceCounter;• API-функция GetSystemTimeAsFileTime;• API-функция GetProcessTimes;• API-функция KiGetTickCount (или вызов пре-

рывания int 0x2A);• API-функция NtQueryInformationProcess

(ProcessInformationClass = ProcessTimes (0x04);

• API-функция NtQueryInformationThread (ThreadInformationClass = ThreadTimes (0x01);

• поля структуры KUSER_SHARED_DATA.

...DWORD TimeStart = GetTickCount();...DWORD TimeEnd = GetTickCount();if ((TimeEnd - TimeStart) > TimeLimit) return true;else return false;...

Команду RDTSC могут перехватывать и ней-трализовать некоторые плагины для OllyDbg. Побороть этот перехват можно, замерив какой-нибудь большой (примерно 10 с) промежуток вре-

Места в KUSER_SHARED_DATA, где можно взять значения времени

Перехват RDTSC в PhantOm

Аппаратные точки останова в OllyDbg

return false;...

Кроме этого, в Windows Vista и выше мож-но воспользоваться особенностью выполнения функции DbgPrintEx, ход выполнения которой под отладкой отличается от обычного и функция выполняется дольше (для этих целей также от-лично подойдет и OutputDebugString, но бывает иногда так, что ее контролируют антиотладочные плагины).

ОТЛАДОЧНЫЕ РЕГИСТРЫНенулевое значение специальных отладочных ре-гистров процессора запросто может послужить фактом, подтверждающим отладку программы. Просто так узнать их содержимое не получит-ся, и для этого можно либо вызвать исключение и прочитать контекст потока, либо воспользовать-ся функцией GetThreadContext:

...GetThreadContext(ThreadHandle, &ThreadContext) if ((ThreadContext.Dr0 != 0)||(ThreadContext.Dr1 != 0)||(ThreadContext.Dr2 != 0)||(ThreadContext.Dr3 != 0)) return true;else return false;...

ФЛАГИ ОТЛАДКИ, КУЧИ И ПРОЧЕЕНаверное, только ленивый не проверял флаг BeingDebugged в своих программах, пытаясь за-щитить их от незадачливого взломщика. Более того, не секрет, что IsDebuggerPresent использует его для своей работы, поэтому на различных фла-гах, состояние которых указывает на наличие от-ладчика, долго останавливаться не будем.

Значение FLG_ HEAP_ ENABLE_ TAIL_CHECK в NtGlobalFlag указывает диспетчеру кучи, что идет процесс отладки и в конце каждого бло-ка, выделяемого из кучи, нужно помещать сиг-натуру в виде восьми байт 0xAB для контроля

KUSER_SHARED_DATA+008 InterruptTime.LowPart

KUSER_SHARED_DATA+00C InterruptTime.Hight1Time

KUSER_SHARED_DATA+010 InterruptTime.Hight2Time

KUSER_SHARED_DATA+014 SystemTime.LowPart

KUSER_SHARED_DATA+018 SystemTime.Hight1Time

KUSER_SHARED_DATA+01C SystemTime.Hight2Time

KUSER_SHARED_DATA+320 TickCount.LowPart

KUSER_SHARED_DATA+324 TickCount.Hight1Time

KUSER_SHARED_DATA+328 TickCount.Hight2Time

мени и проконтролировав регистр edx до и по-сле выполнения команды. Если его содержимое не изменилось, то налицо перехват RDTSC. Эту проверку лучше засунуть в отдельный поток, что-бы не задерживать выполнение основного.

Перехват GetTickCount тоже может присут-ствовать в плагинах, и его легко определить таким вот кусочком кода:

...DWORD TimeStart = GetTickCount();Sleep(100);DWORD TimeEnd = GetTickCount();...

Если разница между TimeEnd и TimeStart меньше законной сотни, то GetTickCount явно пе-рехвачена и висит под контролем какого-нибудь плагина.

Все перечисленное поможет выявить отлад-чик при трассировке программы.

Для выявления отладчика при обыч-ном выполнении команд может помочь API NtQueryInformationProcess в паре с API GetSystemTimeAsFileTime:

..._asm { push 0 push 0x20 push TimeStartPointer // ProcessInformationClass = // ProcessTimes push 0x4 push ProcessID call NtQueryInformationProcess }GetSystemTimeAsFileTime(LPFILETIME (&TimeEnd));if ((TimeEnd.dwLowDateTime – TimeStart.CreateTime.LowPart) > TimeLimit) return true;else

ХАКЕР 10 /177/ 2013Кодинг94

Имя процесса Заголовок окна Класс окна

OllyDbg 1.10 OLLYDBG.EXE OllyDbg-[CPU] OLLYDBG

OllyDbg 1.10+HideDebugger OLLYDBG.EXE -[CPU] OLLYDBG

OllyDbg 2.01 OLLYDBG.EXE OllyDbg-[CPU] OLLYDBG

WinDbg 6.12 windbg.exe WinDbg:6.12 WinDbgFrameClass

Immunity Debugger 1.85 Immunity Debugger.exe Immunity Debugger-[CPU] ID

NtGlobalFlag

FLG_HEAP_ENABLE_TAIL_CHECK 0x00000010

FLG_HEAP_ENABLE_FREE_CHECK 0x00000020

FLG_HEAP_VALIDATE_PARAMETERS 0x00000040

Flags (Win XP) windbg.exe

HEAP_GROWABLE 0x00000002

HEAP_TAIL_CHECKING_ENABLED 0X00000020

HEAP_FREE_CHECKING_ENABLED 0x00000040

HEAP_SKIP_VALIDATION_CHECKS 0x10000000

HEAP_VALIDATE_PARAMETERS_ENABLED 0x40000000

Flags (Win Vista, 7)

HEAP_GROWABLE 0x00000002

HEAP_TAIL_CHECKING_ENABLED 0x00000020

HEAP_FREE_CHECKING_ENABLED 0x00000040

HEAP_VALIDATE_PARAMETERS_ENABLED 0x40000000

ForceFlag

HEAP_TAIL_CHECKING_ENABLED 0x00000020

HEAP_FREE_CHECKING_ENABLED 0x00000040

HEAP_VALIDATE_PARAMETERS_ENABLED 0x40000000

Флаг Win XP (32 bit) Win Vista, 7 (32 bit) Win XP (64 bit) Win Vista, 7 (64 bit)

BeingDebugged PEB+0x02 PEB+0x02 PEB+0x02 PEB+0x02

NtGlobalFlag PEB+0x68 PEB+0x68 PEB+0xbc PEB+0xbc

Flags [PEB+0x18]+0x0c [PEB+0x18]+0x40 [PEB+0x30]+0x14 [PEB+0x30]+0x70

ForceFlag [PEB+0x18]+0x10 [PEB+0x18]+0x44 [PEB+0x30]+0x18 [PEB+0x30]+0x74

KdDebuggerEnabled (user mode)

KUSER_SHARED_DATA+0x2d4 (0x7ffe02d4)

KUSER_SHARED_DATA+0x2d4 (0x000000007ffe02d4)

KdDebuggerEnabled (kernel mode) 0x82b68368 0xfffff80003279310

KdDebuggerNotPresent (kernel mode) 0x82b68368 0xfffff80003279311

Флаги, по которым можно определить наличие отладчика

Значения флагов NtGlobalFlag, Flags и ForceFlag, показывающие наличие отладчика

Заголовки и классы окон, по которым можно определить наличие отладчиков

за переполнением. Это также может быть исполь-зовано для обнаружения отладчика:

..._asm { mov eax, fs:[0x30] mov ebx, [eax + 0x18] // дрес начала кучи mov StartHeapAddr, ebx mov ecx, [ebx + 0x38] // дрес конца кучи mov EndHeapAddr, ecx }Heap = (DWORD*)StartHeapAddr;// канируем кучу на предмет наличия // 0xababababfor(DWORD index = 0; index <= EndHeapAddr - StartHeapAddr; index++) { if (Heapindex / 4 ( == 0xabababab) return true; }...

Если так поступить с кучей, выделяемой для процесса по умолчанию, то можно обойти корректировку NtGlobalFlag, производимую неко-торыми плагинами для OllyDbg (в частности, Olly Advanced и Hide Debugger).

Кроме всего этого, можно использовать API-функции RtlQueryProcessHeapInformation или RtlQueryProcessDebugInformation, с помощью кото-рых можно посмотреть значения флагов кучи.

Флаги • KdDebuggerEnabled,• KdDebuggerNotPresent

можно лекго проверить, воспользовавшись функцией NtQuerySystemInformation, вызвав ее с InformationClass, равным 0x23.

SEH (VEH) ОБРАБОТЧИКИЕсли установить свой обработчик событий с по-мощью функций SetUnhandledExceptionFilter или

AddVectoredExceptionFilter, а затем сделать в про-грамме какое-нибудь исключение, то под отлад-чиком установленный нами обработчик вызывать-ся не будет, и эта особенность может помочь нам зафиксировать факт отладки.

Используя контекст, передаваемый в обработ-чик, можно подправить счетчик команд и после выхода из обработчика продолжить выполнение команд в нужном нам направлении (код, который позволит это сделать, смотри на диске).

ОСОБЕННОСТИ ОТЛАДОЧНОГО РЕЖИМА WINDOWSОчень многие API-функции (или их последова-тельности) по-разному себя ведут при обычном выполнении или при выполнении под отладчи-ком.

К примеру, уже упомянутая функция DbgPrintEx (или OutputDebugString) при выполнении под от-ладчиком вызывает API ZwQueryDebugFilterState, а без отладчика вызова этой функции не происхо-дит (за счет чего она, собственно, под отладчиком дольше и выполняется). Перехватив и отследив вызов этой функции, можно засечь факт отладки.

Благодаря возможности подгружать файлы с отладочными символами вместе с загружаемой библиотекой в отладчике WinDbg и встроенном в Visual Studio отладчике можно легко распознать их наличие:

...HMODULE hLibrary = LoadLibrary(L"ntdll.dll");if (int(CreateFileA("ntdll.dll",GENERIC_READ,0,0,3,0,0)) == -1) return true;...// ли вот такHMODULE hLibrary = LoadLibrary(L"ntdll.dll");HANDLE hRes = BeginUpdateResourceA("ntdll.dll",0);if (EndUpdateResourceA(hRes,0) == 0) return true;...

Суть этого кода заключается в том, что под от-ладкой при открытии библиотеки, к примеру ntdll.dll, отладчик ждет загрузки файла с символами и других манипуляций с файлом открываемой би-блиотеки некоторое время делать не позволяет.

Если для существующего файла сделать CreateFile с параметром OPEN_EXISTING, затем установить для него HANDLE_FLAG_PROTECT_ FROM_CLOSE с помощью SetHandleInformation и далее попытаться закрыть все это дело с по-мощью CloseHandle, то под отладчиком вылезет исключение, чем мы с успехом можем восполь-зоваться:

..._try { HANDLE hFile = CreateFileA ("d:/x_fi les.txt", 0, 0, 0, OPEN_EXISTING, 0, 0); SetHandleInformation(hFile, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE); CloseHandle(hFile); return false; }__except(EXCEPTION_EXECUTE_HANDLER) { return true; }...

Если учитывать, что отлаживаемый процесс должен иметь привилегии отладчика, да и от-ладчик, как правило, без административных прав запускается редко, то мы можем поиметь нужный нам результат.

Это можно сделать, попробовав открыть си-стемный процесс csrss.exe. Если удалось, значит, у испытуемого процесса с привилегиями отлад-чика все в порядке:

..._asm {

ХАКЕР 10 /177/ 2013 Библиотека антиотладчика 95

call CsrGetProcessId // Получаем Id // csrss.exe mov CSRSSId, eax }

// Пытаемся открыть csrss.exeif (OpenProcess(PROCESS_ALL_ACCESS, 0, CSRSSId) != 0) return true;...

Для этих же целей можно задействовать функ-цию DbgSetDebugFilterState, которая возвращает ненулевое значение, если процесс выполняется с админскими правами и имеет отладочные при-вилегии:

..._asm { call DbgSetDebugFilterState mov Flag, eax }if (Flag == 0) return true;...

Еще можно попытаться отсоединить от-лаживаемый поток от отладчика с помощью

Если надумаешь использовать этот прием, то следует учесть наличие плагинов к OllyDbg, которые контролируют эти функции. В неко-торых случаях можно использовать функцию GetLastError после вызова нужных функций. Дело в том, что некоторые плагины просто имитируют неудачное срабатывание этих функций, и несо-ответствие возвращенного ошибочного значения значению, возвращаемому GetLastError, поможет это выявить.

Использовав функцию NtQueryInformationPro-cess 0x1f (ProcessDebugObjectHandle) или 0x1e (ProcessDebugFlags), можно определить факт отладки по наличию различных объектов отладки (соответствующий код ты найдешь на диске, при-лагаемом к журналу).

Кроме всего этого, можно определить имя родительского процесса, и, если оно отлично от explorer.exe или cmd.exe, возможно, стоит за-подозрить неладное.

ЗАКЛЮЧЕНИЕКонечно же, это не все. Это далеко не все. Тема антиотладки глубока, обширна и, наверное, бес-конечна, а журнал и объем статьи, к сожалению, такими свойствами не обладают. Что ж, наде-юсь, у меня будет возможность еще чем-нибудь с тобой поделиться по этой теме в следующих номерах.

функции NtSetInformationThread с параме-тром ThreadInformationClass, равным 0x11 (ThreadHideFromDebugger):

..._asm { push 0 push 0 push 0x11 push -2 call NtSetInformationThread}...

ПРОЦЕССЫ, ОКНА, БИБЛИОТЕКИ И ДРУГИЕ ОТЛАДОЧНЫЕ АРТЕФАКТЫКак нетрудно догадаться из заголовка раздела, любой отладчик имеет какое-нибудь имя про-цесса, открытые окна, загруженные библиотеки и драйверы.

Все это достаточно легко выявить, просто за-действовав некоторые функции, любезно предо-ставленные в наше распоряжение операционной системой.

Это, к примеру, CreateToolhelp32Snapshot со-вместно с Process32First и Process32Next для по-иска нужного процесса или FindWindow для поис-ка нужного окна.

ОТ КСПЕРТА

В статье приводится хорошая классификация антиотладочных приемов и соответствующие примеры. Подобные техники активно используются в разном программном обеспечении. Однако стоит отметить, что в исполь-зовании антиотладки больше заинтересованы те, кто хочет защитить код программы. И это далеко не всегда вирусописатели. Множество техник защиты от отладки используется в коммерческих упаковщиках, которые применяются для защиты легального ПО от реверса. Злоумышленники, ко-нечно, тоже не против усложнить разбор «начинки» своего поделья, но так как для них это не несет прямого ущерба (в отличие от софта, для которого можно разобрать, например, генерацию кода активации), то и много вре-мени на это не тратится. Вирусописатели в первую очередь думают о мо-нетизации и защите детектирования своих продуктов, поэтому акценты при написании защитных механизмов несколько смещены в сторону обфу-скации и прочего.

В , «Л К » : , -

А М , ESET :

Антиотладочные приемы встречаются практически в любой вредоносной программе. И если раньше разработчики вредоносных программ стреми-лись главным образом усложнить антивирусным компаниям анализ и тем самым замедлить выход вакцины, то на сегодняшний день засилье раз-ношерстных троянцев не требует зачастую досконального изучения вре-доносной программы для добавления ее в антивирусные базы. И сегодня основная цель разработчиков вредоносных программ состоит уже не в про-тивостоянии с аналитиком, а в противодействии различным авторизиро-ванным системам для анализа новых образцов вредоносных программ, так называемым песочницам. Да и сами антиотладочные трюки ведут себя по-рой не очень стабильно на различных ОС или зависят от конкретной версии сервис-пака или ядра. Поэтому в современных вредоносных программах мы чаще встречаем что-нибудь интересное с точки зрения обнаружения вы-полнения на виртуальных машинах, нежели хитрые антиотладочные трюки.

Реакция разных отладчиков на исключения

VS 2008 SP1 OllyDbg 1.10 OllyDbg 2.01 Immunity Debugger 1.85 WinDbg 6.01 Nanomite 0.1 Ida Pro 5.0

Int 3 (0xCC) + +

Int 3 (0xCD, 0x03)

DebugBreak +

IceBreakPoint (0xF1 +

Int 2D + + + +

Int 2C + +

RaiseException (STATUS_BREAKPOINT) + + + + +

RaiseException (STATUS_SINGLE_STEP) +

RaiseException (DBG_PRINTEXCEPTION_C) + + + + + + +

RaiseException (DBG_RIPEXCEPTION) + + + + + +

RaiseException (DBG_CONTROL_C) +

Trap Flag + +

ХАКЕР 10 /177/ 2013Кодинг96