Сегодня мы будум регистрировать хорошо
известную всем почтовую программу TheBat
версии 2.00.6.
Инструменты, которыми мы будем
пользоваться:
- отладчик SoftIce
- декомпилятор
делфи форм DEDE - hex-редактор
hiew
Начнем. Для начала посмотрим чем
запакован главный файл программы "thebat.exe",
а еже ли не запакован, то на чем написан.
Сделаем это с помощью того же PEID, загрузим в
PEID "thebat.exe" и в меню программы выберем
"hardcore scan", увидим следующее: "Borland Delphi
6.0 - 7.0". Значит файл все же не запакован и
написан на delphi, скорее всего седьмой версии.
До версии 2.xx Бат защищался с помощью asprotect,
теперь, очевидно, авторы решили не тратить
зря денег и попробовать защитить его самим.
Вероятно это связано с существенным
подорожанием asprotect на рынке защит.
Что ж посмотрим, что получилось у авторов. Т.к.
это делфи приложение, исследовать будем с
помощью DEDE. Запустим DEDE откроем в нем файл
"thebat.exe" и нажмем кнопку "process",
после выполнения этого процесса нам
предложат провести дополнительный анализ
для нахождения нераспознанных процедур,
согласимся с этим и немного подождем.
После того как DEDE завершит анализ, перейдем
во вкладку "Procedures", там присутствует
много форм, нам же нужна форма ввода
регистрационной информации. Выберем по
имени класса "TRegInputForm" и посмотрим ее
события:
FormCloseQuery 006A0D5C 0015 ; нам
нужно это событие
FormClose 006A0FD0 0010
bOKClick 006A1048 000F
bCencelClick 006A107C 0013
FormCreate 006A10B0 0011
_PROC_006A0E6C 006A0E6C FFFF
_PROC_006A0E6C 006A10D0 FFFF
_PROC_006A0E6C 006A1100 FFFF
_PROC_006A0E6C 006A1108 FFFF
_PROC_006A0E6C 006A117E FFFF
Дизассемблируем событие "FormCloseQuery",
щелкаем по нему правой клавишей мыши и
выбираем "disassemble". Теперь смотрим, что в
там происходит:
006A0D5C 55 push ebp
006A0D5D 8BEC mov ebp, esp
...вырезано...
006A0D79 55 push ebp
006A0D7A 68600E6A00 push $006A0E60
***** TRY
|
006A0D7F 64FF30 push dword ptr fs:[eax]
006A0D82 648920 mov fs:[eax], esp
006A0D85 8B45FC mov eax, [ebp-$04]
* Reference to field TRegInputForm.ModalResult : TModalResult
|
006A0D88 83B84C02000001 cmp dword ptr [eax+$024C], +$01
006A0D8F 0F85B0000000 jnz 006A0E45
006A0D95 8D45B4 lea eax, [ebp-$4C]
006A0D98 50 push eax ; помещаем eax в стек
006A0D99 8D55B0 lea edx, [ebp-$50]
006A0D9C 8B45FC mov eax, [ebp-$04]
* Reference to control TRegInputForm.lPwd : TEdit
|
006A0D9F 8B80FC020000 mov eax, [eax+$02FC] ;
|
006A0DA5 E8EA22E5FF call 004F3094 ; получает
текст из поля "password", это поле имеет
атрибут visible=off, поэтому мы его не видим,
теперь это строка константа = "bat2"
006A0DAA 8B45B0 mov eax, [ebp-$50] ; помещается
по адресу eax
006A0DAD 50 push eax ; помещаем
eax в стек
006A0DAE 8D55AC lea edx, [ebp-$54]
006A0DB1 8B45FC mov eax, [ebp-$04]
* Reference to control TRegInputForm.lSum : TEdit
|
006A0DB4 8B8014030000 mov eax, [eax+$0314] ;
|
006A0DBA E8D522E5FF call 004F3094 ; получает
текст из поля "Key Checksum"
006A0DBF 8B45AC mov eax, [ebp-$54] ; помещается
по адресу eax
006A0DC2 50 push eax ; помещаем
eax в стек
006A0DC3 8D55A8 lea edx, [ebp-$58]
006A0DC6 8B45FC mov eax, [ebp-$04]
* Reference to control TRegInputForm.lKey : TEdit
|
006A0DC9 8B80F8020000 mov eax, [eax+$02F8] ;
|
006A0DCF E8C022E5FF call 004F3094 ; получает
текст из поля "The Bat! Key"
006A0DD4 8B45A8 mov eax, [ebp-$58] ; помещается
по адресу eax
006A0DD7 5A pop edx
006A0DD8 59 pop ecx
|
006A0DD9 E81EF8E7FF call 005205FC
006A0DDE 8B45FC mov eax, [ebp-$04]
* Reference to field TRegInputForm.OFFS_0365
|
006A0DE1 8D9065030000 lea edx, [eax+$0365]
006A0DE7 8D45B4 lea eax, [ebp-$4C]
006A0DEA B940000000 mov ecx, $00000040
* Reference to: System.Move(void;void;void;void;Integer);
|
006A0DEF E8101DD6FF call 00402B04 ; System.Move(void;void;void;void;Integer);
006A0DF4 8B45FC mov eax, [ebp-$04]
* Reference to field TRegInputForm.OFFS_031D
|
006A0DF7 8D901D030000 lea edx, [eax+$031D]
006A0DFD 8D45B4 lea eax, [ebp-$4C]
|
006A0E00 E83FE2E7FF call 0051F044 ; это
важная нам процедура, отметим ее, здесь
собственно и проверяется регистрационная
информация
006A0E05 8B55FC mov edx, [ebp-$04] ;
* Reference to field TRegInputForm.OFFS_0360
|
006A0E08 898260030000 mov [edx+$0360], eax
006A0E0E 8B45FC mov eax, [ebp-$04]
* Reference to field TRegInputForm.OFFS_0360
|
006A0E11 81B860030000B8F00000 cmp dword ptr [eax+$0360], $0000F0B8 ; проверка,
равно ли значение по [eax+$0360], $0000F0B8?
006A0E1B 7428 jz 006A0E45 ; если
значения равны, прыгает на адрес 006A0E45, иначе
продолжает
006A0E1D 8B45FC mov eax, [ebp-$04]
|
006A0E20 E873B0E5FF call 004FBE98 ; сообщение,
о неверной регистрационной информации
006A0E25 8BD0 mov edx, eax
* Reference to pointer to GlobalVar_0086384C
|
006A0E27 A140248600 mov eax, dword ptr [$00862440]
* Reference to field GlobalVar_0086384C.OFFS_00C4
|
006A0E2C 8B80C4000000 mov eax, [eax+$00C4]
* Reference to: TeeLisB.TChartListBox.SetShowActive(TChartListBox;Boolean);
|
006A0E32 E8AD30E0FF call 004A3EE4 ;
здесь выводится сообщение, о неверной
регистрационной информации
006A0E37 8B45FC mov eax, [ebp-$04]
* Reference to : TApplication._PROC_00517B60()
|
006A0E3A E8216DE7FF call 00517B60
006A0E3F 8B45F8 mov eax, [ebp-$08]
006A0E42 C60000 mov byte ptr [eax], $00
006A0E45 33C0 xor eax, eax ; <- прыжок с 006A0E1B
006A0E47 5A pop edx
006A0E48 59 pop ecx
006A0E49 59 pop ecx
006A0E4A 648910 mov fs:[eax], edx
****** FINALLY
|
006A0E4D 68670E6A00 push $006A0E67с ; помещается
в стек, адрес возврата
006A0E52 8D45A8 lea eax, [ebp-$58]
006A0E55 BA03000000 mov edx, $00000003 ; помещается
в edx, 3
* Reference to: System.@LStrArrayClr(void;void;Integer);
|
006A0E5A E81140D6FF call 00404E70
006A0E5F C3 ret ; возвращается
на 006A0E67
* Reference to: System.@HandleFinally;
|
006A0E60 E96B39D6FF jmp 004047D0
006A0E65 EBEB jmp 006A0E52
****** END
|
006A0E67 5B pop ebx ; <- 006A0E67
006A0E68 8BE5 mov esp, ebp
006A0E6A 5D pop ebp
006A0E6B C3 ret
Теперь зайдем в отмеченную нами процедуру
по адресу 0051F044, где проверяется
регистрационная информация.
0051F044 55 push ebp
0051F045 8BEC mov ebp, esp
0051F047 83C4A8 add esp, -$58
0051F04A 8955F8 mov [ebp-$08], edx
0051F04D 8945FC mov [ebp-$04], eax
0051F050 33C0 xor eax, eax ; очищается
eax
0051F052 8945F4 mov [ebp-$0C], eax
0051F055 8D55A8 lea edx, [ebp-$58]
0051F058 8B45FC mov eax, [ebp-$04]
0051F05B B940000000 mov ecx, $00000040 ; помещается
в ecx 40h = 64 dec
* Reference to: System.Move(void;void;void;void;Integer);
|
0051F060 E89F3AEEFF call 00402B04 ; System.Move(void;void;void;void;Integer);
0051F065 8D45A8 lea eax, [ebp-$58]
0051F068 E86BFFFFFF call 0051EFD8
0051F06D 8D45A8 lea eax, [ebp-$58]
0051F070 E833FFFFFF call 0051EFA8
0051F075 66C745F2FFFF mov word ptr [ebp-$0E], $FFFF
0051F07B 8D45A8 lea eax, [ebp-$58]
0051F07E 8945EC mov [ebp-$14], eax
0051F081 33C0 xor eax, eax ; очищается
eax
0051F083 8945E8 mov [ebp-$18], eax
; начало цикла
0051F086 8B45EC mov eax, [ebp-$14]
0051F089 8B55E8 mov edx, [ebp-$18]
0051F08C 8A0410 mov al, byte ptr [eax+edx]
0051F08F 668B55F2 mov dx, word ptr [ebp-$0E]
* Reference to : TASN1Primitive._PROC_004484DC()
|
0051F093 E84494F2FF call 004484DC
0051F098 668945F2 mov [ebp-$0E], ax
0051F09C FF45E8 inc dword ptr [ebp-$18]
0051F09F 837DE840 cmp dword ptr [ebp-$18], +$40
0051F0A3 75E1 jnz 0051F086 ; цикл
проверки, после чего значение по [ebp-$0E]
должно быть F0B8 hex
; конец цикла
0051F0A5 66817DF2B8F0 cmp word ptr [ebp-$0E], $F0B8 ; если
значения равны - продолжает иначе прыгает
на 005205F2,
0051F0AB 0F8541150000 jnz 005205F2 ; где
выходит из процедуры и выводит сообщение о
некорректной регистрации
0051F0B1 8B45A8 mov eax, [ebp-$58]
0051F0B4 3D03459C00 cmp eax, $009C4503
0051F0B9 0F8FB70A0000 jnle 0051FB76
0051F0BF 0F8405150000 jz 005205CA
0051F0C5 3DF481CEC2 cmp eax, $C2CE81F4 ;
сравнивает eax с C2CE81F4 hex
0051F0CA 0F8F57050000 jnle 0051F627
0051F0D0 0F84F4140000 jz 005205CA
0051F0D6 3D98DD60A1 cmp eax, $A160DD98 ; сравнивает
eax с A160DD98 hex
0051F0DB 0F8FA6020000 jnle 0051F387
0051F0E1 0F84E3140000 jz 005205CA
0051F0E7 3D396C0092 cmp eax, $92006C39 ; сравнивает
eax с 92006C39 hex
0051F0EC 0F8F53010000 jnle 0051F245
0051F0F2 0F84D2140000 jz 005205CA
0051F0F8 3D268EEC8A cmp eax, $8AEC8E26 ; сравнивает
eax с $AEC8E26 hex
0051F0FD 0F8FA4000000 jnle 0051F1A7
...вырезано...
0052057F 7F12 jnle 00520593
00520581 7447 jz 005205CA
00520583 2D972F347B sub eax, $7B342F97 ;
вычитается из eax 7B342F97 hex
Не будем вникать в смысл этих проверок, а
просто пропатчим процедуру таким образом,
чтобы по [ebp-$0E] всегда было F0B8 hex и затем
происходил прыжок на адрес 005205D2, что
происходит в случае нормальной регистрации.
00520588 7440 jz 005205CA
0052058A 2D45659000 sub eax, $00906545 ; вычитается
из eax 00906545 hex
0052058F 7439 jz 005205CA
00520591 EB3F jmp 005205D2
00520593 2DE4C27D7C sub eax, $7C7DC2E4 ; вычитается
из eax 7C7DC2E4 hex
00520598 7430 jz 005205CA
0052059A 2DCBF5AE00 sub eax, $00AEF5CB ; вычитается
из eax 00AEF5CB hex
0052059F 7429 jz 005205CA
005205A1 EB2F jmp 005205D2
005205A3 3D7E11C07E cmp eax, $7EC0117E ; сравнивает
eax с 7EC0117E hex
005205A8 7F12 jnle 005205BC
005205AA 741E jz 005205CA
005205AC 2DEACC947D sub eax, $7D94CCEA ; вычитается
из eax 7D94CCEA hex
005205B1 7417 jz 005205CA
005205B3 2D7AE40D00 sub eax, $000DE47A ; вычитается
из eax 000DE47A hex
005205B8 7410 jz 005205CA
005205BA EB16 jmp 005205D2
005205BC 2D0CB0767F sub eax, $7F76B00C ; вычитается
из eax 7F76B00C hex
005205C1 7407 jz 005205CA
005205C3 2D62AD3600 sub eax, $0036AD62 ; вычитается
из eax 0036AD62 hex
005205C8 7508 jnz 005205D2
005205CA 8B45A8 mov eax, [ebp-$58] ; в
eax помещается значение по адресу [ebp-$58]
005205CD 8945F4 mov [ebp-$0C], eax
005205D0 EB20 jmp 005205F2 ;
005205D2 8B55F8 mov edx, [ebp-$08]
005205D5 8D45A8 lea eax, [ebp-$58]
005205D8 B940000000 mov ecx, $00000040
* Reference to: System.Move(void;void;void;void;Integer);
|
005205DD E82225EEFF call 00402B04 ; System.Move(void;void;void;void;Integer);
005205E2 0FB745F2 movzx eax, word ptr [ebp-$0E]
005205E6 8B55F8 mov edx, [ebp-$08]
005205E9 0FB75209 movzx edx, word ptr [edx+$09]
005205ED 2BC2 sub eax, edx
005205EF 8945F4 mov [ebp-$0C], eax
005205F2 8B45F4 mov eax, [ebp-$0C]
005205F5 8BE5 mov esp, ebp
005205F7 5D pop ebp
005205F8 C3 ret
Т.о. эта процедура проверки правильности
регистрационной информации и вызывается
она всякий раз, когда нужно проверить эти
данные. Вызовем SoftIce, введем "addr thebat" и
поставим точку останова на начало
процедуры проверки 0051F044, для этого введем
"bpx 0051F044". Теперь отпустим SoftIce и
закроем TheBat. Запустим программу и окажемся
в отладчике в начале той самой процедуры.
Теперь по F10 протрассируем код до адреса
0051FA5, или же поставим не него точку останова
"bpx 0051F0A5" и отпустим SoftIce, в любом случае
мы будем находится по адресу 0051F0A5, где
происходит сравнение значения по адресу
[ebp-$0E] с константой F0B8 hex. Запишем по [ebp-$0E] F0B8
hex, для этого введем в SoftIce команду "e
ebp-0E" и запишем B8F0 (в обратной
последовательности). Теперь введем "e
ebp-58+9" и запишем туда два нуля, т.к. в конце
процедуры от нужного нам числа F0B8 hex,
отнимается значение по адресу "ebp-58+9" и
если оно не равно нулю, число изменится, нам
же надо сохранить это значение по выходу из
процедуры.
005205E9 0FB75209 movzx edx, word ptr [edx+$09]
005205ED 2BC2 sub eax, edx ; F0B8 - 0 = F0B8
После этого отпускаем SoftIce и даем программе
запуститься. При этом пресловутое окно
отсчета времени пользования программой, не
появилось. Смотрим в окно "О программе",
надпись "UNREGISTERED EVALUATION COPY" исчезла,
вместо нее красуется "REGISTERED TO" и
беспорядочный набор символов вместо нашего
имени. Чтобы там стояло наше имя, нам надо
записать его по адресу после "ebp-58+9",
где мы вписывали два нуля.
Теперь пропатчим thebat.exe, сделаем это с
помощью hex-редактора hiew. Запустим hiew,
откроем thebat.exe, нажмем F4 и выберем режим
дизассемблера, теперь перейдем по адресу
51f0a5, для этого нажмем F5 и введем этот адрес.
Нажмем F3, затем F2 и введем:
mov w, [ebp] [-0E], 0F0B8
lea eax, [ebp] [-58] [+9]
mov w, [eax], 0000
mov w, [eax+2], 31323300
jmp 11F9D2
31323300 - это опкод 123, на кого будет
зарегистрирована программа, можете
изменить это на ваше имя.
Вот собственно и все, теперь приведу,
произведенные в файле изменения:
0011E4A6: 81 C7
0011E4A7: 7D 45
0011E4AB: 0F 8D
0011E4AC: 85 45
0011E4AD: 41 B1
0011E4AE: 15 66
0011E4AF: 00 C7
0011E4B1: 8B 00
0011E4B2: 45 00
0011E4B3: A8 C7
0011E4B4: 3D 40
0011E4B5: 03 02
0011E4B6: 45 31
0011E4B7: 9C 32
0011E4B8: 00 33
0011E4B9: 0F 00
0011E4BA: 8F E9
0011E4BB: B7 13
0011E4BC: 0A 15
В процессе работы с программой нашел еще
одну серьезную недоработку, связанную с
защитой программы, с помощью которой любой
ничего не смыслящий в кракинге человек
может с легкость продолжить пользоваться
Батом после завершения пробного периода, не
переводя часы назад и ничего не меняя в
системных файлах. Как известно, после
завершения пробного периода программа
показывает соответствующее окно с тремя
кнопками: "OK", "EXIT" и "How To Buy",.
Если в одном из наших соединений стоит
опция "автоматически при старте
проверять почту" после нажатии кнопки
"EXIT" программа не выйдет, а предложит
завершить начатый сеанс связи, на что мы
ответим отказом, после чего поставим на
любом из представленных в списке
соединений опцию "Keep This Task". Теперь
сеанс связи будет всегда активен и
программа не завершит работу, а
следовательно мы снова сможем продолжать
ей пользоваться.
Для нас это, конечно, не суть важно, скорее
это будет полезно самим разработчикам.
ЗЫ: Все написано единственно в учебных
целях, а не абы как...