Программизм |
|||||||||||||||||||||||||
|
|||||||||||||||||||||||||
UUencode, BASE64 и все-все-всеДавным-давно, когда дискеты были большими, О борьбе с корпоративными почтовыми системами я уже писал. Как выяснилось, изобретательные администраторы не устают подкидывать нам всё новые и новые темы для Какой файл считается подозрительным? Если подумать, то разумным решением можно назвать запрет на заражённые исполняемые модули. Например, Казалось бы, можно переименовать файл — например, из Ситуация, на первый взгляд, безвыходная, однако, как известно, на каждую хитрую гайку найдётся свой болт. Но для этого придётся вспомнить, что значит «вложить файл в письмо». Давным-давно, когда дискеты... то есть модемы могли передавать только
Теперь вопрос в том, как выбрать диапазон печатных символов, чтобы отобразить множество полученных шестибитных чисел. Первый стандарт назывался Unix-to-Unix encode или, сокращённо, uuencode. Там для получения печатного символа к числу от 0 до 64 добавлялось 32, а 0 кодировался не пробелом, а знаком обратного апострофа (`). Если написать таблицу преобразования, то она выглядела бы так: `!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ Как видим, здесь используются только знаки препинания, цифры и заглавные буквы, так что даже если регистр букв не при передаче не сохраняется ( Стандарт uuencode подходил и для использования в электронных письмах, но кто-то большой и умный придумал стандарт MIME, который несколько изменил внутреннюю структуру письма. А заодно поменялся и алгоритм кодирования двоичных данных. Вернее, принцип-то остался тот же, но решено было избавиться от лишнего ведущего байта и изменить таблицу кодирования. Новый алгоритм получил название BASE64, а ниже приводится его таблица: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ Итак, робот, призванный следить за чистотой корпоративной почты, легко опознаёт вложенный файл по заголовку MIME или uuencode и вырезает этот файл из письма. Чтобы обмануть его, можно попробовать применить нестандартную таблицу кодирования и не писать стандартных заголовков. То есть превращаем «подозрительный» файл в текст и методом Проблема состоит в том, что у корреспондента должен быть декодер, а переслать ему исполняемый модуль я не могу. Возникает естественное желание написать декодер на каком-нибудь встроенном языке Windows, то есть CScript или VBScript, но оказывается, что там нет функций для работы с двоичными файлами — объект К счастью, у 99.9% пользователей Windows установлен пакет Microsoft Office, а возможности встроенного в него языка VBA существенно превосходят возможности VBScript. Значит, используем VBA и Excel (последнее — исключительно дело привычки, подойдут также Word, Access или PowerPoint). Ниже приводится текст декодера: Option Explicit Option Base 0 Private Type OPENFILENAME lStructSize As Long hwndOwner As Long hInstance As Long lpstrFilter As String lpstrCustomFilter As String nMaxCustFilter As Long nFilterIndex As Long lpstrFile As String nMaxFile As Long lpstrFileTitle As String nMaxFileTitle As Long lpstrInitialDir As String lpstrTitle As String flags As Long nFileOffset As Integer nFileExtension As Integer lpstrDefExt As String lCustData As Long lpfnHook As Long lpTemplateName As String End Type Private Declare Function GetOpenFileName Lib "comdlg32.dll" _ Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long Private Const CodeChars As String = _ "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя" Private Function c2i(ByVal c As String) As Integer c2i = InStr(1, CodeChars, c) - 1 End Function Private Sub Decode(ByVal pStr As String, ByRef pBuf() As Byte) Dim count As Integer, count0 As Integer, l As Integer, i As Integer pStr = Trim$(pStr) l = Len(pStr): i = 1 count0 = Fix(l / 4) * 3 If l Mod 4 > 0 Then count0 = count0 + l Mod 4 - 1 ReDim pBuf(count0 - 1) As Byte Do While l > 0 pBuf(count) = CByte((c2i(Mid$(pStr, i, 1))) * 4 Mod 256 + _ (c2i(Mid$(pStr, i + 1, 1))) \ 16) count = count + 1 l = l - 2: If l = 0 Then Exit Do pBuf(count) = CByte((c2i(Mid$(pStr, i + 1, 1))) * 16 Mod 256 + _ (c2i(Mid$(pStr, i + 2, 1))) \ 4) count = count + 1 l = l - 1: If l = 0 Then Exit Do pBuf(count) = CByte((c2i(Mid$(pStr, i + 2, 1))) * 64 Mod 256 + _ (c2i(Mid$(pStr, i + 3, 1)))) count = count + 1 l = l - 1 i = i + 4 Loop End Sub Sub DecodeFile() Dim FileName As OPENFILENAME Dim c As String Dim Buf() As Byte Dim n As Integer With FileName .lStructSize = 76 .lpstrFilter = _ "Текстовые файлы (*.txt)" & Chr$(0) & "*.txt" & Chr$(0) & _ "Все файлы (*.*)" & Chr$(0) & "*.*" & Chr$(0) .nFilterIndex = 1 .lpstrFile = Chr$(0) + Space(255) .nMaxFile = 256 .lpstrTitle = "Выберите файл для декодировки" .flags = &H881804 End With If GetOpenFileName(FileName) = 0 Then Exit Sub Open FileName.lpstrFile For Input Access Read As #1 Line Input #1, c Open Left$(FileName.lpstrFile, FileName.nFileOffset) & c _ For Binary Access Write As #2 Do While Not EOF(1) Line Input #1, c If Len(c) = 0 Then Exit Do Decode c, Buf Put #2, , Buf Loop Close #2 Close #1 End Sub Формат текстового файла прост — в первой строке идёт имя двоичного файла, который должен получиться в результате декодировки, а затем — собственно данные. Для того, чтобы декодировать файл, надо (сюрприз!) запустить макрос Теперь возникает следующая сложность — нужен кодер. На чём его писать? Хотелось бы на том же самом VBA, но оказывается, что функции работы с двоичными файлами реализованы там криво, и если при записи эта кривизна обходится, то при чтении — нет. Можно писать на C, но это не спортивно. К счастью, под руками у меня всегда есть интерпретатор REXX, язык, используемый с тех пор, как я работал под OS/2. Ниже приводится код программы: # regina "%~f0" %* & exit CodeChars = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя" "del a.out 2>nul" parse arg inf call lineout "a.out", inf do while chars(inf)>0 l = min(chars(inf),54) c = charin(inf,,l) call lineout "a.out", encode(c) end exit encode :procedure expose CodeChars parse arg c buf = "" i = 1 do while i<=length(c)%3*3 c1 = c2d(substr(c,i,1))%4 c2 = (c2d(substr(c,i,1))*16+c2d(substr(c,i+1,1))%16)//64 c3 = ((c2d(substr(c,i+1,1))*4)+(c2d(substr(c,i+2,1))%64))//64 c4 = c2d(substr(c,i+2,1))//64 buf = buf || substr(CodeChars, 1+c1, 1) buf = buf || substr(CodeChars, 1+c2 ,1) buf = buf || substr(CodeChars, 1+c3 ,1) buf = buf || substr(CodeChars, 1+c4 ,1) i = i+3 end select when length(c)-i=0 then do c1 = c2d(substr(c,i,1))%4 c2 = (c2d(substr(c,i,1))*16)//64 buf = buf || substr(CodeChars, 1+c1, 1) buf = buf || substr(CodeChars, 1+c2 ,1) end when length(c)-i=1 then do c1 = c2d(substr(c,i,1))%4 c2 = (c2d(substr(c,i,1))*16+c2d(substr(c,i+1,1))%16)//64 c3 = ((c2d(substr(c,i+1,1))*4))//64 buf = buf || substr(CodeChars, 1+c1, 1) buf = buf || substr(CodeChars, 1+c2 ,1) buf = buf || substr(CodeChars, 1+c3 ,1) end otherwise end return buf Зная, что символы в строке нумеруются с 1, после ключевого слова В общем, удачи вам, быстрого интернета и правильно настроенных почтовых систем! 20.01.2004 |
ПоискСм. такжеПользователи всех клонов *nix давно уже могут писать командные файлы на любом языке. Для этого надо лишь указать в первой строке имя интерпретатора... »»» Контрольный вопрос: какая из компонент аппаратуры сильнее всего влияет на скорость исполнения приложений?... »»» архив с драйвером занимает около Рекомендую
e.g.Orius Copyright noticeъ) Все материалы, размещённые на странице, являются неотъемлемой собственностью автора с вытекающими отсюда правами, как ©, так и (ъ). Некоммерческое их распространение всячески приветствуется, разумеется, при условии сохранения ссылки на оригинал. Что касается коммерческого использования — пишите письма, договориться можно всегда. Удивительное рядом
Пишите письма
Счётчики |