
Исследование защиты программы HDD Thermometer

Скриншот программы HDD Thermometer
Программа HDD Thermometer позволяет в реальном времени контролировать температуру жестких дисков вашего компьютера. Заявлена вроде бы даже как бесплатная, но так это или нет, давайте посмотрим.
Офсайт программы давно сдох, проект больше не развивается, поэтому вот полный комплект, необходимый для запуска. Благо, что программа небольшая.
Запускаем термометр и сразу получаем наг-скрин со странным текстом на смеси "французского с нижегородским".

Наг-скрин
Бесплатная программа и тут же навязчивое требование доната с регистрацией. Аффтар, ты уж определись в этой жизни. Как говорится, или сними крест или надень трусы. Посмотрим, с чего лучше всего начать исследование.

Окно "О программе"
В окне "О программе" сразу бросается в глаза строчка "НЕЗАРЕГИСТРИРОВАННАЯ ВЕРСИЯ". Программа мультиязычная с внешними языковыми файлами, поэтому нужную строчку сперва надо искать в них. Поиском обнаруживается файл lang\rus.lng с блоком:
;-- about form ----------------------------
About_Title = О программе
About_Version = Версия
About_build = билд
About_TheAuthorIs = Автор программы:
About_RegisteredTo = Заргистрировано на
About_ProgId = Номер программы
About_UNREGISTERED = НЕЗАРЕГИСТРИРОВАННАЯ ВЕРСИЯ
Значит в исполняемом файле надо искать строчку "About_UNREGISTERED". Ссылка на эту строчку в дизассемблере находится легко:
Code (Assembler) : Убрать нумерацию
- CODE:004244B2 mov eax, ds:off_426984
- CODE:004244B7 mov eax, [eax]
- CODE:004244B9 push eax
- CODE:004244BA lea eax, [ebp+var_2A8]
- CODE:004244C0 push eax
- CODE:004244C1 mov ecx, offset _str_About_UNREGISTE.Text
- CODE:004244C6 mov edx, [ebp+var_C]
- CODE:004244C9 mov eax, [ebx+68h]
- CODE:004244CC call sub_411794
- CODE:004244D1 mov edx, [ebp+var_2A8]
- CODE:004244D7 mov eax, ds:off_426984
- CODE:004244DC call @System@@LStrAsg$qqrpvpxv
- ; System::__linkproc__ LStrAsg(void *,void *)

Перекрестные ссылки на указатель
Перекрестные ссылки на этот указатель показывают, что кроме двух адресов инициализации есть еще два места, где этот указатель используется. И первый же участок кода дает нам все необходимое для понимания:
Code (Assembler) : Убрать нумерацию
- CODE:00421430 mov eax, [eax]
- CODE:00421432 mov eax, [eax+3Ch]
- ; Вызвать какую-то функцию проверки
- CODE:00421435 call sub_413958
- CODE:0042143A test al, al
- ; Если она вернула AL=0, то перейти на ветку алгоритма со строкой о
- ; незарегистрированной программе
- CODE:0042143C jz short loc_42146F
- CODE:0042143E mov eax, ds:off_426A48
- CODE:00421443 push dword ptr [eax]
- CODE:00421445 push offset _str____.Text
- CODE:0042144A lea eax, [ebp+var_28] ; int
- CODE:0042144D call sub_413B18
- CODE:00421452 push [ebp+var_28]
- CODE:00421455 lea eax, [ebp+var_24]
- CODE:00421458 mov edx, 3
- CODE:0042145D call @System@@LStrCatN$qqrv
- ; System::__linkproc__ LStrCatN(void)
- CODE:00421462 mov edx, [ebp+var_24]
- CODE:00421465 mov eax, [ebx+18h]
- CODE:00421468 call sub_40BB0C
- CODE:0042146D jmp short loc_42147F
- CODE:0042146F ; -----------------------------------------------
- CODE:0042146F loc_42146F:
- ; Указатель на строку "Незарегистрированная версия"
- CODE:0042146F mov edx, ds:off_426984
- CODE:00421475 mov edx, [edx]
- CODE:00421477 mov eax, [ebx+18h]
- CODE:0042147A call sub_40BB0C

Программа успешно "зарегистрирована"
Действительно, окно с требованием регистрации при запуске программы исчезло, в окне "О программе" отображается, что она на кого-то зарегистрирована. Вариант с патчем оказался успешный. Посмотрим, можно ли обратить алгоритм проверки серийного номера. Эту часть исследований надо начать с уже знакомой нам функции проверки серийника.
Code (Assembler) : Убрать нумерацию
- CODE:00413958 push ebp
- CODE:00413959 mov ebp, esp
- CODE:0041395B push 0
- CODE:0041395D push 0
- CODE:0041395F push 0
- CODE:00413961 push ebx
- CODE:00413962 mov [ebp+var_4], eax
- CODE:00413965 mov eax, [ebp+var_4]
- ; Загрузить из реестра имя пользователя и серийный номер
- CODE:00413968 call @System@@LStrAddRef$qqrpv
- ; System::__linkproc__ LStrAddRef(void *)
- CODE:0041396D xor eax, eax
- CODE:0041396F push ebp
- CODE:00413970 push offset loc_4139DA
- CODE:00413975 push dword ptr fs:[eax]
- CODE:00413978 mov fs:[eax], esp
- CODE:0041397B push 0 ; int
- CODE:0041397D lea eax, [ebp+var_8]
- CODE:00413980 push eax ; int
- CODE:00413981 mov ecx, offset _str_user.Text ; int
- CODE:00413986 mov edx, offset _str_Software_RSD_So.Text ; int
- CODE:0041398B mov eax, 80000001h ; hKey
- CODE:00413990 call sub_411B64
- CODE:00413995 push 0 ; int
- CODE:00413997 lea eax, [ebp+var_C]
- CODE:0041399A push eax ; int
- CODE:0041399B mov edx, offset _str_Software_RSD_So.Text ; int
- CODE:004139A0 mov ecx, offset _str_key.Text ; int
- CODE:004139A5 mov eax, 80000001h ; hKey
- CODE:004139AA call sub_411B64
- CODE:004139AF mov ecx, [ebp+var_C]
- CODE:004139B2 mov edx, [ebp+var_8]
- CODE:004139B5 mov eax, [ebp+var_4]
- ; Функция проверки правильности серийника
- CODE:004139B8 call sub_413828
- ; Сохранить результат в EBX
- CODE:004139BD mov ebx, eax
- CODE:004139BF xor eax, eax
- CODE:004139C1 pop edx
- CODE:004139C2 pop ecx
- CODE:004139C3 pop ecx
- CODE:004139C4 mov fs:[eax], edx
- CODE:004139C7 push offset loc_4139E1
- CODE:004139CC loc_4139CC:
- CODE:004139CC lea eax, [ebp+var_C]
- CODE:004139CF mov edx, 3
- CODE:004139D4 call @System@@LStrArrayClr$qqrpvi
- ; System::__linkproc__ LStrArrayClr(void *,int)
- CODE:004139D9 retn
- CODE:004139DA ; ------------------------------------------
- CODE:004139DA loc_4139DA:
- CODE:004139DA jmp unknown_libname_43
- CODE:004139DF ; ------------------------------------------
- CODE:004139DF jmp short loc_4139CC
- CODE:004139E1 ; ------------------------------------------
- CODE:004139E1 loc_4139E1:
- ; Восстановить результат проверки из EBX в EAX
- CODE:004139E1 mov eax, ebx
- CODE:004139E3 pop ebx
- CODE:004139E4 mov esp, ebp
- CODE:004139E6 pop ebp
- CODE:004139E7 retn
Code (Assembler) : Убрать нумерацию
- CODE:00413828 push ebp
- CODE:00413829 mov ebp, esp
- CODE:0041382B push ecx
- CODE:0041382C mov ecx, 4
- CODE:00413831 loc_413831:
- CODE:00413831 push 0
- CODE:00413833 push 0
- CODE:00413835 dec ecx
- CODE:00413836 jnz short loc_413831
- CODE:00413838 push ecx
- CODE:00413839 xchg ecx, [ebp+var_4]
- CODE:0041383C push ebx
- CODE:0041383D mov [ebp+var_C], ecx
- CODE:00413840 mov [ebp+var_8], edx
- CODE:00413843 mov [ebp+var_4], eax
- CODE:00413846 mov eax, [ebp+var_4]
- CODE:00413849 call @System@@LStrAddRef$qqrpv
- ; System::__linkproc__ LStrAddRef(void *)
- CODE:0041384E mov eax, [ebp+var_8]
- CODE:00413851 call @System@@LStrAddRef$qqrpv
- ; System::__linkproc__ LStrAddRef(void *)
- CODE:00413856 mov eax, [ebp+var_C]
- CODE:00413859 call @System@@LStrAddRef$qqrpv
- ; System::__linkproc__ LStrAddRef(void *)
- CODE:0041385E xor eax, eax
- CODE:00413860 push ebp
- CODE:00413861 push offset loc_41392E
- CODE:00413866 push dword ptr fs:[eax]
- CODE:00413869 mov fs:[eax], esp
- CODE:0041386C lea eax, [ebp+var_10]
- CODE:0041386F mov ecx, [ebp+var_4]
- CODE:00413872 mov edx, [ebp+var_8]
- CODE:00413875 call @System@@LStrCat3$qqrv
- ; System::__linkproc__ LStrCat3(void)
- CODE:0041387A mov eax, [ebp+var_10]
- CODE:0041387D call sub_4035A4
- CODE:00413882 mov [ebp+var_1C], eax
- CODE:00413885 fild [ebp+var_1C]
- CODE:00413888 fdiv flt_41393C
- CODE:0041388E call @System@@TRUNC$qqrv
- ; System::__linkproc__ TRUNC(void)
- CODE:00413893 mov ebx, eax
- CODE:00413895 lea eax, [ebp+var_14]
- CODE:00413898 push eax
- CODE:00413899 mov ecx, ebx
- CODE:0041389B mov edx, 1
- CODE:004138A0 mov eax, [ebp+var_10]
- CODE:004138A3 call @System@@LStrCopy$qqrv
- ; System::__linkproc__ LStrCopy(void)
- CODE:004138A8 lea eax, [ebp+var_18]
- CODE:004138AB push eax
- CODE:004138AC mov ecx, ebx
- CODE:004138AE add ecx, ecx
- CODE:004138B0 lea edx, [ebx+1]
- CODE:004138B3 mov eax, [ebp+var_10]
- CODE:004138B6 call @System@@LStrCopy$qqrv
- ; System::__linkproc__ LStrCopy(void)
- CODE:004138BB push offset _str_HT_.Text
- CODE:004138C0 mov eax, [ebp+var_14]
- CODE:004138C3 call sub_411F88
- CODE:004138C8 lea edx, [ebp+var_24]
- CODE:004138CB call sub_412010
- CODE:004138D0 push [ebp+var_24]
- CODE:004138D3 push offset _str___1.Text
- CODE:004138D8 mov eax, [ebp+var_18]
- CODE:004138DB call sub_411F88
- CODE:004138E0 lea edx, [ebp+var_28]
- CODE:004138E3 call sub_412010
- CODE:004138E8 push [ebp+var_28]
- CODE:004138EB lea eax, [ebp+var_20]
- CODE:004138EE mov edx, 4
- CODE:004138F3 call @System@@LStrCatN$qqrv
- ; System::__linkproc__ LStrCatN(void)
- CODE:004138F8 mov edx, [ebp+var_20]
- CODE:004138FB mov eax, [ebp+var_C]
- ; Сравнить две строчки
- CODE:004138FE call @System@@LStrCmp$qqrv
- ; По результатам сравнения установить регистр BL
- CODE:00413903 setz bl
- CODE:00413906 xor eax, eax
- CODE:00413908 pop edx
- CODE:00413909 pop ecx
- CODE:0041390A pop ecx
- CODE:0041390B mov fs:[eax], edx
- CODE:0041390E push offset loc_413935
- CODE:00413913 loc_413913:
- CODE:00413913 lea eax, [ebp+var_28]
- CODE:00413916 mov edx, 3
- CODE:0041391B call @System@@LStrArrayClr$qqrpvi
- CODE:00413920 lea eax, [ebp+var_18]
- CODE:00413923 mov edx, 6
- CODE:00413928 call @System@@LStrArrayClr$qqrpvi
- CODE:0041392D retn
- CODE:0041392E ; ------------------------------------------------------
- CODE:0041392E loc_41392E:
- CODE:0041392E jmp unknown_libname_43
- CODE:00413933 jmp short loc_413913
- CODE:00413935 loc_413935:
- ; Восстановить результат сравнения их EBX в EAX
- CODE:00413935 mov eax, ebx
- CODE:00413937 pop ebx
- CODE:00413938 mov esp, ebp
- CODE:0041393A pop ebp
- CODE:0041393B retn

Содержимое стека перед проверкой
В регистрах EAX и EDX лежат указатели на наш введенный левый серийный номер и строчку, с которой он сравнивается. Для регистрационного имени "ManHunter / PCL" это будет строка "HT-FA40-1AAE". Перезапустим программу и попробуем зарегистрировать ее с найденной парой имя-серийник.

Программа успешно зарегистрирована
На этот раз у нас "честная" регистрация. Мы ничего не патчили, а отловили правильный серийник в отладчике. Обратите внимание на указатели на строчки в стеке. Там используется строчка "A52F99BD10441434" из окна "О программе", обозначенная как "Номер программы". На разных компьютерах это значение разное, я проверил. Поэтому правильную пару имя-серийный номер придется каждый раз подбирать заново. Это, конечно, не вариант, поэтому будем реверсить дальше. Под отладчиком видно, что части серийного номера в регистре EAX возвращаются из процедуры 00411F88.
Code (Assembler) : Убрать нумерацию
- ; Регистрационное имя
- CODE:004138C0 mov eax, [ebp+var_14]
- ; Подсчитать хэш
- CODE:004138C3 call sub_411F88
- CODE:004138C8 lea edx, [ebp+var_24]
- CODE:004138CB call sub_412010
- CODE:004138D0 push [ebp+var_24]
- CODE:004138D3 push offset _str___1.Text ; "-"
- ; Номер программы
- CODE:004138D8 mov eax, [ebp+var_18]
- ; Подсчитать хэш
- CODE:004138DB call sub_411F88
- CODE:004138E0 lea edx, [ebp+var_28]
- CODE:004138E3 call sub_412010
Code (Assembler) : Убрать нумерацию
- ; EAX - накопительный регистр для хэша
- CODE:00411FB8 mov ebx, 1
- ; Первый цикл - поочередно перебираются байты переданной строчки
- CODE:00411FBD loc_411FBD:
- ; EDX - указатель на строку
- CODE:00411FBD mov edx, [ebp+var_4]
- CODE:00411FC0 movzx edx, byte ptr [edx+ebx-1]
- CODE:00411FC5 shl edx, 8
- CODE:00411FC8 xor eax, edx
- CODE:00411FCA mov edx, 8
- ; Второй цикл - 8 раз регистр EAX или умножается на 2 или XORится с 8005h
- CODE:00411FCF loc_411FCF:
- ; Выбрать вариант действия на основании установленного бита в регистре AH
- CODE:00411FCF test ah, 80h
- CODE:00411FD2 jz short loc_411FDD
- ; EAX умножается на 2 и XORится с константой 8005h
- CODE:00411FD4 add eax, eax
- CODE:00411FD6 xor eax, 8005h
- CODE:00411FDB jmp short loc_411FDF
- CODE:00411FDD ; -----------------------------------------
- CODE:00411FDD loc_411FDD:
- ; EAX умножается на 2
- CODE:00411FDD add eax, eax
- CODE:00411FDF loc_411FDF:
- ; Следующая итерация второго цикла
- CODE:00411FDF dec edx
- CODE:00411FE0 jnz short loc_411FCF
- ; Следующая итерация первого цикла
- CODE:00411FE2 inc ebx
- CODE:00411FE3 dec ecx
- CODE:00411FE4 jnz short loc_411FBD
- CODE:00411FE6 loc_411FE6:
- ; Оставить только младшее слово, это и есть искомая часть серийника
- CODE:00411FE6 and eax, 0FFFFh
Просмотров: 6559 | Комментариев: 3
Метки: исследование защиты

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

ManHunter
(21.04.2014 в 18:35):
А там блоки практически одинакового повторяющегося кода, меняются только по два указателя в каждом блоке. Два тыка мышкой на меняющийся указатель - и вышел. На одном блоке да, не очень наглядно получилось.

irokkezz
(21.04.2014 в 17:30):
"Здесь не условия и не проверки, а просто последовательно загружаются строки из языкового файла для дальнейшего использования" - очень часто попадаются такие программы.
Прости, не совсем понял, как ты вышел на адрес 426984?
Прости, не совсем понял, как ты вышел на адрес 426984?

Добавить комментарий
Заполните форму для добавления комментария

http://pastebin.com/mVRgqP8L