Исследование программы Audio Mp3 Maker v 1.10-1.13 by Wersion
|
Программист коллегам:
- Сегодня на работе опять весь день @нанизмом занимался.
- Что, винды переставлял?
- Да нет... просто др#чил...
|
Итак, как это было. Когда-то, очень давно, я скачал эту довольно неплохую вещь для того, чтобы наделать
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, делаем:
В одном из них адрес строки с нашим кодом, в другом - с реальным. Берём 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: array of byte): Longint;
//надо сначала найти нужный адрес
var
posit: Integer;
begin
Result := 0;
for posit := 1 to 1024 do
begin
if Buffer[Posit] = $8B then
//ищем наши инструкции в 16-м виде(см. начало(асм. листинг))
if Buffer[Posit + 1] = $1B then //это инструкции до push ebx, push ecx
if Buffer[Posit + 2] = $8B then
if Buffer[Posit + 3] = $4D then
if Buffer[Posit + 4] = $E8 then
if Buffer[Posit + 5] = $53 then //$53=push ebx
if Buffer[Posit + 6] = $51 then //$51=push ecx
begin //тут, надеюсь, вс¸ ясно.
Result := Posit;
Exit;
end
else
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;
repeat
asm //си¸ делается так, чтобы присвоить lpBaseAdr значение StartAddr
mov eax, StartAddr //что обычными средствами Delphi сделать проблематично.
mov lpBaseAdr, eax;
end;
ReadProcessMemory(ProcessHandle, lpBaseAdr, @Buffer, 1030, BR);
//читаем кусок
SeekResult := SeekInBuffer(Buffer); //ищем в н¸м наши инструкции
if SeekResult = 0 then
begin
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);
if not 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;
if not 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 then
begin
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);
until false;
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 then
if event.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT
then
begin
//дождались! 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; //нам надо значение регистра Ecx
asm
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);
until false;
//-----------------------------------------------------//
CodeString := '';
for i := 1 to BREAD do //очищаем код от мусора и су¸м в строку
begin
if 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. <- наставили меня на путь истинный.
|