Нашел у себя в закромах интересный способ обхода перехвата диспетчера 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 любого значения и чтение его, прочитанное значение всегда будет нулем в виртуалках.
Запуск пользовательского потока в обход перехвата диспетчера 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 любого значения и чтение его, прочитанное значение всегда будет нулем в виртуалках.
Если не изменяет память, то Clerk отписывался про данному способу детекта VMware на virustech (была тема про способы детекта)
ОтветитьУдалитьА где (и зачем) подобные извращения применимы на практике?
ОтветитьУдалитьНе, я не утверждаю что не применимы, просто из контекста совершенно непонятно зачём всё это.
Сr4sh
ОтветитьУдалитьСледует рассматривать это как интересную головоломку или разминку для мозга, не более того.
а клерка совсем забанили ? прикольный штрих был, таких больше не делают
ОтветитьУдалитьНа VirtualBox под lin можно патч накатить чтобы работало.
ОтветитьУдалитьНа XEN работает без проблем.