Программист коллегам:
- Сегодня на работе опять весь день @нанизмом занимался.
- Что, винды переставлял?
- Да нет... просто др#чил...
Итак, как это было. Когда-то, очень давно, я скачал эту довольно неплохую вещь для того, чтобы наделать
Mp3-файлов с одного Audio-диска. У меня тогда не было ни времени, ни знаний, поэтому я решил
воспользоваться Crack-сайтами. Нашедши около 7 серийных номеров к версии 1.10, я убедился, что ни
один из них не работает. Позже я кое-что делал с этой программой, поменял кучу байтов,она даже стала
похожей на зарегистрированную, но в конце концов выкинула мне сообщение о истечении *0-дневного
срока.
И я закинул сию вещь на год. После, набравшись ума, вновь принялся за исследование.
Вот оно:
Что используем:
Softice 4.x & Icedump 6.x
Win32 Disassembler v X.X.
Delphi 4-6.
Начнём.
Запускаем программу. Жмём Register, вводим имя и любой код. Видим сообщение "Invalid...",
запоминаем. Запускаем W32Dasm. Ищем строку в String Data References. Я нашёл следующий код:
:0040D844 53 push ebx (1)
:0040D845 51 push ecx (2)
:0040D846 E8AE9E0000 call 004176F9 (3)
:0040D84B 83C408 add esp, 00000008
:0040D84E 85C0 test eax, eax (4)
:0040D850 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Message"
|
:0040D852 68BC7B4400 push 00447BBC
:0040D857 7518 jne 0040D871 (5)
* Possible StringData Ref from Data Obj ->"Thank you, please restart programs"
|
:0040D859 68E47D4400 push 00447DE4
:0040D85E 8BCE mov ecx, esi
:0040D860 E889CE0100 call 0042A6EE
:0040D865 8B16 mov edx, dword ptr [esi]
:0040D867 8BCE mov ecx, esi
:0040D869 FF92C4000000 call dword ptr [edx+000000C4]
:0040D86F EB37 jmp 0040D8A8
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040D857(C)
|
* Possible StringData Ref from Data Obj ->"Invalid reg code"
|
:0040D871 68D07D4400 push 00447DD0 (6)
:0040D876 8BCE mov ecx, esi
:0040D878 E871CE0100 call 0042A6EE
:0040D87D EB29 jmp 0040D8A8
Итак, что же мы видим? В (1) и (2) функции передаются 2 параметра, в (3) она вызывается.
В (4) мы проверяем значение EAX на равенство 0, потом в (5) прыгаем на (6) если это не так.
Соответственно, начиная с (6) идёт подготовка и выдача сообщения о неверности РИ.
Что мы делаем? Ставим брейкпоинт на (1), начинаем регистрироваться. Попадая в в Softice, делаем:
d ebx
d ecx
В одном из них адрес строки с нашим кодом, в другом - с реальным. Берём Icedump и копируем реальный код
на диск. Вводим их там где надо и получаем зарегистрированный Audio Mp3 Maker®.
0Днак0!
Всё это только начало. Меня всё время мучали следующие вопросы: при такой защите Coxsoft (теперь они
расцвели и стали MjSoft) загнулись бы на корню. Кроме того, мой код работал не только для версии 1.10, но
и для 1.13. Следовательно, алгоритм его генерации не меняли никогда. Тогда трудно объяснить
существование 7 неверных серийных номеров(см. начало).
Это всё значит, что код зависит от конфигурации компьютера или чего-нибудь ещё. На сем факте, не заметив
оного, proudly обломались 4 команды и 3 крэкера(и все ост., кот. я не нашёл). MjSoft специально сделали
такую ламерскую проверку(ост. на уровне), чтобы к ним скорее нашли с/н и оставили бы их в покое.
Наш кейген должен будет проделать всю нашу работу самостоятельно. Он станет интеллектуальным
отладчиком. Берем Delphi 6 и пишем.
Вот исходники:
program main;
{$APPTYPE CONSOLE}//мне так больше нравиться{$R KeyGen.res}//иконкаuses
SysUtils, Windows; //необходимый минимумvar
BW, BREAD: Cardinal;
Event: DEBUG_EVENT; //отладочное событие
XCode: array[1..255] of byte; //массив с нашим кодом
Answer: char; //ответ пользователя
WriteAddress_0, cAddress_0: Longint; //нужные нам адреса
WriteAddress_1, cAddress_1: Pointer;
NewByte: byte; //его будем писать в тело программы
CodeString: string; //строка с кодом
StartupInfo: TStartupInfo; //информация для запуска
ProcessInfo: TProcessInformation; //информация о процессе
Context: _CONTEXT; //контекст нашего процесса -- сод. значения регистров и т.д.
i: integer; //не помню чтоfunction SeekInBuffer(Buffer: arrayof byte): Longint;
//надо сначала найти нужный адресvar
posit: Integer;
begin
Result := 0;
for posit := 1 to 1024 dobeginif Buffer[Posit] = $8B then//ищем наши инструкции в 16-м виде(см. начало(асм. листинг))if Buffer[Posit + 1] = $1B then//это инструкции до push ebx, push ecxif Buffer[Posit + 2] = $8B thenif Buffer[Posit + 3] = $4D thenif Buffer[Posit + 4] = $E8 thenif Buffer[Posit + 5] = $53 then//$53=push ebxif Buffer[Posit + 6] = $51 then//$51=push ecxbegin//тут, надеюсь, вс¸ ясно.
Result := Posit;
Exit;
endelse
Result := 0;
end;
end;
function SeekBpPlace(ProcessHandle: Cardinal): Longint; //главная функция поискаvar
lpBaseAdr: Pointer;
BR: Cardinal;
StartAddr, SeekResult: Longint;
Buffer: array[1..1030] of byte; //для прочитанных кусков программы.begin
StartAddr := $401000;
repeatasm//си¸ делается так, чтобы присвоить lpBaseAdr значение StartAddr
mov eax, StartAddr //что обычными средствами Delphi сделать проблематично.
mov lpBaseAdr, eax;
end;
ReadProcessMemory(ProcessHandle, lpBaseAdr, @Buffer, 1030, BR);
//читаем кусок
SeekResult := SeekInBuffer(Buffer); //ищем в н¸м наши инструкцииif SeekResult = 0 thenbegin
Inc(StartAddr, 1024); //если ничего не нашли, ид¸м дальше.end;
until SeekResult <> 0; //пока поиск не закончится
Result := SeekResult + StartAddr; //добавляем StartAddr и получаем искомое.end;
//--------------------------------------------//begin
Writeln('THIS PROGRAM IS FOR EDUCATIONAL USE ONLY!');
Writeln('YOU CAN USE IT ONLY IF YOU HAVE REGISTERED COPY OF AUDIO MP3 MAKER!');
Writeln('OTHERWISE, IT IS ILLEGAL TO USE THIS PROGRAM!');
Writeln;
Write('Hit <Y>,<Enter> to agree or <N>,<Enter> to disagree/exit==>');
Read(Answer);
if UpCase(Answer) <> 'Y' then
ExitProcess(0);
ifnot FileExists('keyinfo.key') then//чтоб нас не использовали как крэкbegin
Writeln('You aren''t registered user!');
Writeln('Hit <Enter> to exit...');
Readln;
ExitProcess(0);
end;
RenameFile('keyinfo.key', 'keyinfo.key.org'); //делаем резервную копию
Writeln('Welcome.'); //тут, надеюсь, тоже вс¸ ясно
Writeln(#4 + ' This is keygen for Audio Mp3 Maker v. 1.10/1.11/1.12/1.13!');
Writeln(#4 + ' Tested on Audio Mp3 Maker v. 1.10/1.13');
Writeln(#4 + ' Author: Wersion');
Writeln(#4 + ' E-mail: wcrkgroup2002@mail.ru');
Writeln('What do you need to do:');
Writeln(#4 +
'When program will be loaded, press ''Try it'' on the nag-screen');
Writeln(#4 + 'After that, press ''Register'' in the program''s window!');
Writeln(#4 + 'Enter your name.');
Writeln(#4 + 'Enter any code, for example ''any code'' :-).');
Writeln(#4 + 'Press''OK''');
Writeln(#4 + 'Wait.');
Sleep(10000);
FillChar(StartupInfo, Sizeof(StartupInfo), #0); //заполняем структуру
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOWDEFAULT;
ifnot FileExists('amm.exe') then//он, конечно, нам нужен.begin
Writeln(#4 +
' File not found (''Amm.exe'') ! Please run me in program''s directory!');
Readln;
ExitProcess(0);
end;
{Созда¸м процесс и становимся отладчиком}if CreateProcess(nil, PChar('Amm.exe'), nil, nil, false,
DEBUG_ONLY_THIS_PROCESS, nil, nil, StartupInfo, ProcessInfo) then
Writeln(#4 + 'Process created successfully...')
else
ExitProcess(0);
Writeln(#4 + 'Seeking target...');
WriteAddress_0 := SeekBpPlace(ProcessInfo.hProcess) + 6;
//сюда надо поставить брейкпоинт
Writeln(#4 + ' Target found at 0x' + IntToHex(WriteAddress_0, 6));
asm//вы это уже видели
mov eax,WriteAddress_0
mov WriteAddress_1,eax
end;
//--------------------------------------------//repeat
WaitForDebugEvent(Event, 0);
if event.dwDebugEventCode = EXIT_PROCESS_DEBUG_EVENT thenbegin
Writeln(#4 + ' Process terminated by user! Unable to get code!');
Readln;
ExitProcess(0);
end;
if FindWindow(Pchar('#32770'), Pchar('Register')) <> 0 then//дожд¸мся начала регистрацииbegin
ContinueDebugEvent(Event.dwProcessId, Event.dwThreadId, DBG_CONTINUE);
//продолжаем отладку и выходим из цикла(бесконечного, кстати). Break;end;
ContinueDebugEvent(Event.dwProcessId, Event.dwThreadId, DBG_CONTINUE);
untilfalse;
NewByte := $CC;
//инструкция INT 03, воспринимается нами(отладчиком) как брейкпоинт.
WriteProcessMemory(ProcessInfo.hProcess, WriteAddress_1, @NewByte, 1, BW);
//устанавливаем его
Writeln(#4 + ' Target patched...');
Writeln(#4 + ' Breakpoint enabled...');
repeat//ждем появления INT 03
WaitForDebugEvent(Event, Infinite);
Writeln(#4 + ' Waiting...');
if event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT thenif event.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT
thenbegin//дождались! Writeln(#04+' Breakpoint reached...');
NewByte := $51; //восстанавливаем старую инструкцию push ecx
WriteProcessMemory(ProcessInfo.hProcess, WriteAddress_1, @NewByte, 1,
BW);
Writeln(#4 + ' Target patched again (original restored)...');
Context.ContextFlags := CONTEXT_INTEGER;
GetThreadContext(ProcessInfo.hThread, Context); //получаем контекст
cAddress_0 := Context.Ecx; //нам надо значение регистра Ecxasm
mov eax,cAddress_0
mov CAddress_1,eax
end;
Writeln(#4 + 'Reading code...');
ReadProcessMemory(ProcessInfo.hProcess, cAddress_1, @Xcode, 255, BREAD);
//читаем код с мусором в буфер
TerminateProcess(ProcessInfo.hProcess, 0); //завершаем программу
Writeln(#4 + 'Terminating process...');
Break; //выходим из бесконечного циклаend;
ContinueDebugEvent(Event.dwProcessId, Event.dwThreadId, DBG_CONTINUE);
untilfalse;
//-----------------------------------------------------//
CodeString := '';
for i := 1 to BREAD do//очищаем код от мусора и су¸м в строкуbeginif Xcode[i] <> 0 then
CodeString := CodeString + Chr(Xcode[i])
else
Break;
end;
Writeln(#4 + ' Your code: ' + CodeString);
Writeln(#4 + ' Your have 1 minute to copy it to clipboard.');
Writeln(#4 + ' Enjoy!');
Sleep(60000);
//собственно, это вс¸.end.
Что сказать в заключение?
Что сделать кейген с чистого листа при пратически полном отсутствии документации по функциям
отладки совсем не просто. А ещё, все исходники являются моей интеллектуальной собственностью и
защищаются законами об авторских правах.
Created by Wersion. E-mail: wcrkgroup2002@mail.ru
Комментарии/вопросы - приветствуются.
Greats to: Iczelion, Dr. Golova. <- наставили меня на путь истинный.
Программирование: исследование и создание ключа для Audio Mp3 Maker v1.10-1.13 от Wersion
Автор этого исследования - разработчик, который интересовался пониманием работы программы и созданием способа генерации валидных серийных номеров для нее. Он использовал различные инструменты и техники, включая дизассемблирование исполняемого файла программы, анализ ее кода и использование отладочных инструментов, таких как Softice и Icedump.
Созданный автором ключ позволяет пользователям зарегистрировать программу без необходимости покупки легитимной копии. Автор подчеркивает, что это только для образовательных целей и не должно использоваться для незаконных действий.
В этом исследовании включены детальные комментарии к коду, объясняющие, что каждый раздел делает, и как он вносит свой вклад в общую функциональность ключа. Также предоставлены шаг за шагом инструкции по использованию ключа и устранению любых возникших проблем.
В целом, это хорошо написанное и информативное исследование, которое предлагает ценные сведения о мире обратной инженерии и разработки программного обеспечения.
Исследование программы Audio Mp3 Maker v 1.10-1.13 по созданию кейджена для регистрации.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.