Вариация на тему предыдущего примера
Вариация на тему предыдущего примера
Простая вариация предыдущего подхода включает в себя многократный обход данных. Иногда это необходимо в случае с данными большого объема и ситуаций, когда сначала приходится просмотреть все данные, чтобы отличить интересные данные от неинтересных. В плане реализации это означает, что после первого обхода данных надо:
- Перейти обратно к началу потока данных (который может быть файлом) при помощи seek( )или API-вызова.
или
- Закрыть и вновь открыть дескриптор файла. Зачастую это единственный выбор, когда читаются данные из вывода программы, подобной last.
Вот пример, когда такой подход может пригодиться. Представьте, что надо справиться с проблемой в защите, связанной с тем, что кто-то получил несанкционированный доступ к одной из учетных записей. Один из первых вопросов, который приходит на ум, - «был ли получен доступ и к другим учетным записям с той же машины?». Найти полный ответ на такой кажущийся простым вопрос может оказаться сложнее, чем кажется. Давайте попробуем решить эту проблему. Приведенный ниже отрывок кода для SunOS принимает в качестве первого аргумента имя пользователя и необязательное регулярное выражение в качестве второго, чтобы отфильтровать узлы, которые мы хотим проигнорировать:
Stemplate = "А8 А8 А16 1"; # для SunOS 4.1.x Srecordsize = length(pack($template,())); ($user,$ignore) = @ARGV;
print "-- ищем узлы, с которых регистрировался пользователь Suser --\n"; open(WTMP,"/var/adm/wtrcp") or die "Невозможно открыть wtmp:$!\n": while (read(WTMP,Srecord.Srecordsize)) {
($tty, $name,$host:$time)=unpack($template.$reccKd):
if ($user eq $name){
next if (defined Signore and $host =" /$ignore/o); if (length($host) > 2 and 'exists $contacts{$nost}) {
$connect = localti.Tie($time):
$contacts{$ROSt}=$time:
write: >
print "-- ищем другие соединения с этих узлов --\п"; die "Невозможно перейти в начало wtmp:$'\n" unless (seek(WTMP,0,0));
while (read(WTMP,$record.$recordsize)) {
($tty,Sname,$hostt $time)=unpack($template,$record);
ft если это запись не о завершении работы с системой и нас
# интересует этот узел и это соединение установлено для
ft «другой» учетной записи, тогда записываем эти данные
if (substr($name,1,1) ne "\0" and exists $contacts{$host} and $name ne $user){
Sconnect = localtime($time); write;
}
} close(WTMP);
ft вот формат вывода, вероятно, его потребуется скорректировать
и в зависимости от шаблона
format STDOUT =
@«««« @««««««« @«««««««««
$name,$host,Sconnect
Сначала программа просматривает файл wtmp в поисках записей о регистрации в системе пользователей под «скомпрометированным» именем. По мере нахождения таких записей пополняется хэш, в который записываются имена всех узлов, где регистрировался пользователь под этим именем. Затем программа возвращается к началу файла и просматривает его заново, выполняя на этот раз поиск записей о соединениях с узлов из списка, и выводит совпадения по мере их появления. Не составит труда изменить эту программу так, чтобы она просматривала все файлы из каталога, в котором хранятся файлы ротации журнала wtmp.
Единственная проблема этой программы - ее «узкая специализация». Она будет искать только точное совпадение имен узлов. Если злоумышленник регистрировался в системе, используя динамический адрес, получаемый от провайдера (что бывает часто), то очень велика вероятность, что имена узлов будут отличаться при каждом соединении. Тем не менее, даже неполные решения, подобные этому, очень сильно помогают.
Помимо простоты, у рассмотренного нами подхода есть еще преимущества: он быстрее и требует меньших затрат памяти по сравнению с другими методами. Он лучше всего справляется с журналами, в которых регистрируются данные без поддержки состояния, о которых мы говорили раньше в этой главе. Но иногда, особенно при работе с данными с состоянием, необходимо использовать другие методы.