Как сделать Laser Squad из XCOM: этюд для GDB в OSX

в 7:57, , рубрики: gdb, ocaml, OS X, XCOM: Enemy Unknown, перевод, Разработка под OS X, метки:

image

Вот так теперь выглядит игра Laser Squad, которую я увидел очень давно. Тогда она была во всех смыслах проще и выглядела так:

image

При этом принципиально игра не изменилась. Просто стала сложнее: куча типов ресурсов, множество вариантов их применения плюс «инфляция»: каждая следующая мастерская, лаборатория или спутниковый канал стоят больше, чем предыдущие. С одной стороны, это все дает возможность играть разными стратегиями развития, но иногда так хочется поиграть просто в Laser Squad! Поэтому…

Поэтому я пошел на App Store и за каких-то 29$ купил XCOM. Метода хака ресурсов в играх не изменился с момента появления Первого Сейва: сохраняемся, меняем количество целевого ресурса, сохраняемся еще раз, запускаем hex-редактор, находим место, в котором первое контрольное значение изменилось на второе контрольное значение, пишем туда знаковое целое (что-то типа 0x7FFF) — и наслаждаемся резко упрощенным геймплеем.

Но тут меня ждал облом. Даже два: во-первых, последовательно сохраненные файлы отличались размером. Во-вторых, в той части файла, которая относилась к условному заголовку, соответствующие значения были, но их изменение приводило к тому, что XCOM жаловался на битые файлы: очевидно, в формате предусмотрены какие-то контрольные суммы, которые не совпадают после такого грубого внесения изменений.

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

  1. запустили игру;
  2. прицепились отладчиком;
  3. посмотрели количество целевого ресурса;
  4. нашли в памяти процесса множество адресов с этим значением и сохранили;
  5. поменяли количество целевого ресурса;
  6. нашли в памяти новое множество адресов и пересекли (set intersection) с предыдущим;
  7. повторили пп. 5 и 6 пока не остался один-единственный адрес;
  8. записали в этот адрес желаемое количество целевого ресурса.

Но здесь есть сложности. Первая в том, что в общем случае виртуальная память процесса во всех современных ОС не является непрерывной, а перебирать все пространство возможных адресов — слишком долго. К счастью, в OS X есть замечательный утиль vmmap, который на выходе отдает подробную карту памяти процесса в виде:

% vmmap 15552

shared memory 3afff000-3b000000 [4K] rw- / rw- SM = SHM
MALLOC_SMALL 3b000000-3b800000 [8192K] rw- / rwx SM = PRV DefaultMallocZone_0x4816000
MALLOC_LARGE 3b800000-3b84c000 [304K] rw- / rwx SM = PRV DefaultMallocZone_0x4816000
shared memory 3b84c000-3b854000 [32K] rw- / rw- SM = SHM
IOKit 3b85d000-3b866000 [36K] rw- / rw- SM = ALI
MALLOC_LARGE 3b866000-3b891000 [172K] rw- / rwx SM = PRV DefaultMallocZone_0x4816000
shared memory 3b891000-3b899000 [32K] rw- / rw- SM = SHM
MALLOC_LARGE 3b8a2000-3b8ee000 [304K] rw- / rwx SM = PRV DefaultMallocZone_0x4816000

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

С точки зрения задачи и имеющихся сложностей GDB идеален: он скриптуется. Это означает, что его можно запускать в пакетном режиме и скармливать ему списки команд или из файла или из командной строки.

Вообще, с помощью GDB делаются две вещи: поиск адреса(ов), и запись нужного значения по заданному адресу. Поиск адресов нужного значения делается в GDB командой find (сюрприз!). Для одного из приведенных выше диапазонов и значение 100 команда будет выглядеть так: find / w 0x3b800000, 0x3b84c000 100. Если нужной нам адресу окажется 0x3b800004, то командой записи значения 200 в этот адрес будет: set {int} 0x3b800000 = 200.

В теории все прозрачно. Но уже на попытке присоединиться к процессу у меня возник очередной облом: GDB получал в лоб segfault'ом и падал в корку. Проверил права: id эффективного пользователя для дебаггера и игры совпадают — мои. Попробовал присоединиться к top'у, запущенному в соседнем шелле — получилось только от рута. Присоединиться к процесу XCOM, даже от рута — не получается, segfault…

Дальнейшее разбирательство прояснило две вещи:

  • GDB, который идет в комплекте с XCode, очень старый
  • Система безопасности процессов в OS X на голову выше, чем в Microsoft Windows Vista и Windows 7 не потому, что «оно там внутри UNIX», а «Винда мастдай».

Лирическое отступление: различные вредоносные программы пытаются или вовсе не иметь собственного отдельного процесса, или маскироваться под системный, или прятать свой процесс. Первые два подхода сводятся к возможности «запрыгнуть» в процесс честной программы и дальше творить черные дела от ее доброго имени. Третий обычно требует «запрыгивание» в ядро ​​операционной системы. Так вот, в OS X даже присоединиться к соседнему процессу, запущенному с моими-же правами, у меня получилось только после прочтения статьи, в которой этот процесс подробно расписан. Вкратце, при вызове определенных API ядро сначала убеждается, что код, сделавший этот вызов, подписан надлежащим сертификатом. А потому, для того чтобы наш подход заработал, необходимо:

  1. создать локальный самоподписанный сертификат для подписи кода с максимальным уровнем доверия;
  2. поставить новый GDB;
  3. подписать запускаемый файл gdb свежесозданным сертификатом.

В случае, если-бы мне захотелось уйти от использования gdb и писать в адресное пространство XCOM'а из своего процеса, то мне понадобился-бы дополнительный Info.plist, который нужно вкомпилировать в исполняемый файл. За подробным описанием подхода отправляю к статье, из которой я всю эту механику и подчерпнул. Еще одна годная статья, в которой хорошо прописана механика подписи кода и нарезки прав процессу, лежит прямо на Хабре.

После всех этих танцевальных па GDB наконец смог присоединиться к процессу игры и я смог протестировать поиск вручную. Поскольку данных было очень много (списки даже из нескольких десятков адресов могут сильно утомить, если сравнивать их глазами), то поверх vmmap и GDB была написана обвязка, которая автоматизировала процесс поиска и правки значений в памяти.

До сих пор не уверен, от чего было получено больше удовлетворения: от игры в старый добрый Laser Squad или от процесса превращения в него XCOM.

Автор: zbroyar

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js