Необходимые инструменты:
- W32Dasm 8.9x
- TRW 1.2x
- HIEW 6.x
- Delphi 4.0
Итак, приступим...
После недолгого осмотра мы приходим к выводу, что:
-
Программа написана с использованием CA-Visual Objects
(Что усложняет задачу, но не очень)
- Используется для каких-то целей библиотека халявнух функций FUNCky
6.0
- TC.EXE используется как wrapper для запуска _tc.exe
Итак, запускаем. И что же мы видим??? "License wizard". Хмм..
Интересно. А затем - "Please enter your evaluation key...".
Ну что же... Ключа у меня нет, на мой запрос компания не ответила. Будем
ломать...
Нетрудно догадаться, что все функции защиты выполняет tcsecure.dll. Нам
же лучше. Ищем, где она вызывается... Я нашел вот такой код:
* Possible StringData Ref from Data Obj ->"TCSECURE.DLL" |
|
|
|
:0044812A B8EC7B4900
:0044812F 50
:00448130 B8A0020000
:00448135 50
|
mov eax, 00497BEC
push eax
mov eax, 000002A0
push eax |
* Reference To: ISARUN.PrepCreateInstance, Ord:0000h |
|
|
|
:00448136 E8CD250400
:0044813B 0BC0
:0044813D 0F851B000000
|
Call 0048A708
or eax, eax
jne 0044815E |
И чуть дальше, уже после джампа:
* Reference To: CAVORT20.Send, Ord:023Eh |
|
|
|
:004481D0 E86B1E0400
:004481D5 81C408000000
|
Call 0048A040
add esp, 00000008 |
* Reference To: CAVORT20._P2Logic, Ord:03EFh |
|
|
|
:004481DB E8581E0400
:004481E0 0BC0
:004481E2 7505
:004481E4 E91B000000
|
Call 0048A038
or eax, eax
jne 004481E9
jmp 00448204 |
После вызова Send() мы вываливаемся аккурат в tcsecure.dll в License:PerformLicenseVerification.
Хмм... Оччень интересные у них экспорты :))) Особенность CA-Visual Objects,
однако...
Итак, смотрим tcsecure.dll. Вспоминаем, что мы видели при попытке ввести
Evaluation Key. "Invalid Evaluation Key!". Ищем в String Reference'ах.
Нашли? А теперь смотрим чуть выше:
:010316B9 FF5354
:010316BC 0BC0
:010316BE 0F8538000000
|
call [ebx+54]
or eax, eax
jne 010316FC |
* Reference To: CAVORT20.uiLineNum_RT, Ord:0561h |
|
|
|
:010316C4 8B1DF8760D01
:010316CA C70324000000 |
mov ebx, dword ptr [010D76F8]
mov dword ptr [ebx], 00000024 |
* Possible StringData Ref from Data Obj ->"Invalid evaluation
license key." |
Патчим or eax,eax \ jne 010316FC на mov al,1 \ jmp 010316FC.
Дальше... "The evaluation license key is invalid". Ищем...
:01021011 FF5354
:01021014 0BC0
:01021016 0F8556010000 |
call [ebx+54]
or eax, eax
jne 01021172 |
Патч аналогичен.
Запускаем... И что же? Expired... Почему? Объясню позже. А сейчас достаточно
пропатчить все это дело...
:01031BE7 8B4618
:01031BEA 0BC0
:01031BEC 7505
:01031BEE E91C020000
|
mov eax, dword ptr [esi+18]
or eax, eax
jne 01031BF3
jmp 01031E0F |
Меняем or eax,eax \ jne 01031BF3 на xor eax,eax \ nop \ nop. Хмм... Не
помогает. Ага. Это переход если у нас версия с ключом. Ну что же... Смотрим
выше...
Следует отметить, что у этой версии есть Debug, и отладочные строки то
и дело проглядываю (заметно облегчая слом). Итак смотрим чуть выше...
Что это?
* Reference To: CAVORT20.Today, Ord:0298h |
|
|
|
:01031B2A E8F90D0700
:01031B2F 8945DC
:01031B32 8B7508
:01031B35 8B4610
:01031B38 2B45DC
:01031B3B 7105 |
Call 010A2928
mov dword ptr [ebp-24], eax
mov esi, dword ptr [ebp+08]
mov eax, dword ptr [esi+10]
sub eax, dword ptr [ebp-24]
jno 01031B42 |
Очевидно, что это получение текущей даты. После джампа идет какой-то
блок выбора. Очевидно, он и выполняет вычисление expiration date. Ну что
же. Посмотрим на "комментарий" (Ну вы поняли, про что это я).
- "We appear...". Понятно как патчить? Поясняю:
:01031B42 3DB9000000
:01031B47 7E05
:01031B49 E90A000000 |
cmp eax, 000000B9
jle 01031B4E
jmp 01031B58 |
* Referenced by a (U)nconditional or (C)onditional Jump
at Address:
|:01031B47(C)
| |
:01031B4E B801000000
:01031B53 E905000000 |
mov eax, 00000001
jmp 01031B5D |
* Referenced by a (U)nconditional or (C)onditional Jump
at Address:
|:01031B49(U)
| |
:01031B58 B800000000 |
mov eax, 00000000 |
* Referenced by a (U)nconditional or (C)onditional Jump
at Address:
|:01031B53(U)
| |
:01031B5D 0BC0
:01031B5F 7505
:01031B61 E966000000 |
or eax, eax
jne 01031B66
jmp 01031BCC |
Нам надо, чтобы сработал переход на 01031B66, А для этого необходим ненулевой
eax. Как это получить? Нужно, чтобы сработал jmp по адресу 01031B53, а
для этого, очевидно, надо, чтобы сработал jmp по адресу. 01031B47. Патчим
его - 7E на EB.
Все! Запустилось! Посмотрим в About...
Evaluation expires 13.09.2093
Unlimited Voice Ports
1,691,250,000 Voice Minutes
33,825 Billing Customers
(Я вводил номер из всех 1). Это не есть хорошо. Очевидно, что наш номер
каким-то образом повлиял на вводимые ограничения. Теперь предстоит поискать,
где записывается наша информация о том, что мы ввели. Поиск в реестре
не дал должных результатов. В .ini тоже. Я пригляделся... И что же? В
каталоге с TeleCount'ом появился новый файл - tc.spv. Я его удалил и снова
появилось - "Evaluation...". Да, кстати, TeleCount делает его
backup в катологе с форточками под именем vtspvftc.sys. Это так.. Удалять
надо обоих.
Итак, теперь нам надо реверсировать защиту keyfile'а. Ищем его в referenca'х.
Нашли? Хорошо. Два раза? Еще лучше... Это очевидно. Нам надо его не только
считывать, но и создавать в первый раз. Легко догадаться, что нам нужен
тот, что по адресу 102E4DE. Скроллируем вниз, читаем комментарии. Что
же это?:
* Reference To: TCSECURE.StrDecompress |
:
0102E668 E8CF0E0000
:0102E66D 81C404000000
:0102E673 8945F8 |
|
call 0102F53C
add esp, 00000004
mov dword ptr [ebp-08], eax |
* Reference To: CAVORT20.uiLineNum_RT, Ord:0561h |
:0102E676 8B1DF8760D01
:0102E67C C7031D000000 |
|
mov ebx, dword ptr [010D76F8]
mov dword ptr [ebx], 0000001D |
* Possible StringData Ref from Data Obj ->"TeleCountSecurityLicenseProfile" |
:
0102E682 B810D70A01
:0102E687 50
:0102E688 8B45F8
:0102E68B 50 |
|
mov eax, 010AD710
push eax
mov eax, dword ptr [ebp-08]
push eax |
* Reference To: TCSECURE.Decrypt |
:0102E68C E8730F0000
:0102E691 81C408000000
:0102E697 8945F8 |
|
call 0102F604
add esp, 00000008
mov dword ptr [ebp-08], eax |
Запускаем TRW. Непосредственно убеждаемся, что на вход StrDecompress
подается содержимое нашего tc.spv, А ее выход подается на Decrypt, а "TeleCountSecurityLicenseProfile",
очевидно, ключ. После Decrypt смотрим на выход.... Ого! Какие-то парметры,
да еще и текстовые. Интересно... Залазим в StrDecompress...
Мда... Оказывается, используется функция MemDecompress из FUNCky 6. Для
особо любопытных скажу, что там используется inflate by Mark Adler, etc.
Зачем вызывается два раза? Первый раз из запакованных данных вытаскивается
длина их после распаковки, затем уже выделяется память и распаковывается.
Опять запускаем TRW, чтобы выяснить параметры вызова.
Итак:
- Параметры первого вызова:
Первый - исходные данные, второй - их длина. Третий и четвертый - 0.
Пятый - адрес памяти, куда записать распакованную длину.
Выход: по адресу, указанному в пятом параметре возвращается длина.
- Параметры второго вызова:
Первый - исходные данные, второй - их длина. Третий - адрес буфера,
куда поместить распакованные данные, Четвертый - длина распакованных
данных (результат после первого вызова). Пятый - адрес, куда записать,
сколько байт распаковано.
Все! Можно напрямую вызывать функцию MemDecompress из FUNCky60.dll. Теперь
залазим в Decrypt... Опять вызывается функция из FUNCky 6 -- _decrypt.
Читаем документацию... И что же? Оказывается это простой побайтный xor,
только у каждого байта ключа предварительно включается старший бит. Все!
Теперь рассмотрим тот вызов, где создается tc.spv. Аналогично с точностью
до наоборот: MemCompress и _encrypt из FUNCky 6. Про _encrypt мы уже все
знаем - xor - он и в Африке xor, при исследовании при помощи TRW параметров
функции MemCompress выяснилось следующее:
Первый - адрес памяти, "что упаковывать", второй - длина, третий
- буфер, куда складывать сжатую информация, четвертый - 0x138h (Хоть убейте,
не знаю зачем), пятый - буфер, куда записывается кол-во байт после упаковки.
И все! Я написал маленькую программку на Делфях, показывающую полный процес
записи\восстановления tc.spv: (Сорри за отсутствие форматирования, имхо,
и так понятно).
program test;
Uses SysUtils,WIndows;
{$APPTYPE CONSOLE}
var
f,f1,f2:file of byte;
i:integer;
b,b1:byte;
len:dword;
p,p1,p2:pointer;
pass:string='TeleCountSecurityLicenseProfile';
procedure FDecompress (adr1,adr2,adr3,adr4,adr5:DWORD);stdcall;external
'funcky60.dll' name 'MemDecompress';
procedure FCompress (adr1,adr2,adr3,adr4,adr5:DWORD);stdcall;external
'funcky60.dll' name 'MemCompress';
begin
for i:=1 to length(pass) do pass[i]:=chr(ord(pass[i]) or $80);
if paramstr(1)='-in' then begin
AssignFile(f,'tc.spv');
AssignFile(f1,'tc.in.spv');
ReWrite(f1);
ReSet(f);
GetMem(p,FileSize(f));
GetMem(p1,FileSize(f));
GetMem(p2,FileSize(f));
BlockRead(f,p^,FileSize(f));
FDecompress(DWORD(p),FileSize(f),0,0,DWORD(p2));
len:=DWORD(p2^);
FDecompress(DWORD(p),FileSize(f),DWORD(p2),len,DWORD(p1));
for i:=1 to len do begin
b:=Byte(pointer(Integer(p2)+i-1)^);
b1:=i mod 31;
if b1=0 then b1:=31;
b:=b xor ord(pass[b1]);
Byte(pointer(Integer(p2)+i-1)^):=b;
end;
BlockWrite(f1,p2^,len);
CloseFile(f);
CloseFile(f1);
end
else if paramstr(1)='-out' then begin
AssignFile(f1,'tc.in.spv');
AssignFile(f2,'tc.out.spv');
ReSet(f1);
ReWrite(f2);
GetMem(p,FileSize(f1)+1024);
GetMem(p1,FileSize(f1)+1024);
GetMem(p2,FileSize(f1)+1024);
BlockRead(f1,p2^,FileSize(f1));
len:=FileSize(f1);
for i:=1 to len do begin
b:=Byte(pointer(Integer(p2)+i-1)^);
b1:=i mod 31;
if b1=0 then b1:=31;
b:=b xor ord(pass[b1]);
Byte(pointer(Integer(p2)+i-1)^):=b;
end;
FCompress(DWORD(p2),len,DWORD(p),$138,DWORD(p1));
BlockWrite(f2,p^,DWORD(p1^));
CloseFile(f1);
CloseFile(f2);
end;
end.
Ну что же. Скармливаем ей tc.spv и получаем такое на выходе:
001=33858
002=1
003=33858
004=33825
005=CA,TF,FM,AR
006=0050
007=3382533825
008=1
009=1
010=65535
011=0
012=0
013=33825
014=33825
015=25508096
Хммм. Что же это значит? Очевидно, различные параметры, которые определяют
функционирование программы. Да, сразу скажу, что параметр 015 - CRC всего
файла, я это не знал и в первый раз проковырялся пол-часа, пока узнал,
где грабли. Патчится в следующем месте (догадайтесь, почему):
:0102F355 3DB3030000
:0102F35A 0F8578000000 |
cmp eax, 000003B3
jne 0102F3D8 |
Теперь мы можем самостоятельно изменять параметры и модифицировать keyfile.
Итак, методом научного тыка выяснились все параметры в KeyFile (я приведу
их в виду схемы):
- 001=Start of Expiration Period
- 002=IsEval
- 003=Evaluation Length
- 004=Type of application:
1- TeleCount *NOT For Resale*
2- TeleCount Lite
3- TeleCount Enterprise
4- TeleCount IP
5- TeleCount IP Enterprise
6- TeleCount Enterprise Billing
7- CommStats
8- CommStars Enterprise
- 005=Different addons (toll fraud, etc.)
- 006=Version
- 007=Serial number
- 008=Number of extensions (0=Unlimited)
- 009=Number of truncks (0=Unlimited)
- 010=Number of voice ports (65535=Unlimited)
- 011=Number of data ports (65535=Unlimited)
- 012=Number of data servers (65535=Unlimited)
- 013=Number of voice minutes (0=Unlimited)
- 014=Number of billing customers (1=Unlimited)
- 015=CRC
Все! Можно считать TeleCount полностью взломанным.
|