В этой статье речь пойдет о подборе ключа к архивам, созданным с помощью Visionaire Studio, которые хранят в себе игровые ресурсы.
Visionaire Studio - это среда для упрощенного создания игр в жанре adventure, с её помощью были созданы такие игры, как, например: The Dark Eye: Chains of Satinav, A New Beginning, Deponia. В общем, практически весь репертуар продуктов студии Daedalic Entertainment. Самого процесса распаковки я касаться не буду, так как для этого уже существуют несколько утилит: (скрипт для QuickBMS, VISExt, Unpakke) и более-менее полное описание формата содержимого файла (краткое описание формата VIS*). Минусом всех этих утилит является необходимость собственноручно вбивать ключ, которым зашифрован VIS-файл (ключ шифрования для одной и той же игры может быть различным в зависимости от, например, локализации, а также ключи не сразу добавляются в утилиты при выходе новой игры), попробуем разобраться, как вычислить этот ключ программно.
Как видно из описания формата, в качестве шифрования применяется XOR, где ключом служит половина MD5-хэша, полученного от имени файла (не всегда), то есть ключ шифрования имеет формат [0-9a-f]{16}.
Теперь, для упрощения исследования, нам понадобятся несколько VIS-файлов с известными ключами. Ключи можно получить из вышеперечисленных утилит для распаковки, а файлы, например, из вышеуказанных игр (обычно искомый файл называется data.vis). Если качать игры целиком нет желания, то в качестве примера можно взять этот небольшой файл (выкладывался в качестве примера на XeNTaX). На его примере я объясню алгоритм восстановления ключа.
Скачаем QuickBMS и запустим его, указав путь к скрипту распаковки (visionaire.bms), путь к VIS-файлу (d9ef88157bcbcae0.vis) и добавив -a DUMP, чтобы сдампить расшифрованное содержимое первого "сегмента", который содержит в себе информацию о смещении, размере и типе каждого файла, который хранится внутри.
По сути, нас интересуют первые 48 байт созданного в результате вышеописанной операции файла visionaire_dump.dat. Почему? Потому что, если посмотреть скрипт visionaire.bms, то мы увидим, что в оригинальном VIS-файле (до расшифровки) первые 4 байта отведены под сигнатуру (VIS3), следующие 4 байта под размер "файла-индекса" (который мы будем рассматривать чуть ниже) и далее начинается содержимое, которое циклически ксорится на ключ.
Рассмотрим первые 48 байт из файла, полученного после расшифровки скриптом для QuickBMS:
1 2 3 |
48 44 52 00 00 00 00 00 00 2B 5B 00 00 2B 5B 00 00 00 08 00 00 2B 5B 00 00 6C A8 00 00 6C A8 00 00 00 00 00 00 98 03 00 00 4D 95 00 00 4D 95 00 |
48 44 52 00 - это сигнатура (HDR), которую можно пропустить, далее данные хранятся фрагментами по 16 байт в формате offset - zsize - size - type.
То есть 00 00 00 00 - offset, 00 2B 5B 00 - zsize, 00 2B 5B 00 - size, 00 00 08 00 - type.
Если взять какие-либо другие VIS-файлы и посмотреть результат расшифровки, то мы увидим, что первые 8 байт всегда выглядят одинаково, то есть получение первой половины ключа не составляет абсолютно никакой проблемы. Очевидно, что вторая половина ключа тоже легко находится, так как поле offset второго фрагмента эквивалентно полям zsize и size (они обычно равны) предыдущего, что логично. Соответственно вторая половина ключа также легко восстанавливается, так как у нас есть исходные данные и ожидаемый результат.
Теперь напишем простой скрипт, который автоматизирует восстановление ключа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
use strict; use warnings; use File::Basename; # Сигнатура верного заголовка, которая используется для получения первого фрагмента ключа my @want = qw/48 44 52 00 00 00 00 00/; # Хэш для валидации символов ключа my %xor_val = map {$_ => 1} (0..9, 'a'..'f'); my @key = (); # Выводим FAQ, если скрипту не передан аргумент if(scalar @ARGV < 1) { print "Usage: ".basename($0)." file.vis"; exit; } # Читаем 40 байт из VIS-файла open A, '<', $ARGV[0] or die $!; read A, my $data, 8 + 16 + 16; close A; # Проверяем начальную сигнатуру if(substr $data, 0, 4 eq 'VIS3') { print "Invalid signature (expected VIS3)\n"; exit; } # Получаем первые 8 байт ключа и проверяем валидность символов my @data = split //, substr $data, 8, 8; for(my $i = 0; $i < scalar @data; $i++) { my $byte = hex $want[$i] ^ ord $data[$i]; push @key, $xor_val{chr $byte} ? chr $byte : '?'; } # Расшифровываем часть данных, используя полученный фрагмент ключа my @data_xor = (); @data = split //, substr $data, 8 + 16, 8; for(my $i = 4; $i < scalar @data; $i++) { push @data_xor, chr (ord $data[$i] ^ ord $key[$i % scalar @key]); } # Расшифрованное смещение my $offset = hex unpack 'H8', join '', @data_xor; # 8 байт оригинальных данных, которые будут использованы для получения ключа my $i_value1 = hex unpack 'H8', join '', substr $data, 8 + 8, 4; my $i_value2 = hex unpack 'H8', join '', substr $data, 8 + 8 + 4, 4; # Получаем оставшийся фрагмент ключа, поксорив ожидаемое содержимое на исходные данные my $k_value1 = $offset ^ $i_value1; my $k_value2 = $offset ^ $i_value2; # Преобразуем фрагменты в необходимый вид push @key, pack 'N', $k_value1; push @key, pack 'N', $k_value2; # Выводим найденный ключ print @key; |
Скачать скрипт одним файлом: vis_keytool.pl
Вот и всё, в качестве проверки я попробовал получить ключ для нескольких файлов - всё отлично сработало, что подтвердила дальнейшая удачная распаковка ресурсов с помощью VISExt, например, этой картинки:
Вот что всегда радует в ваших статьях/заметках - так это всякие интересные картинки вконце/вначале (сейчас вспомнилась картинка из заметки про стенанографию).
Блин, что б без ошибки написать коммент - не судьба. "СтеГанография".
Не работет. Скачал файл XeNTaX, потом VISExt пытался картинки получить, везде пишет error read, ключ вводил..
Картинка в посте не из этого файла. Найдите где-нибудь одну из упомянутых игр, скачайте и возьмите из неё vis-файлы.
Распаковать удалось. Однако. Однако графические файлы не открываются. Не пойму в чем кроется загвоздка. Звуки все прослушиваются на ура а вот графика не работает. Прошу помоги, объясни в чем косяк кроется.
Графические файлы могут быть дополнительно закодированы.
http://forum.xentax.com/viewtopic.php?f=10&t=5467 раздел IV. Encryption
Скажи в твоем случае графические файлы были закодированы? Если да то чем ты пользовался, каким декриптером.
Приведены же программы в начале поста. Тот же VISExt должен корректно извлекать.
Способ неактуален. К большому сожалению.
8 лет прошло...
Сам принцип получения может быть и действует сейчас.... вот только утилиты уже давно не работают с этой хернёй... в смысле с vis studio.
1. НЕТ файла, скрипта, который нужно открыть перво-наперво. Текстовые файлы внутри игры абсолютно не то, что нужно (проверено пять раз).
2. Далее бесполезно открывать какой-либо архив, в консоли ошибка ИНВАЛИД.... и всё.
3. Если сохранить текстовый файл в конце неудачных попыток открыть предыдущие файлы, в текстовом сохраняется всякий бред, описывающий вопросы и проблемы с сохранениями самой игры. И ни слова о каких-либо ошибках, которые должны быть, раз это ЛОГ-файл...
Вывод: способ получить ресурсы кардинально изменился, а в статье более НИ ОДИН шаг, даже самый первый, НЕ работает.
Ожидаемо, за 9 лет думаю не одна версия Visionaire Studio вышла.
Извини, но для кого эта статья ?
Ту не сидят только одни программисты, знающие, что со всем этим делать.
Переводчики тоже НЕ программисты.
Для тех, кто хочет освоить навыки программирования через прикладную задачу.
Если бы в статье был инструмент для пользователей, то это был бы как минимум исполняемый файл с GUI, а не скрипт.
Приветствую!
А есть ли способы упаковки обратно? Хочу подменить английскую озвучку на немецкую, но даже без изменений пытался с помощью unpakke04b, но игра не хочет принимать полученный файл. Игра The Dark Eye: Chains of Satinav, ключ af0512966ae16580.