Системный шпионаж в *nix часть 1 Крис Касперски ака мыщъх Хакер, номер #087, стр. 087-134-4 Часть виртуальных страниц, принадлежащих процессу, может быть выгружена на диск, и тогда в файле /dev/mem ее не окажется. При хроническом недостатке оперативной памяти значительный процент адресного пространства процесса попадает на диск, и хотя совместно используемые библиотеки вытесняются в последнюю очередь, они все-таки вытесняются (особенно редко используемые функции). Это значит, что прежде чем ковыряться в /dev/mem, необходимо загрузить соответствующую функцию в память. А как это сделать? Например, просто ее вызывать. Вызов функции не гарантирует загрузки всех принадлежащих ей страниц, но нам этого и не надо! Достаточно, чтобы загрузилась первая страница, чтобы воткнуть в начало функции jump на свой thunk. Для нейтрализации побочных эффектов функцию обычно вызывают с инвалидными аргументами, чтобы она завершилась без выполнения, однако это грязный трюк, на который ведутся далеко не все функции. В частности, gets() упорно ожидает ввода с клавиатуры, даже если в качестве указателя ей передать нуль. Если память, принадлежащая функции, доступна на чтение (а в Linux/BSD она доступна), нам достаточно прочитать несколько байт от начала функции — это гарантированно загрузит принадлежащую ей страницу в физическую память. Собственно говоря, для поиска перехватываемой функции в /dev/mem нам все равно потребуется ее сигнатура, так что без чтения здесь не обойтись. Весь вопрос в том, как определить адрес функции? Существует, как минимум, два пути: получить указатель средствами языка Си или вызывать dlsym. В обоих случаях результаты будут различны, что и подтверждает программа get_addr.c, определяющая адрес функции gets (исходный код приложен на диске). Компилируем ("gcc get_addr.c -o get_addr -ldl") и запускаем полученный файл на выполнение (ключ -ldl подключает библиотеку dl, экспортирующую функции dlopen и dlsym). На мыщъхином компьютере результат выглядит так: Результат работы программы get_addr x = gets:08048364h 08048364h:FF 25 A8 98 04 08 68 08 00 00 00 E9 D0 FF FF FF ... base libc.so.6:400179E8h 400179E8h:00 C0 02 40 D8 79 01 40 30 B5 15 40 6C 66 01 40 ... glsym(,"gets"):4008CE60h 4008CE60h:55 89 E5 57 56 53 83 EC 2C 8B 75 08 E8 AC 4D FB ... Указатель на функцию gets, судя по адресу (08048364h), смотрит на секцию .plt, то есть находится во "владениях" текущего процесса. Первые байты функции равны FFh 25h A8h 98h 04h 08h, что соответствует команде JMP [080498A8h]. Выходит, это еще не сама функция, а только переходник к ней! Адрес 080498A8h хранится в двойном слове, лежащем в GOT. Модификация PLT/GOT обеспечивает перехват функции лишь в пределах текущего процесса, что, с одной стороны, очень даже хорошо, но с другой — очень плохо. Базовый адрес библиотеки - libc (400179E8h) - лежит в непосредственной близости от истинного адреса функции gets (4009CE60h), возвращаемый dlsym. В глаза сразу же бросается классический пролог - 55h/89h E5h (PUSH EBP/MOV EBP,ESP), который мы и будем патчить для перехвата, но сперва разберемся, как работать с /dev/mem. |