Код (Text): #include "stdafx.h" typedef //NTSYSAPI NTSTATUS (NTAPI *Zwf) ( OUT PHANDLE , IN ACCESS_MASK , IN POBJECT_ATTRIBUTES , OUT PIO_STATUS_BLOCK , IN PLARGE_INTEGER , IN ULONG , IN ULONG , IN ULONG , IN ULONG , IN PVOID , IN ULONG ); char testrstr[] = "777"; //NTSYSAPI NTSTATUS NTAPI my_f(OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength) { //[b]puts( testrstr);[/b] __asm mov eax, -1; __asm ret 02ch ; return 0; } int _tmain(int argc, _TCHAR* argv[]) { HMODULE hm = GetModuleHandle(_T("ntdll.dll")); Zwf _Zwf = (Zwf)(GetProcAddress( hm, "ZwCreateFile")); DWORD lpflOldProtect; VirtualProtect(_Zwf, 5, PAGE_EXECUTE_READWRITE, &lpflOldProtect); __asm { mov eax, _Zwf mov byte ptr [eax], 0e9h mov edx, my_f sub edx, eax mov dword ptr [eax+1], edx } CreateFile(_T("new.txt"), 0, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); getch(); return 0; } перехват выполняется и отрабатывает нормально, но если раскомментировать puts( testrstr); , то вылазиет это Unhandled exception at 0x0012ff5c in nat2.exe: 0xC0000005: Access violation writing location 0x7817a99b. наверно какая-то глупая ошибка у меня
не правильно расчитываешь переход. должно получится Код (Text): db 0e9h dd куда-(откуда+5) а у тебя db 0e9h dd куда-откуда
Зачем выполнять сплайсинг заглушки(причём процессор может не поддерживать cmpxchg8b), тогда все потоки суспендить придётся, если можно атомарно(xchg) четыре байта - указатель на UsSystemCall заменить в инструкции mov edx,7FFE0300(для SP1 немного иначе вызов).
А что будет если во время cmpxchg8b какой нибудь поток выполнял начало заменяемых байт и был прерван? Что он будет выполнять после получения процессорного времени? Всмысле переключение контекста произошло во время того, как какой-нибудь поток выполнял заменяемый код, код заменился, управление потом возвращается тому потоку и он будет выполнять уже левые инструкшены или вобще не пойми что?
В ТС-вском случае, может вместо сплайсинга ZwCreateFile подредактировать IAT kernel32, чтобы вместо ZwCreateFile вызывался обработчик, а тот в свою очередь ZwCreateFile?
Это не возможно. Процессор не может быть прерван пока не закончит исполнять инструкцию. Для юзермода все ZwXX экспорты являются заглушками в ntdll для соответствующих сервисов. А в общем бесполезно хукать юзермодные стубы и KiFastSystemCallRet, ибо поток может заюзать сервис посредством прерывания.
CrystalIC Ну я невнятно выразился. Поток начал выполнять первые байты из тех, которые будут заменены. Тут произошло переключение контекста, поток остановлен посреди тех байт. Другой поток делает cmpxchg8b. Когда снова начнет выполнятся первый поток, он будет исполнять уже не те инструкции.
K10 Ты ошибыешся, если думаешь что процессор считает операнд из инструкции, переключится на другой поток, потом восстановит состояние и продолжит исполнять инструкцию хех. Он её всю исполнит сразу, только потом будет может быть прерван.
Ну это понятно, что лучше перехватывать все на уровне ядра. Часто бывает такая ситуация: Заранее известно, в каком приложении (приложениях) будет работать хукающий код. И если это приложение не юзает сервисы ядра путем прерываний и не вызывает native функции, а по честному использует kernel32.CreateFile, то имхо проще и безопаснее патчить IAT чтобы вместо соответствующих native api вызывались обработчики.
Ну исполнил он всю сразу первую инструкцию вместе с операндом. Собирается исполнить следующую, и тут поток прервался...
K10 А следующая нам не нужна.. Примеры в студию короче. В таком случае мы используем перехват KiFastSsystemCall/KiFastSystemCallRet, ибо если нужно перехватить несколько сервисов то незачем использовать сплайсинг множества функций, вызов/возврат любого стуба сходится в одной точке, а от туда и лучше всего выполнить обработку, в первом случае отфильтровать сервис по его номеру в регистре Eax, либо по адресу возврата в стеке.
Эхх, неохота сочинять пример... Что-то типа этого (поправьте если где ошибусь)... Берем первую попавшуюся функцию: kernel32.AddAtomA начало: Код (Text): MOV EDI, EDI ; 88FF - 2 байта PUSH EBP ; 55 - 1 байт ... и т.д. Собираемся записать джамп на обработчик: Код (Text): JMP ABCDEF00 ; E9 ABCDEF00 - 1 + 4 = 5 байт ... Какой нибудь поток выполняет начало AddAtomA: выполнил MOV EDI, EDI. Тут остановился. Когда он снова возобновится, то будет выполнять слудующую PUSH EBP. Но не тут то было - пока он стоял, другой поток переписал эти байты и теперь вместо Код (Text): PUSH EBP ; 55 находится кусок из аргумента джампа: Код (Text): CDEF00 и прочий мусор... Примерно так, в общих чертах
Ну это конечно. Вроде про стуб речь шла, а не про любую последовательность инструкций. В твоём случае мы записываем Jmp_Near_Handler на пять байт ниже функции и атомарно заменяем пролог на короткий переход туда(0xF9EB).
CrystalIC Опять же, запишем JMP, какой-то поток уже успел сделать MOV EDI, EDI; PUSH EBP, вместо короткого перехода, который еще не записан и будет сбитый стек. Либо поток остановлен на месте записываемого джампа. Вобщем, я так понимаю, проблема многопоточности в сплайсинге до сих пор не имеет решения... Даже если суспендить все потоки, нет гарантии, что какой-то из них не будет засуспенжен на заменяемом коде. Если только после суспенда проверять контексты всех потоков, но имхо геморно... А интересовало, чем cmpxchg8b лучше обычных MOV. К стате, на каких процах он поддерживается?
K10 Какой есчо сбитый стек; Ты не понимаешь вообще как планирование исполняется. Две инструкции на одну не заменяются, иначе конечно придётся перечислять потоки, получать контексты, проверять не указывает ли Eip на вторую мод. инструкцию.. В сплайсинге, где изменяемая инструкция имеет размер больший, либо равный инструкции перехода проблем нет. Тем что атомарно позволяет записать 8 байт. Если инструкция длиной больше 4 байт, то это единственный вариант атомарно изменить, 5 байт - опкод перехода(Jmp_Near_XXXX), остальные 3 - от оригинальной инструкции. Вобщем смотри в манах префикс Lock.
Сорри, что немного оффтоп, но вроде бы это не совсем верно для многопроцессорной системы, хотя я сам не уверен, может кто-нибудь поправит/подтвердит?