
Скриншот программы Amazon Chess
Для разнообразия сегодня исследуем какую-нибудь игрушку. Пусть это будет Amazon Chess - красивые шахматы, где в качестве игровых фигур выступают полуобнаженные амазонки. Если же вид вооруженных блондинок и брюнеток отвлекает от игры, то можно переключить на режим обычной шахматной доски. Смущает только цена в десятку баксов.
Итак, качаем дистрибутив, устанавливаем, запускаем. Сразу открывается окно, в котором нам предлагают ввести серийный номер и зарегистрировать игру. На ввод всякого левака получаем ожидаемое "Invalid serial number".

Строка найдена
Исполняемый файл ничем не упакован, строка находится легко. Теперь в дизассемблере посмотрим код, который на нее ссылается.
...
CODE:004AF0FE push dword ptr fs:[eax]
CODE:004AF101 mov fs:[eax], esp
CODE:004AF104 lea edx, [ebp+var_8]
CODE:004AF107 mov eax, [ebx+324h]
CODE:004AF10D call @Mask@TCustomMaskEdit@GetText$qqrv
; Mask::TCustomMaskEdit::GetText(void)
; Получить текст из поля ввода
CODE:004AF112 mov eax, [ebp+var_8]
CODE:004AF115 lea edx, [ebp+var_4]
CODE:004AF118 call @Sysutils@UpperCase$qqrx17System
; Sysutils::UpperCase(System::AnsiString)
; Перевести его в верхний регистр
CODE:004AF11D mov eax, [ebp+var_4]
; Вызвать процедуру проверки серийника
CODE:004AF120 call sub_4A8CB8
CODE:004AF125 test al, al
; Если она вернула AL!=0, то серийник правильный
CODE:004AF127 jnz short loc_4AF150
CODE:004AF129 push 0 ; uType
CODE:004AF12B mov ecx, offset dword_4AF1A4 ; lpCaption
CODE:004AF130 mov edx, offset aInvalidSerialN
; Сообщение "Invalid serial number"
CODE:004AF135 mov eax, ds:off_4B9EA0
CODE:004AF13A mov eax, [eax] ; int
CODE:004AF13C call @Forms@TApplication@MessageBox$qqrpxct1i
; Forms::TApplication::MessageBox(char *,char *,int)
CODE:004AF141 xor edx, edx
CODE:004AF143 mov eax, [ebx+324h]
CODE:004AF149 call @Mask@TCustomMaskEdit@SetText
; Mask::TCustomMaskEdit::SetText(System::AnsiString)
CODE:004AF14E jmp short loc_4AF177
CODE:004AF150 ; ------------------------------------------
; Игра успешно зарегистрирована
CODE:004AF150 loc_4AF150:
CODE:004AF150 mov eax, [ebp+var_4]
CODE:004AF153 call sub_4A8D78
CODE:004AF158 mov eax, ebx
CODE:004AF15A call sub_4AF1E8
CODE:004AF15F push 0 ; uType
CODE:004AF161 mov ecx, offset dword_4AF1A4 ; lpCaption
CODE:004AF166 mov edx, offset aAmazonChessIsR
; Собщение "Amazon Chess is registered"
CODE:004AF16B mov eax, ds:off_4B9EA0
CODE:004AF170 mov eax, [eax] ; int
CODE:004AF172 call @Forms@TApplication@MessageBox
; Forms::TApplication::MessageBox(char *,char *,int)
CODE:004AF177 xor eax, eax
CODE:004AF179 pop edx
CODE:004AF17A pop ecx
CODE:004AF17B pop ecx
...
Дальше есть два варианта - пропатчить процедуру проверки, чтобы она всегда возвращала AL=1, или попробовать разобрать алгоритм проверки.
; Если патчить, то это делается в самом начале процедуры
CODE:004A8CB8 push ebp ; <-- MOV AL,1; RET
CODE:004A8CB9 mov ebp, esp
CODE:004A8CBB add esp, 0FFFFFFF8h
CODE:004A8CBE push ebx
CODE:004A8CBF push esi
CODE:004A8CC0 push edi
CODE:004A8CC1 xor edx, edx
CODE:004A8CC3 mov [ebp+var_8], edx
CODE:004A8CC6 mov [ebp+var_4], eax
CODE:004A8CC9 mov eax, [ebp+var_4]
CODE:004A8CCC call @System@@LStrAddRef$qqrpv
; System::__linkproc__ LStrAddRef(void *)
CODE:004A8CD1 xor eax, eax
CODE:004A8CD3 push ebp
CODE:004A8CD4 push offset loc_4A8D47
CODE:004A8CD9 push dword ptr fs:[eax]
CODE:004A8CDC mov fs:[eax], esp
CODE:004A8CDF xor ebx, ebx
CODE:004A8CE1 cmp [ebp+var_4], 0
CODE:004A8CE5 jz short loc_4A8CF4
CODE:004A8CE7 mov eax, [ebp+var_4]
CODE:004A8CEA call sub_4049D0
; Проверить длину введенного серийного номера
CODE:004A8CEF cmp eax, 17h
CODE:004A8CF2 jz short loc_4A8D01
CODE:004A8CF4 loc_4A8CF4:
CODE:004A8CF4 lea eax, [ebp+var_4]
; Дополнить до маски "00000-00000-00000-00000"
CODE:004A8CF7 mov edx, offset _str_00000_00000_000.Text
CODE:004A8CFC call @System@@LStrLAsg$qqrpvpxv
; System::__linkproc__ LStrLAsg(void *,void *)
CODE:004A8D01 loc_4A8D01:
CODE:004A8D01 mov esi, 3E8h
CODE:004A8D06 mov edi, offset off_4B8A88
CODE:004A8D0B loc_4A8D0B:
CODE:004A8D0B lea edx, [ebp+var_8]
CODE:004A8D0E mov eax, [ebp+var_4]
; Сканеры криптоалгоритмов показывают, что здесь вызывается функция расчета
; хэша MD5 от серийника
CODE:004A8D11 call sub_4A9990
; Расчетная MD5 проверяется по очереди с заранее сохраненными значениями
CODE:004A8D16 mov edx, [ebp+var_8]
CODE:004A8D19 mov eax, [edi]
CODE:004A8D1B call @System@@LStrCmp$qqrv
; System::__linkproc__ LStrCmp(void)
CODE:004A8D20 jnz short loc_4A8D26
CODE:004A8D22 mov bl, 1
CODE:004A8D24 jmp short loc_4A8D2C
CODE:004A8D26 ; ---------------------------------------------------
CODE:004A8D26 loc_4A8D26:
CODE:004A8D26 add edi, 4
CODE:004A8D29 dec esi
CODE:004A8D2A jnz short loc_4A8D0B
CODE:004A8D2C loc_4A8D2C:
CODE:004A8D2C xor eax, eax
CODE:004A8D2E pop edx
CODE:004A8D2F pop ecx
CODE:004A8D30 pop ecx
CODE:004A8D31 mov fs:[eax], edx
CODE:004A8D34 push offset loc_4A8D4E
CODE:004A8D39 loc_4A8D39:
CODE:004A8D39 lea eax, [ebp+var_8]
CODE:004A8D3C mov edx, 2
CODE:004A8D41 call @System@@LStrArrayClr$qqrpvi
; System::__linkproc__ LStrArrayClr(void *,int)
CODE:004A8D46 retn
CODE:004A8D47 ; ---------------------------------------------------
CODE:004A8D47 loc_4A8D47:
CODE:004A8D47 jmp unknown_libname_67
CODE:004A8D4C ; ---------------------------------------------------
CODE:004A8D4C jmp short loc_4A8D39
CODE:004A8D4E ; ---------------------------------------------------
CODE:004A8D4E loc_4A8D4E:
CODE:004A8D4E mov eax, ebx
CODE:004A8D50 pop edi
CODE:004A8D51 pop esi
CODE:004A8D52 pop ebx
CODE:004A8D53 pop ecx
CODE:004A8D54 pop ecx
CODE:004A8D55 pop ebp
CODE:004A8D56 retn
В результате анализа получаем следующее: от введенного серийного номера считается хэш MD5, после чего он сравнивается по очереди с готовыми хэшами из списка внутри программы. Такую схему регистрации мы уже встречали. Способ обхода точно такой же - замена одного из хэшей в таблице на свой и регистрация с заранее известным серийным номером.

Заменяем хэш в блоке
Первый хэш в блоке заменен на MD5 от cерийника "11111-11111-11111-11111", сохраним изменения и попробуем зарегистрировать игру. На этот раз регистрация пройдет успешно. Ну а пропатченный вариант даже не требует ввода серийника.

Игра успешно "зарегистрирована"
Музыкальное сопровождение в игре можно заменить на свое, музыка хранится в открытом виде в файлах main_theme.mp3 и title_theme.mp3