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

AppToService от Basta Computing - полезная программа, позволяющая запускать любые приложения как сервисы Windows. Но, как и большинство полезных программ, требует денег после испытательного срока. Будем это исправлять. Скачать пациента можно с офсайта, последняя версия на сегодняшний день 3.0. После установки запускаем программу, видим список параметров запуска и нехорошую строчку, предупреждающую, что надо бы заплатить.


Скриншот программы AppToService

Если посмотреть повнимательнее, то там же будет ключ командной строки для регистрации:

/Key:x Unlock AppToService.


Программа написана на C++ и ничем не упакована. Сперва попробуем найти строчку с ключом "Key:" и посмотреть как она обрабатывается. Строка в юникоде, поэтому символы разделены нулевым байтом. Участок кода, где идет обращение к этой строке, будет только один.
.text:00403074                 mov     edx, offset aKey ; "Key:"
.text:00403079                 call    sub_402BE0
.text:0040307E                 add     esp, 4
.text:00403081                 test    eax, eax
; Если параметр в командной строке не равен "Key:", то перейти дальше
.text:00403083                 jz      short loc_4030E7
.text:00403085                 or      dword ptr [esp+10h], 2
.text:0040308A                 call    sub_40A54B
.text:0040308F                 mov     eax, [eax+4]
.text:00403092                 lea     ecx, [esp+46h]
.text:00403096                 push    ecx
.text:00403097                 add     eax, 0A4h
.text:0040309C                 push    eax
; Вот это интересная функция, похоже тут проверяется валидность ключа, и
; в зависимости от результатов выполняется прыжок на выход или сообщение об
; успешной регистрации программы
.text:0040309D                 call    sub_4017B0
.text:004030A2                 test    eax, eax
.text:004030A4                 jz      loc_403B0D
.text:004030AA                 call    sub_40A54B
.text:004030AF                 mov     eax, [eax+4]
; Установить флажок зарегистрированности
.text:004030B2                 mov     dword ptr [eax+0C0h], 1
.text:004030BC                 call    sub_40A54B
.text:004030C1                 mov     eax, [eax+4]
.text:004030C4                 lea     ecx, [eax+0CCh]
.text:004030CA                 call    sub_402590
; Вывести на экран сообщение об удачной регистрации
.text:004030CF                 push    offset aOk      ; "OK\n"
.text:004030D4                 mov     dword_4402AC, ebx
.text:004030DA                 call    sub_413BCB
.text:004030DF                 add     esp, 4
; И потом пойти куда-то дальше, это нам уже не очень интересно
.text:004030E2                 jmp     loc_403A95

Посмотрим функцию проверки валидности ключа поподробнее. Кроме всего прочего в ней обнаруживается такой вот интересный кусочек кода:
.text:004019CD                 sar     eax, 1
.text:004019CF                 lea     ecx, [eax+eax+2]
.text:004019D3                 push    ecx             ; cbData
.text:004019D4                 push    edi             ; lpData
.text:004019D5                 push    1               ; dwType
.text:004019D7                 push    0               ; Reserved
.text:004019D9                 push    offset aReserved2 ; "Reserved2"
.text:004019DE                 push    esi             ; hKey
.text:004019DF                 call    ds:RegSetValueExW
.text:004019E5                 push    esi             ; hKey
.text:004019E6                 mov     edi, eax
.text:004019E8                 call    ds:RegCloseKey
.text:004019EE                 push    edi             ; dwErrCode
.text:004019EF                 call    ds:SetLastError

То есть устанавливается ключ в реестре, в который записываются данные регистрации. Под отладчиком можно убедиться, что это так и есть, причем запишется только правильный серийный номер. Что ж, раз ключ записывается, значит он должен где-то потом читаться. Находим еще один код, в котором идет обращение к ключу "Reserved2".
.text:004013AE                 xor     eax, esp
.text:004013B0                 push    eax
.text:004013B1                 lea     eax, [esp+54h+var_C]
.text:004013B5                 mov     large fs:0, eax
.text:004013BB                 mov     eax, offset aReserved2 ; "Reserved2"
.text:004013C0                 lea     esi, [esp+54h+var_48]
.text:004013C4                 mov     ecx, edi
; Где-то тут читается значение ключа из реестра
.text:004013C6                 call    sub_401000
.text:004013CB                 xor     ebx, ebx
.text:004013CD                 mov     [esp+54h+var_4], ebx
.text:004013D1                 call    loc_401160
.text:004013D6                 lea     eax, [esp+54h+Data]
.text:004013DA                 push    eax
.text:004013DB                 mov     eax, [esp+58h+var_48]
.text:004013DF                 mov     dword ptr [esp+58h+Data], ebx
; Тут вызывается проверка валидности ключа
.text:004013E3                 call    sub_4074F0
.text:004013E8                 add     esp, 4
.text:004013EB                 test    eax, eax
; Если проверка не пройдена, то переход
.text:004013ED                 jz      short loc_4013FA
; Тут какая-то дополнительная проверка данных из ключа
.text:004013EF                 xor     eax, eax
.text:004013F1                 cmp     dword ptr [esp+54h+Data], ebx
; Регистр AL устанавливается в зависимости от результатов проверки.
; 0 - все плохо, 1 - программа зарегистрирована
.text:004013F5                 setnl   al
.text:004013F8                 jmp     short loc_4013FC
.text:004013FA loc_4013FA:
; Сюда мы попадаем, если ключ регистрации не найден или он неправильный
.text:004013FA                 xor     eax, eax
.text:004013FC loc_4013FC:
.text:004013FC                 cmp     eax, ebx
; Сохранить куда-то в память флажок "зарегистрировано"
.text:004013FE                 mov     [edi+1Ch], eax
; Переход, если программа не зарегистрирована
.text:00401401                 jz      short loc_401424
.text:00401403                 mov     [esp+54h+var_4], 0FFFFFFFFh
.text:0040140B                 mov     eax, [esp+54h+var_48]
.text:0040140F                 add     eax, 0FFFFFFF0h
.text:00401412                 lea     ecx, [eax+0Ch]
.text:00401415                 or      edx, 0FFFFFFFFh
.text:00401418                 lock xadd [ecx], edx
.text:0040141C                 dec     edx
.text:0040141D                 test    edx, edx
.text:0040141F                 jmp     loc_401589
.text:00401424 loc_401424:
; Программа не зарегистрирована, вычисляется триальный срок и записывается 
; в другой ключ реестра - "Reserved0"
.text:00401424                 push    ebx             ; Time
.text:00401425                 call    __time64
.text:0040142A                 add     esp, 4
.text:0040142D                 push    ebx             ; dwDisposition
.text:0040142E                 mov     ebx, edi
.text:00401430                 mov     [esp+58h+var_3C], eax
.text:00401434                 mov     [esp+58h+var_38], edx
.text:00401438                 call    sub_406700
.text:0040143D                 mov     esi, eax
.text:0040143F                 test    esi, esi
.text:00401441                 jz      short loc_40147A
.text:00401443                 push    0               ; lpcbData
.text:00401445                 push    0               ; lpData
.text:00401447                 push    0               ; lpType
.text:00401449                 push    0               ; lpReserved
.text:0040144B                 push    offset aReserved0 ; "Reserved0"
.text:00401450                 push    esi             ; hKey
.text:00401451                 call    ds:RegQueryValueExW
.text:00401457                 push    esi             ; hKey
.text:00401458                 mov     ebx, eax
.text:0040145A                 call    ds:RegCloseKey
.text:00401460                 push    ebx             ; dwErrCode

Алгоритм проверки теперь понятен. Заодно выяснили ключ, в котором хранится триальный срок. Если его периодически стирать из реестра, то программа будет работать свои 30 дней вечно. Но мы пойдем другим путем, мы просто пропатчим саму проверку. Удобнее всего патч сделать на этом участке кода:
.text:004013EB                 test    eax, eax
; Заменить JZ на JMP, теперь переход будет выполняться в любом случае
.text:004013ED                 jz      short loc_4013FA ; <-- JMP
.text:004013EF                 xor     eax, eax
.text:004013F1                 cmp     dword ptr [esp+54h+Data], ebx
.text:004013F5                 setnl   al
.text:004013F8                 jmp     short loc_4013FC
.text:004013FA loc_4013FA:
; Сюда будет выполняться переход после патча. В регистре EAX должно быть
; ненулевое значение, тогда программа будет считать, что она зарегистрирована.
; Размер команды XOR EAX,EAX равен 2 байта, проще всего заменить ее на MOV AL,1
.text:004013FA                 xor     eax, eax   ; <-- MOV AL,1

Вносим изменения в файл, проверяем. Нехорошая надпись пропала. Для чистоты эксперимента переводим часы на год вперед - все работает как надо.


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

Очередной гвоздь в гроб капитализма забит, теперь программа будет работать без всяких ограничений по времени. А на сэкономленные деньги покупаем букет любимой девушке :)
Теги:
защита, взлом
Добавлено: 21 Сентября 2013 05:19:17 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...