Привет всем! Необходимо перехватить экспорты ядра. Способ перехвата - изменения начального кода. Способ известный - скопировать начальный код, записать вместо него jmp на функцию обработчик, после которого идет сохранненная часть функции и jmp на оставшуюся часть кода. Исследовав код ядра, выяснил, что часть функций (около 20 из 1500 для Win2k, WinXP, Win2003) имеют команды относительного перехода в самом начале. Причем эти команды как ближнего так и дальнего перехода. Проблемма вся в том, что необходимо пересчитывать эти адреса переходов, относительно нового адреса расположения кода. Есть дизассемблер, определяющий опкоды инструкций. Вопрос: 1. Как однозначно определить по опкоду, что это команда перехода ? Смотрел таблицу Jmp - в ней опкоды 0x70..0x7F и 0x0F:0x80..0x0F:0x8F. Но этого недостаточно - к примеру jmp eax имеет 0xFFE0 2. Большинство команд (jo, je, jle, jb, jpe, jnb, jl, jns, js) имеют длинну 2 байта. Это значит надо раздвигать код и вставлять команду дальнего перехода или как-то можно сделать подругому? 3. Если команда jmp - то просто изменить адрес ? В общем если кто занималься данной проблеммой - просьба помочь. Спасибо
Чем больше раз ты изменяешь код ф-ии, которая по жизни - для многопоточного использования, тем больше ты подходишь к BSOD. Уменьшить вероятность действительно можно, записывая самое минимальное кол-во байт в код ф-ии. Для этого наиболее надежный способ - это написать малюсенький дизасм,и от начала ф-ии исоедовать комманды, которые затруться на JMP DWORD PTR (6 байт), главное, чтобы операнды затерльсь целиком. Так например, если у тебя всего 6 байт, а в начале ф-ии стоят push ebp mov ebp,esp push 00 push dword ptr [ebp+0x18] , то это 8 байт, а значит, ты должен записать 6 байт от JMP + 2 раза по NOP, например. Потом в конце ф-ии префикса, выпольнить затертые комманды, и проделать JMP на последующий код оригинальной ф-ии Впринципе, при таком раскладе, можно и забить на Твой пункт №2. Хотя, если я распознаю что там тоже стоит в начале JMP DWORD PTR... , то можно заменить 4-х байтовый адрес сразу =за 1 операцию, а не писать 6 байт... Необязательно выбрирать JMP DWORD PTR. Смотреть взависимость от ситуации ЗЫ: Действительно, ничё нового, просто некоторые тонкости. Именно этим методом я и пользуюсь. ЗЗЫ: Тоже мечтаю услышать другие менения.
По мойму проще использовать вместо jmp intXX Добавить в IDT свой обработчик(например на 20 прерывание) и зтавить вызов int 20h в начале всех отлавливаемых функций(не забьть только запомнить 2 байиа, которые заменили). Дальше в самом обработчике делаем следующее(fasm): pop dword[adres] pushad ... делаем что хотим не забываем востановить байтики ... sub dword[adres],2 popad push dword[adres]
>>Добавить в IDT свой обработчик(например на 20 прерывание) Его может кто-нибудь затереть >>делаем что хотим не забываем востановить байтики Это очень не хорошо - нужно гарантировать, что в этот момент не произойдет вызова функции (плохо в многопоточных приложениях) >>Уменьшить вероятность действительно можно, записывая самое минимальное кол-во байт записывается всего 5 байт и всего один раз (возвращение оригинальных байт не требуется) >>главное, чтобы операнды затерльсь целиком не обязательно Пусть у меня целое число инструкций > 5 байт (чтоб влез jmp) = 12 байт. Я затираю только первые 5. Хотя сохраняю все 12. После этих 12 (где-то в другом месте) я ставлю jmp на адрес не после 5 байт, а после 12 байт. А мусор в этом случае (оставшиеся 7 байт) не учитывается
"возвращение оригинальных байт не требуется" - при отладке приходится очччень много раз включать/выключать хук. Так что без возврата байт никак. "А мусор в этом случае (оставшиеся 7 байт) не учитывается" - чем боольше байт Ты испортил и/или затер, тем больше байт надо возвращать. Чем больше возвращать, тем больше вороятность, что ты во время записи попадешь на место, где планировщик "усыпил" каку-то нить. Всвязи с этим, сремиться к минимуму надо. ЗЫ. Мож о своем методе скажешь ? - иль он тот же ЗЗЫю: вообще, корошая прога, (как я считаю) должна время - от времени проверять, не убрал ли кто хук... если убрал, то поставить заново
>>Мож о своем методе скажешь ? Ничего хитрого нет. Есть функция вывода отчета (функция-перехватчик) BYTE Hook [] = { 0x9C, // pushfd 0x60, // pushad 0x68, 0x00, 0x00, 0x00, 0x00, // push Address 0xE8, 0x00, 0x00, 0x00, 0x00, // call OutLogData 0x61, // popad 0x9D, // popfd }; Адреса заполняются в процессе инициализации. Сразу за ней следуют сохранненые байты оригинальной функции (функция-трамплин), после которой стоин jmp на оставшуюся часть кода. Замена происходит один раз. Замена первых байт функции не требуется. Если происходит вызов экспорта, то управление передается на Hook. Он выполняется, после выполняются первые команды оригинальной функции (здесь и может возникнуть проблемма, если есть команды относительного перехода), а после jmp передает управление оставшейся части кода. Для того, чтобы из драйвера можно было использовать функции ядра и не ловить самого себя, правиться собственная таблица импортов (не на начало функции, а на начало функции-трамплина). Вроде и все. Тоесть замена один раз только при установки перехватчиков и возвращение оригинальных байт, только при снятии.
Вот моё (описывать структуры не буду : банально): pushfd inc call_hook_counter pushad ;-------- ;чё нить длаем и если от ф-ии нужен результат, то флаг need==true ;-------- dec call_hook_counter cmp need_unload,TRUE jne some_exit cmp call_hook_counter,0 jne some_exit ;----------- ;восстанавливаем хук ;----------- kesetevent(...) some_exit: cmp need,TRUE jne exit_orig exit_to_suffix: push param1 push param2 push param... push addr_of_suffix_sub ;---выполняем байтики затёртые jmp orig_code_address exit_orig: ;---выполняем байтики затёртые jmp orig_code_address для того, чтобы выключить хук (дествительно, легче не выключать ))) ) ставится need_unload==TRUE kewaitformutextoblect. ждём.... и всё. ЗюЫю Оччень грубо. Не ржать
>>Его может кто-нибудь затереть ...или винт форматнуть с тем же успехом отлаживаемая прога может проверять(или сходу востанавливать) первые n байтов перед вызовом функции >>Это очень не хорошо - нужно гарантировать, что в этот >>момент не произойдет вызова функции (плохо в >>могопоточных приложениях) Ни кто не мешает запретить прерывания в обработчике. У меня всё нормально работало.
>>Ни кто не мешает запретить прерывания в обработчике. А если многопроцессорная система ? >>У меня всё нормально работало. И ты обошел проблеммы, с которыми я столкнулся, или у тебя другой алгоритм ?
>>А если многопроцессорная система ? А смыс так далеко заглядывать?(если учитывать все возможные варианты, то и жизни на написание проги не хватит.) >>И ты обошел проблеммы, с которыми я столкнулся, или у тебя другой алгоритм ? Я использовал в качестве точки остановки int23h? прописовал эту команду в начало всех отлавливаемых функций из библиотек и exe-ников(правил , правдо, в файле). Вешал в системе драйвер с обработчиком этого прерывания. И всё спакойно ловил. М не вот интересно, как ты правишь dll-ки типа user32 в памяти из драйвера?
>>А смыс так далеко заглядывать?(если учитывать все >>возможные варианты, то и жизни на написание проги не >>хватит.) Весь прикол в том, что это долно работать именно на многопроцессорных системах. Пока я этого не рассматридаю из-за того, что не решены более простые задачи >>Я использовал в качестве точки остановки int23h? >>прописовал эту команду в начало всех отлавливаемых >>функций из библиотек и exe-ников(правил , правдо, в >>файле). Для надежной работы тебе надо запрещать замену 23 прерывания. Ты это смог реализовать ? Или оставил как есть ? >>Вешал в системе драйвер с обработчиком этого >>прерывания. И всё спакойно ловил. Тоесть ты постоянно перезаписывал первый байт ? У меня была такая идея, но я почему-то отказался от нее. Хотя как вариант... может быть. Не мог бы ты об этом немного подробнее ? (или может у тебя есть ICQ - так быстрее общаться, если я конечно не напрягаю). >>Мне вот интересно, как ты правишь dll-ки типа user32 в >>памяти из драйвера? Смысл - статистика системных вызовов. Задача - перехват экспортов ядра, а что твориться в user-mode неважно. В конечном итоге все завязано на ядре - а если нет, то это неважно. Или я не прав ?
>>Тоесть ты постоянно перезаписывал первый байт ? Нет, у меня была чуть иная задача: нужно было подменять результаты некоторых функцию dll, так что управление возвращалось уже на последнюю команду.