client.hack

малоизвестные способы взлома клиент. программ\\ или stealth-patching своими руками

nezumi, no email

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

Клиентские приложения можно ломать по разному. Возможностей — просто море. Главное — фантазию иметь, тогда это не хакерство, а самая настоящая Кама-Сутра получается! Одни хакеры предпочитают локальный взлом, другие — удаленный. Сейчас мы будем говорить о локальном взломе, а удаленный рассмотрим как-нибудь потом. На самом деле, в хакерском мире все взаимосвязано и локальный взлом можно рассматривать как заключительную стадию удаленной атаки.

Будем исходить из того, что на вражеском компьютере уже находится наш собственный код. Забросить его можно различными путями. Например, найти переполняющийся буфер и затолкать в него shell-код или зарядить электронное письмо «нехорошим» вложением. В общем, эта методика уже давно отработана и ошлифована до зеркального блеска. Вирусы, черви, троянские лошади так и прут, только успевай затыкать дыры, а количество дыр, обнаруженных в Windows за последние полгода, переходит все границы терпимости и гуманизма. Каждый месяц приходится качать по полста метров заплаток, практически все из которых — критические. Но кто их качает? Так, считанные единицы! Корабль под названием «Windows» дал серьезную течь, сразу треснув по всем швам. Миллионы не залатанных машин ждут тебя! Подготовка к вторжению идет полным ходом. Хакеры пишут и отлаживают червей. Но… незавидна будет их участь, если первый же антивирус, брандмауэр или MicrosoftAnti-Spyware превратит тысячи строк машинного кода в медленно разлагающуюся труху, догнивающую на свалке истории.

client.hack_image_0.jpg

Рисунок 1 спуск Windows'а на воду

Локальное воздействие на атакуемую машину до сих пор замалчивается как специалистами по информационной безопасности, так и хакерами. Дальше примитивных методов сокрытия файлов и процессов дело обычно не идет. Вызывать native-API операционной системы можно, но… бесполезно. Заразить NTOSKRNL.EXE таким способом все равно не удастся, к тому же любая модификация системных файлов слишком заметна. Лучше воевать с исполняемыми файлами прямо в памяти!

client.hack_image_1.jpg

Рисунок 2 защитные механизмы и результаты их жизнедеятельности

Классика – это не только Достоевский с Толстым, но и изменение защитных байт в файле или памяти (более известное под именем «bithack»). Такой же древний, неинтересный, навязший у всех на зубах прием. Легко обнаруживаемый, да к тому же юридически далеко не безупречный. Модификация программного обеспечения — это статья. А статья — это ласты. Несанкционированный доступ к информации и все такое.

Можно ли хакнуть приложение, не прикасаясь к нему? Вопрос не так глуп, каким кажется. Ведь в нашем распоряжении есть и ментальные методы. Попросту говоря, голова. А у некоторых еще и хвост. Хвост очень помогает! Пусть мы не можем модифицировать код и данные приложения, но насиловать регистры процессора нам никто не запрещает! Ведь процессор, в отличии от программного обеспечения, продается, а не лицензируется, то есть мы можем делать с ним, все, что вздумается.

Рассмотрим типичную программу, вызывающую защитную функцию, за которой следует условный переход, действующий по принципу «свой - чужой». В ассемблерном виде это может выглядеть приблизительно так:

CALL check_something

TEST EAX,EAX

JNZ virus_or_evil_hacker_has_detected_and_will_be_destroy

CALL all_ok_continue_normal_execution

Листинг 1 типичный фрагмент защищенной программы

Что можно сделать? Нечестные варианты: а) подправить функцию check_something так, чтобы она всегда возвращала TRUE (XOR EAX,EAX/DEC EAX/RETN и телемаркет); б) изменить TEST EAX,EAX на XOR EAX,EAX, чтобы независимо от результатов проверки, регистр EAX всегда был равен нулю; в) удалить команду JNZ, заменив ее операцией MOV EAX,EAX (тоже самое, что и NOP, только из двух байтов). Все эти способы серьезно конфликтуют с законом. На нас могут наехать и серьезно затоптать.

А вот сравнительно честный метод взлома: дожидаемся выхода из функции check_something и тут же модифицируем регистр EAX, устанавливая его в 1 или в FFh. Ведь это же наш регистр, выкупленный у компании Intel (или AMD) и авторское право на него не распространяется! Нет-нет, команда TEST EAX,EAX остается нетронутой и целостность ломаемой программы никак не нарушается. Воздействию подвергается лишь сам регистр.

Как вариант, можно дождаться завершения выполнения команды TEST EAX,EAX и подправить регистр флагов, а точнее флаг Zero. Или изменить EIP таким образом, чтобы он прыгал на нужную нам ветку. Никакой закон не может нас заставить выполнять тот код, который мы не хотим! Это ведь сплошное насилие получается!

client.hack_image_2.jpg

Рисунок 3 ломать можно не только программы, но и процессоры

Правда, хороших юристов это не остановит. Уголовный Кодекс в первую очередь смотрит на конечный результат, игнорируя пути его достижения. Зарубить человека можно честно купленным топором, но наряд ли это послужит оправданием. То есть, пользоваться взломанной программой все равно нельзя, но мы ведь и не собираемся ей пользоваться! Мы совсем другого хотим! Внедриться на атакуемый компьютер так, чтобы никакая антивирусная собака даже не тявкнула.

Как это можно осуществить практически? Первое, что приходит на ум, — присоединится к атакуемому процессу путем DebugActiveProcess (или открыть процесс с флагом DEBUG_PROCESS), некоторое время потрассировать его, дожидаясь выполнения нужной нам команды, затем «подрихтовать» регистры и отпустить бразды правления. Звучит прекрасно, но работает только на бумажных процессах. В диких джунглях реального двоичного кода трассировка умирает еще в упаковщике/протекторе. К тому же она очень медленно работает, что тут же демаскирует атаку, не говоря уже о том, что API-функции трассировать нельзя и приходится предусматривать обходной код, в результате чего трассировщик разрастается до размеров слона.

А что если установить аппаратную точку останова? Если только атакуемая программа не предпримет специальных усилий (например, задействует все четыре точки останова под собственные нужды), этот прием может неплохо сработать! Проблема в том, что точка останова устанавливается на линейный, а не физический адрес и потому обработчик отладочного исключения (которое генерируется при обращении к соответствующей ячейке памяти) возбуждается в контексте данного процесса. А это значит, что для успешной реализации атаки мы должны проникнуть в чужое адресное пространство.

Это можно сделать, создав удаленный поток, за что отвечает функция CreateRemoteThread (в 9x она не работает, но 9x уже почти труп) или воспользоваться связкой VirtualAllocEx/WriteProcessMemory. Обе технологии давно отработаны и описаны в сотнях хакерских мануалов. Большинство троянов именно так и работает. А вот это уже нехорошо. Теряется фактор новизны, неожиданности и внезапности. Разработчики антивирусов активно работают над созданием оружия, срубающего внедряющийся код еще на излете. К тому же, вторжение в чужое адресное пространство может быть расценено как модификация приложения в памяти. Юристам ведь не объяснишь, что авторские права распространяются лишь на образ приложения на диске/памяти, но отнюдь не на все адресное пространство целиком!

В принципе, можно разместить отладочный обработчик в области памяти, принадлежащей операционной системе, например, KERNEL32.DLL. Microsoft довольно лояльно относится к таким проделкам и ее практикуют довольно многие коммерческие программы, но здесь есть одно «но». Все, что находится ниже адреса 80000000h, находится в прямом ведении прикладного процесса и на каждый из них проецируется индивидуально. То есть, если мы изменим пару байт в «своей» проекции KERNEL32.DLL, все остальные процессы не узнают об этом! А это значит, что внедрение отладочного обработчика должно осуществляться из контекста атакуемого процесса, следовательно, мы должны внедриться и модифицировать его! Замкнутый круг? Не совсем. Можно, к примеру, модифицировать KERNEL32.DLL на диске. Технически это возможно (хотя и непросто), но… нас сразу же обнаружат! Первой возмутится SFC (а это значит, что ее придется отключать), за ней потянутся антивирусные сторожа, ревизоры, мониторы и прочие силовые структуры. Нет, этот метод не подходит.

А почему бы не загрузить свой драйвер и не разместить отладочный обработчик внутри него? Верхняя половина адресного пространства (от 80000000h и выше) проецируется на все процессы. Сюда загружается NTOSKRNL.EXE и драйвера, а это, значит, что наш драйвер будет «виден» из любого процесса. Стоит только хвост протянуть! Более элегантного и самое главное — законного — способа внедрения, пожалуй, и не придумаешь. От победного финала нас отделяет только одна маленькая проблема. А именно — верхняя половина адресного пространства прикладными процессам недоступна. Попытка передачи управление тут же возбуждает исключение, неизбежное как восход солнца, что следующая программа и подтверждает. Она сканирует адресное пространство и выводит адреса всех страниц, к которым есть доступ на исполнение. Смотрите! Ни одной страницы выше 80000000h здесь не наблюдается.

#include <windows.h>

#include <stdio.h>

main()

{

unsignedintp = 0x00001000; начальный адрес while(p) мотаем цикл по всему мясокомбинату

{

выводим адреса всех страниц, к которым есть доступ на исполнение if (!IsBadCodePtr(p)) printf(«%08Xh »,p); перемещаемся на следующую страницу

p+=0x1000;

}

}

Листинг 2 поиск страниц, пригодных для исполнения

Рисунок 4 страницы памяти, доступные на исполнение

Эх, такую идею испортили… Хотя, стоп, ничего не испортили! Ведь на нулевом кольце (а наш драйвер исполняется именно там), можно творить все, что угодно. Адама сотворить навряд ли получится (с искусственным интеллектом прогресс застрял на уровне дождевого червя), но вот отключить защиту мы сможем. Правда, это будет слишком заметное, уродливое и к тому же небезопасное решение. Защита на верхнюю половину памяти установлена не даром. Она предотвращает приложения от вхождения в разнос и хотя операционная система спокойно проживет и без нее, это будет сплошное варварство. Где наш гуманизм! Давайте мыслить глобально! Природа даровала нам голову и хвост. Хвост можно теребить, а головой — думать.

Проведем простой эксперимент — напишем программу следующего содержания, которая передает управление куда-то в глубину ядра и посмотрим, что при этом происходит.

#include <stdio.h>

main()

{

char *p;

произвольный адрес в верхней половине адресного пространства p = (char *)0xBE0FAC69; передача управления на p

(конечно, это можно было сделать и на си, но на асме круче) __asm{ mov ebx,[p] jmp ebx } } Листинг 3 попытка передачи управления на память ядра Рисунок 5 последствия обращения к ядру из прикладного режима Как и следовало ожидать, мы получаем исключение типа «ошибка доступа» и вместо ядра управление передается на обработчик структурного исключения он же SEH – Structured exception handling, поиск по одноименному ключевому слову в MSDN или Интернете выдает очень много интересного. У каждого процесса имеется как минимум один структурный обработчик, устанавливаемый операционной системой. Он выводит пресловутое сообщение о критической ошибке на экран и завершает приложение. Программист может устанавливать и свои обработчики, перехватывающие исключения и тем или иным образом обрабатывающие критическую ситуацию. К сожалению, все они выполняются в контексте данного процесса, про сложности внедрения в адресное пространство которого мы только что говорили. Тупик? Вовсе нет! На самом деле, SEH получает управление в последнюю очередь, когда бал окончен, закуска съедена, а девушки вытворяют весь SEH на который только способно наше воображение. При возникновении исключения, процессор автоматически переходит в режим ядра и передает управление операционной системе, точнее — тому самому коду, на который указывает соответствующий элемент таблицы прерываний (она же IDT от InterruptDescriptionTable). Любой драйвер может беззастенчиво модифицировать содержимое IDT по своему усмотрению, перехватывая все прерывания, которые ему нужны. Техника перехвата подробно описана в любой книге, посвященной программированию в защищенном режиме и, в частности, учебнике Юрова, который так и называется «Ассемблер — учебник». Еще есть Зубков, Гук и известная рассылка Калашникова, так что не будем здесь повторятся. Рисунок 6 схематичная организация IDT Главное, что решить нашу задачу все-таки возможно. Несмотря на то, что верхняя половина адресного пространства недоступна прикладным процессам, аппаратная точка останова все-таки передает управление обработку, причем, на прикладном уровне это протекает незаметно и до SEH'а дело просто не доходит. Вот такие превратности таят в себе метаморфозы программирования в защищенном режиме, но не будет отвлекаться, а продолжим ползти к финишу, преодолевая различные препятствия на пути. Линейный адрес точки останова хранится в регистрах Dr0-Dr3. Об этом рассказывает «фирменная» документация от Intel и AMD, а моя «Техника и философия хакерских атак» содержит исходный код перехватчика со всеми комментариями. Кстати говоря, электронную версию можно бесплатно утянуть с моего персонального ftp-сервера (подробности во врезке). Но вернемся к нашим баранам. В семействе NT природа точек останова сугубо локальна. Каждый процесс владеет своим набором регистров Dr0-Dr3 и хотя количество точек останова от этого не увеличивается, они срабатывают только в контексте того процесса, в котором были установлены. Елки-палки, опять этот долбанный контекст! Ни одно начинание без него не обходится. Такова природа многозадачной NT, а против нее не попреешь. В 9x все проще. Там точки останова носят глобальный характер, распространяющийся на все процессы, что одновременно хорошо и плохо. Хорошо потому, что для установки точек останова не нужно лезть в чужой процесс, а плохо потому, что точки останова срабатывают в каждом процессе и обработку приходится каждый раз разбираться кто есть кто и куда. К счастью, в нашем распоряжении есть две мощных функции GetThreadContext и SetThreadContext. Первая — читает контекст потока, вторая, соответственно, устанавливает. В общих чертах, алгоритм выглядит так: определяем PID нужного процесса (а определить его можно с помощью вызовов TOOLHELP32, о которых рассказывается в любом хакерском faq или с помощью ничуть не менее известной недокументированной функции ZwQueryInformationProcess, описанной там же). Полученный PID передается функции CreateToolhelp32Snapshot, создающий список процесса со всеми его потоками, разбором которых занимаются функции Thread32First/Thread32Next, работающие по принципу известной парочки FindFirstFile/FindNextFile. Первый поток, как правильно, и является основным, однако, в некоторых случаях это не так, но это уже детали. Как бы там ни было, идентификатор потока передается функции OpenThread… Постой-ка! Чтоза OpenThread? Неткакойфункции! OpenProcess есть, а OpenThread — конструктивно не предусмотрена. Каждый программист это знает! Достаточно взять в руки документацию и прочитать. Ха! Документацию! Нормальные хакеры читают ее в последнюю очередь (если ничего не помогает, прочите, наконец документацию), а перед этим просто вызывают функции наобум или, на худой конец, лезут в MSDN за KnowledgeBase, в которой недвусмысленно сказано, что функция OpenThread все-таки есть. Она честно экспортируется KERNEL32.DLL, но вот в SDK и заголовочные файлы не включена. Ну не включена и все! Так захотелось какому-то менеджеру из Microsoft. Может, у него настроение было плохое или жена не дала, а тут еще OpenThread'ы вские… Надо же на ком-то оторваться. Короче! Заметка под номером Q121093 («PointstoRememberWhenWritingaDebuggerforWin32s»), датируемая застойным 1997 годом, об этом конкретно и говорит. Так что жаловаться на закрытость информации не приходится. Рисунок 7 поиск в MSDN по ключевому слову OpenThread выдает пару интересных статей Функция принимает единственный аргумент — идентификатор потока, а возвращает его дескриптор или голимый нуль, когда с открытием происходит облом (например, недостаточно прав). Менять содержимое контекста на ходу недопустимо (это все равно, что перебегать скоростную автомагистраль на красный свет), поэтому поток перед изменением необходимо затормозить функцией SuspendThread. Что, уже затормозился? Вот теперь можно вызывать GetThreadContext с флагом CONTEXT_FULL и читать контекст, организованный в виде одноименной структуры CONTEXT. Здесь опять появляются сложности. PlatformSDK не приводит никакой информации о CONTEXT'е, мотивируя это тем, что работать с контекстом на низком уровне никому не нужно, он де недокументирован и на каждой платформе реализован по своему… На самом деле, реально есть только одна платформа — INTEL, а все остальное экзотика. Маркетоиды могут говорить что угодно и громоздить один уровень абстракции поверх другого, но программистов этим не проведешь! Разработчики Windows прекрасно понимали, что без работы с регистрами ни одна системная программа все равно не обходится и подарили нам замечательный файл WINNT.H, входящий в состав PlatformSDK и содержащий определения многих недокументированных структур (и структуры CONTEXT в том числе) с более или менее подробными комментариями. Рисунок 8 структура CONTEXT не документирована в Platform SDK, но хорошо описана в WINNT.H Модифицировав регистры процессора по своему усмотрению, мы вызываем функцию GetThreadContext и размораживаем поток с помощью ResumeThread. Все! Теперь наш хакерский обработчик восстанавливает IDT в прежний вид и самоликвидируется, выгружая драйвер из памяти. В сильно упрощенном виде, сие происходит так: получаем дескриптор потока,

вызывая недокументированную функцию OpenThread hThread = OpenThread(Id); if (!hThread) return; даем потоку наркоз

SuspendThread(hThread);

говорим, что нам нужен полный контекст со всеми кишками отладочных регистров

Context.ContextFlags = CONTEXT_FULL;

извлекаем контекст из недр потока GetThreadContext(hThread, Context); … модифицируем регистры отладочные регистры семейства Drx,

устанавливая аппаратную точку останова на хакаемый код … имплантируем обновленный контекст обратно в поток

SetThreadContext(hThread, Context);

пробуждаем поток ResumeThread(hThread); Листинг 4 установка точки останова с помощью модификации контекста Огромный минус этой технологии в том, что она слишком заметна, да к тому же поток может запросто защититься от GetThreadContext, но на этот случай в нашем хакерском бардачке есть отдельный стратегический план. Как известно, KERNEL32.DLL содержит лишь высокоуровневые «обертки» реальных API-функций, ведущих к очередной «обертке» в лице NTDLL.DLL. Реальный код сосредоточен в NTOSKRNL.EXE — подлинном ядре операционной системе, проживающем в верхней половине адресного пространства. Ядерные функции всегда исполняются в чьем-то контексте, который в общем случае является контекстом процесса, вызывавшего ту или иную API-функцию. Процессов, не вызывающих никаких API-функций, в природе не встречается. Даже если процесс состоит из одного лишь return (кстати говоря, Windows 2000 отказывается грузить файлы, без импорта KERNEL32.DLL), определенная часть кода системного загрузчика исполняется в контексте загружаемого процесса и вызывает множество ядерных функций. Короче, загрузить процесс, не потревожив ядра, практически нереально (ну разве что загружать его на виртуальной машине). А это значит, что регистры Drx могут быть установлены прямо из ядра без обращения к API-функциям GetThreadContext и SetThreadContext! Отследить эти махинации практически нереально! Для осуществления задуманного, мы должны перехватить одну или несколько ядреных функций, вызываемых из контекста атакуемого процесса еще до того, как защитный механизм получит управление (после уже будет поздно). Эти условиям отвечает функция ZwCreateFile, хотя если защищенный код расположен в самом начале приложения, она нам ничем не поможет и потребуется встраиваться в загрузчик PE-файлов, что несколько труднее, так что оставим все частности за рамками разговора и вернемся к ZwCreateFile. Дизассемблерный листинг будет выглядеть приблизительно так (естественно, линейный адрес функции в памяти будет совсем другим, но его легко определить по таблице экспорта, ведь NTOSKRNL.EXE это фактически обыкновенный исполняемый файл): text:0042FC00 ZwCreateFile proc near; CODE XREF: sub_49D192+4D↓p text:0042FC00; sub_4B1D24+85↓p … text:0042FC00 text:0042FC00 arg_0= byte ptr 4 text:0042FC00 text:0042FC00 B8 20 00 00+moveax, 20h text:0042FC05 8D 54 24 04leaedx, [esp+arg_0] text:0042FC09 CD 2Eint2Eh text:0042FC0B C2 2C 00retn2Ch text:0042FC0B ZwCreateFileendp text:0042FBFE ; ───────────────────────────────────── text:0042FBFE 8B FFmovedi, edi Листинг 5 дизассемблерный листинг функции ZwCreateFile Структураассемблерногокодавполнетипичнадляфункцийспрефиксом Zw. Сначала идет загрузка регистров, затем INT 2Eh, а за ней RETN XX. Антивирусы об этом хорошо знают и потому «тупое» внедрение команды JMP на_наше_тело сразу же вызовет подозрения (впрочем, к AVP и DrWEB это не относится). А вот подмена команды RETN XX гораздо менее заметна. Проблема в том, что RETN XX занимает три байта, а JMP на_наше_тело — целых пять! К счастью, за концом Zw-функций практически всегда присутствует последовательность 8B FF (команда MOV EDI, EDI, двухбайтовый аналог NOP) оставленная для выравнивания. Все вместе они и дают пять байт. Хорошо! Сохраняем оригинальную команду RETN XX в своем драйвере, делаем JMP, передающий управление хакерскому обработчику, так же расположенному в драйвере. От него требуется две вещи — взвести регистры Drx и восстановить команду RETN XX, тут же передав на нее управление. Короче, выключить свет и замести следы. Ах, да! Непосредственно модифицировать память ядра не получится, ведь она защищена от изменений, но эту защиту легко отключить. Способ первый. Простой, документированный, но слишком заметный и вообще некрасивый. Лезем в реестр, открываем раздел HKLM\SYSTEM\CurrentControlSet\Control\SessionManager\MemoryManagement, и создаем параметр EnforceWriteProtection типа REG_DWORD. Все! Защита от записи отключена! Правда и хакер отключен тоже. Тяжелым ударом сапога. Кто угодно может запустить редактор реестра и посмотреть. Правда, если на атакуемом компьютере установлен soft-ice или некоторые пакетные фильтры (сниффер там или брандмауэр), то этот ключик уже стоит и никаких дополнительных усилий от нас не потребуется. Разработчики антивирусов берутся за голову и злобно матерятся: вот и разбери — имеет право этот ключ тут стоять или нет. Рисунок 9 отключение защиты ядра через реестр Впрочем, при желании к реестру можно даже не обращаться. Защита отключается на лету сбросом бита WP в регистре CR0 (WP это от WriteProtection), и точно так же устанавливается вновь, словно никто ничего и не трогал. Сторожа отдыхают! Весь код укладывается в несколько ассемблерных команд, что намного элегантнее труднопроизносимых ключей реестра (наверное, в Microsoft разработчикам платят как журналистам — за каждый байт, только у журналистов есть редактор, режущий всю воду на корню, а вот у программистов ничего такого нет): movebx, cr0; получаем текущее значение регистра CR0 pushebx; кладем его копию на стек andebx, ~0x10000; сбрасываем WP бит movcr0, ebx; загоняем обновленное значение в CR0, отключая защиту … …; модифицируем код ядра … popebx;; достаем сохраненную копию CR0 из стека movcr0, ebx; загоняем ее в регистр CR0, возвращая защиту в прежний вид Листинг 6 ассемблерный код, отключающий/включающий защиту ядра на лету Кстати говоря, модификация ядерных функций при работе на многопроцессорных машинах эпизодически вгоняет систему в синий экран, что создает определенные проблемы. Более корректный (но и более заметный) путь сводится к правке таблицы экспорта и активному использованию «семафоров». Патч ядра это не шутка! Необходимо иметь опыт работы с симметричными многопроцессорными системами (они же SMP — Symmetric Multi-Processing) и знать кучу вещей из самых разных предметных областей. Но… дорогу осилит идущий! Падет тот, кто бежит! ===== заключение ===== Всякую защиту можно взломать! Проверка собственной целости не спасает! Клиентское приложение, работающее на прикладном уровне, неизбежно обречено на поражение. Внедрение в ядро операционной системы — это термоядерное оружие, сметающее любые преграды на своем пути! Чем активнее совершенствуются защитные системы, тем активнее их начинают ломать. Воззвания к закону только подливают масла в огонь, поощряя поиски легальных путей взлома, а законодательство очень инертно. Однажды принятые законы действуют десятилетия, а для компьютерной индустрии – это целая вечность. Кстати, о вечности. Надо бы совершить вылазку на кухю и чего-нибудь захомячить. client.hack_image_9.jpg Рисунок 10 ядерное оружие, уничтожающее защитные механизмы ===== »> врезка nezumiftp ===== мыщъх поднял экспериментальный ftp-сервер, раздающий свои заначки в электронном виде. IP-адрес: 83.239.33.46 (доменного имени пока нет), порт: 21 (стандартный), логин: xakep, пароль: xakep, папка: /pub, приблизительное время работы: с 14:00 до 06:00 (мыщъх ночной зверь), при возникновении проблем рекомендуется установить пассивный режим ftp-клиента, ограничений на кол-во подключений нет никаких. канал 500 мегабит, так что налегайте. только не все сразу ;) банов не ставлю, мыщъх сисоп добрый Рисунок 11 нора мыщъх'а — смотрите, кто у нас в гостях! или разработчики антивирусов качают хакерские книги! (не иначе как в разведовытально-диверсионных целях)