Отслеживание операций с файлами и сетью
В последнем разделе этой главы следует объединить две сферы действий пользователей. Процессы, управление которых так долго обсуждалось, заняты не только поглощением процессорного времени и памяти. Помимо этого они выполняют операции с файловыми системами и от имени пользователей загружают сеть. Администрирование пользователей требует не забывать и об этом.
Мы сосредоточимся на довольно узком круге вопросов и будем обращать внимание только на операции с файлами и сетью, которые выполняют другие пользователи. Кроме того, мы будем обращать внимание только на те операции, владельца которых можно отследить (другими словами, на конкретные процессы, запущенные конкретными пользователями). Что ж, учитывая это, двинемся дальше.
Отслеживание операций в Windows NT/2000
Попытка найти файлы, открытые другими пользователями, вернее всего сработает, если применять программу, работающую в командной строке, - nthandle Марка Руссиновича (Mark Russinovich), ее можно найти на
http://www.sysinternals.com. Она позволяет показать все открытые дескрипторы на определенной системе. Вот как выглядит ее вывод:
System pid: 2
10: File C:\WINNT\SYSTEM32\CONFIG\SECURITY
84: File C:\WINNT\SYSTEM32\CONFIG\SAM.LOG
cc: File C:\WINNT\SYSTEM32\CONFIG\SYSrEM
dO: File C:\WINNT\SYSTEM32\CONFIG\SECURITY.LOG
d4: File C:\WINNT\SYSTEM32\CONFIG\DEFAULT
e8: File C:\WINNT\SYSTEM32\CONFIG\SYSTEM.ALT
fc: File C:\WINNT\SYSTEM32\CONFIG4SOFTWARE.LOG
118: File C:\WINNT\SYSTEM32\CONFIG\SAM
128: File C:\pagefile.sys
134: File C:\WINNT\SYSTEM32\CONFIG\DEFAULT. LOG
154: File С:\WNNT\3YSTEM32\CON'FIG;'SOFTWARE
1bO: File \3evice\NafiedPipe\
294: File C:\WINNT\PROFILES\Adnirustrator\ntLSer.aa-.; OG
2a4: File C:\WINNT\PROFILES\AdminisTrator\NTUSEH.DAT
SMSS.EXE pid: 27 (NT AUTHORITY:SYSTEM)
4: Section С:\WINNT\SYSTEM32\5MSS.EXE
c: File С'\WINNT
28: File C:\WINNT\SYSTEM
Можно также запросить информацию по конкретным файлам и каталогам:
> nthandle c:\temp
Handle V1.11
Copyright (С) 1997 Mark Russinovich
http://www.sysinternals.com
WINWORD.EXE pid: 652
C:\TEMP\~DFF2B3.tmp WINWORD.EXE pid: 652
C:\TEMP\~DFA773.tmp WINWORD.EXE pid: 652
C:\TEMP\~DF9l3E.tmp
Программа nthandle позволяет получить эту информацию по конкретному процессу при помощи ключа -р.
Использовать ее из Perl очень просто, поэтому не будем приводить примеры. Вместо этого посмотрим на подобную, но более интересную операцию - аудит.
NT/2000 позволяет эффективно отслеживать изменения в файлах, каталогах или иерархии каталогов. Вы могли бы учитывать постоянное повторение операции stat() над нужным объектом, но это потребовало бы слишком больших затрат процессорного времени. В NT/2000 отслеживание изменений можно поручить операционной системе.
Относительно безболезненно эту работу выполняют два модуля: Win32: :ChangeNotify Кристофера Мадсена (Christopher J. Madsen) и Win32: :AdvNotify Амина Мюлей Рамдана (Amine Moulay Ramdane). В примерах этого раздела будет использоваться второй, т. к. он более гибкий.
Работа с модулем Win32: : AdvNotify- это многошаговый процесс.
На следующем шаге нужно создать следящий поток (monitoring thread) для интересующего нас каталога. Win32: :AdvNotify позволяет следить сразу за набором каталогов, для этого необходимо лишь создать несколько потоков. Мы же будем следить только за одним каталогом:
Sthread = $aob]->StartThread(Directory => 'C:\terr.c'.
Filter => All, WatchSubtree -> 0) or die "Невозможно начать поток\п":
Первый параметр этого метода говорит сам за себя; рассмотрим остальные.
Установив Filter в одно из приведенных значений (табл. 4.1) или в их
комбинацию (SETTING 1 | SETTING2 | SETTINGS. ..), можно следить за различными типами изменений.
Таблица 4.1. Параметры Filter в Win32::AdvNotifу
Параметр |
Отмечает |
FILE_NAME |
Создание, удаление, переименование файла(ов) |
DIR_NAME |
Создание или удаление каталога(ов) |
ATTRIBUTES |
Изменение атрибутов любого каталога |
SIZE |
Изменение размера любого файла |
LAST_ WRITE |
Изменение даты модификации файла(ов) |
CREATION |
Изменение даты создания файла(ов) |
SECURITY |
Изменение информации безопасности (ACL и пр.) файла(ов) |
Значение АИ из приведенного примера- это всего лишь постоянная, объединяющая все варианты выбора. Если не указать параметр Filter при вызове метода, то по умолчанию будет использоваться АИ. Параметр WatchSubtree определяет, необходимо ли следить только за указанным каталогом или за каталогом и всеми его подкаталогами.
StartThreadO создает поток, но проверка начинается только после того, как поступает распоряжение об этом:
$thread->EnableWatch() or die
"Невозможно начать наблюдение\п";
Существует также функция OisableWatch(), которую необходимо использовать в программе для завершения проверки.
Мы следим за нужным объектом, но как узнать, изменилось ли что-нибудь? Надо придумать что-то, что позволило бы потоку сообщить нам об изменениях, за которыми мы наблюдаем. Здесь тот же подход, что и в главе 9 «Журналы» при обсуждении сетевых сокетов. Обычно следует вызывать функции, которые заблокированы до тех пор, пока ничего не происходит:
while($thread->Wait(INFINITE)){
print "Что-то изменилось1\п":
last if ($changes++ == 5):
}
Этот цикл while() вызовет метод Wait() для нашего потока. До тех пор пока потоку нечего сообщить, вызов будет заблокирован. Обычно Wair() принимает в качестве параметра число миллисекунд, равное времени ожидания. Мы же передаем специальное значение, которое соответствует «бесконечному ожиданию». Когда Wait() возвращает значение, следует вывести сообщение и ждать дальше, если только уже не были замечены пять других изменений. Теперь можно закончить:
$thread->Terminnte() undef $aobj;
Наша программа пока еще не очень полезна. Нам известно, что что-то изменилось, но мы не знаем ни что изменилось, ни как это произошло. Чтобы исправить эту ситуацию, изменим тело цикла wniie() и добавим определение формата:
while($thread->Wait(INFINITE)){
while ($thread->Read(\@status)){ foreach Sevent (@status){
Sfilename = $event->{FileName); $time = $event->{DateTime};
Saction = $ActionName{$event->{Action}}; write; } } }
format STDOUT =
@««««««««« @««««««««« @«««««««««
Sfilename,$time,Saction
format STDOUT_TOP =
File Name Date Action
Основное изменение- это добавление метода Read(). Он получает информацию об изменении и заполняет элементы списка ©status ссылками на хэш. Каждая ссылка указывает на анонимный хэш, который выглядит примерно так:
{'FileName' => "GLF2425.TMP',
'DateTime' => '11/08/1999 06:23:25р',
'Directory' => 'C:\temp', 'Action' => 3 }
Для каждого изменения могут произойти несколько событий, отсюда и необходимость вызывать Read() в цикле while(). Если соответствующим образом разыменовать содержимое этих хэшей и применить к ним форматирование, то получатся примерно такие данные:
File Name Date Acti01
"DF400E.tmp 11/08/1999 07:29:56p
FILE_.ACTION_PEMO\,'F.D
"DF6C5C. fnp 11/08/1999 07'29'56p
F1LF_AC1ION_ADDED
~OF6E66.tmp 11/08/1999 07:29:56р
FILE_ACTION_ADOED
~DF6E5C.tmp 11/08/1999 07:29:56р
FILE_ACriON_REMOVEO
К сожалению, отслеживание операций с сетью в NT/2000 впечатляет намного меньше. В идеале, как администратор вы хотели бы знать, какой процесс (а, следовательно, какой пользователь) открыл сетевой порт. Печально, но я не знаю ни одного модуля и ни одного инструмента, которые могли бы предоставить такую информацию. Существует один коммерческий инструмент, работающий в командной строке, под названием TCPVstat, который может показать связь процессов с использованием сети. TCPVstat можно найти в пакете TCPView Professional Edition, который доступен на http://www.winternals.com.
Если использовать только некоммерческие инструменты, то придется иметь дело лишь со списком сетевых портов, открытых в настоящее время. Для этого следует применять другой модуль Рамдана -Win32: :1рНе1р. Вот как выглядит код, печатающий нужную информацию:
use Win32: -.IpHelp;
# замечание: в данном случае регистр "IpHelp"
имеет значение' my $iobj = new Win32::IpHelp;
# заполняем список хэшем хэшей $iobj->GetTcpTable(\(a>table,1);
foreach Sentry (@table){
print $entry->{LocalIP}-><Value( . ":" .
$entry->{LocalPort}->{ValLie}. " -> ";
print $entry->{fiemoteIP}->{Value} . ":" .
Sentry->{RemotePort}->{Value}."\n";
}
Посмотрим, как можно сделать то же самое в Unix.
Отслеживание операций в Unix
Для отслеживания операций с файлами и сетью в Unix можно использовать один и тот же подход. Это один из тех редких случаев, когда вызов внешней программы намного предпочтительней. Вик Абель (Vic Abell) преподнес чудесный подарок системным администраторам, написав программу Isof (LiSt Open Files), которую можно найти на ftp:// vic.cc.purdue.edu/pub/tools/unix/lsof. Isof
позволяет отобразить подробную информацию об открытых в настоящий момент файлах и сетевых соединениях на Unix-машине. По-настоящему удивительной эту
программу делает ее переносимость. Последняя версия программы (на момент написания этой книги) работает по крайней мере на 18 видах Unix и поддерживает различные версии этих операционных систем.
Вот как выглядит вывод Isof для одного из запущенного мной процесса. Isof выводит очень длинные строки, поэтому, чтобы сделать информацию более читаемой, после каждой строки вывода я добавил пустую строку.
COMMAND PID USER FO TYPE DEVICE SIZE/OFF NODE NAME
netscape 21065 dno cwd VOIR 172,289"; 8192 12129 /-OTie
netscape 21065 dnb txt VREG 172,1246 1438236Д 656749
/net/ arch-solans (fileserver-./vol/systems/arch-solaris)
netscape 21065 dnb txt VREG 32,6 54656 35172
/usr (,/dev/ dsk/cOtOdOs6)
netscape 21065 dnb txt VREG 32;6 146740 6321
/ubr/lro/ libelf.so.1
netscape 21065 dnb txt VREG 32,6 69292 102611
/usr (/dev/ dsk/cOtOdOs6)
netscape 21065 dnb txt VREG 32,6 21376 79751
/usr/iib/ locale/en_US/en_US.so.1
netscape 21065 dnb txt VREG 32,6 19304 5804
/usrЛib/ libmp.so.2
netscape 21065 dnb txt VREG 32,6 98284 22860
usr/onenwi:' lib/libICE.so.6
netscape 21065 dnb txt VREG 32,6 46576 22891
/usr/opftrwiv lib/libSM.so.6
netscape 21065 drib txt VREG 32.6 1014020 5810
/!jsr/::.t; libc.so.1
netscape 21065 dnb txt VREG 32.6 105788 5849
/usr/ln; libm.so.1
netscape 21065 dnb txt VREG 32,6 721924 5806
netscape 21065 ar.b txt VREG 32 6 156196 5774
netscape 21065 ri^t 0. VCHP 2-1.3 Ot73 5853 > .
pseudo/bls@C:3-> ttcoirpat ->lcter"b>:neii->pis netscape 21065 dnb 3u VCHR 13,12 oto 5821
/devices/ pseudo/mm@0:zero
netscape 21065 dnb 7u FIFO Ox6034d264 C;1 47151 PIPE-> Ox6034d1eO
netscape 21065 dnb 8u met Ox6084cb68 Oxfb210er, TCP host. cos.
ne^.. edu:46575->host2.ccs,neu. edu:6000 (ESTABLISHED)
netscape 21065 dnb 29u met 0x60642848 Ot215868 TCP nost, ccs. re.. edu:46758->
www.mind-bright.se:80 (CLOSE_ WAIT)
Из этого примера можно понять, насколько мощна эта команда. Мы можем увидеть текущий рабочий каталог (VDIR), обычные файлы (VREG), символьные устройства (VCHR), каналы (FIFO) и сетевые соединения (inet), открытые этим процессом.
Самый простой способ применить программу Isof из Perl - вызвать ее в специальном режиме «field» (-F). В этом режиме вывод программы делится на специальным образом отмеченные и разделенные поля, вместо использования колонок в стиле ps. Это позволяет надежно проанализировать и распознать вывод.
У этого способа вывода результатов есть одна особенность. Вывод организован в виде «наборов процессов» (process sets) и «наборов файлов» (file sets), как их называет автор. Набор процессов - это набор полей, относящихся к одному процессу; набор файлов - это подобный же набор для файла. Все приобретет больший смысл, если включить режим разбивки на поля с параметром 0. В этом случае поля будут разделены символом NUL (ASCII 0), а наборы - символом NL (ASCII 12). Вот как будет выглядеть предыдущий вариант вывода команды, если использовать режим разбивки на поля (NUL представлен в виде символов ~@):
p21065~@cnetscape~@u6700"@Ldnb~@
fcwd"@a ~@1 "@tVDIR"@DOx2bOOb4b"@s8192"@i12129"@n/home/dnb"@
ftxt"@a ~@1 >tVREG"@DOx2b004de"@s14382364"@i656749"@n/net/arch-solaris (fileserver:/vol/systems/arch-solaris)"@
ftxt~@a "@1 ~@tVREG"(5)DOx800006~@s54656~@i35172~@n/usr (/dev/dsk/cOtOdOs6)"@ ftxt"@a "@1 "@tVREG"@OOx800006"@s146740"@i6321"@n/usr/lib/libelf.so.1"@ ftxt"@a "@1 "@tVREG"@DOx800006"@s40184"@i6089"ian/usr (/dev/dsk/cOtOdOs6)"@ ftxt"(5>a "@1 "@tVREG"@DOx800006"@s69292"@i102611"@n/usr (/dev/dsk/cOtOdOs6)"@
ftxt"@a ~@1 •@tVREG"@DOx800006"@s21376"@i79751"@n/usr/lib/locale/en_US/ en_US.so.1"@
ftxt"@a ~@1 "@tVREG"№Ox800006"@s19304"@i5804"@n/usr/lib/libmp. so. 2"@
ftxt"@a "@1 ~@tVREG"@DOx800006"@s98284"@i22860"@n/L>sr/openwin/lib/ libICE.so.6"@ ftxt~@a ~@1 "@tVREG"@DOx800006"@s46576"@i22891"@n/usr/openwin/lib/ libSM.so.6"<°>
ftxt"@a ~@1 "@tVREG"@DOx800006"@s1014020"@i5810"@n/usr/lib/libc.so.1"@ ftxt~@a ~@1 ~@tVREG~№Ox800006~@s105788~@i5849~<a>n/usr/lib/libm. so. 1"@ ftxt"@a "@1 "§tVREG"@DOx800006'@s721924"@i5806"@n/usr/lib/libnsl.so.Г@
ftxt"@a "@1 "@tVREG"@DOx800006"§s166196"@i5774"@n/usr/lib/ld.so.Г@ fO"@au~@l "@tVCHR"@DOx600003"iao73~(ai5863"(an/devices/pseudo/ pts@0:3->ttcompat->ldterm->ptem->pts"@
f3"@au"@l "@tVCHR"@DOx34000c"@oO"§i5821"@n/devices/pseudo/mm(s)0:zero"(8i f7"@au"@l "@tFIFO"@dOx6034d264"@o1"@i47151"@nPIPE->Ox6034d1eO"@
f8"@au"@l "@tinet"@dOx6084cb68"@o270380692"(9>PTCP''@nhost.ccs.neu.edu:46575-> host2.cos.neu.edu: 6000"@TST=ESTABLISHED~@
f29"@au"@l "@tinet"@dOx60642848"@o215868~@PTCP"@nhost.ccs.neu.edu:46758-> www.mindbright.se: 80"(a>TST=CLOSE_WAIT~@
Давайте разберемся с этими данными. Первая строка - это набор процессов (это можно понять по первому символу р):
p21065~(acnetscape~@u6700~@Ldnb~(a
Каждое поле начинается с буквы, определяющей его содержимое (о -идентификатор процесса (pid), с - команда, и - идентификатор пользователя (uid) и L - имя пользователя (login)), и заканчивается символом разделителя. Все поля в строке создают набор процесса. Все последующие строки вплоть до очередного набора процесса описывают открытые файлы/сетевые соединения процесса, описываемого своим набором.
Давайте используем этот режим. Если необходимо вывести список всех открытых файлов и процессов, обращающихся к ним, можно применить такую программу:
use Text::Wrap;
Slsofexec = "/usr/local/Din/lsof"; 8 путь к Isof
8 режим (F)ield, разделитель NUL (0), показывать (L)ogin,
8 тип файла (t)ype и имя файла (n)ame
$lsofflag = "-FLOtn";
open(LSOF,"$lsofexec $lsofflag[") or
die "Невозможно запустить $lsofexec:$!\n";
while(<LSOF>){
8 работаем с набором процесса if (substr($_,0,1) eq "p"){
($pid,$login) = split(/\0/);
$pid = substr($pid,1,length(Spid)); }
# работаем с набором файла.
# Замечание: мы интересуемся только обычными файлами
if (substr($_,0,5) eq "tVREG"){
($type,$pathname) = split(/\0/);
# процесс может дважды открыть один и тот же файл,
# поэтому мы должны убедиться, что запишем его
# только один раз
next if ($seen{$pathname} eq $pid); $seen{$pathname} = $pid;
Spathname = substr($pathname,1,length($pathname));
push(@{$paths{$pathname}},$pid);
}
close(LSOF);
for (sort keys %paths){
print "$_:\n";
print wrap("\t","\t",join(" ",@{$paths{$_}})),"\n";
}
В этом случае Isof будет показывать только некоторые из полей. Можно обойти в цикле весь вывод, собирая имена файлов и идентификаторы процессов в хэш списков. Когда будут обработаны все выведенные данные, следует ввести имена файлов в виде отформатированного списка процессов (спасибо Дэвиду Шарноффу (David Muir Sharnof f) за модуль Text: :Wrap):
/usr (/dev/dsk/cOtOdOs6):
115 117 128 145 150 152 167 171 184 191 200 222 232 238
247 251 276 285 286 292 293 296 297 298 4244 4709 4991
4993 14697 20946 21065 24530 25080 27266 27603
/usr/bin/tcsh:
4246 4249 5159 14699 20949
/usr/bin/zsh:
24532 25082 27292 27564
/usr/dt/lib/libXm.so.S: 21065 21080
/usr/lib/ld.so.1:
115 117 128 145 150 152 167 171 184 191 200 222 232 238
247 251 267 276 285 286 292 293 296 297 298 4244 4246 4249 4709 4991
4993 5159 14697 14699 20946 20949 21065
21080 24530 24532 25080 25082 25947 27266 27273 27291
27292 27306 27307 27308 27563 27564 27603
/usr/lib/libc.so.1:
267 4244 4246 4249 4991 4993 5159 14697 14699 20949
21065 21080 24530 24532 25080 25082 25947 27273 27291
27292 27306 27307 27308 27563 27564 27603
Чтобы показать последний код, относящийся к отслеживанию операций с файлами и сетью в Unix, вернемся к поиску запущенных IRC-po-ботов из приведенного ранее примера. Существует более надежный способ найти такие процессы-демоны, чем изучение таблицы процессов. Пользователь может скрыть имя робота, переименовав исполняемый файл, но для того чтобы спрятать сетевое соединение, ему придется очень хорошо потрудиться. В большинстве случаев это соединение с сервером на портах 6660-7000. Программа Isof позволяет без труда отыскивать такие процессы:
Slsofexec = "/usr/local/bin/lsof" Slsofflag = "-FLOc -iTCP: 6660-7000";
ft это срез хэша. используемый для предварительной загрузки
и таблицы хэшей, существование этих ключей мы будем проверять
и позже. Обычно это записывается так:
ft %approvedclients = ("ircll" => undef, "xirc" => undef, ...);
ft (но такой вариант - отличная идея, воплощенная Марком-
ft Джейсоном Доминусом(МагК-иа50п Dominus))
@>approvedclients{"ircH" , "xirc" , "pirc"} = ();
open(LSOF, "$lsofexec $lsofflag|") or die "Невозможно запустить $lsofexec:$! \n" ;
while(<LSOF>){
($pid,$command,$login) = /p(\d+)\000
c(.+)\000 L(\w+)\000/x;
warn "$login использует неразрешенный клиент с именем
$conimand (pid $pid)l\n" unless (exisTs $approvedclients{$command});
close(LSOF);
Это самая простая проверка из всех возможных. Она позволяет отловить тех, кто догадается переименовать eggdrop б pine или -tcsh, и тем более тех, кто даже не попытается спрятать своего робота. Тем не менее, она подвержена тому же недостатку, что и предыдущий вариант тестирования. Если пользователь достаточно умен, он может переименовать робота во что-то из списка «разрешенных клиентов». Чтобы продолжить игру в кошки-мышки, можно предпринять еще как минимум два шага:
Игра в кошки-мышки привела нас к точке, позволяющей завершить эту главу. В главе 3 мы говорили, что пользователи совершенно непредсказуемы. Они делают такие вещи, которые системные администраторы не могут даже предвидеть. Известно старое изречение: «Защиты от дураков не существует, потому что дураки изобретательны». С этим фактом придется считаться при программировании на Perl для администрирования пользователей. В итоге вы будете писать более надежные программы. Когда одна из ваших программ начнет «ругаться» из-за того, что пользователь сделал что-то неожиданное, вы сможете спокойно сидеть и наслаждаться человеческой изобретательностью.