Сегодня мы будум регистрировать хорошо известную всем почтовую программу TheBat. Инструменты, которыми мы будем пользоваться: - отладчик 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". Теперь сеанс связи будет всегда активен и программа не завершит работу, а следовательно мы снова сможем продолжать ей пользоваться. Для нас это, конечно, не суть важно, скорее это будет полезно самим разработчикам.
|