О чем

В последнее время авторами программ
довольно часто используются упаковщики
исполняемых файлов. Делается это с двумя
целями: уменьшить размер экзешника и
защититься от дизассемблирования кода
начинающими кракерами. Да и патчить
упакованный файл не так просто. Но и не так
сложно, как мы увидим позже. В сети можно
найти множество распаковщиков, заточенных
под конкретный пакер, хотя большая часть
защит достаточно просто снимается ручками (драйвер
прямые_руки.sys все поставили?). Попробуем
поиметь упаковщик.

Кого

Попалась мне как-то программка Accent Word Password
Recovery ver. 2.10 от AccentSoft Utilities. Она используется (о
чем говорит название) для перебора «забытых»
паролей к вордовым файлам. Вот только
незарегистрированная версия подбирает
пароли длиной не более 4 символов.

Щупаем

Для регистрации требует ввести 33-символьный
ключ. Стоимость регистрации для граждан
эксСССР — всего 150 рублей. Да я лучше вручную
буду перебирать. По правде говоря, мне нет
надобности вспоминать пароли, я их просто
не ставлю. Пусть желающие читать мои
вордовые файлы наслаждаются. Но, может, кому
и надо, может, у кого склероз, или охота
почитать чужие творения.

Go-Go-Go!!!

Попробуем зарегистрировать Accent Word Password
Recovery (далее просто awrdpr.exe). 

Чем

  • мозги;
  • прямые руки (или прямые драйвера к
    кривым рукам);

  • комп;
  • W32Dasm;
  • LordPE (для снятия дампа и редактирования PE
    заголовка);

  • ToPo (для добавления кодовой секции);
  • QView или другой 16-ричный редактор;
  • FAR (из него удобно запускать мой любимый
    QView);

(взять все это добро можно в инете или у
меня, что маловероятно – жадный)

Где

Где, где… В ВИНДЕ! (98-й).

Как

Для начала скопируем файл awrdpr.exe в original.exe (резервная
копия), в awrdpr.w32 (для дизассемблера) и в null (на
всякий случай). Загрузим файл awrdpr.exe в
дизассемблер W32Dasm. Он немного думает, а
потом выдает нам какой-то извратный листинг,
в котором нет ни списка импортируемых
функций, ни String Reference. Похоже, мы наткнулись
на упаковщик. Точно, если посмотреть
экзешник 16-ричным редактором, в заголовке
увидим две секции UPX0 и UPX1, а также строку «1.20
UPX!». После долгих раздумий приходим к
выводу, что прога упакована UPX-ом. Это хорошо,
ведь если запустить этот паковщик с ключом
–d

upx –d awrdpr.exe

то он должен нам распаковать упакованную
ним же программу. Запускаем. Хорошо, да не
очень. Любимая нами всеми редкая птица
Обломинго снова показывает нам свой … Э,
что-то я увлекся. Прога не распаковывается.
Ну что ж, приложим свои умелые ручки и
немного поскрипим извилинами. Но сначала
немного теории. В упакованном файле сначала
идет небольшой кусок кода, который является
распаковщиком, далее идет упакованный
исходный файл, который нам нужно получить.
После запуска программы выполняется
распаковщик, восстанавливает в памяти
исходную программу и передает ей
управление. Нам нужно найти точку, где
происходит переход от упаковщика к
оригинальному коду программы. Обычно этот
переход выглядит вроде

JMP xxxxxxxx

где xxxxxxxx – адрес точки входа оригинальной
программы. Особые приметы: этот адрес будет
находиться достаточно далеко от того места,
откуда вызывается данная инструкция.
Займемся поиском. Идем в дизассемблер,
выбираем в главном меню Goto -> Goto Program Entry Point.
Видим некий код – это распаковщик. Листаем
ниже, пока не встретим такой код:

:00613656 8903 mov dword ptr [ebx], eax
:00613658 83C304 add ebx, 00000004
:0061365B EBD8 jmp 00613635

*Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00613654(C)
|
:0061365D FF96B84A2100 call dword ptr [esi+00214AB8]

*Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0061361C(C)
|
:00613663 61 popad
:00613664 E997D9DEFF jmp 00401000
:00613669 000000 BYTE 3 DUP(0)

:0061366C 84366100 DWORD 00613684
:00613670 38376100 DWORD 00613738

По адресу 00613664 мы видим интересующий нас
код. Он означает, что после распаковки
оригинальный код находится в памяти по
адресу 00401000. Это и есть OEP (Original Entry Point –
оригинальная точка входа). Запоминаем (или
записываем) этот адрес, нам он еще
понадобится. Переместим зеленую полоску на
данную инструкцию и в строке состояния
увидим такую надпись:

Code Data @:00613664 @Offset 00067A64 in File:awrdpr.exe

00067A64 – смещение в файле, где находится
данная инструкция. В данной точке
распаковщик уже отработал, а оригинальный
код еще не запустился. Здесь нам нужно
зациклить выполнение программы и снять
дамп (отпечаток) процесса. Зациклить проще
всего, прописав по адресу 00613664 инструкцию JMP
00613664. Для этого воспользуемся QView. Запустим
FAR и введем команду:

qview awrdpr.exe

Перейдем к смещению 00067A64 (F5), включим
кодовый режим (F4), включим режим
редактирования (Alt+F9), перейдем в окно кода (Tab)
и введем инструкцию JMP 00067A64. Здесь мы
указали другой адрес, а именно смещение
инструкции в файле. В памяти это будет
выглядеть нужным нам образом:

00613664 JMP 00613664

Сохраняем изменения (Enter, Tab, Alt+F9), выходим.
Программу мы зациклили, можно запускать.
Запускаем. Все тихо, но процесс висит в
памяти, в чем можно убедиться, нажав
Ctrl+Alt+Delete. Теперь можно снимать дамп. Для
этого запускаем LordPE, выбираем в списке
процессов awrdpr.exe, жмем правый батон мыши и в
менюшке кликаем на Dump Full… Выбираем имя
файла, куда сохранять дамп (например, dumped.exe).
Далее убиваем этот процесс (правой кнопкой
-> burn process).

Итак, в файле dumped.exe хранится
распакованный файл. Но запускать его еще
рано. Надо еще поменять адрес точки входа.
Мы его запомнили (или записали) – это 00401000. В
LordPE жмем кнопочку PE Editor, выбираем файл dumped.exe.
Вываливается окошко Basic PE Header Information. ImageBase у
нас равен 00400000, а адрес точки входа в памяти
– 00401000. В графе EntryPoint вбиваем 00001000
(00401000-00400000) и жмем Save. Все. Распакованная
программа должна работать. Запускаем. Точно,
работает, но по прежнему пишет, что не
зарегистрирована.

Скопируем файл dumped.exe в dumped.w32 (для W32Dasm),
загрузим dumped.w32 в дизассемблер и через
некоторое время получим листинг программы.
Теперь будем искать текстовые строки вроде
«программа зарегистрирована/не
зарегистрирована». Жмем кнопку Strn Ref и видим,
что в списке строк идут только строки
латинскими буквами. W32Dasm не понимает
русских букв. Оставим это на его совести.
Сделаем финт ушами: скопируем файл dumped.exe в
dumped1.exe и откроем его в QView. Далее включим 16-ричный
режим (F4), переключимся в виндовую кодировку
(F6 -> F2) и будем искать строку «арег». Жмем F7
и в поле HEX введем последовательность E0 F0 E5 E3
– это 16-ричные коды соответствующих
символов в виндовой кодировке. Каждую
встреченную строку «(не)зарегистрированная
копия» меняем на «(un)registered copy». Далее
сохраняем изменения, копируем файл dumped1.exe в
dumped1.w32 и загружаем dumped1.w32 в W32Dasm. Получаем
листинг. Нажимаем кнопку Strn Ref, ищем строку «registered
copy», находим, двойной щелчок на этой строке
– попадаем в то место кода, где она
используется. Повторяем двойной щелчок и
убеждаемся, что это единственное место.
Смотрим код:

:004071A1 E8EAA7FFFF call 00401990
:004071A6 83C408 add esp, 00000008
:004071A9 85C0 test eax, eax
:004071AB 7533 jne 004071E0

:004071AD 66C745E01400 mov [ebp-20], 0014

* Possible StringData Ref from Data Obj ->"registered copy"
|
:004071B3 BAF9FA5300 mov edx, 0053FAF9
:004071B8 8D45F4 lea eax, dword ptr [ebp-0C]
:004071BB E880E10800 call 00495340
:004071C0 FF45EC inc [ebp-14]
:004071C3 8B10 mov edx, dword ptr [eax]
:004071C5 8B83E0020000 mov eax, dword ptr [ebx+000002E0]
:004071CB E8743B0500 call 0045AD44
:004071D0 FF4DEC dec [ebp-14]
:004071D3 8D45F4 lea eax, dword ptr [ebp-0C]
:004071D6 BA02000000 mov edx, 00000002
:004071DB E8C0E20800 call 004954A0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004071AB(C)
|
:004071E0 8B4DD0 mov ecx, dword ptr [ebp-30]
:004071E3 64890D00000000 mov dword ptr fs:[00000000], ecx
:004071EA 5B pop ebx
:004071EB 8BE5 mov esp, ebp
:004071ED 5D pop ebp
:004071EE C3 ret

По адресу 004071A1 вызывается процедура, далее
проверяется значение регистра AL. Если там
ненулевое значение – переход на выход из
текущей процедуры, если 0, вывод строки «registered
copy». Посмотрим код процедуры по адресу 00401990:
Главное меню -> Goto -> Goto Code Location, вводим
00401990. Смотрим код:

* Referenced by a CALL at Addresses:
|:00405AF4 , :004071A1 , :00408028 , :0040B4EF 
|
:00401990 55 push ebp
:00401991 8BEC mov ebp, esp
:00401993 83C498 add esp, FFFFFF98
:00401996 53 push ebx
:00401997 56 push esi
:00401998 57 push edi
:00401999 33F6 xor esi, esi
:0040199B 8D4598 lea eax, dword ptr [ebp-68]
:0040199E 50 push eax
:0040199F E864B70000 call 0040D108
:004019A4 59 pop ecx
(____вырезано_____)
:004019E3 42 inc edx
:004019E4 83F910 cmp ecx, 00000010
:004019E7 7CDF jl 004019C8
:004019E9 8BC6 mov eax, esi
:004019EB 5F pop edi
:004019EC 5E pop esi
:004019ED 5B pop ebx
:004019EE 8BE5 mov esp, ebp
:004019F0 5D pop ebp
:004019F1 C3 ret

Перед окончанием процедуры по адресу 004019E9 в
регистр EAX загружается какое-то значение. Мы
же хотим, чтобы в нем всегда было 0. Ну так
заменим инструкцию MOV EAX, ESI на XOR EAX,EAX. Эта
инструкция имеет такой же размер, как и та,
что мы меняем (2 байта). Таким образом, в EAX
перед окончанием работы процедуры всегда
будет 0.

Наведем зеленую полоску на инструкцию MOV
EAX, ESI и в строке статуса увидим:

Code Data @:004019E9 @Offset 000019E9 in File:dumped1.exe

Данная инструкция находится в файле
dumped1.exe по смещению 000019E9. Запускаем QView (qview
dumped1.exe), переходим к этому смещению (F5),
включаем кодовый режим (F4), включаем режим
редактирования (Alt+F3), переходим в окно кода (Tab),
вводим инструкцию XOR EAX, EAX, записываемся (Enter
-> Tab -> Alt+F9), выходим. Попробуем запустить.
Бинго! Прога работает и пишет, что копия
зарегистрирована. Попробуем ввести любой
регистрационный ключ – благодарит нас за
регистрацию. В окошке «О программе» в
строке статус видим, что версия не
зарегистрирована. Опа! А, может, она врет?
Попробуем открыть защищенный паролем
вордовый файл и задать условия перебора. В
графе «Минимальная длина» мы можем выбрать
пароль длиннее 4 символов. Значит, все-таки,
врет. Программа зарегистрирована.

Теперь ленивые могут идти спать. А мы
попробуем сделать патч упакованной
программы. А как, спросите вы – ведь она же
упакована? А вот так – отвечу я. Идея
заключается в дописывании своего кода в
упакованную программу, который бы получал
управление после распаковщика (до того, как
запустится оригинальная программа), менял в
памяти нужные байты, а затем отдавал
управление оригинальной программе. Где же
нам взять свободное место для нашего кода? А
приделаем. Для этого воспользуемся
программкой ToPo, разработанной специально
для этих целей. Для начала скопируем файл
original.exe в original1.exe. Далее откроем этот файл в
ToPo. Нам предлагают использовать пустое
место в данной секции или добавить новую
секцию (Create new section). Будем добавлять новую.
Выбираем и жмем ОК. Далее в окошке «Bytes to be
added» вводим нужное нам количество байт.
Понадобится нам около 30 байт. Зачем, станет
ясно позже. Вводим 30, очищаем все галочки,
жмем «Do it!». В окошке «Result» видим следующее:

30 bytes add at: (добавлено 30 байт:)
-memory address:00616000h (по адресу в памяти 00616000h)
-file offset: 00069A00h (смещение в файле 00069A00h)

Записываем эти цифры и закрываем ToPo. Упс,
размер файла увеличился на целых 512 байт.
Это все виновато выравнивание длин секций
по 32-битной границе. Ну да ладно, как-нибудь
справимся. А пока займемся дописыванием
своего кода. Запускаем QView (qview original1.exe),
переходим по смещению, которое нам любезно
сообщил ToPo (00069A00). Включаем кодовый режим,
включаем редактирование и переходим в окно
кода. А теперь чешем думательный девайс (у
кого что) и вспоминаем, что нам нужно
изменить 2 байта по адресу 004019E9 (поменять
инструкцию MOV EAX, ESI на XOR EAX,EAX). Код инструкции
XOR EAX, EAX – 31 C0. Посему пишем такой код:

MOV BYTE PTR [004019E9], 31
MOV BYTE PTR [004019EA], C0
JMP 00000000

Последняя инструкция нам нужна для того,
чтобы передать управление оригинальной (уже
распакованной в памяти) программе после
того, как мы ее слегка пропатчили. Адрес
прыжка нам пока неизвестен (если быть
точным, то просто лень вычислять), его мы
выясним позже. Сохраняем изменения (Enter ->
Tab -> Alt+F9), но пока не выходим. Возьмем и
удалим лишние байты. Наш код занял 21 байт,
посему 491 байт – лишние. Наведем курсор на
следующую за JMP 00000000 команду, нажмем Insert (начало
выделения) и выделим все до конца файла, где
еще раз нажмем Insert (конец выделения). Затем
нажмем Shift+F4 (удаление блока), подтвердим
удаление, нажав Y. Все, файл похудел на 491
байт.

Но это еще не совсем все. Нам еще нужно
подправить код распаковщика таким образом,
чтобы по окончании работы он передал
управление не оригинальной программе, а
нашему кусочку кода, который в памяти будет
располагаться по адресу (мы его записывали
из ToPo) 00616000. Как мы помним (или уже нет),
передача управления происходит по адресу
613664. Нам нужно поменять инструкцию перехода
таким образом, чтобы управление
передавалось адресу памяти 00616000. Дело в том,
что инструкция JMP xxxxxxxx записывается
следующим образом: сначала идет опкод
инструкции JMP число E9, а затем относительный
адрес (т.е. разность адреса перехода и
адреса команды следующей за инструкцией
перехода). Вычислим его. Адрес следующей
команды после JMP 00401000 – 613669. Нам нужно
перейти в адрес 00616000. Поэтому 00616000 – 00613669 =
00002997. Это и будет относительным адресом. Все
это можно посчитать в QView, там есть
калькулятор (Ctrl+F6). Перейдем (мы все еще в QView,
для тех, кто провтыкал вышенаписанное) по
смещению 67A64 (напоминаю, соответствует
адресу в памяти 00613664) и в 16-ричном окне
заменим последовательность E9 97 D9 DE FF на
следующую: E9 (опкод инструкции JMP) 97 29 00 00 (вычисленный
нами относительный адрес, введенный
побайтно задом наперед (блин, прямо
описание сцены из порнофильма), поскольку в
памяти числа хранятся так, что младшие
байты находятся раньше. Сохраним изменения
(Alt+F9). Теперь после распаковщика управление
передается нашему кусочку кода, который
патчит программу. После патча нужно
передать управление по адресу 00401000 (т.е.
оригинальной программе). Перейдем по
смещению 69A00 (соответствует адресу памяти
00616000), где находится наш патч. С последней
инструкцией (JMP 00000000) нужно поступить
аналогично вышеописанному. Итак, вычисляем
относительный адрес: смещение последнего
байта инструкции находится по смещению 69A14,
следующая команда располагалась бы по
смещению 69A15, что соответствует адресу
памяти 00616015. Переход нужно совершить по
адресу 00401000. 00401000 – 00616015 = FFDEAFEB.

Следовательно, команда передачи
управления в эквиваленте 16-ричного кода
будет выглядеть так: E9 EB AF DE FF. Прописываем
эти значения и сохраняем изменения. Выходим.
Запускаем original1.exe. Все работает! Кто сказал
«А у меня че-то не пашет»?! А ну-ка, встал и
винду переустановил 10 раз! Мы же убиваем
файлы awrdpr.exe, dumped.exe, dumped1.exe, *.w32 и
переименовываем original1.exe в awrdpr.exe.

Подведем итоги, пока итоги не подвели нас:

  1. Сняли с проги упаковщик.
  2. Зарегистрили.
  3. Добавили к исходному файлу новую
    кодовую секцию (размер увеличился на 21
    байт).

  4. Написали код, который патчит программу в
    памяти.

  5. Переставили винду 10 раз.

Последняя фрикция

Думаю, пока что с нас хватит. Ну и
напоследок, список всех изменений в
оригинальном файле:

fc /b original.exe awrdpr.exe >patch.txt

Вот файл patch.txt:

Сравнение файлов original.exe и awrdpr.exe:

00000206: 03 04
00000251: 60 70
00000370: 00 2E
00000371: 00 74
00000372: 00 6F
00000373: 00 70
00000374: 00 6F
00000375: 00 30
00000378: 00 15
0000037D: 00 60
0000037E: 00 21
00000381: 00 02
00000385: 00 9A
00000386: 00 06
00000394: 00 20
00000397: 00 E0
00067A66: D9 29
00067A67: DE 00
00067A68: FF 00
FC: awrdpr.exe длиннее, чем original.exe

Далее можно создать файл длиной 21 байт, где
будет храниться код, который патчит
программу. Сделаем это: qview awrdpr.exe, перейдем
(F5) по смещению 69A00 (тут начинается наш код),
нажмем Insert и выделим 21 байт и снова нажмем
Insert. Блок помечен. Сохраним его: Shift+F2, укажем
имя файла patch.bin и смещение в нем 0 и Enter.
Теперь для получения зарегистрированной
копии достаточно внести в исходный файл
изменения, прописанные в файле patch.txt, а
затем выполнить команду:

copy /b awrdpr.exe + patch.bin awrdpr.exe

В результате пропатченный файл потолстеет
на 21 байт и программа от радости будет
делать вид, будто за нее заплатили 150 рублей.

Отмазка

Этот опус носит образовательный характер и
предназначен для разработчиков
программных защит. Автор не несёт никакой
ответственности за возможное
использование материалов данной статьи в
противозаконных целях. 🙂

Оставить мнение

Check Also

Tips’n’Tricks из арсенала андроидовода. Самые интересные, полезные и нестандартные трюки с Android

Многие годы мы рассказывали про самые разные способы оптимизировать, модифицировать и твик…