; MrHammer e-mail:limon-5@yandex.ru ; Кое_какое_исследование защиты в PC_GUARD ; 21 декабря 2003 года Статейка была написана в июле, протектор сам был ислледован где-то вконце июня, когда батрачил на благо универа на практике (т. е. сачковал). И еще, реальные смещения в листингах я не привел, хотя это наверное и к лучшему. Так как интересующемуся защитными алгоритмами, думаю,много не стоит обьяснять, сам знает, а людям, которым нужен быстрый взлом и быстрый крак я в конце мессаги подвожу нек. итог. В конце концов, автор публикует эту статью только в образовательных целях, и не более, ну ,еще , чтоб приколоться. В конце приводятся нек. скрипты, постарался сделать их более-менее чит-ными. Введение. Предлагаю на суд читателей материальчик на тему исследования протекторов. В сети много таких документов, так что не претендую на оригинальность, и мне это не грозит; но вот хотел бы остановить внимание на таком аспекте - в большинстве вышеупомянутых туториалов говорится, что чтобы снять какую-то навесную защиту, нужно сделать то, то и то в отладчике. То есть раскрывают конкретную технику снятия защиты, скромно умалчивая о подробностях нахождения этой методики. Вообщем, далее попытаемся восполнить этот пробел и изучим , к примеру, защиту "PC Guard for Win32". Версия защиты вроде бы 4.15D, но не настаиваю на правильности. Программа, защищенная этой защитой, сам протектор pcgwin32.exe и его библиотечка pcgw32.dll. Более поздней версии протектора я в сети не нашел. Тэк-с, в нашем исследовании незаменимым инструментом будет ИДА (версия не важна; кстати, меня очень прикалывают на форумах просьбы трудящихся дать ссылку на варезный сайт с самой последней версией ИДЫ. Что же они, эти трудящиеся, ковыряют, что им не хватает более ранней версии Иды?:-). И еще Мягкий Лед с незаменимым IceDump/IceExt. Также немного опыта в ковырянии протекторов, а то я могу невольно пропустить очевидные для себя места. Загрузили гвардейца в Иду, исследуем... Кхм, вы все еще исследуете? ;-) С самого же начала гвардеец начинает потчевать нас плюшками (какой хлебосольный, однако). Плюшка намбер раз, старческий маразм: ;////////////////////////////////////////////////////////////////////////////// marazmatik proc near jmp short loc_44A032 db 0Ch ; loc_44A032: pop eax ; eax = offset after call jmp short loc_44A036 db 35h ; 5 loc_44A036: inc eax ; !!! jmp short loc_44A03A db 36h ; 6 loc_44A03A: jmp eax marazmatik endp ;////////////////////////////////////////////////////////////////////////////// после пропуска этого Marazmatik'а и Сайс потеряет способность твердо мыслить (скоропостижно вывалит нас в ОС), и Ида будет смотреть на нас невинными глазами - типа куда там дальше-то, милок. Плюшка намбер два, истина где-то рядом: ;////////////////////////////////////////////////////////////////////////////// do_real_action_1:... jmp short ay_1 db 83h ; Г ay_1: pushf jmp short ay_2 db 0D5h ; - ay_2: jmp short ау_3 db 35h ; 5 ay_4: popf jmp short ау_5 db 89h ; Й ay_5: jmp short do_real_action_2 db 0Bh ; ay_3: jmp short ау_4 do_real_action_2:... ;////////////////////////////////////////////////////////////////////////////// После десяти минут изучения кода В Иде плюшка намбер два начинает страшно бесить. Голова занята вычислениями, а тут еще искать место следующей инструкции в коде, делающей хоть что-то полезное... Как же это все-таки ломает. Что же будем делать? Хе-хе-хе, нашли чем заняться, а ну вытри пенку с носа, и вообще, это мое пиво :-) Недолго думая, будем писать скрипт. Ида согласна на это, так позволим же ей немного поразмяться. Че там напишем-то? Вегной догогой идете товагищи: найдем эти дрянные байты по сигнатуре и превратим их в двойные слова (места в листинге будут меньше занимать), и напишем: куча мусора. И не забудьте про ScreenEA(), классная фича для определения начального адреса. Встаешь на искомое смещение и ScreenEA() вернет его. Вообщем, будем думать, что мы все справились с ситуацией,написали скриптик, если кто-нить не догнал, смотрите у меня, в Приложении - reader(). Изучаем дальше. Во, как стало светлее в этом темном лесе. Но что же делать с этим дельта-смещением? ;///////////////////////////////////////////////////////////////////////////// mov [ebp+число], операнд lea операнд, [ebp+число] и т.д. ;//////////////////////////////////////////////////////////////////////////// Отступление: дельта-смещение,- по-другому, относительное смещение. Применяется в тех ситуациях, когда нежелательно иметь абсолютное смещение в программе, ну например, в тех же протекторах, им же придется прикрывать задницу многим программам, и всякий раз смещение в коде протектора будет другим. Снова тянемся к консоли Иды. Царапаем скрипт, выводящий в виде комментария реальный адрес из дельта-смещения. Вообщем, несложно, есть только один нюанс: если добавляемое смещение размером в слово,двойное слово, то в инструкции она занимает 4 байта, а если размером в байт - то 1 байт. Посмотрите сами в дамп по F4 на соответствующую инструкцию, думаю, станет, яснее. А теперь, примемся за дело - интерпретацию ассемблерного кода. Пошагово не буду конечно, обьяснять, так как я уже сейчас в сомнении, не перегнул ли я палку в стремлении обьяснить мысли как можно понятнее. Вначале сохраняются все восемь общих регистров, отметим про себя этот факт. Вычисляется Image_Base, проверяется флажок. Иницализируются регистры, используемые в последущем цикле расшифровки. Таак, а что это гвардеец-то вытворяет, ать? Решил не отставать от моды, приемчик с вызовом seh. ;/////////////// мусор пропущен. Не в обиде?;-P ////////////////////////////// ; коммент от 21 декабря ; перед каждой расшифровкой гвард всегда как я помню, вызывал обработчик ; исключения. сам гвардеец завернут зарег. версией, а для чистоты эксперимента ; я еще паковал им самим какую-то лошканутую прогу - нотепад. Там циклы ; шифровок равны 5. то есть в демо 5 кругов. pusha mov ebx, 657EAB6Eh ; заметьте чему будет push fs ; равен евх lea eax, [ebp+754C3D86h] ; код установки pop es ; add eax, 8AF45129h ; SEH-фрейма или push eax ; как там правильно mov eax, es:[ebx-657EAB6Eh] ; его еще назвать push eax ; mov es:[ebx-657EAB6Eh], esp ; mov [ebx], edx ; что за фигня? ... jmp далее Втихаря наш гвардеец пытается установить seh-обработчик. И если мы в обезжучивателе отлаживаем такой код, то Сайс как танк попрет прямо до джампа с последующим гейм овер. Ему пофиг, он в нулевом кольце, что хочет то и делает. А ведь в евх совсем левый адрес, и прога просто пишет в космос. Этого винда не может стерпеть и заморозив программу, начинает шарить в стеке проги в поисках seh-обработчика. И передает ему управление, если таковой не имеется, ищет дебаггер(вроде бы два раза его она ищет) и если ничего путного в стеке не находит, сама приканчивает лопухнувшуюся программу. В нашем случае гвардеец чин-чинарем ловит seh и уже с seh-обработчика переходит на цикл расшифровки. Можете это сами проверить, благо, при непереходе на seh флаг направления сброшен, а при переходе - поднят. Немного о циклах расшифровки. В полной, зарегистрированной версии их должно быть более пяти, а в демо их всего пять. Код циклов в Ида-скрипт переводить стоит, так как они не метаморфны и в разных программах меняется только адрес начала зашифрованного блока и байт, используемый для расшифровки. Да и просто кайфово заниматься этими битами. Между циклами гвардеец потеет в попытках запрыгнуть Сайсу на спину:-) Фуу, кончилась треклятая расшифровка. Дальше можно просто продолжить интерпретацию работы протектора. Три самые первые саll-ы инициализируют импорт PC Guard-а. Чтобы в дальнейшем не долбаться с поиском апи-функции, инициализируем и мы тоже. Оу, что за конфетка! Быстренько лезем в 4 процедуру. Даа, у гвардейца губа не дура. переход на seh с помощью DebugBreak, а оттуда уже начинает методично открывать драйвера дебаггеров, даже IsDebuggerPresent не забыл. Ну и, как верх наглости, покушение на святыни Сайса: xor eax, eax mov cr0, eax mov edx, cr0 cmp edx, eax mov cr0, edx cmp edx, eax cmp ebx, edx cmp ebx, edx mov ebx, cr0 cmp eax, ebx mov dr0, eax mov dr1, eax mov dr2, eax mov dr3, eax mov dr5, eax mov dr6, eax mov dr7, eax not esp not esp neg esp neg esp inc esp dec esp Хотя вру. Это будет где-то позже. Ну ничего, смысл не меняется. Дальше. Видим, что после 4 процедурки программа читает двойное слово и проверяет его с каким-то значением. Не знаю, как вы , а мне кажется :-))), что это проверка флага. Немного поисследвав дальше, нам в общем, уже становится ясен принцип работы протектора. Имеется двойное слово, хранящее в себе флаги. Гвардеец читает этот dword, проверяет флаг, если поднят, вызывает процедуру. То есть все идет линейно, так мы скоро и до ОЕР доберемся;-))). Какие процедурки мне там запомнились, опишу-ка в порядке их расположения. ; коммент от 21 декабря ; ниже приведены флаги , то есть гвард читает флаг и если он уст., то ; незамедлительно вызывает процедуру-обработчик. 0х10000000 = Малоизучен. Забивает в РЕ-хидере количество секций случайным числом. : 21 декабря ; ясен пень, канечно, шобы сделать нераьотоспособным дамп. ; а то у меня такой проикол был, открываю в Хью (великая вещь) такой ; дамп , а там у меня 354 секций. Хех. 0х1000 = Проверка на хуканье функций из импорта. SetErrorMode - обработку ошибок берет на себя( во имя seh-а). GlobalAlloc. И дальше вниз проверка на наличие Сайса в NT. GlobalFree. Конец проверки ntice. 0x2000 = Вызов диалога, запрашивающего пароль. диалог создается библиотечкой, она также возвращает пароль в буфере. 0х1000000 = Проверка имени модуля. Снова вызов процедуры антитрейсер. А тут далее идет череда проверок флагов. Извиняюсь, но их я не изучал, конечно, это мне не делает чести, но все, что могу сказать, то что связаны они с Remote protect. method и Identification code protect. method. После этого снова вызов антитрайсера и затем процедура, в которой происходит расшифровка секций. Сама процедурка зашифрована тоже, для ее расшифровки используется контрольная сумма процедуры антитрайсера. ;////////////////////////////////////////////////////////////////////////////// ; 21 декабря ; что я тут хотел сказать етим кускои кода? ; попробую прокмментировать loop do_decrypt ; lea esi, info_block ; Читается структура данных, xor eax, eax ; хранящая в себе смещение not eax ; ориг. секции, ее размер, mov _section_crc_, eax ; и контрольная сумма этой секции. mov eax, [esi] ; Если массив таких данных зак-ся, test eax, eax ; то проыгаем к расшифровке jz near ptr info_blocks_ended ; импорта? Точно не помню. add eax, Image_Base ; А так, расшифровываем. mov edi, eax ; mov ecx, [esi+4] ; call section_decrypt ; ;////////////////////////////////////////////////////////////////////////////// Итак, что мы видим. После расшифровки тела процедурки esi устанавливается на начало очень интересного массива = самой важной вещи в протекторе. Смотрите сами: ;////////////////////////////////////////////////////////////////////////////// number_of_info_blocks dd info_block_1 struc ; сюда указывает esi section_RVA dd ? section_size dd ? section_CRC dd ? info_block_1 ends ;////////////////////////////////////////////////////////////////////////////// Лезем дальше. Чувствуете, становится все горячее и горячее, это мы подбираемся к сокровенным местам гвардейца. ;////////////////////////////////////////////////////////////////////////////// section_decrypt proc near mov ebx, xorek_1 mov edx, xorek_2 loop_: push ecx push edi xor ebx, edx add edx, ebx lea edi, array_for_decrypt ;бинарная строка для push edx ;криптовки-раскриптовки push ebx mov eax, edx xor edx, edx mov bx, 0Ah div bx mov eax, [edi+edx*4] pop ebx pop edx xor ebx, eax rol ebx, 1 xor ebx, password_hash ; вот етот хеш должен быть xor edx, ebx ; инициализирован значением add edx, eax ; при защите паролем ror edx, 1 sub ebx, edx xor edx, password_hash add bl, dl pop edi sub [edi], bl push eax mov eax, _section_crc_ xor al, [edi] rol eax, 1 mov _section_crc_, eax pop eax inc edi pop ecx loop loop_ retn section_decrypt endp Какая лакомая процедурка, у меня аж слюнки текут. Вкусность первая, xorek_2 при Identification Code protect. method не содержит значения. Оно туда записывается после вычисления машинно-привязанного кода(как я понял). При наличии только этой защиты протектор элементарно сносится. Вкусность вторая, десерт. Если не применяются остальные методы защиты, то шифровка паролем - это просто защита от дурака. Наверное, уже заметили, что что в расшифровке мелькает переменная password_hash. Да-а-а, прикол. Берет пароль и крошит его( пускай он будет хоть километровой длины) в двойное слово. Такс, рассмотрим, ситуацию. Допустим, есть прога защищенная паролем, к-рый используется в раскриптовке секций. Реальный пароль мы не знаем конечно. Но нам известна дыра, и какая большая. Для расшифровки используется всего- то двойное слово, и это в нынешнее-то время? Нам всего-то нужно будет найти точно первые несколько байт любой зашифрованной секции, и мы снесем этот протектор к черту. И у нас есть такая возможность, посудите сами: ; почему то у меня софтина тока от Бормана под рукой оказалася У Бормана: Delphi: в кодовой секции типа "Boolean", перед ним еще какой-то dword. rеlоc. может начинаться с 00100000 С++ Builder: CODE - типа C++HOOK Borland C++: DATA - Borland C++ ля-ля-ля и т.п. Да, лазейку, короче, можно, найти. Расшифровка при знании только четырех байт может выдать дофига коллизий, т.е. вхождений. При знании 8 байт - максимум 8 вхождений. Так, окрошку (hash) получили, нужно найти из них одну-единственную правильную. Нам в этом поможет фича гвардейца, контрольная сумма зашифрованной секции. Выбираем секцию поменьше размером и расшифровываем. Если CRC совпадают, то вот она, штучка, что снесет "крышу". Насчет моего исполнения (сильно сказано - 21 декабря) переборщика хеш-паролей смотрите в приложении. Время полного перебора (проц P-100) - 40-50 минут. При одновременном использовании и IDCODE, и пароля придется перебрать 0xffffffff*0xffffffff вариантов (без оптимизации). Математика , конечно, может дать зрение, но я, честно признаюсь, не очень силен в ней. Одно я знаю, эту задачу мой сотый пень(классика!) точно не потянет. Вернемся к нашим баранам. на чем же мы остановились? То бишь, расшифровка секций. А , черт с ней. Выбираемся из процедуры. Осматриваемся. Значит, флаг 0х20000 = проверка crc раскриптованной секции. После завершения расшифровки секций протектор проверяет значение какого-то dword-а на равенству нулю. Если нет, прямиком к процедуре расшифровки секции ресурсов. Как вы могли уже догадаться , это двойное слово является ничем иным, как RVA секции .rsrc, о чем может сказать хотя бы код, добавляющий к нему Image_Base. От того, что я увидел в этой процедурке, меня сильно торкнуло. Зашифровывать каждый лист дерева ресурсов, о какой кайф!!! Вообщем, применяется рекурсивная функция обхода дерева ресурсов, ничего сложного, но почему-то у меня пока не вышло переписать его на Ида-скрипт. Снимал поэтому из оперативки. А теперь, шнелля, шнелля из процедурки , в то место, откуда мы вошли в эту задницу. Одну прцедурку пропустив, мы сможем войти в другую процедурку. Во как! Тама инициализируется уже импорт защищаемой программы. Эту процедурку вначале вроде бы тоже нужно расшифровать.(почему вроде бы - так как я пишу материальчик уже после распаковки, вынюхивая кое-какие остаточные следы;во время исследования как-то влом писать всякий бред, потому пишу щас, после того как принял ванну, вы пил кофэ... кхе... куда меня понесло. Чтобы не быть голословным, приведу пример кода. decrypted_9: xor FOR_OEP, ebx mov ebx, offset_import_dir add ebx, Image_Base mov ecx, iat_size get_next_lib_: mov edx, [ebx+0Ch] ; проверка импортир. библы test edx, edx ; если 0 - то больше нет jz end_idata ; импортир. библиотек mov edx, [ebx] Смотрим на первую строчку кода, там идет процесс создания OEP(будет готов ровно наполовину). Остальной код связан с созданием импорта. Что еще интересное. А-а-а, гвардеец может затирать имена импортируемых библиотек и функций. флаг для этого святотатства = 0х4000000. Отчаливаем из этой процедурки. Если создание импорта прощло нормально, то процедура возвращает -1, иначе ноль. Прыгаем по jnz куда-то. Оппа, снова расшифровка, и тут видим, что здесь используется ранее нами упомянутая переменная FOR_OEP. Смотрите, восстанавливается cтарое состояние обработки ошибок, почему-то затирается почти весь код протектора, ничего не понимаю, потом после выпендрежа переменная FOR_OEP снова хорится с каким-т0 двойным словом, видимо получается ОЕР, так как он складывается с Image_Base и ложится на стек. Ничего не понимаю, что-то рот раастянулся до ушей и не закрывается, глазки еще слипаются, ваще смогу ли всю эту фигню дописать? Так, потом восстанавливаются все восемь регистров и протектор куда -то переходит по адресу из стека, может кто-нить знает? P.S. есть у меня одна хорошая новость, PC Guard запротектен самим собой 2 раза. Хе-хе-хе. Долбайтесь, друзья мои, на здоровье. А я за сим откланиваюсь. Нет, нет, подождите, это было только для красного словца, нужно все-таки кое- какие выводы сделать. Вывод, для кракеров: Для снятия защиты в самом простом случае нужно всего лишь поставить бряк на чтение сохраненного регистра ЕВР. Для восстановления импорта мона пользоваться какой-нить специфической прогой, но хотя я сам обеими рогами, хвостом, копытами и т.п. за сено, фуу, пора, пора. ; коммент от 21 декабря ; гвард восстанавливает евр и спустя несколько строчек ниже ; ваще откидывается на стек с ОЕР. Вывод, просто вывод. После исследования защиты у меня возникло такое чувство, что протектор написан всецело одним человеком, хотя в проге в "о нас" гордо красуется Software Protections Labs. В общем, автора немного унесло в сторону разбавления качества количеством. И за этим количеством он не смог увидеть элементарные ошибки. Кстати, вместе с библиотекой протектор в защищаемой проге будет занимать около 200 kb. Ну в общем мы и дальше ждем протекторов хороших и разных. ПРИЛОЖЕНИЕ. ; 21 декабря ; попытался как-то переделать этот позор. В приложении находятся скрипты для Иды 3. 7 / 4.15 (вроде бы должны на них запускаться). Код был написан чисто для себя, а с написанием материала решил толкнуть их вместе с матерьялчиком. Может, кому-нить понадобятся. ;////////////////////////////////////////////////////////////////////////////// Использование всей этой рухляди - скопировать в отдельный файл с расширением *.idc и вызывать необходимую функцию с консоли. процедуры w32_1 - w32_6 - циклы распаковки. Использовать примерно так - встаем на смещение с зашифрованным кодом (куда передается управление после расшифровки через jmp ...) и печатаем в консоли w32_1 (ScreenEA(),size, byter);size, byter находите сами есстественно. ; size - это размер криптованного куска кода/данных. ; byter - сие есть магическое число для расшифровки кода. section_decrypt() - декриптор секции чтобы запустить, сначала в скрипте нужно указать начальные значения. init_import() - создание импорта протектора Встаем на смещение, куда будут писаться реальные адреса функций импортируемой библиотеки, указываем примерный адрес имен функций и их количество. reader() - забой мусора. Встаем на любой адрес и запускаем. kill_delta() - вычислить реал_аддрес из дельта_смещения. Base = ebp, индекс обычно должно быть равно 2, а размер 4. Встаем на нужную инструкцию. brutforcer - на TASM. Запускается, считает себе втихаря в памяти, при ошибке или окончании работы выводит окошко и завершается. Просто, как в танке. Чтобы узнать, сколько там осталось ему обработать чисел, входим в Сайс, и в большинстве случаев, когда больше не запущено приложений, прерваться должны именно на нем. Смотрим на ecx и делаем выводы. В edi будет количество найденного, нужно лишь разделить его на 4. /////////////////////////////////////////////////////////////////////////////// static w32_1(addr,size,byter) { auto ecx, edx, edi, esi, i, cl, ah, al, j, temp; ecx = size; edx = addr-1; edi = addr;esi=edx; for ( i = size; i > 0; i -- ) { cl = ecx; cl = cl & 0x1f; if (cl==0) esi=edx; al = Byte(esi--); ah = Byte(edi); temp = ah; ah = al; al = temp; for (j = cl; j>0; j--) { temp = al; temp = temp & 0x80; temp = temp >> 7; al = (al & 0xff) << 1; al = (al & 0xff) | temp; } al = al^byter; al = al^ah; PatchByte(edi++,al);ecx--; } } ///////////////////////////////////////////////////////////////////////////// static w32_2(addr,size,byter) { auto ecx,edx,edi,esi,i,cl,ah,al,j,temp; ecx = size; edx = addr-1; edi = addr;esi=edx; for ( i = size; i > 0; i -- ) { cl = ecx; cl = cl & 0x1f; if (cl==0) esi=edx; al=Byte(esi--); ah=Byte(edi); temp=ah; ah=al; al=temp; al=al^ah; al=(al+byter)&0xff; temp=al; temp=temp&0x1; temp=temp<<7; al = (al & 0xff) >> 1; al = (al & 0xff) | temp; cl=ecx; cl = cl & 0xff; al= al^cl; PatchByte(edi++,al);ecx--; } } ///////////////////////////////////////////////////////////////////////////// static w32_3(addr,size,byter) { auto ecx,edx,edi,esi,i,cl,ah,al,j,temp,bl; ecx = size; edx = addr-1; edi = addr;esi=edx;bl=0; for ( i = size; i > 0; i -- ) { cl = ecx; cl = cl & 0x3f; if (cl==0) esi=edx; al=Byte(esi--); ah=Byte(edi); bl=(bl-al)&0xff; temp=ah; ah=al; al=temp; al=al^byter;al=(al-bl)&0xff;cl=cl&0x1f; for (j=cl; j>0; j--) { temp=al; temp=temp&0x80;temp=temp>>7; al=(al&0xff)<<1;al=(al&0xff)|temp; } PatchByte(edi++,al);ecx--; } } ///////////////////////////////////////////////////////////////////////////// static w32_4(addr,size,byter) { auto ecx,edx,edi,esi,i,cl,ah,al,j,temp,bl; ecx = size; edx = addr-1; edi = addr;esi=edx;bl=0; for ( i = size; i > 0; i -- ) { cl = ecx; cl = cl & 0x3f; if (cl==0) esi=edx; al=Byte(esi--); ah=Byte(edi); bl=bl^al; temp=ah; ah=al; al=temp; al=(al-byter)&0xff;al=(al-bl)&0xff; temp=al; temp=temp&0x1;temp=temp<<7; al=(al&0xff)>>1;al=(al&0xff)|temp; PatchByte(edi++,al); ecx--; } } ///////////////////////////////////////////////////////////////////////////// static w32_5(addr,size,byter) { auto ecx,edx,edi,esi,i,cl,ah,al,j,temp,bl; ecx = size; edx = addr-1; edi = addr;esi=edx;bl=0; for ( i = size; i > 0; i -- ) { cl = ecx; cl = cl & 0x3f; if (cl==0) esi=edx; al=Byte(esi--); ah=Byte(edi); bl=bl^al; temp=ah; ah=al; al=temp; al=al^byter;al=al^ah;al=(al+bl)&0xff; PatchByte(edi++,al); ecx--; } } ///////////////////////////////////////////////////////////////////////////// static w32_6(addr,size) { auto ecx,edx,edi,esi,i,cl,ah,al,j,temp; ecx = size; edx = addr-1; edi = addr;esi=edx; for ( i = size; i > 0; i -- ) { cl = ecx; cl = cl & 0x1f; if (cl==0) esi=edx; al=Byte(esi--); ah=Byte(edi); temp=ah; ah=al; al=temp; temp=al; temp=temp&0x1;temp=temp<<7; al=(al&0xff)>>1;al=(al&0xff)|temp; al=al^ah; PatchByte(edi++,al); ecx--; } } ///////////////////////////////////////////////////////////////////////////// static section_decrypt { auto edi,eax,ebx,edx,bx,al,dl,bl,temp,crc,i,size,addr,hash; ; Вот эту всю фигну нуно заполнить до запуска скрипта. ; значения регистров - из контекста кода. ; crc оставить также. ; size, addr - это думаю, члены вышеупомянутой структуры info_block edi= ; ebx= ; edx= ; hash = ; crc=0xffffffff; size= ; addr = ; for (i=size;i>0;i--) { ebx=ebx^edx; edx = edx+ebx ; temp = 0; temp = (edx & 0xffff) % 0xa; eax = Dword(addr + temp*4); ebx = ebx^eax; temp=ebx; temp=temp & 0x80000000; temp=(temp >> 31) & 0x1; ebx = ebx << 1; ebx = ebx | temp; ebx = ebx ^ hash; edx = edx ^ ebx; edx = edx+eax; temp= edx; temp= temp & 0x1; temp= temp << 31 ; edx = (edx >> 1) & 0x7fffffff; edx = edx | temp; ebx = ebx-edx; edx = edx^hash; bl = ((ebx & 0xff) + (edx & 0xff)) & 0xff; ebx = (ebx & 0xffffff00) | bl; temp = Byte(edi); temp = (temp - bl) & 0xff; PatchByte(edi,temp); temp = crc & 0xff; temp =temp^(Byte(edi)); crc = ( crc & 0xffffff00 ) | temp; temp=crc; temp=temp & 0x80000000; temp=(temp >> 31) & 0x1; crc = crc << 1; crc = crc | temp; edi++; } Message( "\n %x \n", crc); } ///////////////////////////////////////////////////////////////////////////// static init_import (adr,count) { auto strig,addr,i; addr=ScreenEA(); for(i=0;i