Простая защита или ломаем TurboLaunch 5.1.3 тремя способами (патч, сниффер, кейген).
Цель: TurboLaunch 5.1.3
Инструменты: OllyDbg 1.10, Dup2 (для создания патча), плагин CodeRipper для OllyDbg, Delphi 7 (для написания кейгена)
Был скучный вечер, я искал программу, которую хотелось бы «поковырять». Сначала правкой одного байта убрал триал у AWBackuper 4.0, это показалось очень не интересно, затем я вспомнил о программке, которая давно (года 4) стоит у меня на компьютере – это TurboLaunch от Savard Software. К ней существуют кейгены (наверное, около 10), но, ни в одном я не увидел одного маленького нюанса (хотя по большому счету он не практически не играет существенной роли), но о нем чуть позже.
И так, let’s begin…
Патчинг
Первым способом я выбрал патчинг, так как для новичков он является самым простым.
Грузим жертву в OllyDbg (дальше просто оля, olly). Запускаем и видим наг-окно, нажимаем «Enter My Registration Code»
Вводим свое имя (DimitarSerg) и «честно купленный» код 1234567890
Очень странно, но видим вот такое сообщение:
Поищем текст сообщения в текстовых строках. ПКМ-> Search for -> All referenced text strings.
Находим:
Чуть выше видим Jump from 00529CD3, то есть пришли мы оттуда.
Отлично, посмотрим, что там у нас:
«Классика»:
CALL TurboLau.0053AEB0
TEST AL,AL
JE @TurboLau_00529D99
Значит, идет проверка и в соответствии с результатом проверки прыжок.
Чуть ниже вот такие строки:
00529D1A |. BA F49D5200 MOV EDX,TurboLau.00529DF4 ; ASCII "REGISTERED TO: "
И такое:
00529D3B |. 68 0C9E5200 PUSH TurboLau.00529E0C ; ASCII "Thank you for registering! Be sure to check out our web site for updated versions of TurboLaunch and other programs written by "
Ну что же, тогда заглянем в 00529CCC |. E8 DF110100 CALL TurboLau.0053AEB0
там еще один колл, а внутри что-то похожее на процедуру регистрации. Ну и зачем нам процедура проверки, если можно обойтись и без нее ?!
И так в начало процедуры регистрации по адресу 540628 делаем «классический» патч
xor eax,eax // обнуляем EAX
inc eax // EAX = EAX +1
Retn // return
Сохраняем изменения. ПКМ-> Copy to executable -> All modifications -> Save file.
Сохраним его под новым именем, например, TurboLaunch1.exe
Хотим радоваться, но получаем в ответ такое:
Ай-ай-ай. Проверка целостности. Ну и Бог с ней:
Перегружаем программу в отладчике, затем ставим breakpoint на вызов функции MessageBoxA
bp MessageBoxA
В окне «Call Stack of main thread» смотрим, откуда пришли:
Делаем ПКМ-> Show Call и оказываемся по адресу
00450CA4 |. E8 8F75FBFF CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA
Листаем выше и видим:
00450C7B |> \84DB TEST BL,BL
00450C7D |. 74 60 JE SHORT TurboLau.00450CDF
Опять проверка.
Меняем условный переход на безусловный (JE –> JMP), сохраняем изменения и стартуем. Ура, программа запускается.
Окна с просьбой купить больше не наблюдаем, в окне About видим «Registered to».
В общем, на кого-то зарегистрировано, вот и хорошо.
Сделать патч в Dup2 или uPPP не является проблемой.
Поиск серийного номера.
Как вы заметили выше, сообщение о правильном/неправильном серийном номере формируется в зависимости от результата
00529CD1 |. 84C0 TEST AL,AL
А строкой выше видим:
00529CCC |. E8 DF110100 CALL TurboLau.0053AEB0
Логично, что вся процедура генерации проходит здесь…
Смотрим:
0053AEB0 /$ 8B90 68010000 MOV EDX,DWORD PTR DS:[EAX+168]
0053AEB6 |. 8B80 64010000 MOV EAX,DWORD PTR DS:[EAX+164]
0053AEBC |. E8 67570000 CALL TurboLau.00540628
0053AEC1 \. C3 RETN
Ставим breakpoint по адресу 0053AEB0 и при вводе имени / регистрационного номера останавливаемся здесь и видим, что происходит считывание введенных данных, а значит, самое интересное находится в CALL TurboLau.00540628.
Трассируем по F7, видим считывание, какие-то превращения, другие операции и т.д. и т.п., в результате, если уделить этой процедуре пару минут внимания и терпения, то во время выполнения инструкции
00540758 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
Мы увидим:
Stack SS:[0012F1AC]=00C66E8C, (ASCII «D1F74F-5L3GRT-3WDULJ»)
Хм, неужели правильный серийный номер ?! Пробуем: имя DimitarSerg и серийный номер D1F74F-5L3GRT-3WDULJ, программа поздравляет нас с приобретением.
Так как серийный номер виден в открытом виде, то сериал сниффер пишется на ура, нужно только посмотреть в регистр EAX по адресу, который идет сразу за 00540758, то есть 0054075B.
Я не буду описывать методику написания сериал сниффера, но скажу, что у команды AT4RE есть такой инструмент, как Serial Sniffer Creator, можете воспользоваться им, должно выглядеть приблизительно вот так:
Но я ее не рекомендую, так как она не сниффает серийники в юникоде и у нее частые глюки с интерфейсом.
На паблике есть несколько примеров (темплейтов) по созданию снифферов, поэтому ассемблер или Delphi в руки и вперед «допиливать».
Кейген
Ну и самое интересное — это, конечно же, написание кейгена.
Как я описывал выше, вся процедура генерации начинается по адресу 00540628
Вводим любые данные регистрации, трассируем. Обращаем внимание на вот это место:
Заглянем в этот колл. Видим некоторые операции, выполняемые над именем, вычисления. Скажу наперед, что здесь происходит первая часть вычислений, результат которых используется чуть-чуть позже. Я не «великий спец» по ассемблеру, поэтому использую плагин CodeRipper.
Я покажу два способа кейгена: инлайн ассемблерного кода на Делфи и полностью переведенный код на паскаль:
Copy Source | Copy HTML
- procedure GenClick( Dummy : Pointer; Sender: PControl );
- var NameBuffer,SerNum:String;
- len,sn_tmp:integer;
- begin
- if Nm.Text<>'' then begin
- NameBuffer:=Nm.Text;
- len:=Length(Nm.Text);
- ASM
- PUSHAD
- mov ECX,NameBuffer
- mov EBX,len
- @TurboLau_0053F42E:
- XOR EAX,EAX
- MOV AL,BYTE PTR DS:[ECX]
- SHL EAX,8
- XOR EDX,EAX
- MOV EAX,8
- @TurboLau_0053F43C:
- TEST DH,080h
- JE @TurboLau_0053F44B
- ADD EDX,EDX
- XOR EDX,01021h
- JMP @TurboLau_0053F44D
- @TurboLau_0053F44B:
- ADD EDX,EDX
- @TurboLau_0053F44D:
- DEC EAX
- JNZ @TurboLau_0053F43C
- INC ECX
- DEC EBX
- JNZ @TurboLau_0053F42E
- MOV EAX,EDX
- AND EAX,0FFFFh
- mov sn_tmp,EAX
- POPAD
- END;
- Edit.text:=Int2Hex((sn_tmp),4);
- end
- else
- Edit.Text:='Enter Your Name';
- End;
Зачем я привел процедуру отдельно? Мне так было легче, сразу видно правильность работы рипнутого кода. Например, для имени DimitarSerg sn_tmp = E330
Ну а дальше опять дело техники: используем плагин CodeRipper, начиная с адреса 5406BC до 540752 (видим, что это большой цикл генерации). На что хочу обратить внимание – это на некоторые коллы. CodeRipper пишет “;<= Jump/Call Address Not Resolved”, большинство из них не нужны и их можно удалить, но вот
005406EE |. E8 F52DECFF ||CALL TurboLau.004034E8
Этот колл ни в коем случае нельзя удалять, так как внутри:
Copy Source | Copy HTML
- @TurboLau_004034E8:
- PUSH EBX
- XOR EBX,EBX
- IMUL EDX,DWORD PTR DS:[EBX+0542008h],08088405h
- INC EDX
- MOV DWORD PTR DS:[EBX+0542008h],EDX
- MUL EDX
- MOV EAX,EDX
- POP EBX
- RETN
Это, как мне подсказали (на тот момент не знал), что это стандартный Random, а DWORD PTR DS:[EBX+0542008h] (он же у меня sn_tmp) – это RandomSeed.
А в рипнутом коде уже не вызывать ее как отдельную процедуру, а просто скопипастить в место ее вызова, вот фрагмент:
Copy Source | Copy HTML
- @TurboLau_005406E9:
- MOV EAX,021h
- // -->CALL @TurboLau_004034E8 ;<= Jump/Call Address Not Resolved
- // содержимое колла
- PUSH EBX
- XOR EBX,EBX
- IMUL EDX,sn_tmp,08088405h
- INC EDX
- MOV sn_tmp,EDX
- MUL EDX
- MOV EAX,EDX
- POP EBX
- MOV EBX,EAX
- INC EBX
- MOV AL,BYTE PTR SS:[EBP-0Dh]
- XOR AL,0FFh
- AND EAX,0FFh
- ADD EBX,EAX
- DEC ESI
- JNZ @TurboLau_005406E9
Еще хочу обратить внимание на строку
00540715 |. BA 1C085400 |MOV EDX,TurboLau.0054081C; ASCII «GF2DSA38HJKL7M4NZXCV5BY9UPT6R1EWQ40I1CP7Z7GOEPQLZ»
Ее можно назвать “ключевой”, так как от нее прямо зависит регистрационный код. В регистр edx записывается указатель на строку по адресу 0054081C.
Полный исходник кейгена на Delphi с асмовскими вставками и на чистом паскале прилагаются, ничего сложного там нет.
Ну и то, с чего я начинал рассказ: после всех этих генераций мы НЕ ОСТАНАВЛИВАЕМСЯ, а топаем дальше и видим какие-то имена/ники и т.д.
Естественно, это же примитивный блэклист:
Copy Source | Copy HTML
- @TurboLau_005407C4:
- MOV EAX,DWORD PTR SS:[EBP-4] ; Цикл проверки блэклиста
- MOV EDX,DWORD PTR DS:[ESI]
- CALL @TurboLau_00405030
- JNZ @TurboLau_005407D4
- XOR EBX,EBX
- JMP @TurboLau_005407DA
- @TurboLau_005407D4:
- ADD ESI,4
- DEC EDI
- JNZ @TurboLau_005407C4
Там проскакивают такие интересные ники, как Nitrogen / TSRh TeaM, REVENGE Crew, FiGHTiNG FOR FUN, TEAM VIRILITY… и т.д. Думаю, намек вы поняли.
Итого 44 имени. Не проблема скопипастить вручную, забить в строковой массив типа
BlackList:array [0..43] of string = (…), а при генерации смотреть, не является ли желаемое вами имя в блэклисте. (файл BlackList.txt тоже прилагаю).
Для “полного счастья” переводим код с асм-вставками на паскаль:
Copy Source | Copy HTML
- //…
- var
- BlackList:array [ 0..43] of string = ('zircon / pc97','freeware','registered user',<br/>'NuZ''c97','Registered','kOUGER! [CB4]','Cosmo Cramer 1997','Cosmo Cramer MJ13',<br/>'MJ13 Forever','cH/Phrozen Crew','Everybody','iCEMAN [uCF]','pank','Henry Pan',<br/>'iTR [CORE]','mpbaer','CORE/JES','Chen Borchang','n03l','ODIN 97','lgb/cORE''97',<br/>'MCC','blastsoft','CORE/DrRhui','Vizion/CORE','TEAM ViRiLiTY','Nambulu','NuZPc97',<br/>'Weazel','Phrozen Crew','TEAM VIRILITY','x3u','Reg Name','FiGHTiNG FOR FUN','RaSCaL [TMG]',<br/>'Nitros^21','TEAM TSRH','ttdown.com','Nitrogen / TSRh TeaM','Free Program','REVENGE Crew',<br/>'Vladimir Kasho','Alexej Melnikov','Seth W. Hinshaw');
- //…
- procedure generate;
- Var
- NameBuffer,SerNum:String;
- EDX,EAX,len,i,a,b,tmp1:integer;
- Textname: PChar;
- begin
- len := GetWindowTextLengthA(TxtNameHwnd);
- if len > 1 then
- begin
- { Get text from name input }
- GetMem(Textname, len + 1);
- GetWindowTextA(TxtNameHwnd,PAnsiChar(Textname),len + 1);
- { Generate Serial }
- KeyStr:= 'GF2DSA38HJKL7M4NZXCV5BY9UPT6R1EWQ40I1CP';
- NameBuffer:=String(Textname);
- SerNum:='';
- Randomize;
- EDX:= 0;
- for i:=1 to len do
- begin
- EDX := EDX xor (ord(NameBuffer[i]) shl 8);
- EAX := 8;
- while EAX <> 0 do
- begin;
- if (EDX shr 8 and $0FF) >= $80
- then
- EDX := EDX shl 1 xor $1021
- else
- EDX := EDX shl 1;
- dec(EAX);
- end;
- end;
- RandSeed := EDX and $0FFFF;
- i:=1;
- while i <> $13 do
- begin
- a := 0;
- b := $13 - i;
- if b > 0 then
- begin
- while b > 0 do
- begin
- a:=Random(33);
- inc(a);
- if i > len then
- tmp1:=ord(NameBuffer[i mod len])
- else
- tmp1:=ord(NameBuffer[i]);
- tmp1:=tmp1 xor $0FF and $0FF;
- a := a + tmp1;
- dec(b);
- end;
- end;
- while a > $21 do
- begin
- a := a - $21;
- end;
- SerNum := SerNum + KeyStr[a];
- inc(i);
- end;
- Insert('-',SerNum,7);
- Insert('-',SerNum,14);
- For i:= 0 to 43 do
- begin
- if NameBuffer = BlackList[i] then
- begin
- SerNum:='BLACKLISTED NAME';
- Break;
- end else ;
- end;
- { Display The Results }
- SetWindowTextA(TxtSerialHwnd,PChar(SerNum));
- FreeMem (Textname, len + 1);
- end
- Else
- { Display Error }
- SetWindowText(TxtSerialHwnd,'Not Enough Characters..');
- end;
В результате у меня получился вот такой вот кейген:
Размер 36,5 Кб (неупакованный).Такой маленький размер кейгена достигается за счет использование KOL (Key Objects Library) – библиотеки для Delphi.Можно еще сделать и с использованием WinApi, вклеить свое лого, добавить xm / v2m трек (так я для публичного кейгена и сделал), но мой совет – не используйте VCL, если пишете кейгены на Delphi… 400кб и больше – это не размер для кейгена!
Кому больше нравится чистый ассемблер – Masm (Fasm, Tasm) в руки.
На этом мой рассказ подходит к концу, надеюсь, кто-то что-нибудь да почерпнет для себя из данной статьи.