Перехват экспортов ядра

Тема в разделе "WASM.WIN32", создана пользователем zss, 17 окт 2004.

  1. zss

    zss New Member

    Публикаций:
    0
    Регистрация:
    11 июл 2004
    Сообщения:
    40
    Адрес:
    Чехов-2
    Привет всем!

    Необходимо перехватить экспорты ядра.

    Способ перехвата - изменения начального кода.



    Способ известный - скопировать начальный код,

    записать вместо него 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 - то просто изменить адрес ?



    В общем если кто занималься данной проблеммой - просьба

    помочь.



    Спасибо
     
  2. PavPS

    PavPS New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2004
    Сообщения:
    109
    Адрес:
    Russia
    Чем больше раз ты изменяешь код ф-ии, которая по жизни - для многопоточного использования, тем больше ты подходишь к 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. Смотреть взависимость от ситуации



    ЗЫ: Действительно, ничё нового, просто некоторые тонкости. Именно этим методом я и пользуюсь.

    ЗЗЫ: Тоже мечтаю услышать другие менения.
     
  3. ProgramMan

    ProgramMan New Member

    Публикаций:
    0
    Регистрация:
    13 янв 2004
    Сообщения:
    263
    По мойму проще использовать вместо jmp intXX

    Добавить в IDT свой обработчик(например на 20 прерывание) и зтавить вызов int 20h в начале всех отлавливаемых функций(не забьть только запомнить 2 байиа, которые заменили).

    Дальше в самом обработчике делаем следующее(fasm):

    pop dword[adres]

    pushad

    ...

    делаем что хотим

    не забываем востановить байтики

    ...

    sub dword[adres],2

    popad

    push dword[adres]
     
  4. zss

    zss New Member

    Публикаций:
    0
    Регистрация:
    11 июл 2004
    Сообщения:
    40
    Адрес:
    Чехов-2
    >>Добавить в IDT свой обработчик(например на 20 прерывание)



    Его может кто-нибудь затереть



    >>делаем что хотим не забываем востановить байтики



    Это очень не хорошо - нужно гарантировать, что в этот момент не произойдет вызова функции (плохо в многопоточных приложениях)



    >>Уменьшить вероятность действительно можно, записывая самое минимальное кол-во байт



    записывается всего 5 байт и всего один раз (возвращение оригинальных байт не требуется)



    >>главное, чтобы операнды затерльсь целиком

    не обязательно

    Пусть у меня целое число инструкций > 5 байт (чтоб влез jmp) = 12 байт. Я затираю только первые 5. Хотя сохраняю все 12. После этих 12 (где-то в другом месте) я ставлю jmp на адрес не после 5 байт, а после 12 байт. А мусор в этом случае (оставшиеся 7 байт) не учитывается
     
  5. PavPS

    PavPS New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2004
    Сообщения:
    109
    Адрес:
    Russia
    "возвращение оригинальных байт не требуется" - при отладке приходится очччень много раз включать/выключать хук. Так что без возврата байт никак.



    "А мусор в этом случае (оставшиеся 7 байт) не учитывается" - чем боольше байт Ты испортил и/или затер, тем больше байт надо возвращать. Чем больше возвращать, тем больше вороятность, что ты во время записи попадешь на место, где планировщик "усыпил" каку-то нить. Всвязи с этим, сремиться к минимуму надо.



    ЗЫ. Мож о своем методе скажешь ? - иль он тот же :)



    ЗЗЫю: вообще, корошая прога, (как я считаю) должна время - от времени проверять, не убрал ли кто хук... если убрал, то поставить заново :)
     
  6. zss

    zss New Member

    Публикаций:
    0
    Регистрация:
    11 июл 2004
    Сообщения:
    40
    Адрес:
    Чехов-2
    >>Мож о своем методе скажешь ?



    Ничего хитрого нет. Есть функция вывода отчета (функция-перехватчик)

    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 передает управление оставшейся части кода.



    Для того, чтобы из драйвера можно было использовать функции ядра и не ловить самого себя, правиться собственная таблица импортов (не на начало функции, а на начало функции-трамплина).



    Вроде и все. Тоесть замена один раз только при установки перехватчиков и возвращение оригинальных байт, только при снятии.
     
  7. PavPS

    PavPS New Member

    Публикаций:
    0
    Регистрация:
    24 фев 2004
    Сообщения:
    109
    Адрес:
    Russia
    Вот моё (описывать структуры не буду : банально):

    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.

    ждём....

    и всё.



    ЗюЫю Оччень грубо. Не ржать :)
     
  8. ProgramMan

    ProgramMan New Member

    Публикаций:
    0
    Регистрация:
    13 янв 2004
    Сообщения:
    263
    >>Его может кто-нибудь затереть

    ...или винт форматнуть :) с тем же успехом отлаживаемая прога может проверять(или сходу востанавливать) первые n байтов перед вызовом функции

    >>Это очень не хорошо - нужно гарантировать, что в этот

    >>момент не произойдет вызова функции (плохо в

    >>могопоточных приложениях)

    Ни кто не мешает запретить прерывания в обработчике.

    У меня всё нормально работало.
     
  9. zss

    zss New Member

    Публикаций:
    0
    Регистрация:
    11 июл 2004
    Сообщения:
    40
    Адрес:
    Чехов-2
    >>Ни кто не мешает запретить прерывания в обработчике.



    А если многопроцессорная система ?



    >>У меня всё нормально работало.



    И ты обошел проблеммы, с которыми я столкнулся, или у тебя другой алгоритм ?
     
  10. ProgramMan

    ProgramMan New Member

    Публикаций:
    0
    Регистрация:
    13 янв 2004
    Сообщения:
    263
    >>А если многопроцессорная система ?

    А смыс так далеко заглядывать?(если учитывать все возможные варианты, то и жизни на написание проги не хватит.)

    >>И ты обошел проблеммы, с которыми я столкнулся, или у тебя другой алгоритм ?

    Я использовал в качестве точки остановки int23h? прописовал эту команду в начало всех отлавливаемых функций из библиотек и exe-ников(правил , правдо, в файле). Вешал в системе драйвер с обработчиком этого прерывания.

    И всё спакойно ловил.

    М не вот интересно, как ты правишь dll-ки типа user32 в памяти из драйвера?
     
  11. zss

    zss New Member

    Публикаций:
    0
    Регистрация:
    11 июл 2004
    Сообщения:
    40
    Адрес:
    Чехов-2
    >>А смыс так далеко заглядывать?(если учитывать все >>возможные варианты, то и жизни на написание проги не >>хватит.)



    Весь прикол в том, что это долно работать именно на многопроцессорных системах. Пока я этого не рассматридаю из-за того, что не решены более простые задачи :)



    >>Я использовал в качестве точки остановки int23h? >>прописовал эту команду в начало всех отлавливаемых >>функций из библиотек и exe-ников(правил , правдо, в >>файле).



    Для надежной работы тебе надо запрещать замену 23 прерывания. Ты это смог реализовать ? Или оставил как есть ?



    >>Вешал в системе драйвер с обработчиком этого >>прерывания. И всё спакойно ловил.



    Тоесть ты постоянно перезаписывал первый байт ?

    У меня была такая идея, но я почему-то отказался от нее. Хотя как вариант... может быть. Не мог бы ты об этом немного подробнее ? (или может у тебя есть ICQ - так быстрее общаться, если я конечно не напрягаю).



    >>Мне вот интересно, как ты правишь dll-ки типа user32 в >>памяти из драйвера?



    Смысл - статистика системных вызовов. Задача - перехват экспортов ядра, а что твориться в user-mode неважно. В конечном итоге все завязано на ядре - а если нет, то это неважно. Или я не прав ?
     
  12. ProgramMan

    ProgramMan New Member

    Публикаций:
    0
    Регистрация:
    13 янв 2004
    Сообщения:
    263
    >>Тоесть ты постоянно перезаписывал первый байт ?

    Нет, у меня была чуть иная задача: нужно было подменять результаты некоторых функцию dll, так что управление возвращалось уже на последнюю команду.