суббота, 21 мая 2011 г.

Анти-обход перехвата APC dispatcher'a

Нашел у себя в закромах интересный способ обхода перехвата диспетчера APC, который придумал Clerk:

Запуск пользовательского потока в обход перехвата диспетчера APC(R3).
 o Восстанавливаем RtlpCalloutEntryList, для исключения глобальных VEH.
 o Создаём удалённый поток.
 o Формируем SEH-фрейм.
 o Взводим TF в контексте.
Поток стартует с взведённым TF. После исполнения первой инструкции диспетчера апк (KiUserApcDispatcher()) генерируется трассировочное исключение. Управление получает диспетчер исключений (KiUserExceptionDispatcher()). Векторные обработчики не вызываются (RtlCallVectoredExceptionHandlers()). Вызывается установленный SEH. Если диспетчер APC захвачен сплайсингом, останов генерируется после исполнения джампа. Если диспетчер APC перенаправлен на диспетчер исключений, посредством установки точки останова, прежде будет вызван SEH(если хэндлер установлен как VEH).

А я затем задумался, можно ли перехватить диспетчер APC в ring3, чтобы даже при установленном TF можно было сплайсить себе наздоровье?

Казалось бы - нет, нереально. Даже учитывая тот факт, что исключение будет сгенерировано лишь на второй инструкции, это ничего не дает.
Да, можно вместо первой записать short jump, но после прыжка опять таки окажемся в SEH.
Сбросить TF флаг наверное также не получится, т.к. lea edi, [esp+arg_C] это всего 4 байта. См. код диспатчера:

.text:7C90EAC0 _KiUserApcDispatcher@20 proc near
.text:7C90EAC0
.text:7C90EAC0 arg_C           = byte ptr  10h
.text:7C90EAC0
.text:7C90EAC0                 lea     edi, [esp+arg_C]
.text:7C90EAC4                 pop     eax
.text:7C90EAC5                 call    eax
.text:7C90EAC7                 push    1
.text:7C90EAC9                 push    edi
.text:7C90EACA                 call    _ZwContinue@8   ; ZwContinue(x,x)
.text:7C90EACF                 nop

Однако, мне пришел в голову один способ обхода, который требует изменения даже не байтов, а всего 1го бита.

Речь идет о фиче Single-Stepping on Branches, Exceptions, and Interrupts.

Устанавливаем BTF флаг (Single-step on branches) в регистре IA32_DEBUGCTL MSR.
Intel пишет, что исключение будет сгенерировано процессором только при branch, services an interrupt, or generates an exception, то есть мы отключаем по сути TF флаг до первого бранча. А это дает возможность спокойно перехватывать диспетчер APC сплайсингом, но через call myFunc, а не через джамп. А уже в myFunc можно делать все что угодно, включая сброс TF флага.

p.s. Кстати как дополнение, всплыл способ обнаружения( до этого мне не известный ) VmWare и VirtualBox - они не виртуализируют IA32_DEBUGCTL MSR, т.е. детект выглядит как запись в IA32_DEBUGCTL MSR любого значения и чтение его, прочитанное значение всегда будет нулем в виртуалках.

воскресенье, 15 мая 2011 г.

Про статический анализ бинарного кода

Некоторые люди не понимают, что обычным статическим анализом нельзя обойтись, чтобы описать весь control-flow какого-либо модуля.
Основные их аргументы - если ф-ция есть, то обязательно будет ссылка на него.

Пример:

открываем notepad.exe в IDA, находим кусок высокоуровнего seh'а(scopetable entry):

.text:01001898 stru_1001898    _msEH <0FFFFFFFFh, offset loc_100752A, offset loc_100753E>

Смотрим сами обработчики - ссылок из кода на них нет, но(!) есть data xref:

.text:01007528                 jmp     short loc_1007557
.text:0100752A
.text:0100752A loc_100752A:                            ; DATA XREF: .text:stru_1001898
.text:0100752A                 mov     eax, [ebp+ms_exc.exc_ptr]
.text:0100752D                 mov     ecx, [eax]
.text:0100752F                 mov     ecx, [ecx]
.text:01007531                 mov     [ebp+var_28], ecx
.text:01007534                 push    eax
.text:01007535                 push    ecx
.text:01007536                 call    _XcptFilter
.text:0100753B                 pop     ecx
.text:0100753C                 pop     ecx
.text:0100753D                 retn
.text:0100753E
.text:0100753E loc_100753E:                            ; DATA XREF: .text:stru_1001898
.text:0100753E                 mov     esp, [ebp+ms_exc.old_esp]
.text:01007541                 mov     esi, [ebp+var_28]
.text:01007544                 cmp     [ebp+var_1C], 0
.text:01007548                 jnz     short loc_1007551
.text:0100754A                 push    esi             ; int
.text:0100754B                 call    ds:_exit
.text:01007551 ; ---------------------------------------------------------------------------

IDA прекрасно все находит и резолвит, т.к. есть:

.text:0100739D                 push    70h
.text:0100739F                 push    offset stru_1001898
.text:010073A4                 call    __SEH_prolog

А теперь идем в тот же notepad.exe, делаем поиск по ф-ции GetSystemTimeAsFileTime, по xref прыгаем на место вызова её и видим:

.text:010070CC sub_10070B1     endp
.text:010070CC
.text:010070CC ; ---------------------------------------------------------------------------
.text:010070CF                 db 5 dup(0CCh)
.text:010070D4 ; ---------------------------------------------------------------------------
.text:010070D4                 mov     edi, edi
.text:010070D6                 push    ebp
.text:010070D7                 mov     ebp, esp
.text:010070D9                 sub     esp, 10h
.text:010070DC                 mov     eax, dword_1009604
.text:010070E1                 test    eax, eax
.text:010070E3                 jz      short loc_10070EC
.text:010070E5                 cmp     eax, 0BB40h
.text:010070EA                 jnz     short loc_1007139
.text:010070EC
.text:010070EC loc_10070EC:                            ; CODE XREF: .text:010070E3
.text:010070EC                 push    esi
.text:010070ED                 lea     eax, [ebp-8]
.text:010070F0                 push    eax
.text:010070F1                 call    ds:GetSystemTimeAsFileTime
.text:010070F7                 mov     esi, [ebp-4]

Опачки, ф-ция не распозналась IDA'ой. Превратим принудительно её в ф-цию нажав p, после чего видно, что на ф-цию нет перекрестных ссылок, её никто не вызывает из данного модуля. То есть в графе, если им описать данный модуль по xref'aм, эта ф-ция будет отсутствовать.

На логичный вопрос - зачем она тогда нужна в коде, если ее никто не вызывает? Ответ прост - раз есть, то скорее всего кто-то вызывает( или в микрософте любят компилировать с выключенной оптимизацией =] ), и этот кто-то - просто другой модуль ( CRT ) и делается это динамически:

notepad зовет _initterm, который находится в рантаймовской либе msvcrt.dll:

msvcrt!_initterm+0xb:
77c39d72 8b06            mov     eax,dword ptr [esi]
77c39d74 85c0            test    eax,eax
77c39d76 7402            je      msvcrt!_initterm+0x13 (77c39d7a)
77c39d78 ffd0            call    eax <= вызов нашей ф-ции (0x010070D4)

Можно конечно эвристически определить, что по такому-то адресу находится код, на который никто не ссылается, собс-но так IDA и поступает, но, если представить на секунду, что код может быть в принципе каким угодно ( обфусцированным, зашифрованным и т.д.), то эвристика не поможет, а ссылок нa него нет - итог: fail.

В принципе это все достаточно очевидно, но видимо не для всех(надеюсь данный пример поможет).

среда, 11 мая 2011 г.

Еще немного про Image Loader

Стало любопытно, какой процент от всех функций ntdll.dll составляет код загрузчика pe файлов, в результате появилась такая статистика( xp sp2 ):

Общее число ф-ций ntdll.dll(включая Zw/Nt) - 2138
Число Zw/Nt ф-ций - 283

Общее число ф-ций используемых лоадером(включая Zw/Nt) - 602
Число Zw/Nt ф-ций используемых лоадером - 54

То есть лоадер в своей работе использует чуть меньше трети всех ф-ций ntdll.dll.

Список Nt/Zw ф-ций используемых лоадером( xp sp2/sp3 ):

NtTestAlert
ZwDelayExecution
NtAllocateVirtualMemory
NtQueryPerformanceCounter
NtSetEventBoostPriority
NtReleaseKeyedEvent
NtCreateEvent
NtClose
NtFreeVirtualMemory
NtQueryVirtualMemory
NtQuerySystemTime
NtQuerySystemInformation
ZwQueryInformationProcess
ZwProtectVirtualMemory
NtReleaseSemaphore
NtOpenKeyedEvent
ZwQueryInformationFile
ZwQueryEaFile
NtCreateFile
ZwSetInformationFile
NtFsControlFile
NtQueryVolumeInformationFile
NtOpenFile
ZwQueryAttributesFile
ZwOpenDirectoryObject
ZwDeviceIoControlFile
ZwWaitForSingleObject
ZwRaiseException
NtQueryDebugFilterState
NtWaitForKeyedEvent
NtOpenSymbolicLinkObject
ZwQuerySymbolicLinkObject
ZwSetInformationProcess
NtUnmapViewOfSection
NtCreateSection
ZwMapViewOfSection
ZwAreMappedFilesTheSame
ZwRaiseHardError
NtOpenSection
ZwQuerySection
ZwOpenProcessToken
ZwQueryInformationToken
ZwOpenKey
ZwQueryValueKey
NtOpenThreadTokenEx
ZwOpenProcessTokenEx
ZwTerminateThread
ZwTerminateProcess
ZwFlushInstructionCache
NtQueryInstallUILanguage
NtQueryDefaultLocale
ZwQueryDefaultUILanguage
ZwSetValueKey
ZwCreateKey


В последующих версиях windows число сервисных ф-ций используемых лоадером примерно остается на одном уровне (vista sp0: 50, win7 sp0: 53)

Пример цепочки вызовов приводящей к ZwDeviceIoControlFile:
PE Loader => RtlAllocateHeap => AcquireBufferLocation => GetPidInfo => GetLoggerInfo => WmipSendWmiKMRequest => WmipDeviceIoControl => ZwDeviceIoControlFile