Blog. Just Blog

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

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

Программа RecoveryRobot предназначена для восстановления информации с поврежденных дисков, а также восстановления удаленных файлов с различных носителей. От аналогичных программ не отличается практически ничем, никакого запредельного функционала на борту нет. В бесплатном режиме восстанавливает небольшой объем данных, за снятие лимитов требует сумму до нескольких сотен вечнозеленых. Ну что ж, добро пожаловать на разделочный стол.

Забираем с офсайта дистрибутив, устанавливаем. Исполняемый файл ничем не упакован, отправляем его в дизассемблер. Ну а параллельно посмотрим, за что можно уцепиться при начальном анализе. Самое приметное - это строка "Unregistered" вы заголовке главного окна программы.

Строка сообщения в ресурсах
Строка сообщения в ресурсах

Строка из заголовка обнаруживается в ресурсах. Ее индекс равняется 367 или 16Fh, если в шестнадцатеричной системе счисления. В листинге находится вот такой код, где этот индекс используется:
  1. .text:0041139C                 mov     edx, [esi]
  2. .text:0041139E                 mov     eax, [edx+48h]
  3. .text:004113A1                 mov     ecx, esi
  4. ; Функция проверки
  5. .text:004113A3                 call    eax
  6. .text:004113A5                 cmp     eax, 64h        ; switch 101 cases
  7. .text:004113A8                 ja      loc_4116A3
  8. .text:004113AE                 movzx   ecx, ds:byte_411748[eax]
  9. .text:004113B5                 jmp     ds:off_411734[ecx*4]
  10. .text:004113BC loc_4113BC:
  11. ; jumptable 004113B5 case 100
  12. .text:004113BC                 lea     edx, [esp+54h+var_34]
  13. .text:004113C0                 push    edx
  14. ; Загрузить строку из ресурсов
  15. .text:004113C1                 mov     edi, 16Fh
  16. .text:004113C6                 call    sub_41DC60
  17. .text:004113CB                 add     esp, 4
  18. .text:004113CE                 mov     edi, eax
Вызывается функция проверки, по ее результату выполняется переход из конструкции SWITCH-CASE. Причем значение, которое выводит на появление строки "Unregistered", равняется 100. Небольшую сложность создает неявный вызов функции проверки через CALL EAX, но тут нам на помощь приходит отладчик.

Обратите внимание, что при обычном старте программа запускает свою вторую копию с параметром в командной строке " -run" и таким образом убегает из отладчика. Надо в меню Debug - Arguments добавить строку запуска, причем обязательно с первым пробелом. Тогда все нормально запустится под отладчиком.

Ставим точку останова на адрес 004113A3, запускаем программу, в момент срабатывания брейкпоинта получаем, что проверка выполняется по адресу 00469DC0.
  1. .text:00469DC0                 push    ebp
  2. .text:00469DC1                 mov     ebp, esp
  3. .text:00469DC3                 and     esp, 0FFFFFFF8h
  4. .text:00469DC6                 push    0FFFFFFFFh
  5. .text:00469DC8                 push    offset sub_657284
  6. .text:00469DCD                 mov     eax, large fs:0
  7. .text:00469DD3                 push    eax
  8. .text:00469DD4                 sub     esp, 0D0h
  9. .text:00469DDA                 mov     eax, ___security_cookie
  10. .text:00469DDF                 xor     eax, esp
  11. .text:00469DE1                 mov     [esp+0DCh+var_14], eax
  12. .text:00469DE8                 push    ebx
  13. .text:00469DE9                 push    esi
  14. .text:00469DEA                 push    edi
  15. .text:00469DEB                 mov     eax, ___security_cookie
  16. .text:00469DF0                 xor     eax, esp
  17. .text:00469DF2                 push    eax
  18. .text:00469DF3                 lea     eax, [esp+0ECh+var_C]
  19. .text:00469DFA                 mov     large fs:0, eax
  20. .text:00469E00                 mov     ebx, [ebp+arg_0]
  21. .text:00469E03                 mov     edx, ebx
  22. .text:00469E05                 lea     eax, [esp+0ECh+var_94]
  23. .text:00469E09                 mov     [esp+0ECh+var_DC], ecx
  24. .text:00469E0D                 call    sub_469CA0
  25. .text:00469E12                 push    0FFFFFFFFh
  26. .text:00469E14                 mov     ecx, 19h
  27. .text:00469E19                 lea     esi, [esp+0F0h+var_B0]
  28. .text:00469E1D                 mov     edx, ebx
  29. .text:00469E1F                 mov     [esp+0F0h+var_4], 0
  30. .text:00469E2A                 call    sub_46A230
  31. .text:00469E2F                 push    offset aKey     ; "KEY"
  32. .text:00469E34                 push    0AFh            ; lpName
  33. .text:00469E39                 push    0               ; hModule
  34. .text:00469E3B                 mov     byte ptr [esp+0F8h+var_4], 1
  35. .text:00469E43                 call    ds:FindResourceW
  36. .text:00469E49                 mov     esi, eax
  37. .text:00469E4B                 push    esi             ; hResInfo
  38. .text:00469E4C                 push    0               ; hModule
  39. .text:00469E4E                 call    ds:LoadResource
  40. .text:00469E54                 push    eax             ; hResData
  41. .text:00469E55                 call    ds:LockResource
  42. .text:00469E5B                 push    esi             ; hResInfo
  43. .text:00469E5C                 push    0               ; hModule
  44. .text:00469E5E                 mov     edi, eax
  45. .text:00469E60                 call    ds:SizeofResource
  46. .text:00469E66                 mov     esi, eax
  47. .text:00469E68                 lea     eax, [esp+0ECh+var_B0]
  48. .text:00469E6C                 push    eax
  49. ...
  50. ...
Интересная функция. Тут из ресурсов загружается какой-то ключ, затем выполняются некие манипуляции и по результатам возвращается злополучная сотня или какое-то другое значение, если программу удовлетворили результаты проверки. Пишем в начало функции пару команд MOV EAX,1 и RET 4, чтобы не порушить стек, сохраняем изменения. Увы, пока ничего не изменилось. Ничего страшного, этот патч тоже пригодится.

Давайте попробуем разобрать проверку серийного номера. При попытке ввести левые данные в качестве серийника программа выдает сообщение "Register Code is wrong". Соответствующая строка с индексом 356 (она же 164h) находится там же в ресурсах:

Строка сообщения в ресурсах
Строка сообщения в ресурсах

Возвращаемся в дизассемблер и ищем в листинге индекс строки из ресурсов, а заодно смотрим, какие ветки алгоритма туда приводят.
  1. ; Проверка длины введенного серийника
  2. .text:00435CF6                 mov     eax, [esi-0Ch]
  3. .text:00435CF9                 test    eax, eax
  4. ; Пустой?
  5. .text:00435CFB                 jz      loc_435ED8
  6. ; Больше или равно 95 символов?
  7. .text:00435D01                 cmp     eax, 5Fh
  8. .text:00435D04                 jge     loc_435ED8
  9. ; Меньше 89 символов?
  10. .text:00435D0A                 cmp     eax, 59h
  11. .text:00435D0D                 jl      loc_435ED8
  12. ...
  13. ...
  14. ...
  15. .text:00435ED8 loc_435ED8:
  16. .text:00435ED8                 lea     ecx, [esp+448h+var_430]
  17. .text:00435EDC                 push    ecx
  18. ; Загрузить строку сообщения из ресурсов
  19. .text:00435EDD                 mov     edi, 164h
  20. .text:00435EE2                 call    sub_41DC60
  21. .text:00435EE7                 add     esp, 4
  22. .text:00435EEA                 push    0               ; unsigned int
  23. .text:00435EEC                 mov     [esp+44Ch+var_4], 0
  24. .text:00435EF7                 mov     eax, [eax]
  25. .text:00435EF9                 push    0               ; lpCaption
  26. .text:00435EFB                 push    eax             ; lpText
  27. .text:00435EFC                 mov     ecx, ebp        ; this
  28. ; Вывести сообщение
  29. .text:00435EFE                 call    ?MessageBoxW@CWnd@@QAEHPB_W0I@Z
  30. ; CWnd::MessageBoxW(wchar_t const *,wchar_t const *,uint)
  31. .text:00435F03                 mov     [esp+448h+var_4], 0FFFFFFFFh
  32. .text:00435F0E                 mov     eax, [esp+448h+var_430]
Тут все понятно, проверяется длина серийника, он должен быть в диапазоне от 89 до 94 символов. Набирать столько циферок мне лениво, поэтому все три условных перехода превращаются в NOP. Сохраняем изменения и смотрим дальше.

А дальше следуют еще две проверки, по результатам которых могут появиться сообщения, что код неправильный или же что у серийника истек срок действия. Коды строчек находятся в ресурсах, вывод сообщений отслеживается на адресах двух условных переходов:

Строки сообщений в ресурсах
Строки сообщений в ресурсах
  1. .text:00435D50                 mov     edx, [edx+10h]
  2. .text:00435D53                 mov     ecx, esi
  3. .text:00435D55                 push    ecx
  4. .text:00435D56                 mov     ecx, eax
  5. ; Функция проверки корректности серийного номера
  6. .text:00435D58                 call    edx
  7. .text:00435D5A                 test    eax, eax
  8. .text:00435D5C                 jz      loc_435E88
  9. .text:00435D62                 call    ?AfxGetModuleState
  10. .text:00435D67                 mov     eax, [eax+4]
  11. .text:00435D6A                 mov     edx, [eax]
  12. .text:00435D6C                 mov     ecx, eax
  13. .text:00435D6E                 mov     eax, [edx+0C0h]
  14. .text:00435D74                 call    eax
  15. .text:00435D76                 mov     edx, [eax]
  16. .text:00435D78                 mov     edx, [edx+4]
  17. .text:00435D7B                 mov     ecx, esi
  18. .text:00435D7D                 push    ecx
  19. .text:00435D7E                 mov     ecx, eax
  20. ; Функция проверки срока действия серийного номера
  21. .text:00435D80                 call    edx
  22. .text:00435D82                 test    al, al
  23. .text:00435D84                 jnz     short loc_435DC3
  24. .text:00435D86                 lea     eax, [esp+448h+var_434]
  25. .text:00435D8A                 push    eax
Тут обе функции проверки снова вызываются неявно, значит снова пускаем в бой отладчик. Ставим точки останова на оба неявных вызова и получаем две функции проверки:
  1. .text:00469C20 sub_469C20      proc near
  2. .text:00469C20                 push    ebp
  3. .text:00469C21                 mov     ebp, esp
  4. .text:00469C23                 and     esp, 0FFFFFFF8h
  5. .text:00469C26                 sub     esp, 0Ch
  6. .text:00469C29                 push    ebx
  7. .text:00469C2A                 push    esi
  8. .text:00469C2B                 mov     esi, [ebp+arg_0]
  9. .text:00469C2E                 push    edi
  10. .text:00469C2F                 push    esi
  11. .text:00469C30                 call    sub_469CC0
  12. .text:00469C35                 mov     edx, esi
  13. .text:00469C37                 lea     edi, [esp+18h+var_8]
  14. .text:00469C3B                 call    sub_469AF0
  15. .text:00469C40                 push    0               ; Time
  16. .text:00469C42                 call    __time64
  17. .text:00469C47                 mov     ebx, [esp+1Ch+var_8]
  18. .text:00469C4B                 mov     edi, [esp+1Ch+var_4]
  19. .text:00469C4F                 add     esp, 4
  20. .text:00469C52                 mov     esi, eax
  21. .text:00469C54                 sub     esi, ebx
  22. .text:00469C56                 mov     ecx, edx
  23. ...
  1. .text:00469CC0 sub_469CC0      proc near
  2. .text:00469CC0                 push    0FFFFFFFFh
  3. .text:00469CC2                 push    offset sub_64ED28
  4. .text:00469CC7                 mov     eax, large fs:0
  5. .text:00469CCD                 push    eax
  6. .text:00469CCE                 sub     esp, 24h
  7. .text:00469CD1                 mov     eax, ___security_cookie
  8. .text:00469CD6                 xor     eax, esp
  9. .text:00469CD8                 mov     [esp+30h+var_10], eax
  10. .text:00469CDC                 push    esi
  11. .text:00469CDD                 mov     eax, ___security_cookie
  12. .text:00469CE2                 xor     eax, esp
  13. .text:00469CE4                 push    eax
  14. .text:00469CE5                 lea     eax, [esp+38h+var_C]
  15. .text:00469CE9                 mov     large fs:0, eax
  16. .text:00469CEF                 mov     edx, [esp+38h+arg_0]
  17. .text:00469CF3                 lea     eax, [esp+38h+var_2C]
  18. .text:00469CF7                 call    sub_469CA0
  19. .text:00469CFC                 mov     [esp+38h+var_4], 0
  20. .text:00469D04                 cmp     [esp+38h+var_18], 19h
  21. .text:00469D09                 jz      short loc_469D1D
  22. .text:00469D0B                 cmp     [esp+38h+var_14], 10h
  23. .text:00469D10                 jb      loc_469D99
  24. ...
Как мы выяснили из проверок перед условными переходами, обе функции должны возвращать EAX=1. Также не забываем о необходимости восстановить стек при возврате. Поэтому патчим обе функции командами MOV EAX,1 и RET 4. Сохраняем изменения, запускаем. Программа падает с ошибкой. Под отладчиком видно, что одно из пропатченных мест видоизменяется, значит на этот участок памяти попадает какой-то релок. Они в исполняемом файле, в принципе, вообще не нужны, поэтому можно совершенно безболезненно от них избавиться.

Убираем релоки из файла
Убираем релоки из файла

Делается это модификацией PE-заголовка через взведение флага "Relocations stripped". Удобнее всего подобную операцию выполнять в PE Tools. На мой взгляд, удалять релоки надо независимо от того, попадают они на место патча или нет.

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

Вот и все, на попытку повторной регистрации программа пишет, что она уже зарегистрирована. В заголовке красуется лицензия уровня "Expert". Ограничений на объем восстанавливаемых данных тоже нет, так же как и нет никаких стремных уведомлений при попытке эти данные восстановить. Цель достигнута.

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

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

Комментарии

Отзывы посетителей сайта о статье
Илья (01.01.2024 в 13:05):
Спасибо, статья помогла разобраться с причиной, почему не выполняется код с самописной тестовой dll-кой. Причем на Win7 выполняется, а на десятке и 11 - нет. Пишет "Программа "путь.dll" не предназначена для выполнения в Windows...". Удалил релоки из заголовка - заработало. Оказалось, дело было в том, что не указал флаг readable в секции .reloc.
ManHunter (08.08.2019 в 09:16):
Релоки попадают на этот адрес. Надо в PE-заголовке в флагах поставить "relocation stripped". Вроде трезвый писал статью, чо столько пропустил...

Дополнил, спасибо!
Михаил (08.08.2019 в 08:56):
Нашел проблемку, почему то по адресу 00469DC0 не хочет корректно пропатчивать рет4 при повторном открытии дебаггера (пробовал Олю и х32дбг) вижу испорченный код
https://i87.fastpic.ru/big/201...385abe33.jpg
хотя если хекс редактором открыть то там есть рет4, но при запуске программа крошится в этом месте
Михаил (07.08.2019 в 20:17):
Ясно, спасибо, буду смотреть значит где я ошибся
ManHunter (07.08.2019 в 20:15):
Это не отличие, это я уже потом экспериментировал с типом лицензии, хотел повысить до бизнеса. Изначально там тоже 1.
Михаил (07.08.2019 в 20:05):
Вот и отличие от текста:
000691C0: 55 B8
000691C1: 8B 04
По тексту кругом mov eax,1
ManHunter (07.08.2019 в 16:43):
Сравнение файлов RecoveryRobotPro.! и RECOVERYROBOTPRO.EXE
000350FB: 0F 90
000350FC: 84 90
000350FD: D7 90
000350FE: 01 90
000350FF: 00 90
00035100: 00 90
00035104: 0F 90
00035105: 8D 90
00035106: CE 90
00035107: 01 90
00035108: 00 90
00035109: 00 90
0003510D: 0F 90
0003510E: 8C 90
0003510F: C5 90
00035110: 01 90
00035111: 00 90
00035112: 00 90
00069020: 55 B8
00069021: 8B 01
00069022: EC 00
00069023: 83 00
00069024: E4 00
00069025: F8 C2
00069026: 83 04
00069027: EC 00
000690C0: 6A B8
000690C1: FF 01
000690C2: 68 00
000690C3: 28 00
000690C4: ED 00
000690C5: 64 C2
000690C6: 00 04
000690C7: 64 00
000691C0: 55 B8
000691C1: 8B 04
000691C2: EC 00
000691C3: 83 00
000691C4: E4 00
000691C5: F8 C2
000691C6: 6A 04
000691C7: FF 00

Патч из статьи, все работает. И во всех трех случаях в оригинале выход из функций через RETN 4.
Михаил (07.08.2019 в 16:38):
Все равно какая то ерунда, после всех манипуляций при попытке запустить программу она создает у себя в каталоге файл edrdump0.dmp и выкидует окно ошибки с предложением отправить разрабам. И второй момент по 367 индексу там точно RET4 так как там при входе в Call заканчивается все простым RET
ManHunter (06.08.2019 в 14:33):
Это я забыл указать в статье, что программа запускает свою вторую копию с параметром в командной строке " -run" и таким образом убегает из отладчика. Надо в меню Debug - Arguments добавить строку запуска, причем обязательно с первым пробелом. Тогда все нормально запустится под отладчиком.

Добавил в статью, спасибо за уточнение.
Михаил (06.08.2019 в 14:29):
Подскажите как быть, если в Олли и х32dbg программа не реагирует на брекпоинты. Запускается, затем после остановки и даже закрытия дебаггера программа остается быть запущенной

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

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

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