Нашел у себя в закромах интересный способ обхода перехвата диспетчера 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 любого значения и чтение его, прочитанное значение всегда будет нулем в виртуалках.