Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Темная сторона Силы | Автор: ManHunter
Скриншот программы ID3 Editor
Скриншот программы ID3 Editor

ID3 Editor - неудобный, некрасивый и абсолютно бесполезный редактор тегов MP3-файлов. Может быть его единственный плюс заключается в поддержке различных операционных систем. А еще это поделие требует пятнашку зелени за регистрацию, иначе оно будет работать только несколько дней. С удобством и полезностью я помочь не могу, а вот отучить софтину от вредных привычек - это завсегда пожалуйста.

Забираем с офсайта дистрибутив (анкету заполнять не обязательно, над формой есть кнопка для скачивания), устанавливаем, запускаем. Регистрация выполняется через меню "Help".

Сообщение о неправильной регистрации
Сообщение о неправильной регистрации

При вводе в поле серийного номера 24 символов активируется кнопка "OK", но при попытке зафиксировать это выводится сообщение о неправильной регистрации. Исходные данные для поиска у нас теперь есть, исполняемый файл ничем не накрыт, значит можно сразу отправить его в дизассемблер. Когда листинг будет получен, надо будет поискать текст сообщения, а также код, где он используется.
  1. .text:0042F4B2                 push    eax
  2. .text:0042F4B3                 push    ecx
  3. ; Вызвать функцию проверки серийника
  4. .text:0042F4B4                 call    sub_4245E0
  5. .text:0042F4B9                 add     esp, 0Ch
  6. .text:0042F4BC                 test    al, al
  7. ; Если AL не нулевой, значит серийник правильный
  8. .text:0042F4BE                 jnz     short loc_42F4FE
  9. .text:0042F4C0                 mov     eax, [esi+9Ch]
  10. .text:0042F4C6                 lea     edx, [esp+4Ch+MultiByteStr]
  11. .text:0042F4CA                 push    edx
  12. .text:0042F4CB                 push    offset a240e7cee1c2945
  13. ; "240E7CEE1C29454C9A16DF1C323D0C34"
  14. .text:0042F4D0                 push    eax
  15. .text:0042F4D1                 call    sub_424600
  16. .text:0042F4D6                 add     esp, 0Ch
  17. .text:0042F4D9                 test    al, al
  18. .text:0042F4DB                 jnz     short loc_42F514
  19. ; Сообщение о неправильной регистрации
  20. .text:0042F4DD                 push    0               ; unsigned int
  21. .text:0042F4DF                 push    24h             ; uType
  22. .text:0042F4E1                 push    offset aTheSerialNumbe
  23. ; "The serial number supplied is incorrect"...
  24. .text:0042F4E6                 call    ?AfxMessageBox@@YGHPB_WII@Z
  25. .text:0042F4EB                 cmp     eax, 6
  26. .text:0042F4EE                 jz      short loc_42F51B
  27. .text:0042F4F0                 push    offset Class    ; Src
  28. .text:0042F4F5                 mov     ecx, edi
  29. .text:0042F4F7                 call    sub_401E60
  30. .text:0042F4FC                 jmp     short loc_42F514
  31. .text:0042F4FE ; ---------------------------------------------
  32. .text:0042F4FE loc_42F4FE:
  33. .text:0042F4FE                 mov     eax, [esi+0A8h]
  34. .text:0042F504                 cmp     dword ptr [eax-0Ch], 0
  35. .text:0042F508                 jle     short loc_42F514
  36. .text:0042F50A                 push    0               ; unsigned int
  37. .text:0042F50C                 push    40h             ; uType
  38. .text:0042F50E                 push    eax             ; wchar_t *
  39. .text:0042F50F                 call    ?AfxMessageBox@@YGHPB_WII@Z
Все просто. Вызывается функция проверки серийника, если в результате вернулся ненулевой регистр AL, то серийник считается правильным. В противном случае выполняется еще одна проверка, и тут, похоже, считается хэш от серийника и сравнивается с контрольным. При совпадении он тоже признается корректным. Обращать хэш мы не будем, давайте посмотрим функцию проверки серийника:
  1. .text:004245E0 sub_4245E0      proc near
  2. .text:004245E0                 mov     eax, [esp+arg_8]
  3. .text:004245E4                 mov     ecx, [esp+arg_4]
  4. .text:004245E8                 mov     edx, [esp+arg_0]
  5. .text:004245EC                 push    eax
  6. .text:004245ED                 push    ecx
  7. .text:004245EE                 push    edx
  8. .text:004245EF                 call    sub_424450
  9. .text:004245F4                 add     esp, 0Ch
  10. .text:004245F7                 neg     eax
  11. .text:004245F9                 sbb     eax, eax
  12. .text:004245FB                 neg     eax
  13. .text:004245FD                 retn
  14. .text:004245FD sub_4245E0      endp
Функция проверки небольшая, точнее это не сама функция, а обертка над ней. По перекрестным ссылкам узнаем, что она вызывается из двух мест. Ну да, все правильно, проверка при вводе серийника и проверка при старте программы. Записываем в начало функции проверки команды MOV EAX,1 и RET, чтобы она всегда возвращала нужное нам значение. Сохраняем изменения, запускаем, выбираем файл для редактирования, ииииииии...

Ошибка проверки триального состояния
Ошибка проверки триального состояния

Одного патча проверки серийника недостаточно, где-то проверяется еще какое-то значение. Возвращаемся в дизассемблер, чтобы выяснить где и что.
  1. ; Ссылка на строку
  2. .data:005CC0C4 off_5CC0C4      dd offset aFailedToCheckT
  3. ; "Failed to check the trial status. Abort"...
  1. ; Указатель на ссылку на строку
  2. .text:0040CF68                 mov     ecx, off_5CC0C4
  3. .text:0040CF6E                 jmp     loc_40D35C
  4. ...
  5. ...
  6. ; Вывести сообщение
  7. .text:0040D35C loc_40D35C:
  8. .text:0040D35C                 push    0               ; unsigned int
  9. .text:0040D35E                 push    10h             ; uType
  10. .text:0040D360                 push    ecx             ; wchar_t *
  11. .text:0040D361                 call    ?AfxMessageBox@@YGHPB_WII@Z
Если прокрутить листинг чуть выше, то выяснится, что там щедро насыпано как проверок, так и переходов на этот участок кода. Патчить их все слишком некрасиво. Зато еще немного выше обнаруживается красивая проверка и условный переход, который махом перепрыгивает всю эту свистопляску:
  1. .text:0040CE8A                 push    edx             ; lpSubKey
  2. .text:0040CE8B                 call    sub_424740
  3. .text:0040CE90                 add     esp, 0Ch
  4. .text:0040CE93                 test    al, al
  5. .text:0040CE95                 jnz     loc_40D0FA
  6. .text:0040CE9B                 push    0
Заменяем условный переход на безусловный, сохраняем изменения, запускам. Вот теперь все нормально, никаких сообщений не появляется. Зато по истечении нескольких дней при открытии файла начинает появляться сообщение об истечении пробного периода.

Триальное окно
Триальное окно

В принципе, если его просто закрывать, то ничего страшного не произойдет, программа будет работать и дальше. Но давайте сделаем красиво. Вот процедура, которая выводит это сообщение, найти ее в коде по ссылке на строку сообщения не составит труда.
  1. .text:0042F530 sub_42F530      proc near
  2. .text:0042F530                 push    0FFFFFFFFh
  3. .text:0042F532                 push    offset loc_55FD48
  4. .text:0042F537                 mov     eax, large fs:0
  5. .text:0042F53D                 push    eax
  6. .text:0042F53E                 sub     esp, 14h
  7. .text:0042F541                 push    esi
  8. .text:0042F542                 push    edi
  9. .text:0042F543                 mov     eax, dword_5D166C
  10. .text:0042F548                 xor     eax, esp
  11. .text:0042F54A                 push    eax
  12. .text:0042F54B                 lea     eax, [esp+2Ch+var_C]
  13. .text:0042F54F                 mov     large fs:0, eax
  14. .text:0042F555                 mov     esi, ecx
  15. .text:0042F557                 mov     [esp+2Ch+var_20], esi
  16. .text:0042F55B                 push    3Fh
  17. .text:0042F55D                 push    0F2h
  18. .text:0042F562                 push    4
  19. .text:0042F564                 call    sub_42ED70
  20. .text:0042F569                 mov     [esp+2Ch+var_4], 0
  21. .text:0042F571                 push    0Ah             ; int
  22. .text:0042F573                 lea     ecx, [esi+90h]
  23. .text:0042F579                 push    offset off_58025C ; Src
  24. .text:0042F57E                 mov     dword ptr [esi], offset off_580904
  25. .text:0042F584                 call    sub_401B70
  26. .text:0042F589                 mov     ecx, [esi+8Ch]
  27. .text:0042F58F                 push    0               ; int
  28. .text:0042F591                 push    0               ; int
  29. .text:0042F593                 push    offset aThisTrialVersi
  30. ; "This trial version of ID3 Editor has ex"...
  31. .text:0042F598                 lea     eax, [esp+38h+rc]
  32. .text:0042F59C                 push    eax             ; int
  33. .text:0042F59D                 push    0FFFFFFFFh      ; __int16
Если смотреть по перекрестным ссылкам, процедура вызывается три раза из разных мест. Нам надо только одно, при старте программы. Открываем программу под отладчиком, ставим точку останова по адресу 0042F530 и запускаем на выполнение. Когда точка останова сработает, дотрассируем до выхода из процедуры и вернемся на один из трех участков кода, откуда эта процедура вызывается.
  1. .text:0040D05B                 add     esp, 8
  2. .text:0040D05E                 test    eax, eax
  3. .text:0040D060                 jz      short loc_40D073
  4. .text:0040D062                 push    0               ; int
  5. .text:0040D064                 push    10h             ; uType
  6. ; Вывести сообщение о несовпадении версий
  7. .text:0040D066                 mov     eax, off_5CC0BC
  8. .text:0040D06B                 push    eax             ; int
  9. .text:0040D06C                 call    ?AfxMessageBox@@YGHPB_WII@Z
  10. .text:0040D071                 jmp     short loc_40D0B2
  11. .text:0040D073 ; ----------------------------------------
  12. .text:0040D073 loc_40D073:
  13. .text:0040D073                 lea     ecx, [ebp+240h+var_310]
  14. ; Вывести сообщение об окончании триала
  15. .text:0040D079                 call    sub_42F530
  16. .text:0040D07E                 mov     [ebp+240h+var_244], 1
  17. .text:0040D085                 push    0
Тут не все так просто. Условный переход перекидывает или на сообщение об окончании триального срока, или на сообщение о несоответствии версий. Что это значит я не знаю и знать не хочу. Надо найти условный переход, который будет перепрыгивать все эти проверки. Листаем код выше. Работа с реестром, с системным временем, все это пропускаем и выходим на такую конструкцию:
  1. .text:0040CE8B                 call    sub_424740
  2. .text:0040CE90                 add     esp, 0Ch
  3. .text:0040CE93                 test    al, al
  4. .text:0040CE95                 jnz     loc_40D0FA
  5. .text:0040CE9B                 push    0
  6. .text:0040CE9D                 lea     eax, [ebp+240h+var_27C]
  7. .text:0040CEA0                 push    eax
Заменив условный переход на безусловный, мы убираем сообщение об окончании триального срока. Если теперь перевести часы на месяц вперед, то программа сохраняет свою работоспособность без ненужных напоминаний. Но и на этом еще не все. Если попробовать сделать портативную версию редактора или просто перенести его в другое место, отличающееся от первоначальной установки, то при попытке открыть музыкальный файл мы получим вот такое сообщение:

Предупреждение при изменении пути установки
Предупреждение при изменении пути установки

По аналогии с предыдущими находим строчку в листинге и по единственной перекрестной ссылке переходим на код, где она используется.
  1. .text:0040D356 loc_40D356:
  2. ; Указатель на строку сообщения
  3. .text:0040D356                 mov     ecx, off_5CC0B8
  4. .text:0040D35C loc_40D35C:
  5. .text:0040D35C                 push    0               ; unsigned int
  6. .text:0040D35E                 push    10h             ; uType
  7. .text:0040D360                 push    ecx             ; wchar_t *
  8. .text:0040D361                 call    ?AfxMessageBox@@YGHPB_WII@Z
  9. .text:0040D366 loc_40D366:
  10. .text:0040D366                 mov     ecx, esi
  11. .text:0040D368                 call    sub_404D00
Сюда ведет множество условных переходов:
  1. .text:0040CD88                 cmp     [ebp+240h+var_21F], 90h
  2. .text:0040CD8C                 jnz     loc_40D356
  3. .text:0040CD92                 cmp     [ebp+240h+var_21E], 1Ah
  4. .text:0040CD96                 jnz     loc_40D356
  5. .text:0040CD9C                 cmp     [ebp+240h+var_21D], 1Bh
  6. .text:0040CDA0                 jnz     loc_40D356
  7. .text:0040CDA6                 cmp     [ebp+240h+var_21C], 0E7h
  8. .text:0040CDAA                 jnz     loc_40D356
  9. .text:0040CDB0                 cmp     [ebp+240h+var_21B], 0E3h
  10. .text:0040CDB4                 jnz     loc_40D356
  11. .text:0040CDBA                 cmp     [ebp+240h+var_21A], 6Dh
  12. .text:0040CDBE                 jnz     loc_40D356
  13. .text:0040CDC4                 cmp     [ebp+240h+var_219], 0Ch
  14. .text:0040CDC8                 jnz     loc_40D356
  15. .text:0040CDCE                 cmp     [ebp+240h+var_218], 0C0h
  16. .text:0040CDD2                 jnz     loc_40D356
  17. .text:0040CDD8                 cmp     [ebp+240h+var_217], 54h
  18. .text:0040CDDC                 jnz     loc_40D356
  19. .text:0040CDE2                 cmp     [ebp+240h+var_216], 0DEh
  20. .text:0040CDE6                 jnz     loc_40D356
  21. .text:0040CDEC                 cmp     [ebp+240h+var_215], 38h
  22. .text:0040CDF0                 jnz     loc_40D356
  23. .text:0040CDF6                 cmp     [ebp+240h+var_214], 58h
  24. .text:0040CDFA                 jnz     loc_40D356
  25. .text:0040CE00                 cmp     [ebp+240h+var_213], 2Ch
  26. .text:0040CE04                 jnz     loc_40D356
  27. .text:0040CE0A                 cmp     [ebp+240h+var_212], 0D8h
  28. .text:0040CE0E                 jnz     loc_40D356
  29. .text:0040CE14                 cmp     [ebp+240h+var_211], 0F4h
  30. .text:0040CE18                 jnz     loc_40D356
  31. .text:0040CE1E                 cmp     [ebp+240h+var_210], 2Fh
  32. .text:0040CE22                 jnz     loc_40D356
Что делать с этой хренью? Можно заNOPить каждый переход. Можно поставить безусловный переход в начало проверок, чтобы перепрыгнуть это все. Можно заNOPить весь фрагмент кода. Выберите вариант, который соответствует вашим эстетическим представлениям о патче. Теперь редактор может запускаться откуда угодно, в том числе и со съемного носителя.

Программа успешно "зарегистрирована"
Программа успешно "зарегистрирована"

Цель достигнута. Программой пользоваться вовсе необязательно, она реально ущербная, а вот потренироваться в нейтрализации ее защиты очень даже ничего.

Поделиться ссылкой ВКонтакте
Просмотров: 1660 | Комментариев: 10

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

Комментарии

Отзывы посетителей сайта о статье
Alex (26.11.2019 в 11:43):
Продолжение....
Запускаем программу для отладки. Пытаемся зарегистрироваться: любые имя и компания и любой серийный номер в формате xxxx-xxxx-xxxx-xxxx-xxxx.
Так вываливаемся на брейкпоинте №1. Видим что eax 0, ну нет так не пойдет - 0x1. Получаем окно об успешной регистрации, а затем вываливаемся в брейкпоинте №2, проверяем пишет ли программа наши данные в файл настроек или что-то еще хочет проверить - нет, не проверяет и пишет наши данные, значит все верно.
Осталось проверить есть ли в программе защита на целостность и подпись. Заходим в Hopper в списк импортируемых функций - есть ли что-то из стандартного API (security.framework_ - нет, что в 90% случаев говорит об отсутствии таковых проверок.
В таком случае правим возврат eax по первому брейкпоинту - есть только 3 байта и для обнуления eax, и для записи в младший байт 1. Маловато. Вариантов 2: либо в предыдущей операции обнулить регистр, а потом в этой во младший записать 1 (как вариант or eax, 0x1), либо сразу push 0x1; pop rax (6A 01 58) - как раз 3 байта. Выбираем последний, правим бинарник и вуаля, программа зарегистрирована, в окне About наши данные.
Осталось только определиться - либо просто снять подпись и выложить взломанный бинарник с readme.txt, где разжевать какой формат серийного номера использовать, либо сразу сделать инжект с данными регистрации и правкой проверки по первому бряку. Это уже на ваше усмотрение. Мне второй вариант больше нравится.
Alex (26.11.2019 в 11:42):
Ну такое, так ломать)))
Прежде всего, если программа при запуске определяет что она триальная, значит не нашли узловую проверку. Править последствия (окошки, строки) сомнительный вариант.
Скачал прогу. У меня Мак, поэтому и версия под macOS.
Сразу открываем в Hopper Disassembler, смотрим через Tag Scope список классов. Хм, есть только один интересный - LicenseDlg. В нем куча методов: saveLicense:, getLicense, saveRegistration: и самый интересный okButtonPressed. В последнем проеряется серийный номер - 24 символа (0x18), каждый 5-й символ "-" (0x2d), разблокируется кнопка OK.
И все... Объектно-ориентированное программирование. Как дальше проверяется серийный номер неясно.
Надо либо дебажить, либо снова рыться в коде.
Дебажить еще успеем. Поищем license(d) ->
Есть интерсный-[DocumentController isLicensed]: Ну что сказать в 90% случаев этого достаточно - mov eax, 0x1 и мы в шоколаде. Но хочется выяснить, как мы определяем себя зарегистрированными, увидеть свое имя и компанию в окне About. Видим, что в этом инстасном методе используется переменная m_bLicenseValid. Это уже ближе к телу. Исследуем ее перекрестные ссылки - 2 интересных:
- "-[DocumentController init]:" можно сразу прописать 1 в переменную, но чаще всего программы могут обнулить проверкой это значение, поэтому просто пока берем на заметку;
- "-[DocumentController loadLicense]:" а вот здесь после кучи проверок в нее лишь в одном случае заносится единица. Проверка перед этой операцией и есть искомая, узловая функция. В ней все: проверка серийного номера, константы, хеши (причем их две, одна на валидность серийного номера, другая - на триальный срок), по итогу возвращаем в eax результат, если 1 - серийный номер верный. Ну что ж вот тут и ставим бряк №1.
На всякий случай заглядываем в saveRegistration: - там есть функция записи в файл настроек значений для "Registered company" & "Registered user". Здесь тоже поставим бряк №2 чтобы проверить что эта информация записывается.
user (14.05.2019 в 09:04):
)) Пару "философских" замечаний.
Программа стоит 15 баксов, но в "магазинном виде" не вполне юзабельна.
Пяток потраченных блин часов стоят значительно дороже, но есть и результат.
user (14.05.2019 в 08:50):
Что получилось у меня, по итогу:
Вынутый из инсталляшки без инсталляции комплект программ
работает в "триальном" режиме, портабле, вроде без ограничений.
GUI, если вводить отфонарный код, делается "регистрированной", но только в текущем сеансе. Там ещё пришлось несколько мест поправить.
В общем, заскладировал, при случае поюзаю.
Сенкс за программку.
ManHunter (13.05.2019 в 17:28):
Вот, блин, сколько ж там говна понатыкано. Дополнил статью.
user (13.05.2019 в 17:11):
Ещё неплохо поправить ругань при запуске не из того места, куда инсталлировалась.
Как в консольной версии. Типа портабле.
ManHunter (13.05.2019 в 11:57):
Добавил немного про гуишный вариант.
user (13.05.2019 в 07:10):
Что-то там GUI-версия не вполне добита.
Гляну ещё, по свободе.
А вот консольную версию, похоже, удалось уговорить.
Ну, и запротоколировал правки в таком виде:

Файл id3ed.CRK:

;----------------------------------------------->8
ID3 Editor 1.26.43

1) id3ed.exe - by ManHunter
id3ed.exe
.0040CE95: 0F E9 ;0000C295:
.0040CE96: 85 60 ;0000C296:
.0040CE97: 5F 02 ;0000C297:
.0040CE98: 02 00 ;0000C298:
.0040CE9A: 00 90 ;0000C29A:

2) id3ed.exe - by ManHunter
id3ed.exe
.004245E0: 8B 31 ;000239E0:
.004245E1: 44 C0 ;000239E1:
.004245E2: 24 40 ;000239E2:
.004245E3: 0C C3 ;000239E3:

;----------------------------------------------
1) id3edcmd.exe - Skip trial limit
id3edcmd.exe
.0043B1F5: 76 EB ;0003A5F5:
.0043B1FE: 76 EB ;0003A5FE:
.0043B377: 76 EB ;0003A777:
.0043B4DF: 76 EB ;0003A8DF:
.0043B4E8: 76 EB ;0003A8E8:
id3edcmd.exe
.0043B170: 28 00 ;0003A570:
id3edcmd.exe
.0043B1E2: 14 00 ;0003A5E2:
.0043B1ED: 09 00 ;0003A5ED:
.0043B33B: 65 00 ;0003A73B:
.0043B36F: 09 00 ;0003A76F:
.0043B45A: 28 00 ;0003A85A:
.0043B468: 1A 00 ;0003A868:
.0043B476: 0C 00 ;0003A876:
id3edcmd.exe
.0043B3DF: 0F E9 ;0003A7DF:
.0043B3E0: 85 39 ;0003A7E0:
.0043B3E1: 38 01 ;0003A7E1:
.0043B3E2: 01 00 ;0003A7E2:
.0043B3E4: 00 90 ;0003A7E4:
.0043B4D7: 09 00 ;0003A8D7:

2) id3edcmd.exe - Skip "Modified..Reinstall" message.
id3edcmd.exe
.0043E273: 0F E9 ;0003D673:
.0043E274: 85 D8 ;0003D674:
.0043E275: D9 00 ;0003D675:
.0043E278: 00 90 ;0003D678:
;----------------------------------------------->8
user (11.05.2019 в 10:36):
.. Не, реально полезная штука - там есть и отдельная утиль командной строки.
Сенкс. Заскладировал.
user (11.05.2019 в 10:19):
) Когда-то, в начале 2000-х, начал было ваять свою такую же.
Замышлялось, что упор будет на пакетную обработку сразу многих файлов.
Даже уже диалог запилил, выдранный из WinAMP'а,
да только появились какие-то дела и забросил тогда эту игрушку.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0065 сек.) / Память: 4.5 Mb
Наверх