Blog. Just Blog

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

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

Что-то давненько на разделочном столе нашей лаборатории не было никаких игрушек. Игра AxySnake - это классическая "змейка", но только трехмерная и в режиме "от третьего лица". Отличная графика, приятная озвучка, множество игровых локаций и всяких бонусов. В незарегистрированном варианте доступны не все игровые возможности, плюс показываются всякие нехорошие надписи и ненужные окна.

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

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

Если поискать это сообщение в основном файле, то ничего не будет найдено. Зато поиском по всем файлам эта строка обнаружится в файле proton.dll. Видимо, какая-то игра слов от "protection". Убедившись еще раз, что никаких навесных наворотов на нем нет, отправляем файл динамической библиотеки в дизассемблер.

Окно с сообщением в ресурсах
Окно с сообщением в ресурсах

Но строчка находится не просто в файле, а в ресурсах, да еще и в диалоговом окне. И называется это окно "REG_FAULT". Теперь переходим в дизассемблер и поищем там строчку с названием ресурса окна.
  1. ; Вызвать функцию проверки
  2. .text:1000FB04                 call    sub_10010960
  3. ; Если она вернула EAX=0, то введенный серийник неправильный
  4. .text:1000FB09                 test    eax, eax
  5. .text:1000FB0B                 jnz     short loc_1000FB4E
  6. .text:1000FB0D                 push    eax             ; uType
  7. .text:1000FB0E                 call    ds:MessageBeep
  8. .text:1000FB14                 mov     edx, hInstance
  9. .text:1000FB1A                 push    0               ; dwInitParam
  10. .text:1000FB1C                 push    offset sub_1000E010 ; lpDialogFunc
  11. .text:1000FB21                 push    esi             ; hWndParent
  12. .text:1000FB22                 push    offset aReg_fault ; "REG_FAULT"
  13. .text:1000FB27                 push    edx             ; hInstance
  14. .text:1000FB28                 call    edi ; DialogBoxParamA
  15. .text:1000FB2A                 test    eax, eax
  16. .text:1000FB2C                 jz      short loc_1000FB9B
  17. .text:1000FB2E                 push    64h             ; dwMilliseconds
  18. .text:1000FB30                 call    ebx ; Sleep
  19. .text:1000FB32                 mov     eax, hInstance
  20. .text:1000FB37                 push    0               ; dwInitParam
  21. .text:1000FB39                 push    offset sub_1000E670 ; lpDialogFunc
  22. .text:1000FB3E                 push    esi             ; hWndParent
  23. .text:1000FB3F                 push    offset aReg_dialog ; "REG_DIALOG"
  24. .text:1000FB44                 push    eax             ; hInstance
  25. .text:1000FB45                 call    edi ; DialogBoxParamA
  26. .text:1000FB47                 cmp     eax, 1
  27. .text:1000FB4A                 jz      short loc_1000FB04
  28. .text:1000FB4C                 jmp     short loc_1000FB9B
  29. .text:1000FB4E ; ---------------------------------------
  30. .text:1000FB4E loc_1000FB4E:
  31. .text:1000FB4E                 call    sub_100066B0
  32. .text:1000FB53                 mov     ecx, dword_1003D5CC
  33. .text:1000FB59                 mov     edx, [ecx+18h]
  34. .text:1000FB5C                 push    edx             ; phkResult
  35. .text:1000FB5D                 call    sub_10010820
  36. .text:1000FB62                 mov     eax, hInstance
  37. .text:1000FB67                 add     esp, 4
  38. .text:1000FB6A                 mov     ebx, 1
  39. .text:1000FB6F                 push    0               ; dwInitParam
  40. .text:1000FB71                 push    offset sub_1000E010 ; lpDialogFunc
  41. .text:1000FB76                 push    esi             ; hWndParent
  42. .text:1000FB77                 push    offset aReg_ok  ; "REG_OK"
  43. .text:1000FB7C                 push    eax             ; hInstance
  44. .text:1000FB7D                 mov     dword_1003D5D0, ebx
  45. .text:1000FB83                 call    edi ; DialogBoxParamA
Тут все понятно. Теперь посмотрим на функцию проверки поближе. Кстати, вариант с патчем тут не прокатит. Если модифицировать ее, чтобы всегда возвращался результат EAX!=0, то игра запустится, но сразу же выведет сообщение, что нарушено лицензионное соглашение, что серийник заблокирован и все такое, то есть активируется следующий уровень защиты уже в главном исполняемом файле игры. Хирургию пока отложим, посмотрим, можно ли сгенерировать правильный серийный номер.
  1. .text:10010960 sub_10010960    proc near
  2. .text:10010960                 mov     eax, dword_1003D4B8
  3. .text:10010965                 test    eax, eax
  4. .text:10010967                 jz      loc_10010A4E
  5. .text:1001096D                 mov     ecx, dword_1003D4BC
  6. .text:10010973                 test    ecx, ecx
  7. .text:10010975                 jz      loc_10010A4E
  8. .text:1001097B                 push    offset byte_10132938
  9. .text:10010980                 push    offset byte_1003D458
  10. .text:10010985                 mov     byte_1003D458[eax], 0
  11. ; Проверить корректность регистрационного имени
  12. .text:1001098C                 call    sub_100108E0
  13. .text:10010991                 add     esp, 8
  14. .text:10010994                 test    eax, eax
  15. .text:10010996                 jz      loc_10010A4E
  16. .text:1001099C                 push    offset Data
  17. ; Проверить корректность формата регистрационного номера
  18. .text:100109A1                 call    sub_10010890
  19. .text:100109A6                 add     esp, 4
  20. .text:100109A9                 test    eax, eax
  21. .text:100109AB                 jz      loc_10010A4E
Сперва проверяется введенное регистрационное имя и из него вычисляется некий хеш. Алгоритм хеширования достаточно простой: берется кольцевой буфер размером 11h символов, затем в него посимвольно прибавляются байты регистрационного имени с кодом больше 20h. Буфер заполняется по кольцу 10 раз. Вторая проверка более интересная: берется строка серийного номера, из нее берется по одному символу и каждый символ ищется в контрольной строке "23456789ABCDEFGHJKLMNPQRSTUVWXYZ". Остальные символы не учитываются. Если символ найден, счетчик увеличивается. Позиция найденного символа сохраняется в строке, обозначим ее как хеш серийника. Так, для символа "A" позиция будет равна 8 и так далее. Количество правильных символов должно быть строго 14h. Таким образом мы выяснили алгоритм преобразования регистрационного имени, длину правильного серийного номера и набор символов, из которых он должен состоять.
  1. .text:100109B1                 mov     ecx, 49h
  2. .text:100109B6                 xor     eax, eax
  3. .text:100109B8 loc_100109B8:
  4. .text:100109B8                 movsx   edx, byte_10132938[eax]
  5. .text:100109BF                 add     ecx, edx
  6. .text:100109C1                 inc     eax
  7. .text:100109C2                 cmp     eax, 10h
  8. .text:100109C5                 jl      short loc_100109B8
  9. .text:100109C7                 xor     eax, eax
  10. .text:100109C9 loc_100109C9:
  11. .text:100109C9                 movsx   edx, byte_10132918[eax]
  12. .text:100109D0                 movsx   edx, byte_1002DBB0[edx]
  13. .text:100109D7                 add     ecx, edx
  14. .text:100109D9                 inc     eax
  15. .text:100109DA                 cmp     eax, 11h
  16. .text:100109DD                 jl      short loc_100109C9
  17. .text:100109DF                 mov     al, byte_10132929
  18. .text:100109E4                 and     ecx, 1Fh
  19. .text:100109E7                 cmp     al, cl
  20. .text:100109E9                 jnz     short loc_10010A4E
Едем дальше. Теперь выполняется проверка корректности серийного номера. Сперва к константе 49h поочередно прибавляется 10h символов строки хеша. К результату поочередно прибавляется 11h корректных символов серийника. Затем к полученной сумме применяется битовая операция AND 1Fh. Результат должен равняться третьему с конца байту хеша серийника.
  1. .text:100109EB                 mov     ecx, 32h
  2. .text:100109F0                 xor     eax, eax
  3. .text:100109F2 loc_100109F2:
  4. .text:100109F2                 movsx   edx, byte_10132938[eax]
  5. .text:100109F9                 sub     ecx, edx
  6. .text:100109FB                 inc     eax
  7. .text:100109FC                 cmp     eax, 10h
  8. .text:100109FF                 jl      short loc_100109F2
  9. .text:10010A01                 xor     eax, eax
  10. .text:10010A03 loc_10010A03:
  11. .text:10010A03                 movsx   edx, byte_10132918[eax]
  12. .text:10010A0A                 movsx   edx, byte_1002DBB0[edx]
  13. .text:10010A11                 add     ecx, edx
  14. .text:10010A13                 inc     eax
  15. .text:10010A14                 cmp     eax, 12h
  16. .text:10010A17                 jl      short loc_10010A03
  17. .text:10010A19                 mov     al, byte_1013292A
  18. .text:10010A1E                 and     ecx, 1Fh
  19. .text:10010A21                 cmp     al, cl
  20. .text:10010A23                 jnz     short loc_10010A4E
Аналогичная проверка, только на этот раз константа 32h, а к 10h символам хэша регистрационного имени прибавляются 12h символов серийного номера. Результат битовой операции AND должен равняться предпоследнему байту хеша серийного номера.
  1. .text:10010A25                 mov     ecx, 79h
  2. .text:10010A2A                 xor     eax, eax
  3. .text:10010A2C loc_10010A2C:
  4. .text:10010A2C                 movsx   edx, byte_10132918[eax]
  5. .text:10010A33                 movsx   edx, byte_1002DBB0[edx]
  6. .text:10010A3A                 sub     ecx, edx
  7. .text:10010A3C                 inc     eax
  8. .text:10010A3D                 cmp     eax, 13h
  9. .text:10010A40                 jl      short loc_10010A2C
  10. .text:10010A42                 mov     al, byte_1013292B
  11. .text:10010A47                 and     ecx, 1Fh
  12. .text:10010A4A                 cmp     al, cl
  13. .text:10010A4C                 jz      short loc_10010A64
Последняя проверка. Берется константа 79h, из нее вычитаются 13h символов серийника. Затем уже знакомая нам битовая операция AND и сравнение с последним символом хеша серийника.
  1. .text:10010A4E loc_10010A4E:
  2. .text:10010A4E                 push    edi
  3. .text:10010A4F                 mov     ecx, 1Ah
  4. .text:10010A54                 xor     eax, eax
  5. .text:10010A56                 mov     edi, offset byte_1003D458
  6. .text:10010A5B                 rep stosd
  7. .text:10010A5D                 mov     ?bRegistered@@3HA, eax
  8. .text:10010A62                 pop     edi
  9. .text:10010A63                 retn
  10. .text:10010A64 ; --------------------------------------------
  11. .text:10010A64 loc_10010A64:
  12. .text:10010A64                 mov     eax, 1
  13. .text:10010A69                 mov     ?bRegistered@@3HA, eax
  14. .text:10010A6E                 retn
  15. .text:10010A6E sub_10010960    endp
Если все проверки пройдены успешно, то регистрационное имя и серийный номер считаются правильными. Таким образом, валидной парой будет регистрационное имя "ManHunter / PCL" и серийный номер "AAAAAAAAAAAAAAAAA3HF".

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

Запускаем игру, и... Не тут-то было. Получаем сообщение, что серийный номер неправильный. В динамической библиотеке проверок больше нет, значит мы как раз напоролись на следующий уровень защиты. Отправляем в дизассемблер главный исполняемый файл. Находим в нем строчки сообщения о некорректном серийном номере.
  1. .text:00416D60                 sub     esp, 10h
  2. .text:00416D63                 push    ebx
  3. .text:00416D64                 push    ebp
  4. .text:00416D65                 push    esi
  5. .text:00416D66                 push    edi
  6. .text:00416D67                 xor     edi, edi
  7. .text:00416D69                 push    edi
  8. .text:00416D6A                 call    ds:?TextCls@@YAXK@; TextCls(ulong)
  9. .text:00416D70                 mov     esi, ds:?TextPrint@@YAXPADKKKK@Z
  10. .text:00416D76                 push    1
  11. .text:00416D78                 push    edi
  12. .text:00416D79                 push    0FF0000h
  13. .text:00416D7E                 push    28h
  14. .text:00416D80                 push    offset aLicenseAgreeme
  15. ; "      LICENSE AGREEMENT VIOLATION !    "...
  16. .text:00416D85                 call    esi
  17. .text:00416D87                 push    3
  18. .text:00416D89                 push    edi
  19. .text:00416D8A                 push    0FFFFh
  20. .text:00416D8F                 push    28h
  21. .text:00416D91                 push    offset aYouUseAnIncorr
  22. ; "        You use an incorrect or        "...
  23. .text:00416D96                 call    esi
  24. ...
По единственной перекрестной ссылке переходим на участок кода в файле, откуда эта функция срабатывает.
  1. .text:004123BF                 call    ds:?GetRegCode@@YAPADXZ ; GetRegCode(void)
  2. .text:004123C5                 mov     ecx, 8
  3. .text:004123CA                 mov     esi, eax
  4. .text:004123CC                 mov     edi, offset byte_479D74
  5. .text:004123D1                 xor     eax, eax
  6. .text:004123D3                 rep movsd
  7. .text:004123D5                 pop     edi
  8. .text:004123D6                 mov     dword_479D70, ebx
  9. .text:004123DC                 mov     ecx, 4Dh
  10. .text:004123E1                 pop     esi
  11. .text:004123E2 loc_4123E2:
  12. .text:004123E2                 movsx   edx, byte_479D4C[eax]
  13. .text:004123E9                 inc     eax
  14. .text:004123EA                 cmp     eax, 10h
  15. .text:004123ED                 lea     ecx, [ecx+edx*2]
  16. .text:004123F0                 jl      short loc_4123E2
  17. .text:004123F2                 mov     al, byte_479D74
  18. .text:004123F7                 and     ecx, 1Fh
  19. .text:004123FA                 cmp     al, cl
  20. .text:004123FC                 jz      short loc_412406
Знакомый алгоритм? Да, действительно, под отладчиком хорошо видно, что здесь берется константа 4Dh и к ней прибавляется 10h символов хеша регистрационного имени, причем перед сложением каждый байт умножается на 2. Затем уже знакомая нам битовая операция AND, а потом выполняется сравнение результата с первым байтом хеша серийника. Все ли? Нет, и тут не все с защитой. Если поискать в файле приметные команды "and ecx, 1Fh", то найдется еще пять мест, где выполняются похожие операции с регистрационным именем. Второй символ серийника:
  1. .text:00412711                 mov     ecx, 307h
  2. .text:00412716 loc_412716:
  3. .text:00412716                 movsx   edx, byte_479D4C[eax]
  4. .text:0041271D                 sub     ecx, edx
  5. .text:0041271F                 inc     eax
  6. .text:00412720                 cmp     eax, 10h
  7. .text:00412723                 jl      short loc_412716
  8. .text:00412725                 mov     al, byte_479D75
  9. .text:0041272A                 and     ecx, 1Fh
  10. .text:0041272D                 cmp     al, cl
  11. .text:0041272F                 jz      short loc_412740
Третий символ серийника:
  1. .text:004152F5                 mov     ecx, 86h
  2. .text:004152FA                 xor     eax, eax
  3. .text:004152FC loc_4152FC:
  4. .text:004152FC                 movsx   edx, byte_479D4C[eax]
  5. .text:00415303                 test    al, 1
  6. .text:00415305                 jz      short loc_41530B
  7. .text:00415307                 add     ecx, edx
  8. .text:00415309                 jmp     short loc_415310
  9. .text:0041530B ; ---------------------------------------
  10. .text:0041530B loc_41530B:
  11. .text:0041530B                 neg     edx
  12. .text:0041530D                 lea     ecx, [ecx+edx*2]
  13. .text:00415310 loc_415310:
  14. .text:00415310                 inc     eax
  15. .text:00415311                 cmp     eax, 10h
  16. .text:00415314                 jl      short loc_4152FC
  17. .text:00415316                 mov     al, byte_479D76
  18. .text:0041531B                 and     ecx, 1Fh
  19. .text:0041531E                 cmp     al, cl
  20. .text:00415320                 jz      short loc_415330
Четвертый символ серийника:
  1. .text:0040DF0C                 mov     edx, 7
  2. .text:0040DF11                 xor     eax, eax
  3. .text:0040DF13 loc_40DF13:
  4. .text:0040DF13                 movsx   ecx, byte_479D4C[eax]
  5. .text:0040DF1A                 cmp     eax, 8
  6. .text:0040DF1D                 jge     short loc_40DF23
  7. .text:0040DF1F                 mov     esi, ecx
  8. .text:0040DF21                 jmp     short loc_40DF2A
  9. .text:0040DF23 ; ---------------------------------------
  10. .text:0040DF23 loc_40DF23:
  11. .text:0040DF23                 mov     esi, 1
  12. .text:0040DF28                 sub     esi, ecx
  13. .text:0040DF2A loc_40DF2A:
  14. .text:0040DF2A                 imul    esi, ecx
  15. .text:0040DF2D                 add     edx, esi
  16. .text:0040DF2F                 inc     eax
  17. .text:0040DF30                 cmp     eax, 10h
  18. .text:0040DF33                 jl      short loc_40DF13
  19. .text:0040DF35                 mov     al, byte_479D77
  20. .text:0040DF3A                 and     edx, 1Fh
  21. .text:0040DF3D                 cmp     al, dl
  22. .text:0040DF3F                 jz      short loc_40DF50
Пятый символ серийника:
  1. .text:0040D794                 mov     ecx, 38Ch
  2. .text:0040D799                 xor     eax, eax
  3. .text:0040D79B loc_40D79B:
  4. .text:0040D79B                 movsx   edx, byte_479D4C[eax]
  5. .text:0040D7A2                 cmp     eax, 9
  6. .text:0040D7A5                 jge     short loc_40D7AF
  7. .text:0040D7A7                 lea     edx, [edx+edx*2]
  8. .text:0040D7AA                 lea     ecx, [ecx+edx*2]
  9. .text:0040D7AD                 jmp     short loc_40D7BC
  10. .text:0040D7AF ; ---------------------------------------
  11. .text:0040D7AF loc_40D7AF:
  12. .text:0040D7AF                 lea     esi, ds:0[edx*4]
  13. .text:0040D7B6                 sub     esi, edx
  14. .text:0040D7B8                 neg     esi
  15. .text:0040D7BA                 add     ecx, esi
  16. .text:0040D7BC loc_40D7BC:
  17. .text:0040D7BC                 inc     eax
  18. .text:0040D7BD                 cmp     eax, 10h
  19. .text:0040D7C0                 jl      short loc_40D79B
  20. .text:0040D7C2                 mov     al, byte_479D78
  21. .text:0040D7C7                 and     ecx, 1Fh
  22. .text:0040D7CA                 cmp     al, cl
И самая падлючая проверка - тринадцатый символ серийника. Тут байты хеша имени выбираются не последовательно, а в разнобой. Какие именно - смотрите в отладчике.
  1. .text:0040BD20                 xor     esi, esi
  2. .text:0040BD22                 cmp     eax, 3A98h
  3. .text:0040BD27                 jle     short loc_40BD91
  4. .text:0040BD29                 mov     al, byte_479D59
  5. .text:0040BD2E                 mov     cl, byte_479D58
  6. .text:0040BD34                 mov     dl, byte_479D50
  7. .text:0040BD3A                 imul    eax, ecx
  8. .text:0040BD3D                 mov     cl, byte_479D4F
  9. .text:0040BD43                 imul    edx, ecx
  10. .text:0040BD46                 mov     cl, byte_479D4D
  11. .text:0040BD4C                 sub     eax, edx
  12. .text:0040BD4E                 mov     dl, byte_479D4E
  13. .text:0040BD54                 imul    edx, ecx
  14. .text:0040BD57                 mov     cl, byte_479D51
  15. .text:0040BD5D                 sub     eax, edx
  16. .text:0040BD5F                 mov     dl, byte_479D52
  17. .text:0040BD65                 imul    edx, ecx
  18. .text:0040BD68                 mov     cl, byte_479D80
  19. .text:0040BD6E                 add     eax, edx
  20. .text:0040BD70                 mov     dl, byte_479D4C
  21. .text:0040BD76                 sub     eax, edx
  22. .text:0040BD78                 sub     eax, 0Dh
  23. .text:0040BD7B                 and     eax, 1Fh
  24. .text:0040BD7E                 cmp     cl, al
  25. .text:0040BD80                 jz      short loc_40BD91
Остальные символы серийника не используются и могут быть любыми из числа разрешенных. Собирать правильный серийник надо с первых символов и с 13-го, а затем корректировать последние три символа на основании сгенерированных данных.

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

AxySnake 1.19 KeyGenAxySnake 1.19 KeyGen

AxySnake.1.19.KeyGen-PCL.zip (4,604 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
Игорь (31.08.2017 в 17:59):
Вообще не понимаю негативных комментариев к статьям раздела "Темная сторона Силы" (да и к статьям других разделов тоже).
Подробные разборы, с принтскринами, подписями и пояснениями... Замечательный контент же, на самом деле.
И в целом сайт очень интересный, спасибо автору.
ManHunter (29.07.2017 в 18:27):
Такое ощущение, что я тут кому-то что-то должен. Специально выложил кейген с исходником, открываешь исходник и смотришь, какие символы участвуют в проверке. Блок-схемы пусть рисуют учителя информатики в школе.

ЦитатаГодный контент в массы!

И все бесплатно, естественно. На этом сайте все делается исключительно в личное время, и как его расходовать, а также на что его расходовать - это мое дело. В интернете полно сайтов с хорошим годным контентом, прям куда ни ткни, каждый первый. Почему бы не выбрать себе что-нибудь из них?

А спиннеров тут точно не будет, так же как и видеообзоров.
Анон (29.07.2017 в 12:32):
Такое ощущение складывается что Вас при помощи пыток заставляют анализировать софт. В прошлый раз был минутный патч, в этот раз начало хорошее, а вот концовка в стиле "надоело копаться в этом ховне, пусть читатели сами последнюю проверку разбирают по полочкам".
Я очень надеюсь, что в следующий раз будет подробный разбор кода с комментариями. А если ещё и блок-схемы будут то это просто мечта начинающего (читать как юного) крекера!
Потом можно и видео на ютубчик пилить по этой теме, всяко лучше чем спинеры, летсплеи по Майнкрафту и распаковки хлама с али. Годный контент в массы!
ManHunter (20.07.2017 в 07:06):
Между уровнями - это как раз 13-й символ серийника.
voffka (20.07.2017 в 01:18):
Могу ошибаться, но там кажется еще проверки должны быть, после какого-то уровня. Давно смотрел, не помню.
Vladimir (18.07.2017 в 22:40):
Вы сами как-то про блокировщики рекламы писали, что очень поощряете и одобряете.
А вообще я это просто в качестве мелкого багрепорта написал, движок ж у сайта вроде ваш собственный.
ManHunter (18.07.2017 в 22:34):
А почему сайт не в исключениях? :) Перезалил.
Vladimir (18.07.2017 в 21:48):
У вас в урле к третьему по счёту изображению на странице содержится /ad/. Баннерорезки убивают картинку на раз.

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

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

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