Доброго здравия форумчанам. Думаю, сразу можно позвать Indy_, речь всё о том же. Чтобы отловить все апк, перехватываю KiUserApcDispatcher, но при попытке вызвать оригинал, обламываюсь на Access Violation в недрах KiUserCallForwarder. Пробовал прототип с hex.pp.ua, пробовал такой: Код (C): VOID NTAPI KiUserApcDispatcher( PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2, CONTEXT Context // Целиком в стеке ); И прототип из WRK: Код (C): VOID NTAPI KiUserApcDispatcher( PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2, PVOID NormalRoutine ); И на каждом прототипе при вызове оригинала получаю AV в KiUserCallForwarder на jmp rax. Win10 x64, использую MinHook. Как, всё-таки, перехватить его правильно?
А что мешает открыть IDAPro да и посмотреть как и че там Спойлер: асм Код (ASM): public KiUserApcDispatcher .text:00000001800A39E0 KiUserApcDispatcher proc near ; CODE XREF: KiUserApcDispatcher+3Aj .text:00000001800A39E0 ; DATA XREF: .rdata:00000001801165E8o ... .text:00000001800A39E0 .text:00000001800A39E0 arg_0 = qword ptr 8 .text:00000001800A39E0 arg_8 = qword ptr 10h .text:00000001800A39E0 arg_10 = qword ptr 18h .text:00000001800A39E0 .text:00000001800A39E0 mov rcx, [rsp+arg_10] .text:00000001800A39E5 mov rax, rcx .text:00000001800A39E8 mov r9, rsp .text:00000001800A39EB sar rcx, 2 .text:00000001800A39EF mov rdx, [rsp+arg_0] .text:00000001800A39F4 neg rcx .text:00000001800A39F7 mov r8, [rsp+arg_8] .text:00000001800A39FC shld rcx, rcx, 20h .text:00000001800A3A01 test ecx, ecx .text:00000001800A3A03 jz short loc_1800A3A35 .text:00000001800A3A05 mov rcx, [rsp+0] .text:00000001800A3A09 call KiUserCallForwarder .text:00000001800A3A0E .text:00000001800A3A0E loc_1800A3A0E: ; CODE XREF: KiUserApcDispatcher+65j .text:00000001800A3A0E mov rcx, rsp .text:00000001800A3A11 mov dl, 1 .text:00000001800A3A13 call ZwContinue .text:00000001800A3A18 test eax, eax .text:00000001800A3A1A jz short KiUserApcDispatcher .text:00000001800A3A1C cmp eax, 0C000060Ah .text:00000001800A3A21 jnz short loc_1800A3A2A .text:00000001800A3A23 mov ecx, 30h .text:00000001800A3A28 int 29h ; Win8: RtlFailFast(ecx) .text:00000001800A3A2A ; --------------------------------------------------------------------------- .text:00000001800A3A2A .text:00000001800A3A2A loc_1800A3A2A: ; CODE XREF: KiUserApcDispatcher+41j .text:00000001800A3A2A mov esi, eax .text:00000001800A3A2C .text:00000001800A3A2C loc_1800A3A2C: ; CODE XREF: KiUserApcDispatcher+53j .text:00000001800A3A2C ; KiUserApcDispatcher+6Ej .text:00000001800A3A2C mov ecx, esi .text:00000001800A3A2E call RtlRaiseStatus .text:00000001800A3A33 jmp short loc_1800A3A2C .text:00000001800A3A35 ; --------------------------------------------------------------------------- .text:00000001800A3A35 .text:00000001800A3A35 loc_1800A3A35: ; CODE XREF: KiUserApcDispatcher+23j .text:00000001800A3A35 mov eax, [rsp+0] .text:00000001800A3A38 or rcx, rax .text:00000001800A3A3B mov rax, cs:Wow64ApcRoutine .text:00000001800A3A42 test rax, rax .text:00000001800A3A45 jz short loc_1800A3A0E .text:00000001800A3A47 call rax ; Wow64ApcRoutine .text:00000001800A3A49 mov esi, 0C000000Dh .text:00000001800A3A4E jmp short loc_1800A3A2C .text:00000001800A3A4E KiUserApcDispatcher endp Спойлер: псевдо-си Код (C++): void __noreturn KiUserApcDispatcher() { __int64 v0; // rdx@1 int v1; // eax@3 signed int i; // esi@6 void *retaddr; // [sp+0h] [bp+0h]@2 __int64 v4; // [sp+8h] [bp+8h]@0 __int64 v5; // [sp+10h] [bp+10h]@0 signed __int64 v6; // [sp+18h] [bp+18h]@0 while ( 1 ) { v0 = v4; if ( (unsigned __int64)-(v6 >> 2) >> 0x20 ) { KiUserCallForwarder(retaddr, v4, v5, &retaddr); } else if ( Wow64ApcRoutine ) { Wow64ApcRoutine( (unsigned int)retaddr | 0xFFFFFFFF00000000i64 * (v6 >> 2) | ((unsigned __int64)-(v6 >> 2) >> 0x20), v4, v5, &retaddr); i = 0xC000000D; goto LABEL_7; } LOBYTE(v0) = 1; v1 = ZwContinue(&retaddr, v0); if ( v1 ) { if ( v1 == 0xC000060A ) __fastfail(0x30u); for ( i = v1; ; RtlRaiseStatus((unsigned int)i) ) LABEL_7: ; } } }
HoShiMin, неправильно ставишь хук, иных вариантов нет (вряд ли ниже rsp лежат данные, которые ты затираешь в обработчике хука. наверняка выравнивание стека сбилось). Попробуй обойтись без либы - использовать банальный сплайсинг. Обработчик будет примерно таким: Код (C): void __declspec(naked) wrapper_Render() { __asm { pushad pushfd call [handler_Render] popfd popad push ebp //see disasm lea ebp, [esp - 0x74] jmp [retAddr_Render] } } Тк пишешь под x64 - необходимо сохранять регистры в соответствии с соглашениями (киньте линк, где-то тут на форуме пдф валялась) // http://agner.org/optimize/calling_conventions.pdf
У тс задача очень долгое время длится. Так как это не простая задача. Это локальное решение части общей задачи по анти инжектам. HoShiMin, Я дал вам общее решение, которое покрывает данное частное. Вы ведь понимаете что установка протект-фильтра в памяти юм не возможна, обсуждали ведь. А сам вопрос - что то крэшит, так откройте доки/маны/сурки и гляньте, я не вижу даже вопроса. Как описать крэш, его причины и прочее примитивное - думаю как то глупо и не уместно мой ник упомянать в таких вопросах. Эта примитивная рутина по разбору ошибок.
Сколько ни пытаюсь нормально перехватить, не получается. Исходя из того, что передаётся в KiUserCallForwarder: RAX = [RSP + 18h] - адрес прыжка на APC-процедуру в KiUserCallForwarder RCX = [RSP] -??? RDX = [RSP + 8] - ??? R8 = [RSP + 10h] - ??? R9 = RSP - указатель на CONTEXT, который дальше пойдёт в NtContinue Написал "сырой" обработчик с мостиком в сишный код: Код (ASM): PUBLIC KiUserApcHandler .CODE EXTRN ApcHandler: PROC EXTRN OriginalKiUserApcDispatcher: PROC KiUserApcHandler PROC int 3h push rax push rcx mov rcx, rsp add rcx, 16 call ApcHandler test rax, rax pop rcx pop rax jz Exit call OriginalKiUserApcDispatcher Exit: ret KiUserApcHandler ENDP END Обработчик ApcHandler: Код (C): BOOL FASTCALL ApcHandler(PCONTEXT Context) { // ... Смотрим, что лежит в Context ... return TRUE; } В Context лежит мусор. Что же, всё-таки, лежит в стеке и по каким смещениям? MinHook сплайсит цепочками jmp'ов, регистры не меняет, стек не двигает. Indy_, визор - техника фундаментальная, не спорю (с рк-атакой на MmSecureVirtualMemory для анклавов ознакомился). Но из общих соображений, поставив фильтры на память, загрузку модулей и потоки, отфильтровал уже львиную долю возможных инжектов, включая ядерные. И более того, как побочный эффект, для отдельно взятых процессов можно детектить даже подгрузку сторонних модулей через подмену импортов у доверенных библиотек (или самих библиотек), от чего не защитит визор. И всё же, вопрос скорее не об исправлении ошибок, а о том, как правильно перехватить нестандартную функцию, у которой все аргументы, несмотря на FASTCALL, лежат в стеке.
Быстрофикс. Не знаю, чей CONTEXT лежит в стеке, но пусть он и наполовину мусорный (почему?), но CS, SS и EFlags выглядят как и положено, а в P1Home (первое поле в CONTEXT) лежит адрес моей апк. Похоже на то, что я получил что хотел, но насколько это правильно? Действительно ли на х64 при входе в KiUserApcDispatcher вершина стека указывает на CONTEXT, и первое же поле (для адреса возврата, если сделать ret?) - адрес апк?
HoShiMin, > для отдельно взятых процессов можно детектить даже подгрузку сторонних модулей через подмену импортов у доверенных библиотек (или самих библиотек), от чего не защитит визор. Это уже не задачи визора, он даст сервисные события, а их нужно соотвествующим образом обрабатывать. Вопрос проверки модулей ранее подробно рассматривался, это не решается в общем случае. Как узнать что модуль системный ? Делалось через запрос к службе sfc. Даже если модуль не системный, то он может быть каким то расширением, например ав. Или что то загружается в режиме совместимости.. Видимо нужно отловить событие инжекта, а его полезная нагрузка в случае защиты значения не имеет. > Похоже на то, что я получил что хотел, но насколько это правильно? Действительно ли на х64 при входе в KiUserApcDispatcher вершина стека указывает на CONTEXT Нужно изучить этот интерфейс, тоесть посмотреть какие и как параметры передаются из км, как они обрабатываются в юм. А главное сравнить как интерфейс меняется в версиях, может ли он меняться.
Апну тему, все же смежный вопрос. Почти А NtCreateDebugObject а Вин10 все еще присутствует? Всмысле никаких изменений по поводу этой функи небыло? И аксесс маска DEBUG_OBJECT_TERMINATE все еще работает? Для выполнения только дебаг привилегия нужна?