Денис Фатєєв - Огляд захисту NevoSoft

Вітчизняні дистриб'ютори ігрових програм мислять шаблонно і не роблять висновків зі своїх і чужих прорахунків. Принаймні, таке враження склалося у мене після недавнього вивчення захисту ігр NevoSoft , Яке зайняло небагато чим більше часу, ніж писалася ця коротка стаття.

Чесно кажучи, тут я не планую детально розбирати механізм захисту, будуть показані лише основні моменти, оскільки (як показав досвід попередньої замітки про Alawar і листи читачів) технічні деталі в програмуванні нецікаві звичайним користувачам. Ті з читачів, для кого представляє інтерес сам процес вивчення захисту, зможуть простежити весь шлях дослідження за наведеними нижче висновків. Особливих зусиль це не складе, впораються навіть новачки.

Для наочного прикладу до статті візьмемо першу-ліпшу гру від виробника. Я скачав «Залишитися в живих», яка заявлена на сайті хітом сезону. Встановлюємо гру звичайним способом:

Встановлюємо гру звичайним способом:


Беремо з сайту програму-завантажувач, запускаємо, задаємо їй шлях для установки і чекаємо, поки скочується власне сама гра:

Беремо з сайту програму-завантажувач, запускаємо, задаємо їй шлях для установки і чекаємо, поки скочується власне сама гра:

Для чистоти експерименту, після установки гри слід перезавантажитися, щоб елементи захисту NevoSoft коректно запустилися і ми мали справу з повноцінно захищеною версією гри. Перше, що кидається в очі після перезавантаження - в області повідомлень з'явився значок з фірмовою емблемою і в автозавантаження додалася програма незрозумілого призначення. При запуску гри з'являється типове вікно з повідомленням про тимчасове обмеження і всякої рекламної лушпинням від виробника.

Подивившись на картинки - переходимо до самої суті нашого завдання. Загалом, механізм захисту тут чимось схожий на Alawar, тільки без найменших натяків на серйозний підхід до справи. Alawar хоча б намагався обмежувати доступ до оболонки гри за допомогою використання ASProtect в своїх дистрибутивах, а тут і цього немає. В основі лежить навісна захист і оболонка, яка контролює запуск програм. На відміну від ігор виробництва Alawar, де ця оболонка ( «wrapper») додається до кожної гри, - тут вона єдина для всіх, встановлюється в систему раз і назавжди при інсталяції будь-якої гри від Nevosoft. Здійснимих файл 'drm.exe' в автозавантаженні, що створює значок в треї і час від часу показує рекламу, за сумісництвом є цією самою оболонкою-годинним, перевіряючим обмеження, провідним тріальний звіт часу і все таке інше. Його грізну назву, мабуть, було покликане відлякати непроханих дослідників потенційним наявністю складних криптозахисті , Але насправді виявилося, що нічого такого тут немає і досить відладчика і дизассемблера. Реверсування захисту Nevosoft зводиться до вивчення алгоритмів в цьому виконуваного файлу.

Алгоритм захисту полягає в наступному:

1) Встановлена гра запускається ярликом, що вказує на 'drm.exe' з унікальним ідентифікатором гри в параметрі. Оболонка звертається до внутрішньої бази даних і за ідентифікатором гри шукає відповідні їй параметри: безкоштовне час для користувача, здійсненний файл гри, параметри захисту виконуваного файлу і іншу інформацію. В результаті перевірок завантажується файл гри, в процесі завантаження з нього знімається захист і йому передається управління, одночасно з цим включається таймер безкоштовної гри, якщо гра не «куплена». По завершенні процесу гри, оболонка записує поточне значення залишився безкоштовного часу в базу. Природно, як тільки важливість безплатної часу для гри в базі дорівнюватиме нулю, оболонка більше не дасть запустити гру;

2) Внутрішня база даних являє собою звичайну БД SQLite3 , Зашифровану XOR-му по ключовому масиву розміром 256 байт, що зберігається в тілі оболонки. Дані ключового масиву статичні і в цілях сумісності не змінюються протягом часу (тут і нижче використовуються фрагменти коду на мові Delphi):

bKeyArraySize = 256; arKeyDecrypt: array [0 .. bKeyArraySize - 1] of Byte = ($ 69, $ f7, $ 23, $ a3, $ a2, $ 5f, $ 86, $ 8d, $ c6, $ 43, $ 6d, $ 5b, $ 35, $ c9, $ 7b, $ ff, $ 7f, $ a6, $ a5, $ 75, $ 89, $ 89, $ d8, $ ee, $ 77, $ f2, $ d3, $ 22, $ e4, $ 7a, $ f1, $ b4 , $ 5a, $ 45, $ 5a, $ d0, $ 72, $ a3, $ f4, $ 65, $ b2, $ 5c, $ ff, $ 8b, $ 5a, $ db, $ 61, $ e3, $ f1, $ eb , $ 15, $ a8, $ 22, $ 9b, $ a7, $ 5c, $ ae, $ 27, $ 77, $ 5c, $ bb, $ 43, $ 87, $ 0c, $ 86, $ 23, $ 5d, $ 53, $ 31, $ cf , $ 7a, $ 7e, $ 3f, $ 2a, $ da, $ 0b, $ 1f, $ b6, $ dc, $ 48, $ a4, $ e1, $ f4, $ 85, $ 53, $ d6, $ 2c, $ 50 , $ 28, $ 20, $ f9, $ 84, $ dd, $ 93, $ 0d, $ f3, $ 79, $ df, $ 07, $ ad, $ a3, $ de, $ 60, $ 62, $ b1, $ cb, $ 45, $ 5 d, $ 32, $ 4d, $ bb, $ 38, $ 9c, $ 52, $ de, $ 05, $ 18, $ 31, $ d1, $ ba, $ 5d, $ e9, $ 68, $ cb, $ f1, $ 67, $ 57, $ 54, $ 2a, $ 0d, $ 6e, $ d2, $ 7f, $ 27, $ 35, $ ​​05, $ 82, $ c9, $ b2, $ f9, $ ce, $ 2f, $ 6a, $ d1, $ e8, $ d3, $ 39, $ b2, $ 23, $ 19, $ e6, $ fc, $ 7e, $ 6f, $ 4b, $ 5e, $ b8 , $ C7, $ c4, $ ac, $ 56, $ 6b, $ 71, $ 55, $ 3e, $ 4b, $ ba, $ 9e, $ cf, $ ae, $ 99, $ 67, $ 8e, $ 0a, $ 1f, $ e9, $ f4, $ 0E, $ e4, $ 2c, $ e1, $ fe, $ 32, $ ab, $ d5, $ f5, $ 36, $ 2f, $ 65, $ bd, $ 92, $ 1a, $ 8d, $ a1, $ 54, $ d1, $ f8, $ e5, $ 16, $ 34, $ 9a, $ 65, $ 6a, $ d8, $ 76, $ a5, $ f5, $ e5, $ f2, $ a1, $ be, $ e7, $ c9, $ af, $ 2a, $ 76, $ bb, $ ec, $ 8c, $ e2, $ 49, $ 6c, $ 89, $ 67, $ 33, $ 34, $ fb, $ 91, $ 51, $ d1, $ af , $ 43, $ a1, $ e3, $ 21, $ b6, $ 2d, $ 1a, $ 64, $ 3d, $ 9e, $ 3f, $ 53, $ 52, $ ba, $ c4, $ 44, $ ec, $ 76, $ 3b , $ 79, $ 69, $ 91, $ 72, $ bd, $ d2);

Всякий раз, коли оболонці потрібно прочитати або записати дані в БД, база розшифровується в пам'ять, з нею проводяться необхідні маніпуляції, проводиться шифрування і результат в кладеться назад на диск. Сама шифрована база зберігається в прихованому файлі 'base.db' в каталозі «Application Data» поточного користувача:

Варто зауважити, що оболонка блокує файл бази даних від читання / запису сторонніми програмами (тримає файл відкритим ексклюзивно). При будь-яких маніпуляціях з файлом бази спочатку потрібно завершити процес 'drm.exe'.

При бажанні, ми можемо розшифрувати базу за допомогою такого алгоритму:

function DecryptNevoDatabase (lpSrcFile, lpDestFile: PChar): Boolean; // here I will not using MMF (memory-mapped files) due to approximately // small files size (suppose that the Nevosoft DB is not bigger than 12-15Mb) var hFileRead, hFileWrite: THandle; i: Byte; iNumRead, iNumWrite: Integer; Buffer: array [0 .. bKeyArraySize - 1] of Byte; begin Result: = false; hFileRead: = CreateFile (lpSrcFile, GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFileRead = INVALID_HANDLE_VALUE) then Exit; hFileWrite: = CreateFile (lpDestFile, GENERIC_READ + GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hFileWrite = INVALID_HANDLE_VALUE) then Exit; try // decrypt file repeat if (ReadFile (hFileRead, Buffer, bKeyArraySize, Cardinal (iNumRead), nil)) then begin for i: = 0 to iNumRead - 1 do // where is all street magic happens .. hehe Buffer [i ]: = Buffer [i] xor arKeyDecrypt [i]; if (iNumRead> 0) then begin if (not (WriteFile (hFileWrite, Buffer [0], iNumRead, Cardinal (iNumWrite), nil))) then Exit; // checking bytes count to be written if (iNumWrite <> iNumRead) then Exit; end; end else iNumRead: = 0; until (iNumRead <bKeyArraySize); // setting the flag Result: = true; finally CloseHandle (hFileWrite); CloseHandle (hFileRead); end; end;

Після розшифровки можна дивитися структуру і дані, що зберігаються в базі, за допомогою будь-якого вюверів SQLite, наприклад, SQLite Browser (Картинка клікабельні):

Після розшифровки можна дивитися структуру і дані, що зберігаються в базі, за допомогою будь-якого вюверів SQLite, наприклад,   SQLite Browser   (Картинка клікабельні):

Можна навіть відредагувати дані (тріальний ліміт гри), зашифрувати отриманий файл бази тим же ключем і замінити оригінальну базу своєї - оболонка підхопить наші зміни. До речі, це є одним з варіантів обходу захисту - отримання «вічного» тріалу, але це негарне рішення, «брудний хак».

3) Секція коду виконуваного файлу гри, в якій знаходиться точка входу PE EXE, побайтно зашифрована XOR-му з масивом довжиною 1024 байти, що зберігаються в поле 'crpt_inf' в запису бази даних. Іншими словами, за ідентифікатором гри оболонка завантажує її виконуваний файл (отримуючи ім'я файлу з поля 'exec'), зчитує з БД ключовий масив з поля 'crpt_inf' і виробляє розшифровку EP-секції ключовим масивом, після чого передає управління завантажений файл.

Розшифрувати секцію і отримати «чистий» здійсненний файл гри можна так:

// decrypt PE file section function PEDecrypt_Section (lpPE, lpISH: Pointer; wSectionIdx: Word; lpCryptoData: PChar): Boolean; var ish: IMAGE_SECTION_HEADER; a, b: Byte; i, j: DWord; begin Result: = true; // STUB Move (Pointer (DWord (lpISH) + DWord ((wSectionIdx - 1) * SizeOf (IMAGE_SECTION_HEADER))) ^, ish, SizeOf (ish)); j: = 0; for i: = 0 to ish. SizeOfRawData - 1 do begin Move (Pointer (DWord (lpPE) + ish. PointerToRawData + i) ^, a, 1); Move (Pointer (DWord (lpCryptoData) + j) ^, b, 1); // we explicitly know, that crypto array always has lenght of 1024 bytes if (j <1023) then Inc (j) else j: = 0; // no, no, no, - david blaine .. a: = a xor b; Move (a, Pointer (DWord (lpPE) + ish. PointerToRawData + i) ^, 1); end; AddListBoxItem_Sep ( 'Розпакування успішно завершена.'); AddListBoxItem ( ''); SendMessage (GetDlgItem (hMainDlg, ID_LIST_LOG), WM_VSCROLL, SB_BOTTOM, 0); end; ... // unwrap executable file's EP section function Unwrap_ExecutableFile (lpFileName, lpCryptoData: PChar): Boolean; var hFile, hMapFile: THandle; lpMap: Pointer; e_magic: Word; inh: IMAGE_NT_HEADERS; lpOffset: DWord; wEPSection: Word; strSaveFile: string; begin Result: = false; AddListBoxItem ( 'Вкажіть ім'я файлу для розшифрованого файлу ...'); SendMessage (GetDlgItem (hMainDlg, ID_LIST_LOG), WM_VSCROLL, SB_BOTTOM, 0); if (Get_SaveFileName (hMainDlg, nil, 'виконуваного файлу (* .exe)' + # 0 + '* .exe' + # 0 # 0, strSaveFile)) then begin AddListBoxItem ( 'Файл' + strSaveFile); AddListBoxItem_Sep ( 'Виробляється розпакування виконуваного файлу ...'); CopyFile (lpFileName, PChar (strSaveFile), false); hFile: = CreateFile (PChar (strSaveFile), GENERIC_READ + GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile = INVALID_HANDLE_VALUE) then begin AddListBoxItem_Sep ( 'Помилка відкриття результуючого файлу!'); Exit; end; hMapFile: = CreateFileMapping (hFile, nil, PAGE_READWRITE, 0, 0, nil); CloseHandle (hFile); if (hMapFile = 0) then begin AddListBoxItem_Sep ( 'Не можу записати результуючий файл!'); Exit; end; lpMap: = MapViewOfFile (hMapFile, FILE_MAP_READ + FILE_MAP_WRITE, 0, 0, 0); CloseHandle (hMapFile); if (lpMap = nil) then begin AddListBoxItem_Sep ( 'Помилка відкриття результуючого файлу!'); Exit; end; // doing some checks (if this file really win32 PE EXE) Move (lpMap ^, e_magic, SizeOf (e_magic)); // 'MZ' if (e_magic = IMAGE_DOS_SIGNATURE) then begin // lpOffset now points on PE headers lpOffset: = DWord (lpMap) + PDword (DWord (lpMap) + $ 3C) ^; // read 'nt headers' Move (Pointer (lpOffset) ^, inh, SizeOf (inh)); if (inh. Signature = IMAGE_NT_SIGNATURE) then // 'PE00' // seems to valid win32 PE header begin // retrieving EP section index (1 based) wEPSection: = PESectionFromRVA (Pointer (lpOffset), inh. OptionalHeader. AddressOfEntryPoint) ; if (wEPSection> 0) then begin // lpOffset now points to array of 'image secton headers' lpOffset: = lpOffset + SizeOf (IMAGE_NT_HEADERS); Result: = PEDecrypt_Section (lpMap, Pointer (lpOffset), wEPSection, lpCryptoData); end else AddListBoxItem_Sep ( 'Помилка визначення виконуваного файлу (IMAGE_NT_SIGNATURE)!'); end else AddListBoxItem_Sep ( 'Помилка перевірки виконуваного файлу (IMAGE_NT_SIGNATURE)!'); end else AddListBoxItem_Sep ( 'Помилка перевірки виконуваного файлу (IMAGE_DOS_SIGNATURE)!'); UnmapViewOfFile (lpMap); end else AddListBoxItem_Sep ( 'Операція скасована користувачем.'); end;


В цілому, дана захист не складніше використовуваної в Alawar, але чисто технічно тут багато рутинних операцій і справа не обмежується правкою двох байт. У зв'язку з цим вирішено було написати утиліту, що автоматизує процес дослідження (власне, шматки коду, наведені вище, взяті з неї):

Завантажити утиліту можна   тут   ;  исходники (на Delphi 7)   тут
Завантажити утиліту можна тут ; исходники (на Delphi 7) тут .

Врахуйте, що дана програма представлена ​​лише для освітніх цілей і ні в якому разі не може використовуватися для виготовлення піратських копій ігор; прочитайте на сайті Відмова від відповідальності .

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

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: