Blog. Just Blog

Расшифровка ресурсов игр RPG Maker MV

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

В игры я обычно не играю - времени нет. Но тут мне на глаза попалась RPG-игрушка, вроде ничего особенного, но чем-то зацепила. Путешествия по карте в составе отряда, головоломки, мечи и магия, незамысловатая боевка и все такое. Но суть не в этом. Часть графических ресурсов игры лежала в открытом виде, а часть в виде файлов с расширениями ".rpgmvp", которые не открываются обычными средствами. Краткое расследование показало, что игра собрана в программе RPG Maker MV, а файлы ресурсов зашифрованы с ее же помощью. Любопытство взяло вверх над игровыми головоломками, ковыряться в файлах мне гораздо интереснее.

Кроме картинок обнаружились еще и звуковые файлы с похожим расширением ".rpgmvo". Причем если посмотреть на внутренности всех этих файлов, то в графических файлах сразу бросится в глаза xml-секция, которую в файлы записывает Photoshop. Это не обязательно, если предварительно прочистить метаданные, то угадать в двоичных данных содержимое PNG-файла сможет только наметанный глаз. Я на созерцании внутренностей графических файлов разных форматов уже собаку съел, структуру PNG и даже заголовки секций узнаю без труда.

Содержимое графического файла
Содержимое графического файла

В зашифрованных звуковых файлах также легко обнаружить структуры, соответствующие OGG-файлам. О внутреннем формате намекает последняя буква расширения: "p" для PNG и "o" для OGG. На форумах разработчиков подобных игрушек я узнал, что может быть еще один вариант пошифрованных медиаданных, там расширение будет ".rpgmvm" и соответствовать оно будет исходным файлам в формате M4A. Это тоже звуковые файлы. В моем случае таких файлов не было.

Содержимое звукового файла
Содержимое звукового файла

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

Зашифрованные данные
Зашифрованные данные

Сравнение с обычными PNG-файлами дало понимание, что после 16-байтного заголовка идет 16 байт, в которых должен быть стандартный заголовок PNG-файла, но там шифрованные данные. После них идет уже содержимое оригинального PNG-файла. И действительно, если сохранить данные из шифрованного файла, начиная с 32-го символа, затем выдрать из любого PNG-файла первые 16 байт, а потом дописать к ним сохраненные данные, то получится полноценный PNG-файл, который открывается во всех графических программах. Это достаточно топорное решение, потому что в PNG-файлах первые 16 байт неизменные и их шифрование можно просто игнорировать, заменяя оригинальными данными. С форматом OGG такая вольница не прокатывает, тем более с M4A. Там в 16-байтный блок в начале файла попадают критические данные, без полноценного восстановления которых файл получится битым. И именно по этой причине практически все онлайновые говносервисы по расшифровке ресурсов RPG Maker MV могут обрабатывать только графические файлы.

После нескольких часов, проведенных на ресурсах разработчиков игр, удалось выяснить, что в комплекте с каждой игрой идет системный файл с настройками сборки. Конкретно для движка RPG Maker MV он называется "System.json" и находится в папке "www\data", если считать от корневой папки игры. В нем должен быть параметр "encryptionKey". Там же видны два булевых параметра с характерными названиями "hasEncryptedImages" и "hasEncryptedAudio", оба из которых имеют значение TRUE. То есть в игре присутствуют шифрованные графические и аудио-данные.

Ключ шифрования
Ключ шифрования

Если представить ключ шифрования в виде последовательности двухбайтных данных "17 1f 1d 78 62 91 ab 46 c2 b4 0a 81 f6 6a d9 38", то получится как раз 16 байт, что соответствует длине зашифрованных данных. Пока я решил не пускать в дело тяжелую артиллерию и поизучать данные глазами. За объект исследований взял все тот же статичный заголовок PNG-файлов.

Оригинальный PNG-заголовок:
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52

Ключ:
17 1f 1d 78 62 91 ab 46 c2 b4 0a 81 f6 6a d9 38

Зашифрованные данные:
9e 4f 53 3f 6f 9b b1 4c c2 b4 0a 8c bf 22 9d 6a

Обратите внимание на нулевые байты в оригинальном заголовке и соответствующие им байты в ключе шифрования и зашифрованных данных. Для нулевых исходных байт байты в ключе и выходных данных совпадают. Такое может быть в случае трех операций: ADD, OR или XOR. Проверкой других байт все прочие команды кроме XOR отпадают. Дополнительно проверяем на доступных аудиофайлах.

Оригинальный OGG-заголовок:
4f 67 67 53 00 02 00 00 00 00 00 00 00 00 ?? ??

Ключ:
17 1f 1d 78 62 91 ab 46 c2 b4 0a 81 f6 6a d9 38

Зашифрованные данные:
58 78 7a 2b 62 93 ab 46 c2 b4 0a 81 f6 6a 00 4d

Как видно, схема получается такая же. Тут нулевых байт много, все совпадает. Последние два байта зависят от оригинального файла, поэтому без ключа их не восстановить. Ну что, теперь у нас есть алгоритм шифрования. Первые 16 символов исходного файла шифруются побайтно с байтами из ключа, потом это все дописывается к статичному заголовку. Чтобы не тратить время на полноценный кодинг на Ассемблере, я быстренько накидал скрипт-расшифровщик на PHP. Запускать из папки с установленной игрой.
  1. //--------------------------------------------------------------
  2. // Расшифровка ресурсов игр RPG Maker MV
  3. // (C) ManHunter / PCL
  4. // www.manhunter.ru
  5. //--------------------------------------------------------------
  6.  
  7. function decrypt($dir) {
  8.   global $key_data;
  9.   if ($d=opendir($dir)) {
  10.     while($f=readdir($d)) {
  11.       if ($f=='.' || $f=='..') { continue; }
  12.       $file_name=$dir.'/'.$f;
  13.       if (is_dir($file_name)) {
  14.         decrypt($file_name);
  15.       }
  16.       else {
  17.         $info=pathinfo($file_name);
  18.         $info['extension']=strtolower($info['extension']);
  19.         switch($info['extension']) {
  20.           case 'rpgmvp': {
  21.             $new_ext='png';
  22.             break;
  23.           }
  24.           case 'rpgmvm': {
  25.             $new_ext='m4a';
  26.             break;
  27.           }
  28.           case 'rpgmvo': {
  29.             $new_ext='ogg';
  30.             break;
  31.           }
  32.           default: {
  33.             $new_ext='';
  34.             continue;
  35.           }
  36.         }
  37.  
  38.         if ($new_ext!='') {
  39.           $f1=fopen($file_name,'r');
  40.           $header_data=fread($f1,16);
  41.           if (substr($header_data,0,5)=='RPGMV') {
  42.  
  43.             $new_name=$info['dirname'].'/'.$info['filename'].'.'.$new_ext;
  44.             echo 'Decrypting: '.$file_name.' --> '.$new_name."\n";
  45.  
  46.             $encrypted_data=fread($f1,16);
  47.  
  48.             for ($i=0$i<16$i++) {
  49.               $encrypted_data[$i]=
  50.                 chr(ord($encrypted_data[$i])^hexdec('0x'.$key_data[$i]));
  51.             }
  52.             $f2=fopen($new_name,'w+');
  53.             fwrite($f2,$encrypted_data);
  54.             while(!feof($f1)) {
  55.               $data=fread($f1,1000000);
  56.               fwrite($f2,$data);
  57.             }
  58.             fclose($f2);
  59.           }
  60.           else {
  61.             echo 'Incorrect file: '.$file_name."\n";
  62.           }
  63.           fclose($f1);
  64.         }
  65.       }
  66.     }
  67.   }
  68. }
  69.  
  70. $json_file='www/data/System.json';
  71. if (file_exists($json_file)) {
  72.   $system_data=file_get_contents($json_file);
  73.   if ($json=json_decode($system_data,true)) {
  74.     if (isset($json['encryptionKey'])) {
  75.       echo 'Encryption Key: '.$json['encryptionKey']."\n";
  76.       $key_data=preg_split('/(..)/is',$json['encryptionKey'],
  77.         0,PREG_SPLIT_DELIM_CAPTURE+PREG_SPLIT_NO_EMPTY);
  78.  
  79.       decrypt('www');
  80.  
  81.       if (isset($json['hasEncryptedImages'])) {
  82.         unset($json['hasEncryptedImages']);
  83.       }
  84.       if (isset($json['hasEncryptedAudio'])) {
  85.         unset($json['hasEncryptedAudio']);
  86.       }
  87.       unset($json['encryptionKey']);
  88.       $f=fopen($json_file,'w+');
  89.       fwrite($f,json_encode($json));
  90.       fclose($f);
  91.  
  92.       echo 'Done';
  93.     }
  94.   }
  95.   else {
  96.     echo 'ERROR: No encryption Key';
  97.   }
  98. }
  99. else {
  100.   echo 'ERROR: No System.json file';
  101. }
После обработки ресурсов скрипт удаляет ключи из файла настроек, чтобы игра работала уже с расшифрованными данными. Не забывайте про резервные копии.

Кстати, ключ можно не искать в файле настроек, а вытащить из любого зашифрованного графического файла игры. Просто ХОR'ите шифрованные 16 байт с оригинальным заголовком PNG-файла и получаете ключ, с помощью которого можно расшифровать уже и другие данные.

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

Комментарии

Отзывы посетителей сайта о статье
tmp (29.02.2024 в 19:41):
Что за игра то? очень интересно)

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

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

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