/\                                                       /\
        /  \_                                                   _/  \
   ____/    /__      T E A M  .  F I F T Y  .  T H R E E      __\    \____
   \__     ____\\-                                 ____    -//____     __/
     /    /_____                             ______\   \__________\    \
   _/    /_\    \_________________________-//     //    ____/    /_\    \_
 __\            _\  ____ /___  _    \_    \/     //    ___\/_            /__
))______________\_    _______\_|__   /____\/_____\    /gf  _/______________((
                  \___\\-       /___//-         /____((-
              TEAM FiFTY THREE  TUTORiALS PACK # SEVENTEEN


Пишем Serial Sniffer (Oraculum)
Автор..: anorganix of ARTeam
Перевод: nightcat of TEAM-53


ВСТУПЛЕНИЕ
Сперва я хочу сказать, что если вы опытный реверсер, то эта статья для вас будет выглядеть детской, но  для новичков она (наверно) будет полезна. Я помню как тядело мне было в самом начале понимать вещи, которые "все знают, как делаются".

Вот почему я хочу попробовать объяснить всё это простым языком, чтобы все поняли. Читайте...



ЧТО ТАКОЕ Serial Sniffer И ГДЕ ЕГО МОЖНО ИСПОЛЬЗОВАТЬ?
Для такого класса программ есть спеуиальный термин - "Oraculum", нов данной статье я предпочитаю  ипосльзовать название Serial Sniffer.

Цель этого домкумента - отобразить ситуацию в которой вы не можете понять алгорит вычисления серийного комера и не можете написать кейген. Наша цель - это простенький CrackMe, который я написал специально для того, чтобы попрактиковаться. Пожалуйста, помните, что регистрационыый алгоритм этого CrackMe очень прост и в нём нет защиты; наша цель - написать сниффер, а не кейген.



ЧТО НАМ НУЖНО ДЛЯ НАЧАЛА РАБОТЫ
-OllyDbg
-Borland Delphi (или любой другой язык для написания сниффера)
-ну и конечно сам CrackMe.



ПОЕХАЛИ
Давайте запустим ОllyDbg и найдём там нашу проверку серийного номераЕлси вы еще не использовать PEiD, то знайте - цель написана на Delphi:


0045073C PUSH EBP
0045073D MOV EBP,ESP
0045073F ADD ESP,-10
00450742 MOV EAX,CrackMe.0045055C
00450747 CALL CrackMe.00405BC8

Давайте запустим крякми и попробуем ввести какой-либо серийный номер и имя... "invalid code
entered!". Открываем окно "Referenced text strings" и ищем там это сообщение.

Затем ставим брейкпоинт на вызов регистрации(смотрите ниже) и жмём кнопку "Check":

004503EF CALL CrackMe.0040421C
004503F4 JNZ SHORT CrackMe.00450408
004503F6 MOV EDX,CrackMe.00450450       ;ASCII "Code accepted!"
004503FB MOV EAX,DWORD PTR DS:[EBX+2FC]
00450401 CALL CrackMe.0042F44C
00450406 JMP SHORT CrackMe.00450418
00450408 MOV EDX,CrackMe.00450468       ;ASCII "Invalid code entered!"

Вы прервётесь по адресу 004503EF. Посмотрите в регистры EAX и EDX... да-да, EAX содержит правильный серийный номер, а EDX - неверный, который мы ввели. Думаю не нужно говорить, что если EAX = EDX, то программа покажет нам сообщение "Code accepted!". В данном случае это вся информация, которая нам нужна, чтобы написать сниффер. Мы знаем, что по адресу 004503EF (теперь он будет называться "magic address") EAX содержит правильный серийный номер. Давайте займёмся программированием.



ПИШЕМ Serial Sniffer в Delphi
Чтобы разработать сниффер я буду использовать Delphi 7 Enterprise. Вы можете использовать любой язык, который нам нравится, если вы сможете повторить мои шаги. Перед непосредственно программирования давайте на минутку задумаемся, что нам нужно сделать:

- запустить программу в приостановленном режиме
- прочитать оригинальные байты, которые мы собираемся пропатчить по магисческому адресу
- записать несколько байт по магическому адресу, чтбы программа вошла в непрекращающийся цикл
- позволить программе запутиться
- следить попала ли программа в цикл по магическому адресу
- если попала, то приостановить программу и считать серийный номер из EAX
- восстановить оригинальные байты (очистить цикл) и продолжить выполнение программы

Код не откомментирован на 100%, надеюсь вы его поймёте:


Sniffer code (Delphi)
{...}

const

//этот код заставит программу войти в непрекращающийся цикл
LOOP: array [0..1] of Byte = ($EB,$FE);

{...}

function SniffSerial(PI: PROCESS_INFORMATION; Ctx: _Context): string;
var
  X: Cardinal;
  Buff: PChar;
begin
// выделяем немного памяти
  GetMem(Buff,50);

// останавливаем программу, чтобы получить контекст
  SuspendThread(PI.hThread);
  GetThreadContext(PI.hThread,Ctx);

// считываем [EAX] (правильный серийный номер)
  ReadProcessMemory(PI.hProcess,Pointer(Ctx.Eax),Buff,50,X);

// устанавливаем результат и освобождаем буффер
  Result:=Trim(Buff);
  FreeMem(Buff);
end;

procedure TfrmMain.btnSniffClick(Sender: TObject);
var
  PI: PROCESS_INFORMATION;
  SI: STARTUPINFO;
  Context: _CONTEXT;
  Buffer: PChar;
  ORI array [0..1] of Byte; G:
  S: string;
  W: DWORD;
begin
// дисаблим кнопку (избегаем запуска цели несколько раз)
  btnSniff.Enabled:=False;

// выделяем паямть и инициализируем вары
  GetMem(Buffer,255);
  FillChar(PI,SizeOf(TProcessInformation),#0);
  FillChar(SI,SizeOf(TStartupInfo),#0);
  SI.cb:=SizeOf(SI);

//создаём процесс (засуспеженный)
  if not CreateProcess('CrackMe.exe',nil,nil,nil,False,
                       CREATE_SUSPENDED,nil,nil,SI,PI) then
begin
// активируем кнопку
  btnSniff.Enabled:=True;

// устанавливаем новый лог
  lblLog.Caption:='Failed to load process!';
  Exit;
end;

// читаем оригинальные байты
  ReadProcessMemory(PI.hProcess,Pointer($004503EF),@ORIG,2,W);

// пишем непрерывный цикл
  WriteProcessMemory(PI.hProcess,Pointer($004503EF),@LOOP,2,W);

// восставнавливаем работу программы
  ResumeThread(PI.hThread);
  Context.ContextFlags:=$00010000+15+$10;

// устанавливаем новый лог
  lblLog.Caption:='Process patched!'+#13+
  'Now enter a name and press the "Check" button...';

while GetThreadContext(PI.hThread,Context) do
begin
  // проверяем достигли ли мы цикла?
  if Context.Eip=$004503EF then
  begin
  
  // получаем серийный номер и кладём его в "S"
  S:=SniffSerial(PI,Context);

  // восстанавливаем оригинальныйе байты и продолжаем работу программы
  WriteProcessMemory(PI.hProcess,Pointer($004503EF),@ORIG,2,W);
  ResumeThread(PI.hThread);

  // копируем серийный номер в буффер обмена и устанавливаем новый лог
  Clipboard.AsText:=S;
  lblLog.Caption:='Your serial has been copied to clipboard!';
  end;

  // ждём 10 миллисекунд
  Sleep(10);
  Application.ProcessMessages;

  // если пользователь хочет закрыть сниффер до выхода из программы, то закрываем и цель тоже

  if WantToClose then
  begin
     TerminateThread(PI.hThread,0);
     Close;
  end;
end;

// высвобождаем память выделенную в начале
FreeMem(Buffer);

// активируем кнопку
btnSniff.Enabled:=True;
end;

Скомпилируйте приаттаченный к этой статье исходник, если у нас что-то не получилось. У вас получится неплохой Serial Sniffer.



ЗАКЛЮЧЕНИЕ
Итак, вот и конец истории. Я надеюсь, что всё, что я сказал, я надеюсь, что всё сказанное будет полезным для вас. Я советую, как всегда, использовать этот материал только в образовательных целях, а не для взлома программ.

Спасибо вам за чтение этой статьи!