dllmain

троянизация приложений — технологии XXI века

крис касперски ака мыщъх, a.k.a nezumi, no-email

в хакерской практике достаточно часто возникает потребность внедрить в готовый exe-файл свой код (не обязательно вредоносный ;-) так, чтобы он получал управление первым, не вызывал ругательств со стороны антивирусов и вообще по возможности вел себя максимально скрытно, не бросаясь в глаза. как это сделать? мыщъх знает как. и сейчас обстоятельно расскажет. но сначала слегка покурит. итак…

Существует целое семейство утилит, предназначенных для «склейки» нескольких программ в один файл, с общим названием «джойеры» (от англ. joiner — соединитель): Joiner by Blade, SuperGlue, MicroJoiner, Juntador и множество других, подробный обзор которых можно отрыть в статье «Клейкий софт» из #65 Хакера (http://www.xakep.ru/magazine/xa/065/044/1.asp), однако, качество склейки оставляет желать лучшего: большинство джойнеров просто помещают внедряемый файл в оверлей основного exe файла и при запуске копируют его на диск во временную папку или (еще чаще — в системный каталог Windows, прав записи в который у простого пользователя, не сидящего под администратором, разумеется, нет, и операция накрывается медным Тазом), но в любом случае, копирование занимает какое-то время, замедляя загрузку программ, что рождает в голове пользователе смутные подозрения.

Более совершенные джойнеры работают по принципу упаковщиков исполняемых файлов и проецируют внедренный exe непосредственно в оперативную память, что не только ускоряет загрузку, но и с точки зрения PE-формата выглядит намного более полит. корректно («честный» PE-файл с оверлеем — это вообще-то редкость).

Однако, все джойнеры без исключения рано или поздно попадают в антивирусные базы, поскольку представляют собой _готовые_ утилиты, в которых легко выделить постоянную сигнатуру (даже если они выполнены на полиморфной основе).

Правильные хакеры ведут себя не так и склеивают программы самостоятельно. Это совсем нетрудно сделать! Понадобиться лишь верный друг hiew и минимальные навыки программирования на Си. Ну и, конечно, трава. Без травы сознание коллапсирует в черную дыру, хвост висит и мозг ни хрена не работает. Впрочем, мы увлеклись. Вернемся к технической стороне проблемы, но прежде сделаем одно важное техническое замечание.

Внедряемый код необязательно должен быть вирусом, червем, rootkit'ом или любой другой _вредоносной_ заразой (тем более, что для одного вред, для другого благо, взять хотя бы свинью и хохла), поэтому, во избежание наездов на мыщъх'а и прочих недоразумений, условимся называть внедряемый код X-кодом.

Примерыисполняемых файлов, прилагаемые к настоящей статье, совершенно безвредные и могут запускаться без опаски, как с правами администратора, так и без таковых. Хорошая идея — прогнать их через всех имеющихся у читателя антивирусов и пронаблюдать за их реакцией.

Всякий exe-файл, импортирует одну или несколько динамических библиотек (Dynamic Link Library или, сокращенно, DLL), прописанных в таблице импорта. При запуске exe-файла, системный загрузчик анализирует таблицу импорта, загружает все перечисленные в ней динамические библиотеки, вызывая функцию DllMain для инициализации каждой DLL, и только после этого передает управление на оригинальную точку входа (Original Entry Point или, сокращенно OEP) запускаемого exe-файла.

Таким образом, если мы добавим в таблицу импорта подопытного exe-файла «свою» DLL, проблема внедрения X-кода будет успешно решена. Самое замечательное, что DLL может быть написана на любом языке (хоть на ассемблере, хоть на DELPHI) и совершенно неподвластна антивирусам, поскольку они органически неспособны распознавать неизвестную заразу. Ах, да… Эвристические анализаторы. Но эти штуки очень легко обойти (чему посвящено множество статей, в том числе и моих, которые можно найти на nezumi.org.ru).

Прежде чем вторгаться в базовые структуры PE-файла, неплохо бы получить хотя бы общее представление о его устройстве. В MSDN входит спецификация на PE-формат («Microsoft Portable Executable and Common Object File Format Specification»), написанная на враждебном для нас английском языке и ориентированная главным образом на честных программистов. Мыщъх исправил этот недостаток, переписав спецификацию на русский язык и переориентировав ее на хакеров. Электронная копия доступа для бесплатной скачки по адресу http://nezumi.org.ru/souriz/PE-desc-n-inject.zip.

ОК, теперь… нет, курить мы пока не будем, а подготовим «дрозофилу», предназначенную для внедрения X-кода, пример которой, написанный на языке Си, приведен ниже:

#include <stdio.h>

main()

{

printf(«I'm nezumi\n»); выводим что-нибудь на экран } Листинг 1 файл-дрозофила «inject.c», предназначенный для внедрения X-кода Компилируем ее любым подходящим компилятором (например, в случае Microsoft Visual C++ командная строка будет выглядеть так: «$cl.exe inject.c»), после чего на диске образуется файл inject.exe, выдающий при запуске следующее приветствие на экран (мыщъх обращает внимание, что это _консольная_ программа, которая должна запускаться из-под FAR'а или штатного командного интерпретатора cmd.exe, при запуске из «Проводника» окно консоли автоматически закроется сразу же после завершения программы и мы ни хвоста не увидим): $inject.exe I'm nezumi Листинг 2 результат работы программы-дрозофилы до внедрения X-кода Разобравшись с дрозофилой, займемся подготовкой динамической библиотеки, то есть, того самого X-кода, который мы будем внедрять внутрь inject.exe. В простейшем случае, исходный код будет выглядеть так: #include <stdio.h> #include <windows.h> создаем фиктивную экспортируемую функцию,

которую потом будет импортировать дрозофила __declspec(dllexport) int dummy(){ return 0;} /* точка входа в dll, получающая управление при различных обстоятельствах */ BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { приветствие, выводимое до запуска дрозофилы

if (fdwReason==DLL_PROCESS_ATTACH) printf(«hello,world!\n»);

приветствие, выводимое перед завершением работы дрозофилы if (fdwReason==DLL_PROCESS_DETACH) printf(«good-bye,world!\n»); } Листинг 3 исходный код динамической библиотеки injected_dll.c, подготовленной к внедрению Обратите внимание, что DllMain в отличии от EntryPoint вызывается _многократно_ а) при подключении к процессу; б) при завершении процесса или выгрузки динамической библиотеки API-функцией FreeLibrary; в) при создании процессом нового потока; г) при завершении одного из существующих потоков процесса. Другими словами, DllMain позволяет отслеживать определенные системные события и адекватным способном реагировать на них. В данном случае, мы выводим «hello,world!» перед запуском дрозофилы и «good-bye,world!» перед завершением ее работы. Компиляция динамической библиотеки осуществляется следующим образом: «$cl.exe injected_dll.c /LD», где ключ «/LD» сообщает компилятору, что необходимо сгенерировать именно DLL, а не EXE (как он делает это по умолчанию). ===== внедрение X-DLL в таблицу импорта дрозофилы ===== Берем, значит, hiew в свои загребущие лапы, открываем файл «inject.exe», переходим в hex-режим по <ENTER>, давим <F8> для отображения PE-заголовка, нажимаем <F10> (Dir) и среди прочих элементов IMAGE_DATA_DIRECTORYвыбираем секцию импорта (Import), расположенную в данном случае по RVA-адресу равному 5484h и раскинувшуюся в ширину на целых 28h байт (см. рис. 1). Клавиша <ENTER> переносит нас к структуре Import Directory Table, о которой мы поговорим чуть попозже, а пока обсудим, как найти указатель на Import Directory Table в отсутствии hiew'а. Двойное слово, лежащее по смещению 80h от начала PE-заголовка (легко опознаваемого визуально по сигнатуре «PE»),и будет RVA-адресом, указывающим на Import Directory Table, а следующее двойное слово хранит ее размер. Так что для поиска таблицы импорта, hiew совсем необязателен. Рисунок 1 поиск указателя на таблицу импорта в hiew'е и в PE-файле Таблица импорта представляет собой достаточно сложное сооружение иерархического типа. Вершину иерархии занимает структура Import Directory Table, фактически являющаяся массивом подчиненных структур типа IMAGE_IMPORT_DESCRIPTOR, каждая из которых содержит RVA-указатель на имя загружаемой динамической библиотеки, ссылки на OriginalFirstThunk и FirstThunk с именами/ординалами импортируемых функций (причем, поле OriginalFirstThunk не является обязательным и может быть равно нулю). Два других поля — TimeDateStamp (временная отметка) и ForwarderChain (форвардинг) так же необязательны и потому для подключения своей собственной DLL нам необходимо заполнить всего лишь два поля структуры _IMAGE_IMPORT_DESCRIPTOR: Name и FirstThunk, а так же создать таблицу Import Address Table (сокращено, IAT), импортирующую по меньшей мере одно имя (в данном случае «dummy»). typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORDCharacteristics; 0 for terminating null import descriptor

DWORDOriginalFirstThunk; RVA to original unbound IAT }; DWORDTimeDateStamp; TimeDateStamp

DWORDForwarderChain; -1 if no forwarders DWORDName; name of the dll

DWORDFirstThunk; RVA to IAT } IMAGE_IMPORT_DESCRIPTOR; Листинг 4 прототипструктуры IMAGE_IMPORT_DESCRIPTOR Если вместе стройной иерархии структур в нашей голове возникла каша, то не стоит волноваться — это нормально! По ходу дела она «утрясется» и все структуры усядутся по своим местам, так что оставим их «дозревать» и сосредоточимся на текущих проблемах. Чтобы внедрить X-DLL, в Import Directory Table необходимо добавить еще один экземпляр структуры IMAGE_IMPORT_DESCRIPTOR, но просто так сделать это не получится, поскольку сразу же за концом Import Directory Table начинается IAT первой динамической библиотеки и нам просто _некуда_ втиснуться, если, конечно… не перенести Import Directory Table в какое-нибудь другое место! А что?! И перенесем! Вот только покурим сначала или заточим хороший отбивной. Повторяем описанную последовательность действий с hiew'ом еще раз, перемещаясь в начало таблицы импорта (а точнее, как мы уже знаем, на первый элемент Import Directory Table), давим <Gray-*> («звездочку» на цифровой клавиатуре) и перемещаясь курсорными клавишами, выделяем бардовым цветом 28h байт (размер Import Directory Table), после чего давим <Gray-*> еще раз и нажав <F2> сохраняем блок в файл, для определенности назвав его idt-org (см. рис. 2). Рисунок 2 сохранение оригинальной Import Directory Table в файл idt-org Теперь прокручиваем файл, клацая <PageDown> до тех пор, пока не выйдем на оперативный простор свободного места, оккупированного длинной вереницей нулей. В нашем случае это место располагается по адресу 405810h, непосредственно за концом таблицы импорта (см. рис. 3). Рисунок 3 свободное место за концом таблицы импорта, пригодное для перемещения Import Directory Table Теперь нам необходимо скопировать оригинальную Import Directory Table на новое место, не забыв при этом зарезервировать место для одного элемента структуры типа IMAGE_IMPORT_DESCRIPTOR, в который мы чуть позже поместим «нашу» динамическую библиотеку, которая будет проинициализирована самой первой (что очень полезно для борьбы с антивирусами, «иммунизирующими» exe-файлы путем «прививки» им специальной dll-вакцины, выполняющий проверку целостности содержимого образа исполняемого файла). Поскольку, как нетрудно подсчитать (см. листинг 4), размер структуры IMAGE_IMPORT_DESCRIPTOR составляет 14h байт, а незанятая область начинается с адреса 405810h мы должны передвинуть курсор по адресу 405824h, задавить <Gray-*>, выделить 28h байт (размер оригинальной Import Directory Table), задавать <Gray-*> еще раз, _обязательно_ переместить курсор в начало выделенного блока, нажать <Ctrl-F2> (Get Block), ввести имя файла, в который мы только что сохранили блок — «idt-org»и считать его с диска (см. рис. 4). Рисунок 4 оригинальная Import Directory Table, перемещенная на новое место Теперь возвращаемся в начало файла и корректируем RVA-адрес таблицы импорта, который в данном случае составит 5824h. Почему 5824h, а не 405824h?! Да потому, что RVA адреса получаются путем вычитания базового адреса (прописанного в заголовке PE-файла и равного в данном случае 400000h) от виртуального адреса (равного в данном случае 405824h). Причем, с учетом порядка байт старшинства байт, принятого на процессорах x86 (младшие биты располагаются по меньшим адресам), мы должны записать 24 58 (см. рис. 5), а не 58 24 как поступают многие начинающие хакеры, а потом удивляются почему оно не работает. Значит, открываем файл «inject.exe»в hiew'e, находим «PE» сигнатуру, опускаем курсор вниз на 80h байт, видим там 84 54 (см. рис. 1), нажимаем <F3> для разрешения редактирования, меняем адрес на 24 58, сохраняем изменения по <F9> и выходим… за пивом. Пиво для хакеров — это святое! Рисунок 5 корректируем указатель на новую таблицу импорта Проверяем работоспособность файла — вдруг она пострадала?! Запускаем «inject.exe»и (если все операции были проделаны правильно) на экране появится триумфальное приветствие. В противном же случае — система откажется загружать файл или выбросит исключение. $inject.exe I'm nezumi Листинг 5 результат работы программы-дрозофилы после переноса таблицы импорта, как видно, ни одного тушканчика в результате экспериментов не пострадало Смочив пересохшее горло пивом, приступаем к самой сложной и самой ответственной части — заполнению структуры IMAGE_IMPORT_DESCRIPTOR. Начнем с того, что переместим курсор в конец Import Directory Table, подогнав его к адресу 405850h и запишем имя функции-пустышки («dummy»), оканчивающееся нулем и предваренной двумя нулями, а следом за ним – имя внедряемой динамической библиотеки «injected_dll.dll» (впрочем, порядок их расположения может быть и другим, системному загрузчику на такие мелочи уже давно положил). Сделав это, перемещаемся на первый байт, ранее зарезервированный нами для структуры IMAGE_IMPORT_DESCRIPTOR, и начинаем колдовать. Первое двойное слово оставляем равным нулю. За ним идут 4 байта, отведенные для TimeDataStamp и мы, желая слегка извратиться, занесем сюда IAT, т. е. двойное слово, содержащее RVA-адрес импортируемой функции. В нашем случае эта функция зовется «dummy», а ее имя (предваренное двумя нулями!) начинается с RVA-адреса 5850h. Учитывая обратный порядок байт x86,пишем: 50 58. Пропустив следующее двойное слово (Forwarder Chain), в поле «Name» записываем RVA-адрес имени внедряемой динамической библиотеки «injected_dll.dll», равный в нашем случае 5858h. Остается заполнить последнее поле — Import Address Table, содержащее RVA-адрес таблицы IAT, размещенной нами поверх поля TimeDateStamp с RVA адресом равным 5814h. Вот, собственно говоря, и все… После добавления новой структуры IMAGE_IMPORT_DESCRIPTOR в массив Import Directory Table последний будет выглядеть так (см. листинг 6). .00405810:00 00 00 00-50 58 00 00-00 00 00 00-58 58 00 00 PX XX .00405820:14 58 00 00-AC 54 00 00-00 00 00 00-00 00 00 00 ¶X мT .00405830:00 58 00 00-00 50 00 00-00 00 00 00-00 00 00 00 X P .00405840:00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00 .00405850:00 00 64 75-6D 6D 79 00-69 6E 6A 65-63 74 65 64 dummy injected .00405860:5F 64 6C 6C-2E 64 6C 6C-00 00 00 00-00 00 00 00 _dll.dll Листинг 6 Import Directory Table с внедренной X-DLL Остается сущая мелочь. Вернуться в начало файла, отсчитать от «PE» заголовка 80h байт, исправив указатель на таблицу импорта с 5824h на 5810h и увеличив ее размер до 3Сh. Сохраняем проделанные изменения и, набрав побольше воздуха в грудь, залпом выпиваем оставшееся пиво и запускаем файл «inject.exe»: $inject.exe hello,world! I'm nezumi good-bye,world! Листинг 7 демонстрация работы внедренной динамической библиотеки Оторвать мыщъху хвост!!! Это работает!!! Причем, не просто работает, а очень даже хорошо работает и внедренная динамическая библиотека послушно выводит «hello,world!» еще до запуска файла-дрозофилы и «good-bye,world!» непосредственно перед ее завершением! Красота!!! Вот только… посторонняя dll нам очень сильно мешает, вызывая естественное желание задвинуть ее куда-нибудь подальше. И тут мы плавно переходим ко второй части нашего рассказа. ===== копирование X-DLL в NTFS-stream ===== Файловая система NTFS выгодно отличается от FAT'а тем, что поддерживает потоки (streams), так же называемые атрибутами (attributes), но чтобы не путать их с атрибутами типа «только на чтение», мы будем придерживаться первого термина. Каждый файл имеет по меньшей мере один безымянный поток, хранящий актуальные данные файла. Именно его размер высвечивает «Проводник» Windows и продвинутые файловые менеджеры типа FAR'а. Однако, мы можем создавать и дополнительные потоки, отделяя их имя от имени файла знаком двоеточия («:»), например, my_file:my_stream1, my_file:my_stream2. Штатные средства Windows не поддерживают работы с именованными потоками и потому добраться до их содержимого не так-то просто. Не существует никакой (стандартной) возможности определить — имеет ли данный файл именованные потоки или нет. Чувствуете, куда я клоню? Давайте спрячем X-DLL внутрь «inject.exe», поместив ее в именованный поток. Внимание! Это совсем не тоже самое, что в «тупую» склеить два файла как и поступает большинство джойнеров. При копировании X-DLL в NTFS-поток видимый размер «дрозофилы» _не_ увеличивается и при открытии файла «inject.exe»функцией fopen(«inject.exe», «rb») _никаких_ следов присутствия X-DLL в ней _не_ окажется!!! Более того, при передачи файла через http для проверки антивирусной службой в on-line, передается только безымянный поток (содержащий полезную программу, _без_ X-DLL) и, естественно, антивирусы в ней ничего не обнаружат. Кстати говоря, большинство антивирусов сканируют только безымянный поток! Так что X-DLL может чувствовать себя в относительной безопасности, тепле, сухости и комфорте. Открываем «inject.exe»в hiew'е, привычным движением переходим к таблице импорта: <ENTER>, <F8>, <F10>, <стрелка вниз>, <ENTER> и заменяем имя динамической библиотеки «injected_dll.dll» на «inject.exe:x.dll«, где «inject.exe» —имя подопытного файла, в который мы собираемся внедрить X-DLL, а «x.dll» – имя самой внедряемой динамической библиотеки Рисунок 6 переименование внедренной динамической библиотеки для вызова ее из именованного NTFS-потока Теперь необходимо _скопировать_ «injected_dll.dll» в «inject.exe:x.dll», что легко осуществить с помощью FAR'а. Подогнав курсор к динамической библиотеке «injected_dll.dll», нажимаем <Shift-F5> и пишем «inject.exe:x.dll». Все! По завершению копирования исходную динамическую библиотеку «injected_dll.dll» можно удалить. Больше она нам не понадобиться! Кстати говоря, размер файла «inject.exe» после создания нового именованного потока не увеличился ни на байт!!! Дисковые ревизоры (вместе с прочими системами контроля) — отдыхают!!! Рисунок 7 копирование внедряемой библиотеки в именованный NTFS-поток Запускаем файл «inject.exe» и убеждаемся, что его работоспособность в результате последних махинаций ничуть не пострадала: $inject.exe hello,world! I'm nezumi good-bye,world! Листинг 8 результат работы дрозофилы, несущий внутри себя именованный поток с внедренной динамической библиотекой ===== переход от теории к практики ===== Внедрение своей собственной динамической библиотеки это, конечно, очень хорошо, однако, на практике гораздо чаще приходится сталкиваться с тем, что требуется внедрить _чужой_ исполняемый файл. Что делать?! Преобразовывать его в DLL?! Конечно же нет! Достаточно просто слегка доработать нашу X-DLL, научив ее запускать exe-файлы посредством API-функции CreateFile, при этом сами исполняемые файлы можно (и нужно) поместить в именованные NTFS-потоки, число которых фактически неограниченно. Причем, если внедряемый exe тащит за собой динамические библиотеки или другие компоненты, они так же могут быть внедрены в NTFS-потоки (естественно, в текущем каталоге их уже не окажется и потому исполняемый файл придется подвергнуть правке на предмет изменения всех путей). Если же этот файл упакован (а большинство «боевых» утилит типа систем удаленного администрирования редко поставляются в открытом виде), наша X-DLL может перехватить API-функции CreateFile/LoadLibrary, автоматически отслеживания обращения к отсутствующим файлам и подсовывая вместо них соответствующие им именованные NTFS-потоки. Другой немаловажный момент. Отправляя exe-файл с внедренной в него X-DLL по почте, записывая его на лазерный диск или любой другой не-NTFS носитель, мы теряем _все_ именованные потоки и программа тут же отказывает в работе, ругаясь, что не может найти dll (см. рис. 8): Рисунок 8 результат работы inject.exe, после пересылки по электронной почте Ситуация кажется критической, можно даже сказать драматической, но на помощь приходит благородный архиватор RAR, обладающий уникальной способностью сохранять все NTFS-потоки, какие только есть в файле. Запускаем RAR (см. рис. 9), выбираем «inject.exe», нажимаем кнопку «добавить» (или давим <CTRL-A>), после чего в свойствах архива взводим галочку «сохранять файловые потоки» (вкладка «дополнительно»). Так же, при желании можно создать SFX-архив на тот случай, если у получателя не окажется RAR'а, но это уже технические детали. Рисунок 9 использование RAR'а для сохранения именованных потоков Повторяем процедуру пересылки файла по электронной почте еще раз, распаковываем полученный архив, запускаем «inject.exe» и… о чудо! Он работает (см. листинг 9), как самый настоящий хомяк! $inject.exe hello,world! I'm nezumi good-bye,world! Листинг 9 результат работы «inject.exe» после пересылки по электронной почте в упакованном rar-архиве ===== заключение ===== Технологии внедрения в исполняемые файлы не стоят на месте и развиваются вместе с защитными механизмами и операционными системами. Извечная проблема меча и щита — кто усовершенствуется первым. Использование готовых утилит, работающих в полностью автоматическом режиме, это во-первых, не престижно, а, во-вторых, слишком ненадежно. Разработчики антивирусов даром свой хлеб не едят! Чтобы не погореть на мелочах, весь X-код следует писать самостоятельно — своими лапами и хвостом. До тех пор, пока он существует в единственном экземпляре, у защитных систем не остается никакого шанса предотвратить атаку! ===== »> врезка всегда заметай за собой следы ===== Некоторые файлы (особенно упакованные протекторами) скрупулезно следят за своей целостностью и на попытку внедрения реагируют прямо так скажем не совсем адекватно, однако… поскольку X-DLL получает управление поперед остальных, она может восстановить таблицу импорта в памяти, как будто все так и было. Словно, к ней никто и не прикасался. Для этого достаточно вызывать API-функцию VirtualProtect, присвоив соответствующим регионами памяти атрибут PAGE_READWRITE, восстановить таблицу импорта (оригинал которой легко сохранить в X-DLL), а затем восстановить атрибут PAGE_READONLY с помощью все той же VirtualProtect. Более того, X-DLL может выделить блок памяти из кучи с помощью API-функции VirtualAlloc, скопировать туда свое тело (которое, естественно должно быть полностью перемещаемо, то есть сохранять работоспособность независимо от базового адреса загрузки) и… выгрузить ставшей ненужную X-DLL вызовом FreeLibrary (на тот случай, если какой-то хитрый механизм проверки целостности решит перечислить список загруженных модулей). Маленький технический нюанс: на процессорах с поддержкой битов NX/XD (запрещающий исполнение кода в страницах памяти, не имеющих соответствующих атрибутов), выделяемому блоку памяти следует присвоить атрибут PAGE_EXECUTE_READWRITE, в противном случае, если у пользователя задействован аппаратный DEP для всех приложений (а не только системных компонентов, как это происходит по умолчанию), вместо выполнения машинного кода система выбросит исключение и… выполнение троянизированной программы завершится в аварийном режиме. ===== »> врезка пересчет CRC ===== В заголовке PE-файла имеется специальное поле, содержащее контрольную сумму. В подавляющем большинстве случаев оно равно нулю, но если это не так, то после вмешательства в таблицу импорта, контрольную сумму необходимо пересчитать. Этим занимается утилита EDITBIT.EXE, запущенная с ключом /RELEASE. Она входит как в штатную поставку компилятора Microsoft Visual Studio, так и в Platform SDK, так что проблем с ее поиском возникнуть не должно. Но если же они все-таки возникнут, можно попробовать просто обнулить контрольную сумму. В некоторых случаях это прокатывает, но… не всегда.