Сегодня мы будум регистрировать хорошо известную всем почтовую программу 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”. Теперь сеанс связи будет всегда активен и программа не завершит работу, а следовательно мы снова сможем продолжать ей пользоваться.
Для нас это, конечно, не суть важно, скорее это будет полезно самим разработчикам.
ЗЫ: Все написано единственно в учебных целях, а не абы как…
Автор: Karcheev A M