OllyDbg - альтернатива SoftICE
Пособие расчитано на новичков, которые сталкиваются с OllyDbg впервые.
Сайт
разработчиков OllyDbg
Форум для пользователей OllyDbg
Уверен, многие согласятся
со мной в том, что SotfICE для нас - это стандарт de facto. Но сегодня я хотел
бы познакомить вас с новым шедевром, популярность которого возрастает с каждым
днем. За него не просят денег, его не нужно устанавливать. И он действительно
хорош, хоть и требует доработки. Хорош настолько, что может заменить SoftICE.
Пусть не во всем, но во многом...
А имя ему - OllyDbg. Или просто Olly.
1. Что такое Olly?
Olly - 32-битный отладчик для работы под Win9x, WinMe, WinNT, Win2k и WinXP.
Он поддерживает все процессоры серии 80x86, Pentium, MMX, 3DNow!, включая расширения
Athlon, SSE инструкции и соответствующие форматы данных. Возможно отображение
данных в любом формате: HEX, ASCII, UNICODE, 16- и 32-битные знаковые/беззнаковые/шестнадцатиричные
целые числа, 32/64/80-битные вещественные числа,
адреса, дизассемблирование на MASM, IDEAL или HLA. OllyDbg может подключаться
к уже запущенному процессу или создавать новый. Одна из примечательных особенностей
отладчика - анализатор, который распознает процедуры и количество входных аргументов,
циклы, таблицы, константы и строки, вызовы API функций и многое другое. Анализатор
не является компиляторо-зависимым и одинаково хорошо работает
со всеми РЕ-файлами. И это далеко не все...
2. Как работать с Olly?
Вообще, отладчик сопровождается хорошим хелпом, в котором можно найти полное
описание всех свойств, настроек, режимов работы отладчика и много чего еще.
Так что переписывать его, думаю, не стоит. Однако факт написания мануала на
английском языке может затруднить его понимание русскоязычным населением. Поэтому
кое-какие основные моменты я не могу оставить без внимания.
Пройдемся по панели инструментов...
Первая слева кнопка - открыть исполняемый файл. Горячая клавиша: [ F3 ]. Затем идут "Перезагрузить файл - [Ctrl + F2 ]" (загружает последний открывавшийся файл) и "Закрыть файл - [ Alt + F2 ]".
Следом - пара кнопок "Выполнить - [ F9 ]" и "Пауза - [ F12 ]". С ними все должно быть понятно.
Далее по списку режимы трассировки:
Step in - [ F7 ] - исполняет одну
команду за раз.
При обработке вызова (call) передает управление (входит) в вызываемую подпрограмму,
останавливаясь на первой команде в ней.
Step over - [ F8 ] - также исполняет одну команду за раз, но при обработке вызовов
старается выполнить подпрограмму за один шаг, не передавая ей управление. Но!
Управление все же передается в подпрограмму, если на вызов установлена точка
останова, или в ней генерируются исключительные ситуации (прерывания).
Чтобы не нажимать F7 или F8 сотни раз подряд, Olly предусматривает режим "анимации"
(Animate) [ Ctrl + F7 ] или [ Ctrl + F8 ]. Это режим автоматического пошагового
выполнения, т. е. команды выполняются одна за
одной без необходимости тыкать на [ F7 ] или [ F8 ].
Анимация останавливается
* при нажатии [ Esc ]
* если встречается точка останова или исключение.
Trace into - [ Ctrl + F11 ] - режим похож на анимацию, только окна отладчика
при этом не обновляются, и ведутся логи изменения регистров, памяти и т. п.
Trace into выполняет процесс со входом в подпрограммы (как при step in) В окне
Debugging Options -> Trace [ Alt + O ] можно изменить настройки
режима трассировки
Trace over - [ Ctrl + F12 ] - то же, что и trace into,
но подпрограммы выполняются за один шаг, как при step over.
Execute till RET - [ Ctrl + F9 ] - выполнять программу до инструкции RET.
В окне Debugging Options -> Trace [ Alt + O ] можно отметить пункт "After
executing till RET, step over RET", чтобы останавливать выполнение программы
на следующей за RET команде. Иначе останов будет осуществляться перед RET.
Далее идут кнопки, открывающие окошки с различной инфой (логи, подгружаемые
модули, память процесса, ветви процесса, описатели, окна и т. д. и т. п.), с
которыми мы будем знакомиться при появлении в этом необходимости. А сейчас перейдем
к практической части.
Пример 1. Поиск регистрационного
кода
В качестве примера будем использовать (ry0's
Crackme #3
Итак, приступим.
Запускаем Olly, нажимаем [ F3 ] - открыть файл и открываем ccm_3.exe.
Отладчик тормозит на точке входа в программу (первый стоп можно поставить также
на системную точку останова или вход в процедуру окна. Эти параметры можно изменить
в Options -> Debugging options -> Events):
00401000 6A 00 push 0 ; pModule = NULL 00401002 E8 3D040000 call <jmp.&kernel32.GetModuleHandleA> ; GetModuleHandleA 00401007 A3 00314000 mov dword ptr [403100], eax 0040100C 6A 00 push 0 ; lParam = NULL 0040100E 68 26104000 push 00401026 ; DlgProc = ccm_3.00401026 00401013 6A 00 push 0 ; hOwner = NULL 00401015 68 E8030000 push 3E8 ; pTemplate = 3E8 0040101A 50 push eax ; hInst 0040101B E8 D0030000 call <jmp.&user32.DialogBoxParamA> ; DialogBoxParamA 00401020 50 push eax ; ExitCode 00401021 E8 18040000 call <jmp.&kernel32.ExitProcess> ; ExitProcess 00401026 55 push ebp
Теперь жмем [ F9 ] - выполнить. Далее
как обычно - вводим имя юзера (PowerUser) и серийник (654321), но на "Регистрировать"
не давим. Возвращаемся в Olly и зажимаем [ Ctrl + N ]. Нам откроется окно "Names"
-
список всех импортируемых программой API функций. Ага, есть упоминание о GetDlgItemTextA.
Ставим курсор на эту API функцию (клик левой), а затем вызываем popup-меню (клик
правой). Устанавливаем точки останова (Set breakpoint on every reference). Точку
останова также можно установить из командной строки (если установлен плагин
CMDLINE.DLL). Для этого [ Alt + F1 ], а там уж как в SICE'е:
bpx GetDlgItemTextA [ввод]
Теперь в окне приложения (ccm_3.exe) клик по 'register'...
0040113A 6A 20 push 20 ; Count = 20 (32.) 0040113C 57 push edi ; Buffer 0040113D 68 EC030000 push 3EC ; ControlID = 3EC (1004.) 00401142 FF75 08 push [arg.1] ; hWnd 00401145 E8 DA020000 call <jmp.&user32.GetDlgItemTextA> ; GetDlgItemTextA 0040114A 85C0 test eax, eax 0040114C 75 04 jnz short 00401152 0040114E B0 01 mov al, 1 00401150 EB 08 jmp short 0040115A 00401152 FF75 08 push [arg.1] ; Arg1 00401155 E8 96010000 call 004012F0 ; ccm_3.004012F0 0040115A 50 push eax 0040115B FF75 08 push [arg.1] 0040115E E8 61020000 call 004013C4 00401163 EB 06 jmp short 0040116B 00401165 33C0 xor eax, eax 00401167 C9 leave 00401168 C2 1000 retn 10 0040116B B8 01000000 mov eax, 1 00401170 C9 leave 00401171 C2 1000 retn 10
Начинаем трассировку.
[ F8 ] - для пошаговой трассировки - по одной команде за раз. Но! При выполнении
вызовов (call), в случае если на него установлена точка останова или подпрограмма
генерирует исключения, будет осуществляться передача управления в подпрограмму.
Чтобы этого избежать, можно попробовать комбинацию [ Shift + F8 ].
Итак, [ Shift + F8 ] для выполнения call GetDlgItemTextA, а затем [ F8 ] для
пошагового выполнения.
0040114A 85C0 test eax, eax 0040114C 75 04 jnz short 00401152 0040114E B0 01 mov al, 1 00401150 EB 08 jmp short 0040115A 00401152 FF75 08 push [arg.1] ; Arg1 00401155 E8 96010000 call 004012F0 ; ccm_3.004012F0
Буфер, адресуемый регистром EDI получает
введенный юзером рег. код. Обратите внимание, что строка, на которую указывает
регистр, отображается справа от его значения в окне регистров. Весьма удобно,
согласитесь.
Далее идет проверка, ввел ли пользователь этот код. Если ввел - выполняем процедуру
ccm_3.004012F0
Посмотрим, что она из себя представляет. Чтобы передать управление в подпрограмму,
давим [ F7 ].
004012F0 55 push ebp 004012F1 8BEC mov ebp, esp 004012F3 83C4 DC add esp, -24 004012F6 53 push ebx 004012F7 56 push esi 004012F8 8D5D DF lea ebx, dword ptr [ebp-21] 004012FB 6A 20 push 20 ; Count = 20 (32.) 004012FD 53 push ebx ; Buffer 004012FE 68 EB030000 push 3EB ; ControlID = 3EB (1003.) 00401303 FF75 08 push [arg.1] ; hWnd 00401306 E8 19010000 call <jmp.&user32.GetDlgItemTextA> ; GetDlgItemTextA 0040130B 85C0 test eax, eax 0040130D 75 07 jnz short 00401316 0040130F B8 01000000 mov eax, 1 00401314 EB 47 jmp short 0040135D 00401316 66:813F 5245 cmp word ptr [edi], 4552 0040131B 75 08 jnz short 00401325 0040131D 8A57 02 mov dl, byte ptr [edi+2] 00401320 80F2 2D xor dl, 2D 00401323 74 07 je short 0040132C
Так-так. Читаем имя юзера в буфер,
адресуемый регистром EBX, и проверяем, введено ли это имя, или его впомине нет.
А вот потом(!) проверяем, чтобы первые 3 символа ключа были не иначе как 'RE-'.
Протрассировав до адреса :00401316 не торопитесь. Чтобы не повторять процесс
заново, изменим серийник в памяти так, чтобы первыми символами были 'RE-', а
не введенные нами. Для этого: 1. В окне регистров выделите значение регистра
EDI (левым кликом), т. к. именно он указывает на введенный серийник. Затем правой
кнопкой мыши и выберите 'Follow in Dump'. Или в окне кода выделите строку
00401316 66:813F 5245 cmp word ptr [edi], 4552
Потом правой кнопкой и выбирайте
'Follow in Dump -> Memory Address'. В окне данных покажется введенная строка:
0012FB13 36 35 34 33 32 31 00 D5 77 59 00 10 01 35 01 00 654321..wY...5.. 0012FB23 00 D6 0C 01 E1 40 03 01 00 01 00 00 00 5F 3A D4 .....@......._:.
Выделите 3 первых байта (36h, 35h,
34h) и жмите [Ctrl + E] - откроется окно редактирования данных. Изменяем '654'
на 'RE-' и продолжаем трассировку. В некоторых ситуациях можно просто изменить
значение флага. Для этого необходимо дважды щелкнуть по цифре справа от нужного
флага в окне регистров.
0040132C 33F6 xor esi, esi 0040132E BA 01000000 mov edx, 1 00401333 8BCB mov ecx, ebx 00401335 0FB64C0A FF movzx ecx, byte ptr [edx+ecx-1] 0040133A 8D3431 lea esi, dword ptr [ecx+esi] 0040133D 42 inc edx 0040133E 48 dec eax 0040133F 75 F2 jnz short 00401333 00401341 8BC6 mov eax, esi 00401343 69C0 66060000 imul eax, eax, 666 00401349 35 EFBEADDE xor eax, DEADBEEF 0040134E C1C8 03 ror eax, 3
Это есть генерация серийника.
00401351 53 push ebx 00401352 50 push eax 00401353 E8 0B000000 call 00401363
А теперь переводим DWORD-число из
EAX в ASCII-строку.
0040139A 57 push edi 0040139B 56 push esi 0040139C 53 push ebx 0040139D 8BF0 mov esi, eax 0040139F 8BD8 mov ebx, eax 004013A1 46 inc esi 004013A2 8A06 mov al, byte ptr [esi] 004013A4 3C 00 cmp al, 0 004013A6 75 F9 jnz short 004013A1 004013A8 B8 02000000 mov eax, 2 004013AD 8A57 03 mov dl, byte ptr [edi+3] 004013B0 8A4E FF mov cl, byte ptr [esi-1] 004013B3 32D1 xor dl, cl 004013B5 75 09 jnz short 004013C0 004013B7 47 inc edi 004013B8 4E dec esi 004013B9 3BF3 cmp esi, ebx 004013BB 7F F0 jg short 004013AD 004013BD 8D40 01 lea eax, dword ptr [eax+1] 004013C0 5B pop ebx 004013C1 5E pop esi 004013C2 5F pop edi 004013C3 C3 retn
Здесь происходит проверка полученной
строки с регистрационным кодом пользователя, начиная с четвертого символа. И
проверка производится по диагонали, т. о. последний символ полученной строки
будет четвертым символом серийника, предпоследний - пятым и т. д. В итоге мы
имеем правильный ключ:
Name: PowerUser
Code: RE-C0857DBF
Пример 2. Пропатчивание
В качестве примера юзаем тот же (ry0's
Crackme #3
Будем удалять nag-экран, который появляется при запуске ccm_3.exe.
Итак, открываем .EXE-шник в Olly. Затем в окне кода щелкаем правой кнопкой мыши.
В появившемся popup-меню выберите Search for -> All referenced text strings
Ага, вот они, все строчечки:
00401082 push 0040311F ASCII "Trial Version" 00401087 push 004030F9 ASCII "Register your copy of program, please" 00401192 push 00403000 ASCII "(ry0's Crackme #3" 004011A4 push 00403012 ASCII "Protection: Name - Serial... 00401234 push 004030C8 ASCII "http://www.cydem.org.ua" 00401239 push 004030C3 ASCII "open" 0040125B push 004030E0 ASCII "mailto:cryo@cydem.org.ua" 00401260 push 004030C3 ASCII "open" 004013D2 mov edx, 00403067 ASCII "Try to enter Name and Code first" 004013DF mov edx, 00403096 ASCII "Still unregistered." 004013EC mov edx, 004030AA ASCII "At last! Registered now." 004013FA push 00403088 ASCII "Registration."
Теперь двойной щелк по нужной строке
- и мы снова в окне кода, но уже на том месте, откуда к этой строке идет обращение:
00401066 68 80000000 push 80 0040106B FF75 08 push [arg.1] 0040106E E8 D5030000 call <jmp.&user32.SendMessageA> 00401073 803D 2D314000 01 cmp byte ptr [40312D], 1 0040107A 0F84 EB000000 je 0040116B 00401080 6A 00 push 0 00401082 68 1F314000 push 0040311F 00401087 68 F9304000 push 004030F9 0040108C FF75 08 push [arg.1] 0040108F E8 AE030000 call <jmp.&user32.MessageBoxA> 00401094 E9 D2000000 jmp 0040116B
Немного выше видим проверку истинности
байта по адресу :40312D. Если ИСТИННО - перейти куда-то, если ЛОЖНО - показать
MessageBox. Все предельно понятно. Нужно изменить 'je 0040116B' на 'jmp 0040116B'.
Для этого - двойной клик по строке
0040107A 0F84 EB000000 je 0040116B
В появившемся окне меняем 'je' на
'jmp' и отмечаем "Fill with NOP's". Давим 'Assemble'. Все. Можно проверить работу,
запустив ccm_3 на выполнение [ F9 ]. Работает! (Однако будьте осторожны и при
пропатчивании следите за тем, чтобы длина новой команды не превышала длины изменяемой.)
Но мы пропатчили лишь память процесса, а не сам исполняемый файл. И внесенные
изменения теперь надо сохранить. Правый клик мышой и выбираем "Copy to executable
-> All modifications". Olly еще поспрашивает, действительно ли стоит сохранять
все изменения и под каким именем писать новый файл. После этого можно закрыть
отладчик и проверить, что мы намутили... Все идеально!!! И очень просто.
Автор: (ry0 12.10.2003 P.S. Большое спасибо (ry0 за отличную статью и разрешение разместить ее на нашем сайте.
Copyright © 2002-2004 hack4joy