Системный шпионаж в *nix часть 1 Крис Касперски ака мыщъх Хакер, номер #087, стр. 087-134-1 Системно-независимый перехват библиотечных функций Как узнать, какие функции вызывает подопытная программа? Например, в Windows существует целый арсенал шпионских средств, но *nix-хакерам весь инструментарий приходится разрабатывать самостоятельно. Сейчас мыщъх покажет, как осуществляется перехват и подмена системных и библиотечных функций в Linux и *BSD. [введение] При всей непохожести Windows и Linux между ними можно выделить общие черты. Обе системы образуют "слоеный пирог" из библиотек различных уровней иерархий. Ядерные функции Windows NT сосредоточены в файле ntoskrnl.exe, доступ к которым осуществляется через прерывание INT 2Eh (NT 3.5x, NT4.x, W2K) или через INT 2Eh/sysenter (XP, Longhorn). В Linux для той же цели используется INT 80h (x86 BSD использует гибридный механизм, одновременно поддерживая как INT 80h, так и call far 0007h:00000000h). Ядро реализует базовые функции ввода/вывода, распределения памяти, создания/завершения процессов и т.д., причем, если NT предоставляет низкоуровневые полуфабрикаты, над которыми еще предстоит поработать, ядерные функции Linux (они же "системные вызовы", а по-английски sys-calls) вполне юзабельны. Тем не менее, прямые обращения к ядру с прикладного уровня встречаются редко. Вместо этого приложения предпочитают использовать системно-независимую библиотеку libc.so.x – отдаленный аналог kernel32.dll из Windows. Эта библиотека загружается в физическую память всего один раз, а затем проецируется на адресное пространство всех используемых ее процессов ("so" расшифровывается, как "shared object [file]", а x – номер версии, например, "libc.so.6"). Помимо libc, существуют и другие библиотеки, например libncurses.so.x, отвечающая за управление курсором и отрисовку псевдографики в текстовом режиме ("аналог" user32.dll). Библиотеки могут подключаться как на стадии загрузки elf-файла через таблицу символов (аналог таблицы импорта), так и динамически по ходу выполнения программы посредством вызова функций dlopen/dlsym (аналог LoadLibrary/GetProcAddress). Наконец, всякая программа содержит большое количество непубличных и неэкспортируемых функций, которые также требуется перехватывать. [обзор возможных методов] Условимся рассматривать универсальные методики перехвата, не требующие модификации ни подопытного файла, ни ядра и работающие под любой *nix-подобной системой (возможно, с небольшими переделками). Начнем с классики, то есть издалека. Один из самых популярных методов перехвата, активно используемый под Windows и называемый "методом модификации [таблицы] импорта", выглядит так: * создаем отладочный процесс вызовом fork()/exec()/ptrace(PTRACE_TRACEME [в BSD — PT_TRACE_ME, в дальнейшем BSD-объявления будет приводиться через слэш]) или подключаемся к уже запущенному процессу через ptrace(PTRACE_ATTACH/PT_ATTACH, pid, 0, 0); * через функцию ptrace(PTRACE_PEEKTEXT/PT_READ_I, pid, addr, 0) читаем глобальную таблицу смещений (Global Offset Table, GOT) — аналог таблицы импорта; * посредством функции ptrace(PTRACE_POKETEXT/PT_WRITE_I, pid, addr, data) модифицируем указатели на нужные нам функции, заменяя их на offset thunk, где thunk — наш обработчик, внедренный в адресное пространство процесса тем или иным путем (например, при помощи той же PTRACE_POKETEXT/PT_WRITE_I); |