Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:for-ever [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== for-ever ======
 +<​sub>​{{for-ever.odt|Original file}}</​sub>​
 +
 +====== вечная жизнь в играх своими руками ======
 +
 +крис касперски ака мыщъх, no-email
 +
 +//​когда человек тратит столько времени и сил на дорогу,​ и наконец доходит,​ он уже не может себе позволить увидеть все таким, как оно есть на самом деле…//​
 +
 +//​Пелевин,​ "​Принц Госплана"//​
 +
 +**получить бессмертие и полный боекомплект практически в любой игре — это реально! потребуется всего лишь ****hex****-редактор и несколько минут свободного времени. мыщъх делится древними алхимическими рецептами,​ дошедшими до нас со временен ****ZX****-****SPECTRUM**** и накопившими огромный потенциал. хвост так и чешется его реализовать!**
 +
 +===== введение =====
 +
 +Что хорошего в вечной жизни? Да ничего в ней хорошего нет, если разобраться! Это же сплошные напряги и тоска смертная. Никакого тебе суицида,​ только бесконечные патроны. И сердце не екнет при случке с монстром,​ появляющимся как раз тогда, когда боезапас на исходе и здоровья нет ни хрена. Взломанная игра теряет свое очарование. Азарт — исчезает,​ а чувство глубокого удовлетворения от прохождения последнего уровня рассеивается как утренний туман. Мыщъх в этом случае говорит:​ "​нельзя все ломать,​ на чем-то нужно и сидеть"​.
 +
 +Но все-таки,​ без взлома никакое хорошее дело не обходится. Игра может содержать баг, делающий ее непроходимой (если в UFO 2 замучить живого таоста,​ то Cydonia/​L'​Tech в ресерче не появится //​**никогда**//​) или просто хочется погонять монстров — снять напряжение после тяжелого дня и не думать ни о здоровье,​ ни о патронах. Наконец,​ взлом интересен сам по себе.
 +
 +Рецепт бессмертия обычно представляет манускрипт со смещением ячейки,​ которую необходимо хакнуть,​ прописав сюда максимальное количество жизней или заменив инструкцию DEC на NOP. Как найти эту магическую позицию во многомегабайтной мешанине кода и данных?​ Некоторые скажут — взять дизассемблер и проанализировать программу,​ но… современные игры так велики,​ что этот проект даже не обсуждается. Пошлем советчиков в жопу и пойдем кратчайшим путем, который только знает мыщъх: **поиск различий между дампами на диске и в памяти**. Во времена MS-DOS существовало множество программ для автоматизации взлома игр (gametools, gamewizard),​ но под Windows ничего подобного так и не появилось!
 +
 +Мыщъх покажет как взламывать игры вручную,​ работая только хвостом и еще головой (голова конечно лучше хвоста,​ но с одной головой жить хреново). Эта техника одинаково хорошо работает как под Windows, так и под UNIX, открывая неограниченные перспективы. Утопите монстров в крови, поджарьте их на медленном огне!
 +
 +{{for-ever_Image_0.jpg?​553}}
 +
 +Рисунок 1 в поисках вечной жизни
 +
 +===== >>>​ врезка взлом игр по-ментовски или убойная сила II =====
 +
 +─ [10] ─ RU.HACKER (2:​5060/​9.18) ─────────────────────────────────── RU.HACKER ─
 +
 + ​Msg ​ : 53 из 560  ​
 +
 + .т : Roman Lesnichenko ​ 2:​465/​666 ​ .он 13 .оя 95 01:49 
 +
 + ​.ому : Eugene Muzychenko  ​
 +
 + Subj : хакеpы ​
 +
 +────────────────────────────────────────────────────────────────────────────────
 +
 +Hello Eugene!
 +
 +Friday November 10 1995 00:42, Eugene Muzychenko wrote to Andrey Fedotov:
 +
 +AF>> Ну чего сложно объяснить как каой-нить гейм на жизни поломать?​
 +
 +AF>> Он же не просит тебя объяснить как банковскую сеть сломать.
 +
 +EM> Блин, откуда это постоянное "​ломать"​ и при чем тут хакеры?​ :-/
 +
 +Вот именно! Хакерство— это не взлом! Это образ жизни! Союз души, головы и хвоста!
 +
 +Я, например,​ впервые сев за ИБМ совместимую тачку ЕС1840 ;) в далеком 89 году, уже
 +
 +через месяц с помощью системного Дебага наклацал тонну всяких BUSHIDO, CATS,
 +
 +ZAXON, PACMAN...etc и хакером себя не считаю,​ хоть и успел нага… натворить ;-)
 +
 +Просто хотел узнать что такое Ассемблер. Вот и все. И не было ни Интрудеров,​
 +
 +ни ГамеТулсов,​ ни АвтоХаков ни других модных импортных штучек :)
 +
 +Меня удивляет,​ что при теперешней ситуации с литературой (можно купить все что
 +
 +хочешь),​ с софтом (практически любые отладчики на выбор и на любителя) люди
 +
 +осмеливаются просить научить их ломать игрушки,​ причем БУДУЧИ В ФИДО!
 +
 +Я это к тому, что все же надеюсь,​ что сюда просто так с улицы не приходят,​
 +
 +и должны иметь хоть какие-то знания.
 +
 +У нас на работе охранник,​ бывший мент, увидевший комп полгода назад,
 +
 +сломал на хрен UFO! Причем без знаний чего либо в компьютерах вообще!
 +
 +Просто ему показали как сравнивать файлы и все! И дали HEX-editor.
 +
 +Купил чего-то,​ записался,​ сравнил файлы, подправил и вперед.
 +
 +И ни кого ни о чем не спрашивал!
 +
 +Сейчас он уже себе комп собрал почти. Собирается программизму учиться.
 +
 +Просто ПРЕТ человека,​ и все этим сказано. И я уверен,​ что он дойдет до всего САМ!
 +
 +Я имею ввиду основные идеи и тенденции.
 +
 +EM> интересно,​ как может "​затянуть"​ описание метода взлома?​ Hy напишу я:
 +
 +EM> "​набери DEBUG, набери там U xxxx:xxxx, найди в тексте строчку,​
 +
 +EM> посмотри,​ что там в квадратных скобках..."​. Как тупое описание
 +
 +EM> без понимания _сути_ может что-то стимулировать?​ стимулировать
 +
 +EM> может только сильное собственное желание,​ помноженное на любовь
 +
 +EM> к анализу...
 +
 +См. выше :-)
 +
 +Roman
 +
 +===== общая тактика и стратегия взлома =====
 +
 +Патроны,​ жизни, артефакты и прочее барахло — все это //​переменные//,​ хранящиеся в определенных //​ячейках//​ и всегда выражаемые //​числом//​. С точки зрения компьютера,​ эти ячейки ничем не отличаются от огромного множества остальных,​ содержащих в себе координаты монстров,​ текстуры и прочие объекты игрового мира. Как установить за что отвечает та или иная ячейка?​ Неэффективность дизассемблирования мы уже отмечали,​ но… что если просто методично изменять одну ячейку за другой,​ наблюдая за реакцией игры, которой скорее всего будет срыв крыши или повисание.
 +
 +Зная точное количество жизней/​патронов,​ мы значительно сужаем круг поиска,​ исследуя только те ячейки,​ которые содержат нужное значение. Однако,​ следует помнить,​ что соответствие может быть как прямым,​ так и обратным. Одни программисты ведут учет жизней,​ другие — смертей,​ причем отсчет может вестись как единицы,​ так и от нуля, а в некоторых случаях и -1. Допустим,​ у нас есть три нерастраченных жизни. Означает ли это, что переменная live_count обязательно будет равна трем? Разумеется,​ нет! В ней вполне может два (и тогда игра закачивается когда live_count < 0), или ноль (игра заканчивается при live_count >​ 2). Возможны и другие значения. С патронами в этом плане все обстоит намного лучше и чаще всего они хранятся в памяти "​как есть",​ однако,​ количество ложных срабатываний все равно будет очень велико! Допустим,​ у нас есть 50 патронов и мы ищем 32h в дампе программы. Да там этих 32h целый миллион! До конца сезона все не переберешь!
 +
 +Ключ к решению лежит в //​изменениях//​! Наблюдая за характером изменения различных ячеек, мы легко отделим зерна от плевел. План наших действий выглядит так:
 +
 +  - снимаем с программы дамп (сохраняем состояние игры в файле);​
 +  - двигаем жопой без потери жизней и патронов,​ после чего снимаем еще один дамп;
 +  - делаем один выстрел (теряем несколько процентов жизни) и получаем очередной дамп;
 +  - повторяем предыдущую операцию несколько раз (необяз 3 дампов обычно достаточно);​
 +Сравнение первого и второго дампов (сейвов) показывает кучу отличий,​ соответствующих передвижениям монстров и прочим изменениям игрового мира. Но ни патронов,​ ни жизней в изменившихся ячейках не оказывается — ведь эти параметры заведомо не менялись! ОК, вычеркиваем изменившиеся ячейки из списка "​подозреваемых"​ лиц, и сравниваем второй дам с третьим,​ игнорируя ранее изменявшиеся ячейки. На этот раз отличий будет не так уж и много. Ищем ячейки,​ дельта изменения которых соответствует количеству выстрелов или потерянных жизней/​здоровья. Если таких ячеек больше одной, повторяем операцию 3 до тех пор, пока не останется только одна изменившаяся ячейка или, как вариант,​ последовательно хачим все подходящие ячейки,​ в надежде,​ что рано или поздно нам повезет.
 +
 +В некоторых играх количество патронов хранится в нескольких переменных,​ дублирующих друг друга, но только одна из них значима,​ а остальные на хакерском жаргоне называются "​тенями",​ отвечающими,​ например,​ за вывод текущего значения на экран. При модификации "​теневой"​ переменной количество патронов/​жизней чаще всего остается неизменным,​ и даже если оно возрастает,​ оружие перестает стрелять задолго до исчерпания своего "​боезапаса",​ а нас все равно убивают. Ну не уроды?​!
 +
 +Сравнивать можно как дампы памяти,​ снятые с работающей программы,​ так и "​сейвы"​ — файлы сохранений. Зная адрес нужной ячейки,​ мы можем повесить резидента,​ прописывающего сюда максимально возможное значение и при необходимости обновляющего его каждые несколько секунд (или чаще). Еще можно запустить soft-ice и, установив точку останова,​ перехватить тот код, который уменьшает количество патронов с каждым выстрелом — тогда мы сможем его хакнуть. Но это потребует дополнительных телодвижений,​ что не всегда удобно,​ поэтому многие хакеры ограничиваются правкой сейвов,​ выдавая игроку полный боезапас и максимум здоровья,​ однако,​ подлинное бессмертие в этом случае уже не достигается — патроны и жизни продолжают убывать и чтобы не умереть,​ их приходится постоянно пополнять. Кроме того, некоторые монстры убивают вас наповал одной ракетой!
 +
 +Маленький ликбез для самых начинающих:​ для хранения переменных используется шесть основных типов данных:​ //​**байт**//,​ //​**слово**//​ и //​**двойное**////​**слово**//,​ которые могут быть как со знаком,​ так и без. Диапазон допустимых значений приведен в таблице 1. Без переделки программы,​ мы не можем присвоить больше игровых единиц,​ чем вмещается в назначенный программистом тип переменной,​ который обычно приходится определять экспериментально.
 +
 +На этом ноте мы заканчиваем с теорией и переходим к практике.
 +
 +|тип|минимальное значение|максимальное значение|
 +|unsigned byte|0|255|
 +|signed byte|-128|127|
 +|unsigned word|0|65.535|
 +|signed word|-32.768|32.767|
 +|unsigned dword|0|4.294.967.295|
 +|signed dword| -2.147.483.648|2.147.483.647|
 +
 +Таблица 1 основные типы данных и соответствующий им диапазон значений
 +
 +{{for-ever_Image_1.png?​553}}
 +
 +Рисунок 2 главная страница open-source проекта DOOM legacy,​ портироващего бессмертную игрушку на win32/linux
 +
 +==== хак в памяти ====
 +
 +Начнем со сравнения дампов памяти. Выберем игру и будем над ней издеваться. Пусть это будет например,​ DOOM Legacy – лучший порт классического DOOM'​а,​ бесплатно распространяемый вместе с исходными текстами и замечательно работающий как под LINUX, так и под win32 (см рис. 2). На момент написания этих строк последней стабильной версий была версия 1.42. Вот прямой линк для скачки:​ http://​prdownloads.sourceforge.net/​doomlegacy/​ legacy142.exe?​download. Для согласования наших действий рекомендуется использовать именно эту версию,​ иначе все смещения уползут черт знает куда, но если вы себя чувствуете достаточно подготовленным мыщъхем — попробуйте потерзать более свежие беты, доступные с основной страницы проекта:​ http://​sourceforge.net/​projects/​doomlegacy.
 +
 +Кроме DOOM Legacy нам так же потребуются wad-файлы из оригинального DOOM/DOOM2 или HERITC'​а. Ну DOOM-то наверняка у каждого найдется! Запускам все это хозяйство и мочим монстров добрые полчаса,​ не в силах оторваться от экрана. Да… ностальгия — великая вещь, а монстры — уроды. Однозначно! Ладно, хрен с ними, давайте лучше ломать.
 +
 +{{for-ever_Image_2.png?​463}}
 +
 +Рисунок 3 это не сон и не эмулятор,​ это полноценный перенос DOOM'​а на win32!
 +
 +Берем любой приличный (а можно и неприличный) дампер,​ например,​ знаменитый PE Tools (http://​www.wasm.ru/​baixado.php?​mode=tool&​id=124) или не менее знаменитый LordPE (http://​www.wasm.ru/​baixado.php?​mode=tool&​id=44),​ находим процесс "​legacy.exe"​ и снимаем с работающей игрушки дамп типа "​full dump",​ обзывая файл "​dump_1.exe"​ или как-то так. (чтобы переключиться из полноэкранного режима необходимо нажать <​Ctrl>​+<​Esc>​). Условимся считать,​ что в этот момент у нас имеется 50 патронов.
 +
 +{{for-ever_Image_3.png?​553}}
 +
 +Рисунок 4 мы,​ хакеры,​ работаем там, где другие отдыхают!
 +
 +Возвращаемся в игру, немного перемещаемся (или просто стоим как идиоты) и дампимся еще раз, создавая файл dump_2.exe. Затем стреляем и сбрасываем dump_3, стреляем еще и получаем dump_4.exe.
 +
 +После всех операций у нас в хвосте оказывается четыре файла: dump_1.exe/​dump_2.exe с 50 патронами и dump_3.exe, dump_3.exe с 49- и 48 патронами соответственно. Теперь мы должны сравнить все четыре файла и найти такие ячейки,​ которые совпадают в dump_1.exe и dump_2.exe, но отличаются у всех остальных. Переменные,​ отвечающие за хранение количества патронов будут где-то среди них.
 +
 +Для решения этой задачи мыщъх написал небольшую утилиту,​ исходный код которой приведен ниже:
 +
 +#define MAX_DUMPS0x10//​ макс. кол-во поддерживаемых дампов
 +
 +main(int c, char **v)
 +
 +{
 +
 +// объявляем переменные
 +
 +int pos=0; int a,b,flag; unsigned char ch[MAX_DUMPS];​ FILE* f[MAX_DUMPS];​
 +
 +
 +
 +// проверяемаргументы
 +
 +if (c-- < 4) return printf("​-err:​ need more files\n"​);​
 +
 +
 +
 +// открываем все файлы
 +
 +for (a=0;​a<​c;​a++)
 +
 +if (!(f[a]=fopen(v[a+1],"​rb"​)))
 +
 +return printf("​-err:​ cant open %s\n",​v[a+1]);​
 +
 +
 +
 +// печатьименфайлов
 +
 +printf("​raw offset"​);​for (a=1;​a<​c;​a++) printf("​\t%s",​v[a+1]);​
 +
 +
 +
 +while(1)
 +
 +{
 +
 +// чтение очередной ячейки из каждого файла
 +
 +for(a=0;​a<​c;​a++) if (!fread(&​ch[a],​1,​1,​f[a])) return 0; pos++;
 +
 +
 +
 +// если ячейки двух первых дампов совпадают они отбрасываются
 +
 +if (ch[0] - ch[1]) continue;
 +
 +
 +
 +// поиск совпадающих ячеек во втором и всех последущих дампах
 +
 +// (такие ячеки отбрасываются как "​левые"​ и на фиг не нужные)
 +
 +for(a=flag=1;​a<​c;​a++)
 +
 +for(b=a;​b<​c;​b++) if ((a-b) && (ch[a]==ch[b])) flag=0; ​
 +
 +
 +
 +// печать "​правильных"​ ячеек
 +
 +if (flag)for (printf("​\n%08Xh:",​pos-1),​a=1;​a<​c;​a++)
 +
 +printf("​\t%02X",​ch[a]);​
 +
 +} printf("​\n"​);​
 +
 +}
 +
 +Листинг 1 простая утилита fck.c для сравнения дампов
 +
 +Компилируем программу любым Си-компилятором (например,​ в случае MicrosoftVisualC++ командная строка будет выглядеть так: "​cl.exe /​Oxfck.c"​) и запускаем свежеиспеченный файл на выполнение:​ "​fck dump_1.exedump_2.exedump_3.exedump_4.exe>​o"​. Полученный результат (перенаправленный в файл "​o"​) должен выглядеть так:
 +
 +raw offsetdumped_2.exedumped_3.exedumped_4.exe
 +
 +000A2FB0h:​B2h2BhB6h
 +
 +000A2FB6h:​C6h00h63h
 +
 +000A2FC0h:​40h00h01h
 +
 +000A2FC2h:​AEh00h46h
 +
 +000A2FC8h:​16h00h65h
 +
 +000A2FCAh:​18h00h56h
 +
 +000A2FCCh:​60h00h65h
 +
 +000A2FCEh:​15h00h72h
 +
 +000A2FD0h:​EEh00h73h
 +
 +000A2FD2h:​EEh00h69h
 +
 +000A2FD4h:​C0h00h6Fh
 +
 +000A2FDCh:​5Ah00h31h
 +
 +000A2FEAh:​DCh00h36h
 +
 +000A2FEDh:​80h00h0Bh
 +
 +000A2FEEh:​2Ah00h15h
 +
 +000A2FF0h:​08h00hEEh
 +
 +000A2FF1h:​1Ch00hF1h
 +
 +000A2FF6h:​59h00h06h
 +
 +000A2FFAh:​02h00h49h
 +
 +000A2FFCh:​FCh00h6Eh
 +
 +000CFBA0h:​FEhFDhFFh
 +
 +00152262h:​A1h60h00h
 +
 +001523E2h:​A1h60h00h
 +
 +**00166574h:​32h31h30h**
 +
 +**001666B4h:​32h31h30h**
 +
 +**00168F28h:​32h31h30h**
 +
 +001772A5h:​65h64hFFh
 +
 +00177538h:​CCh4AhFFh
 +
 +001775ECh:​C7h26hFFh
 +
 +00177A10h:​08h04hFFh
 +
 +00177AC5h:​49h48hFFh
 +
 +001C8724h:​06h0Fh03h
 +
 +Листинг 2 результат сравнения дампов памяти,​ снятых с программы. ячейки,​ предположительно содержащие патроны,​ выделены полужирным шрифтом
 +
 +В глаза сразу же бросается стройная цепочка 32h, 31h, 30h, соответствующая следующим десятичным числам:​ 50, 49, 48. Ага! Оторвать мыщъх'​у хвост если это количество патронов! Эта переменная встречается в дампе трижды по смещениям 00166574h, 001666B4h, 00168F28h. Одна из них настоящая,​ остальные — тени. Как найти нужную?​
 +
 +Для начала переведем "​сырые"​ файловые смещения в виртуальные адреса. Проще всего это сделать в помощью hiew'​а (http://​www.wasm.ru/​baixado.php?​mode=tool&​id=112). Грузим dumped_1.exe,​ давим <F5> (goto), вводим "​сырое"​ смещение 166574 и нажимаем <​enter>​ — hiew тут же показывает в верхней строке соответствующий виртуальный адрес (PE.00568574),​ а значение байта под курсором равно 32h (см. рис. 5),​ значит,​ все правильно!
 +
 +{{for-ever_Image_4.png?​553}}
 +
 +Рисунок 5 перевод сырых смещений в виртуальные адреса в hiew'e
 +
 +Загружаем soft-ice (подопытная игрушка при этом уже должна быть запущена),​ нажимаем <​Ctrl-D>​ и говорим "​addr legacy",​ заставляя отладчик переключиться на контекст нужного процесса (в данном случае:​ legacy.exe – главного исполняемого файла игрушки). Вводим "​wd",​ чтобы появилось окно дампа (если окно уже присутствует на экране,​ то вводить "​wd"​ не надо) и пишем "​g 568574",​ где 568574 — виртуальный адрес предполагаемой ячейки памяти с патронами. Отладчик показывает в дампе содержимое памяти. Команда "​e"​ позволяет его редактировать в интерактивном режиме. Как вариант,​ можно написать "​e 568574 66",​ где 66 – количество патронов в шестнадцатеричном исчислении.
 +
 +{{for-ever_Image_5.png?​552}}
 +
 +Рисунок 6 soft-ice отличное средство для пополнения запаса патронов
 +
 +Захачив предполагаемую ячейку с патронами выходим из отладчика по <​Ctrl-D>​ и смотрим — добавили нам патронов или нет (в некоторых играх изменения отображаются только после следующего выстрела). Ни хрена! Патроны продолжают убывать,​ а враги напирают и долго мы так не продержимся! Сурово!
 +
 +Пробуем вторую ячейку — 1666B4h, лежащую (как утверждает hiew) по виртуальному адресу 5686B4h, но количество патронов по прежнему остается неизменным. А вот на третий раз нам действительно везет и патроны послушно увеличиваются до нужного значения (см. рис. 7). Следовательно,​ искомая переменная это 168F28h с виртуальным адресом 56AF28h. В любой момент мы можем вызывать отладчик,​ набрать "​addr legacy <​enter>​ e 56AF28 FF",​ пополняя запас патронов до максимального значения,​ вот только постоянно лазить в soft-ice слишком напряжно,​ да и не у всех он есть.
 +
 +{{for-ever_Image_6.png?​553}}
 +
 +Рисунок 7 боезапас успешно пополнен!
 +
 +Поступим проще — напишем программу,​ которая будет висеть в фоне и подкидывать нам новые патроны каждые несколько секунд или даже несколько раз в секунду. Вот это действительно "​подарок свыше"​! Исходный код настолько прост, что свободно укладывается в пару строк (остальное – объявление переменных и комментарии):​
 +
 +#define AMMO_ADDR0x56AF28
 +
 +#define AMMO_VALUE66
 +
 +#define AMMO_SIZE1
 +
 +main(int c, char** v)
 +
 +{
 +
 +// объявляем переменные и проверяем аргументы командной строки
 +
 +int x; HANDLE h; unsigned int ammo = AMMO_VALUE; if (c < 2) return -1;
 +
 +
 +
 +// открываемпроцесс
 +
 +if (!(h=OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION ,​0,​atol(v[1]))))
 +
 +return printf("​-err:​open process %d\n",​atol(v[1]));​
 +
 +
 +
 +// несколько раз в секунду пополняем запас патронов
 +
 +// 669 – задержка между обновлениями в миллисекундах
 +
 +while (WriteProcessMemory(h,​ AMMO_ADDR, &ammo, AMMO_SIZE, &​x))Sleep(669);​
 +
 +}
 +
 +Листинг 3 программа авто-патчер add_ammo_clip.c,​ висящая резидентом подкидывающая нам новые патроны
 +
 +Программа принимает идентификатор процесса (PID) в качестве аргумента командной строки,​ который можно определить с помощью "​Диспетчера задач"​ (см. рис. 8) или средствами toolhelp32 по имени исполняемого файла (к статье прилагается программа proc-list.cpp,​ показывающая как это сделать). После завершения игрушки,​ наш автопатчер завершается автоматически. Он так же может быть применен для хака других игрушек — необходимо лишь скорректировать AMMO_ADDR на адрес нужной ячейки,​ AMMO_VALUE – на желаемое значение,​ а AMMO_SIZE на размер переменной.
 +
 +{{for-ever_Image_7.png?​418}}
 +
 +Рисунок 8 определение идентификатора процесса (PID) при помощи диспетчера задач
 +
 +Запускаем нашу утилиту и оттягиваем монстров по полной,​ то есть не по-детски. При быстрой стрельбе патроны слегка убывают,​ но тут же вновь восстанавливаются в исходное значение. Красота! Но держать резидента постоянно загруженным в памяти — не красиво и совсем не по-хакерски.
 +
 +{{for-ever_Image_8.png?​553}}
 +
 +Рисунок 9 с автопатчером количество патронов застывает на отметке 66
 +
 +Настоящие мыщъх'​и правят машинный код программы так, чтобы патроны (жизни,​ здоровье) не убывали. Код, обращающийся к ячейке с патронами,​ можно найти двояко:​ по перекрестным ссылкам и через аппаратную точку останова. Первый способ ориентирован на дизассемблер и вообще говоря не очень надежен,​ второй — на отладчик и срабатывает в 100% если, конечно,​ программа дружит с soft-ice.
 +
 +Начнем с перекрестных ссылок. Загружаем dumped_1.exe в IDA PRO и дождавшись,​ когда дизассемблирование завершится,​ переходим по адресу 56AF28h (<g>, "​0x56AF28"​) и смотрим — удалось ли ей восстановить перекрестные ссылки или нет. Ага! Есть две перекрестные ссылки:​ одна на чтение,​ другая — на запись.
 +
 +.data:​0056AF28 dword_56AF28dd 32h; DATA XREF: .text:​004615AA↑r
 +
 +.data:​0056AF28**;​ .text:​00461711↑w**
 +
 +Листинг 4 IDA PRO нашла две перекрестных ссылки к ячейке с патронами одна из которых на запись (она выделена полужирным)
 +
 +Переходим к той ссылке что на запись (в данном случае это .text:​00461711h)и видим следующий код:
 +
 +.text:​00461707movdword_575BB8,​ 1
 +
 +**.text:​00461711movdword_56AF28[ecx],​ eax**
 +
 +.text:​00461717movdword_575BBC,​ eax
 +
 +Листинг 5 код,​ обращающийся к "​патронной"​ ячейке
 +
 +Ни хрена не понятно что тут, зачем и куда! К тому же ячейка 56AF28h адресуется через регистр ECX, то есть используется в качестве базового указателя. Вероятнее всего, мы имеем дело со структурой,​ описывающей состояние игрока и переменная с количеством патронов — член этой структуры. В этом случае перекрестные ссылки нам не помогают,​ но с другими играми они могут и сработать (кстати говоря,​ если нет IDA, можно воспользоваться hiew'​ом и поискать ссылку на ячейку "​прямым текстом",​ записав ее адрес "​задом наоборот"​ с учетом порядка следования байт на x86, т.е. в данном случае искомая строка будет выглядеть так: "​28 AF 56 00"​).
 +
 +Переходим к точкам останова. Вызываем soft-ice привычным нажатием <​Ctrl-D>​ (игра при этом уже должна быть запущена),​ переключаемся на нужный процесс командой "​addr legacy",​ и ставим бряк на запись памяти:​ "​bpm 56AF28 w",​ где 56AF28 — адрес ячейки с патронами. Если не указывать "​w",​ soft-ice будет всплывать на чтении количества патронов,​ то есть постоянно (под VM Ware после нескольких всплытий виртуальная машина обрушивается. впрочем,​ это явный баг, который разработчики в обозримом будущем должны исправить).
 +
 +После установки аппаратной точки останова на запись,​ soft-ice всплывает при первом же выстреле,​ отображая следующий код (см. рис. 10). Не обращайте внимание на гамму – отладчик не смог восстановить VGA-палитру,​ переопределенную игрушкой и текст едва можно разглядеть.
 +
 +{{for-ever_Image_9.png?​553}}
 +
 +Рисунок 10 точка останова на "​патронную"​ переменную сработала и soft-ice выдал машинную инструкцию,​ уменьшающую количество патронов
 +
 +На всякий случай мыщъх (как порядочный хакер) приводит ключевой фрагмент листинга в "​текстовом режиме",​ поскольку графика на бумаге наверняка будет не видна (издержки дешевой полиграфии,​ это вам не Финляндия,​ это гораздо хуже):
 +
 +00442A91 8D B3 60 01 00 00leaesi, [ebx+160h]
 +
 +00442A97 8B 0C 02movecx, [edx+eax]
 +
 +00442A9A 8B 94 8B C8 00 00 00movedx, [ebx+ecx*4+0C8h]
 +
 +00442AA1 4A__decedx__
 +
 +00442AA2 8D 84 8B C8 00 00 00leaeax, [ebx+ecx*4+0C8h]
 +
 +**00442AA9 89 10mov[eax], edx**
 +
 +__00442AAB 8B 93 9C 00 00 00movedx, [ebx+9Ch]__
 +
 +00442AB1 8B 83 B8 01 00 00moveax, [ebx+1B8h]
 +
 +00442AB7 C1 E2 05shledx, 5
 +
 +00442ABA 8B 7C 02 1Cmovedi, [edx+eax+1Ch]
 +
 +00442ABE 85 FFtestedi, edi
 +
 +00442AC0 74 4Cjzshort loc_442B0E
 +
 +Листинг 6 фрагмент кода игровой программы,​ отвечающий за уменьшение количества патронов (строка на которой всплыл soft-ice подчеркнута,​ инструкция,​ изменяющая значение "​патронной"​ ячейки выделена полужирным)
 +
 +Отладчик всплыл на команде "​442AABh:​mov edx,​[ebx+9Ch]"​ (в вышеприведенном листинге она выделена подчеркиванием). Значит,​ к памяти обратилась предшествующая ей команда "​442AA9h:​mov [eax],​edx",​ выделенная для наглядности полужирным шрифтом. Это архитектура у x86 процессоров такая — генерировать отладочное исключение после выполнения породившей его команды. Ладно, не будем высаживаться,​ лучше попробуем проанализировать код, насколько это возможно в данных условиях.
 +
 +Сначала какое-то значение грузится в регистр EDX, считываясь по довольно сложной адресации [ebx+ecx*4+0C8h],​ затем EDX уменьшается на единицу командой DEC EDX, после чего обновленный EDX записывается в ту же самую ячейку памяти командами lea eax,​[ebx+ecx*4+0C8h]/​mov [eax],​edx. Следовательно,​ ячейка [ebx+ecx*4+0C8h] хранит количество патронов,​ а DEC — и есть та зараза,​ что коварно уменьшает их с каждым выстрелом на единицу (речь, естественно,​ идет про пистолет,​ остальное оружие расстается со своим боезарядом еще быстрее).
 +
 +Замена DEC EDX на NOP заморозит патроны на неограниченный срок, а если приколоться и вместо DEC поставить INC, каждый выстрел будет добавлять нам один патрон. Вот так бонус! Правда,​ при достижении некоторой границы произойдет переполнение и патроны обратятся в нуль. А, может быть, и не обратятся — все зависит от того насколько правильно спроектирована программа.
 +
 +Хакнть DEC можно как в памяти,​ слегка модфицировав наш автопатчер,​ так и на диске — непосредственно в исполняемом файле, если он не упакован каким-нибудь хитрожопным навесным протектором,​ который не так-то просто снять. Наша гейма ничем таким не защищена и патчиться элементарно.
 +
 +Создав резервную копию legacy.exe, загружаем его в hiew, переходим в дизассемблерный режим двойным нажатием ENTER'​а,​ давим <F5> и вводим адрес команды DEC — "​.442AA1"​ (точка указывает редактору,​ что это виртуальный адрес, а не смещение в файле). Нажимаем <F3> для разрешения редактирования и по ENTER'​у заменяем DEC EDX на NOP или INC EDX — это уж кому как больше по вкусу. Сохраняем изменения по <F9> и выходим (при этом необходимо предварительно выйти из игры, т. к. Windows блокирует доступ к исполняющимся в файлам).
 +
 +{{for-ever_Image_10.png?​552}}
 +
 +Рисунок 11 исправление DEC EDX на INC EDX _увеличивает_ патроны с каждым выстрелом
 +
 +Запускаем игру и смотрим — все ли у нас получилось?​ А то! Получилось еще как! Теперь монстров можно отправлять на мясокомбинат. Мяса (и трупов) будет просто гора! Можно даже в соседний лес продавать (все равно мыщъх мяса не ест, ну разве шашлык там или колбасу).
 +
 +Как вариант,​ можно не вводить адрес в hiew'​е а просто поискать значения байтиков,​ равных в данном случае:​89h 10h/8Bh 93h 9Ch 00h 00h 00h/8Bh 83hB8h 01h 00h 00h/C1hE2h 05h. К этому трюку обычно прибегают тогда, когда hiew недоступен,​ а под рукой есть только примитивный hex-редактор типа HexWhorkshop,​ ни хрена не поддерживающий трансляции виртуальных адресов.
 +
 +Аналогичным способом колются патроны к дробовику,​ BFG, количество жизней,​ проценты здоровья и т. д. Хачить игры — совсем не сложно! У опытных мыщъхей на это уходит всего лишь несколько минут. Кстати говоря,​ несмотря на то, что исходные тексты данной игры доступы,​ хануть программу отладчиком намного проще и быстрее,​ чем разбираться с сорцами.
 +
 +===== >>>​ врезка что находится в куче =====
 +
 +Большинство игрушек хранят свои переменные в секции данных,​ которую все дамперы дампят по умолчанию. Некоторые программы используют для этой цели динамическую память,​ называемую кучей (heap), что значительно хуже. Если в снятом дампе ничего похожего на здоровье не обнаружено,​ необходимо сдампить не только образ файла (image), но еще и кучу.
 +
 +В LordPE это делается так: в контекстом меню выбирается пункт "​dump region"​ и в открывшемся списке регионов выделяются все блоки типа COMMIT/​PRIVATE с атрибутами доступа на чтение и запись. Иногда переменные находится в самом большом блоке, иногда —нет. Короче,​ тут без эксперимента ничего конкретного не скажешь!
 +
 +{{for-ever_Image_11.png?​496}}
 +
 +Рисунок 12 кладем кучу в дамп
 +
 +==== хак на диске ====
 +
 +Модификация игр в памяти — мощная штука, но все-таки не свободная от ограничений. Программы,​ защищенные различными протекторами (типа star-force) активно сопротивляются снятию дампа, а под linux нормальных дамперов вообще нет! В этих (и многих других) случаях приходится прибегать к альтернативному методу — правке файлов состояния игры ("​сейвов"​).
 +
 +Тактическая стратегия выглядит как обычно:​ сохраняется в saved_1, перемещаемся без потери здоровья (патронов) и сохраняется в saved_2, затем стреляем один раз (тратим несколько процентов здоровья) и сохраняемся в saved_3. Сравниваем полученные файлы нашей утилитой fck.exe и смотрим различия. Выбрав наиболее вероятных "​кандидатов",​ правим их в hex-редакторе,​ загружаем исправленный файл в игру и, если патроны/​здоровье не изменились,​ правим следующий байт и т. д.
 +
 +Вернемся к DOOM'​у. Подготавливаем три сейва (doomsav0.dsg,​ doomsav1.dsg и doomsav2.dsg) и сравниваем их друг с другом. Опс! Все они имеют разный размер:​ 2440, 2586 и 2650 байт. Плохо дело! Судя по всему, сэйв имеет сложную структуру и простое побайтовое сравнение скорее всего ничего не даст, поскольку ячейки,​ отвечающие за хранение патронов (здоровье) окажутся расположенными по различными смещениям.
 +
 +Расшифровка структуры сэйв файлов — сложное,​ но очень увлекательное дело, "​заразившее"​ множество светлых умов. Основным оружием становится интуиция и нечеткий "​скользящий"​ поиск — мы ищем совпадающие (или просто похожие) фрагменты и корректируем смещения с привязкой к ним.
 +
 +В частности,​ doomsavX.dsg имеет следующую структуру:​ сначала идет заголовок,​ содержащий имя сэйва, версию игры и прочую лабуду.
 +
 +0000000000: ​ 32 00 F4 77 00 00 00 00 │ 00 00 00 00 00 00 00 00  2 Їw
 +
 +0000000010: ​ 2B 7E 9C F7 00 00 00 00 │ 76 65 72 73 69 6F 6E 20  +~Ьў version
 +
 +0000000020: ​ 31 34 32 00 00 00 00 00 │ 61 66 33 32 00 52 37 59  142  af32 R7Y
 +
 +0000000030: ​ 65 73 00 B3 19 31 00 8F │ 29 32 30 00 A8 43 4F 66  es │↓1 П)20 иCOf
 +
 +0000000040: ​ 66 00 BE 9F 4E 6F 00 6E │ 77 59 65 73 00 C8 37 4E  f ╛ЯNo nwYes ╚7N
 +
 +Листинг 7 заголовок сэйва
 +
 +За заголовком расположен какое-то бессистемный блок, всегда начинающийся со смещения 100h:
 +
 +0000000100: ​ 31 34 35 37 36 33 32 38 │ 0D 00 C8 00 02 00 64 00  14576328♪ ╚ ☻ d
 +
 +0000000110: ​ 00 00 90 01 28 00 90 01 │ 02 00 28 00 00 00 2C 01  Р☺( Р☺☻ (  ,☺
 +
 +0000000120: ​ 64 00 00 00 07 00 00 00 │ 03 00 00 00 00 DC 05 01  d  •  ♥  ▄♣☺
 +
 +0000000130: ​ 00 00 00 04 03 01 00 4C │ 00 20 00 00 00 00 00 00  ♦♥☺ L
 +
 +Листинг 8 "​бессистемный"​ блок
 +
 +К бессистемному блоку примыкает характерная структура с косыми потоками рожиц. Ее смещение и размер различны и варьируются в очень широких пределах. Мы еще не знаем, за что она отвечает,​ но что бы здесь ни располагалось,​ вести сравнение нужно не с начала файла, а с начала этой самой структуры:​
 +
 +0000000140: ​ FF 00 00 01 01 01 01 00 │ 01 01 01 08 00 01 1C 01     ☺☺☺☺ ☺☺☺◘ ☺∟☺
 +
 +0000000150: ​ 0A 00 01 01 01 0B 00 01 │ 01 01 0C 00 01 01 01 10  ◙ ☺☺☺♂ ☺☺☺♀ ☺☺☺►
 +
 +0000000160: ​ 00 01 01 01 11 00 01 01 │ 01 12 00 01 01 01 13 00  ☺☺☺◄ ☺☺☺↕ ☺☺☺‼
 +
 +0000000170: ​ 01 01 01 14 00 01 01 01 │ 15 00 01 4C 01 16 00 01  ☺☺☺¶ ☺☺☺§ ☺L☺■ ☺
 +
 +Листинг 9 косые потоки рожиц — что они значат?​
 +
 +А как определить это самое начало?​ Очень просто — точно так, как астрономы определяют переменные и вспыхивающие звезды! Звездное небо не остается постоянным и некоторые звезды изменяют свой блеск (иногда — значительно). Но как найти их среди тысяч других?​ Очень просто — проецируем один звездный снимок на стену, затем удаляем его из проектора и вставляем другой,​ снятый чуть позже, добавясь,​ чтобы звезды располагались на тех же самых местах,​ а теперь быстро-быстро меняем снимки один за другим. Переменные звезды начинают характерно мерцать!
 +
 +Используем эту технику для поиска отличающихся байт! Нам потребуется только наш верный хвост и FAR. Подгоняем курсор к doomsav0.dsg и давим <F3> (view), а затем <F4> (hex-mode). Нажимаем <+> для перехода к следующему файлу (doomsav1.dsg),​ а затем <-> для возвращения к предыдущему. Повторяем эму операцию несколько раз, убеждаясь,​ что косые потоки рожиц смещаются на значительные расстояния,​ поскольку начинаются с разных смещений. Нажимая <​Alt-8>​ (goto) изменяем стартовое смещение файла doomsav1.dsg так, чтобы рожицы перестали прыгать. Словно мы крутим "​синхронизацию"​ на осциллографе или накладываем отпечатки пальцев друг на друга, добиваясь наибольшего совпадения. В моем случае,​ разница в базовом смещении "​рожиц"​ составила 7 байт. То есть, чтобы они засинхронизовались один файл необходимо просматривать начиная со смещения F0h, другой — F7h. ОК! Рожицы совпадают и никаких различий между ними не обнаруживается! За что же они отвечают?​
 +
 +{{for-ever_Image_12.png?​553}}
 +
 +Рисунок 13 синхронизация "​рожиц"​ в FAR'е
 +
 +{{for-ever_Image_13.png?​553}}
 +
 +Рисунок 14 быстро переключаясь между файлами по <​+>/<​->​ ищем изменившиеся ячейки
 +
 +Возвращаемся в игру и, //**не сходя со своего места**//,​ убиваем одного монстра. Сохраняемся. Ага! Различий по прежнему нет. Значит,​ "​рожицы"​ отвечают не за трупы. Обращаем внимание,​ что по мере прохождения игры рожиц становится все больше и больше. Так, может быть, это и есть картографирование?​ Проверяем свою гипотезу и действительно — стоит нам войти в новый сектор,​ как сразу же добавляется новая порция рожиц. Интересно:​ хранят ли они только карту или еще и состояния дверей?​ Это легко выяснить экспериментально!
 +
 +За рожицами начинается соврем другая структура данных,​ в которой на первый взгляд нет никакой закономерности и которая чудовищно изменяется между двумя соседними сохранениями. Логично предположить,​ что здесь сосредоточена святая святых — описание объектов игрового мира. Но как во всем этом разобраться?​
 +
 +0000000740: ​ 00 50 05 00 00 30 0E 40 │ **FF FF** BF 01 00 00 00 00  P♣  0♫@  ┐☺
 +
 +0000000750: ​ 03 26 00 00 68 2E F1 00 │ 00 00 08 00 00 00 08 00  ♥& ​ h.ё  ◘  ◘
 +
 +0000000760: ​ 09 00 00 00 30 07 00 00 │ 30 0E 40 **FF FF** BF 38 03  ○  0•  0♫@  ┐8♥
 +
 +0000000770: ​ 04 00 00 00 01 00 00 00 │ 00 03 20 00 00 34 2F F1  ♦  ☺  ♥  4/ё
 +
 +0000000780: ​ 00 00 00 08 00 00 00 08 │ 00 0A 00 00 00 E0 06 00  ◘  ◘ ◙  р♠
 +
 +0000000790: ​ 00 50 0D 40 **FF FF** BF 01 │ 00 00 00 00 03 24 04 00  P♪@  ┐☺ ​ ♥$♦
 +
 +00000007A0: ​ 00 30 F1 00 00 00 70 00 │ 00 00 70 00 0B 00 00 00  0ё  p  p ♂
 +
 +Листинг 10 фрагмент структуры,​ описывающей состояние игрового мира
 +
 +При внимательном осмотре дампа мы обнаружим,​ что константа FFFFh встречается намного чаще, чем остальные. Это ключ к понимаю структуры файла, но… где тот замок, куда его вставить?​ Смотрим:​ константы расположены на различном расстоянии друг от друга, значит мы имеем дело со структурой переменного размера или со списком,​ завершаемым "​термирующим"​ символом FFFFh. Если эта структура,​ то где-то должен храниться ее размер,​ выражаемый в байтах,​ словах или двойных словах. Как найти его в дампе?
 +
 +Возьмем две ближайших константы,​ расположенные по смещению 748h и 76Bh. Как нетрудно подсчитать,​ их разделяет 23h байта. Следовательно,​ размер структуры не может выражаться ни словами,​ не двойными словами (23h не кратно двум), а только байтам. Ищем число 23h в окрестности наших констант. Его нет! Поэтому,​ можно предположить,​ что FFFFh используется в качестве тремирующего символа,​ то есть служит знаком конца списка. Остается только написать программу,​ отображающую содержимое списков в удобночитемом виде — тогда искать различия будет намного проще. Однако,​ это довольно сложная задача,​ решение которой требует уймы времени и терпения. Зато потом мы сможем "​убивать"​ любых монстров или добавлять новых, подкладывать аптечки и другие артефакты,​ словом,​ творить чудеса,​ но это будет потом.
 +
 +Сейчас же мы ограничимся тем, что пополним запас патронов,​ здоровья и брони, а так же дадим герою все оружие из которого мыщъх предпочитает совсем не BFG, а обыкновенный дробовик,​ причем одностволку!
 +
 +Вместо сравнения сэйвов,​ мы используем альтернативный подход,​ называемый "​прямым константным поиском"​. Допустим,​ у нас сложилась следующая ситуация:​ здоровье — 68%, броня — 95%, пистолетных патронов — 73 (200 max), патронов для дробовика — 24 (50 max). Переводим 68 и 95 в шестнадцатеричную систему исчисления,​ получаем 44h и 5Fh соответственно. Отгружаем игру в doomsav.dsg и загружаем этот файл в hiew. Давим <F7> (search) и ищем 44h где оно есть (//​**внимание!**////​ при поиске чисел > 255 необходимо помнить,​ что младший байт располагается по меньшему адресу и потому ищется в обратном порядке,​ то есть для поиска 1234////​h////​ в ////​hiew////'​е необходимо ввести 34 12//).
 +
 +Ячейка с искомым значением тут же обнаруживается по смещению B4h. Может это и не здоровье,​ но… рядом с ней лежит 5Fh, а это, как мы помним,​ наша броня:
 +
 +000000B0: ​ 00 00 00 00-**__44 00__**** 5F 00**-01 00 01 0A-00 00 00 00  D _ ☺ ☺◙
 +
 +000000C0: ​ 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
 +
 +000000D0: ​ 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
 +
 +000000E0: ​ 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
 +
 +000000F0: ​ 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 30  0
 +
 +Листинг 11 фрагмент сэйв-файла,​ с ячейками хранящими здоровье (подчеркнуто) и броню (выделено полужирным)
 +
 +Исправляем оба числа на FFFFh и загружаем исправленный сэйв в игру. Держите мой хвост! У нас получилось!!!!
 +
 +{{for-ever_Image_14.png?​553}}
 +
 +Рисунок 15 c такой броней и здоровьем никакие враги не страшны!
 +
 +Теперь разберемся с оружием и патронами. Добавить патроны — несложно,​ но прежде необходимо заполучить оружие. Обычно оно хранится в игре в переменных-флагов:​ одно значение соответствует состоянию "​оружие есть",​ другое — "​оружия нет"​. Флаги бывают битовыми,​ байтовыми,​ словными и двухсловными. Битовый флаг хранит все оружие в одной ячейке. Например,​ 01h (0000000001b) означает пистолет,​ 02h (0000000010b) – дробовик,​ а 03h (0000000011b) — пистолет с винчестером. Ладно, завязываем с теорией и переходим к активным действиям.
 +
 +{{for-ever_Image_15.png?​553}}
 +
 +Рисунок 16 бензопила — не самое мощное оружие,​ но чуть позже с ее помощью мы завладеем BFG
 +
 +Возвращаемся в DOOM. На самый первый уровень. Выходим на балкон и видим пилу. В смысле бензопилу. Сохраняемся,​ затем не совершая никаких движений сохраняемся еще. Берем пилу и сохраняемся в третий раз (см. рис. 16). Мы получаем следующие файлы: doomsav0.dsg,​ doomsav1.dsg и doomsav2.dsg. Натравливаем на них fck.exe и видим результат:​
 +
 +raw offsetdoomsav1.dsgdoomsav2.dsg
 +
 +00000008h:​CEh35h
 +
 +00000009h:​DDhCFh
 +
 +0000000Ah:​1Ch22h
 +
 +0000000Ch:​E8h15h
 +
 +0000000Dh:​2ChB1h
 +
 +0000000Eh:​D9h98h
 +
 +00000014h:​51h54h
 +
 +000000BAh:​01h07h
 +
 +00000121h:​0Bh44h
 +
 +00000123h:​01h02h
 +
 +00000127h:​00h47h
 +
 +00000128h:​00hFDh
 +
 +00000129h:​01h00h
 +
 +0000012Bh:​00h20h
 +
 +0000012Ch:​00h01h
 +
 +00000135h:​00h47h
 +
 +00000136h:​00hFDh
 +
 +00000137h:​01h00h
 +
 +00000139h:​00h20h
 +
 +0000013Ah:​00h01h
 +
 +**__0000013Dh:​03h83h__**
 +
 +0000028Ah:​24h26h
 +
 +000002A9h:​00h08h
 +
 +000002ABh:​01h00h
 +
 +Листинг 12 до и после взятия бензопилы ("​подозреваемая"​ ячейка выделена полужирным)
 +
 +Да тут до фига изменений! Даже не сообразишь с чего начинать… Будем рассуждать логически. Если оружие храниться с байтовом флаге, то он скачкообразно поменяет свое значение с FALSE (ноль) на TRUE (обычно 1 или FFh) или наоборот. Если это битовый флаг, то после захвата бензопилы,​ нужная нам переменная будет отличаться всего одним битом.
 +
 +Начнем с ячейки 12Ch – она поменяла свое значение с 00h на 01h. Допустим,​ этот флаг хранит бензопилу. Тогда логично предположить,​ что остальное оружие хранится где-то рядом (хотя это бывает и не всегда),​ однако,​ соседние с не ячейки меняют свое значение в сумбурно-хаотичном порядке и на флаги не тянут.
 +
 +А вот ячейка 13Dh очень даже похожа! Смотрите — сначала в ней было 03h (00000011b),​ а после взятия бензопилы — 83h (10000011b). Скорее всего, первый справа бит хранит оружие типа "​кастет",​ второй — пистолет,​ а последний — пилу. Чтобы проверить наше предположение необходимо добыть еще одно оружие,​ например,​ дробовик и посмотреть.
 +
 +raw offsetdoomsav1.dsgdoomsav2.dsg
 +
 +00000000h:​32h33h
 +
 +000000A8h:​05h06h
 +
 +000000BAh:​01h02h
 +
 +0000010Ch:​00h10h
 +
 +00000129h:​0Bh13h
 +
 +**00000145h:​83h87h**
 +
 +00000642h:​00h01h
 +
 +0000064Ch:​00h34h
 +
 +Листинг 13 до и после взятия дробовика ("​подозреваемая"​ ячейка выделена полужирным)
 +
 +Вот черт! Ячейка по смещению 13Dh ушла в туман, но зато ячейка по смещению 145h изменилась с 83h (10000011b) на 87h (10000111b),​ то есть после взятия дробовика добавился третий бит. Это явно она! Именно здесь храниться все оружие,​ которое добыл игрок. Вот только ее смещение непостоянно и в разных файлах оно сильно неодинаковое… но мы-то знаем, что в doomsav1.dsg/​doomsav2.dsg оно гарантировано равно 145h! Загружаем doomsav2.dsg в hiew и меняем 87h на FFh (все оружие).
 +
 +Открываем исправленный сэйв и смотрим что из оружие у нас есть. У нас есть все! Вот только патронов не хватает,​ а что толку от оружия,​ если оно не стреляет?​ Приходится хачить!
 +
 +Как мы помним,​ у нас есть 73/200 пистолетных патрона и 24/50 патронов для дробовика. Переводим эти числа в hex-форму — 49h/C8h, 18h/32h и смотрим где они в дампе:
 +
 +00000100: ​ 31 34 35 37-36 33 32 38-**49 00****__C__****__8 00__**-**18 00****__32 00__** 14576328; E ► 2
 +
 +00000110: ​ 00 00 __2C 01__-00 00 __32 00__-00 00 00 00-00 00 00 00  ,☺  2
 +
 +00000120: ​ 05 00 00 00-02 00 00 00-00 13 00 01-00 00 00 00  ♣  ☻  ‼ ☺
 +
 +00000130: ​ 00 01 00 00-00 20 00 00-00 00 00 00-00 00 00 01  ☺  ☺
 +
 +Листинг 14 ячейки,​ ответственные за хранение пистолетных патронов (текущее/​максимальное количество) и зарядов для дробовика
 +
 +Ага! Лежат рядышком! А следом за ними идет 00h 00h 2Ch 01h. Ничего не напоминает?​ С учетом обратного порядка байт 2Ch 01h равно 12Ch или 300 в десятичном исчислении — максимально возможное количество зарядов плазмогана/​BFG на данном уровне. А два нуля — это количество самих зарядов. Тогда последовательности 00h 00h 32h 00h ничего не остается как отвечать за число ракет (00 ракет в наличии,​ 50 — максимум). Меняем все эти байты на FFh и получаем максимальный боезапас:​
 +
 +00000100: ​ 31 34 35 37-36 33 32 38-**FF FF FF FF-FF FF FF FF** 14576328yyyyyyyy
 +
 +00000110: ​ **FF FF FF FF-FF FF FF FF**-00 00 00 00-00 00 00 00  yyyyyyyy
 +
 +00000120: ​ 05 00 00 00-02 00 00 00-00 13 00 01-00 00 00 00  ♣  ☻  ‼ ☺
 +
 +00000130: ​ 00 01 00 00-00 20 00 00-00 00 00 00-00 00 00 01  ☺  ☺
 +
 +Листинг 15 фрагмент исправленного сэйва, дающий все оружие с кучей патронов
 +
 +Загружаем исправленный сэйв в DOOM, нажимаем <7> для выбора BFG и встаем на тропу войны.
 +
 +{{for-ever_Image_16.png?​553}}
 +
 +Рисунок 17 мыщъх на тропе войны
 +
 +Остальные игрушки ломаются схожим образом. Вот, например,​ UFO, которое кладет все сейвы в каталоги GAME_X, причем кладет их очень много! Почти полсотни файлов! К счастью,​ утилита fc.exe из штатной поставки Windows, поддерживает джокеры и позволяет сравнивать множество файлов сразу: "​fc.exe /bGAME_X\* GAME_Y\* > log", избавляя нас от необходимости делать это вручную. Утилита fck.exe джокеров не поддерживает (кто возьмется ее доработать?​),​ но можно поступить проще, воспользовавшись простым пакетным файлов следующего содержания:​
 +
 +for %%A IN (*) DO fck.exe game_1\%%A game_2\%%A game_3\%%A >> log
 +
 +Листинг 16 командный файл, позволяющей утилите fck обрабатывать множество файлов
 +
 +Сравнение сэйвов показывает,​ что при добавлении новых исследований в проект,​ один или несколько байтов в research.dat устанавливаются в единицу. Наблюдая за изменениями легко установить,​ что файл имеет блочную структуру и размер блока равен 16h байтам,​ а флаг, "​разрешающий"​ исследовательский проект находится по смещению 08h от начала блока.
 +
 +Приведенная ниже программа открывает все исследования,​ в том числе и Cydonia/​L'​Tech без которого игра не может закончится,​ зачастую становясь непроходимой.
 +
 +#define MAX_BUF_SIZE(10*1024)
 +
 +main(int x, char **argv)
 +
 +{
 +
 +int a,t,c, x=0; FILE *fsave, *fback; unsigned char gamebuf[MAX_BUF_SIZE];​
 +
 +unsigned char fn_save[MAX_BUF_SIZE];​ unsigned char fn_back[MAX_BUF_SIZE];​
 +
 +
 +
 +#define BLOCK_SIZE0x16
 +
 +#define off_ENABLE_RESEARCH8
 +
 +
 +
 +#define FN_SAVE "​\\research.dat"​
 +
 +define FN_BACK "​\\research.da_"​
 +
 +
 +
 +if (x<​2)return printf("​USAGE:​ ufo1-2AlienContainmentBugFix.exe path_to_save\n"​\
 +
 +"​example:​ ufo1-2AlienContainmentBugFix.exe GAME_1\n"​);​
 +
 +
 +
 +printf("​* * * ENABLING Cydonia or L'Tech City in RESEARCH LIST * * *\n");
 +
 +
 +
 +strcpy(fn_save,​ argv[1]); strcat(fn_save,​ FN_SAVE);
 +
 +strcpy(fn_back,​ argv[1]); strcat(fn_back,​ FN_BACK);
 +
 +
 +
 +// делаем резервную копию
 +
 +fsave = fopen(fn_save,"​rb"​);​ c = fread(gamebuf,​ 1, MAX_BUF_SIZE,​ fsave);
 +
 +fback = fopen(fn_back,​ "​wb"​);​fwrite(gamebuf,​1,​c,​ fback);
 +
 +
 +
 +// добавляем Cydonia/​L'​Tech в исследовательский проект
 +
 +for (a = 0; a < c; a += BLOCK_SIZE) gamebuf[a + off_ENABLE_RESEARCH] = 1;
 +
 +
 +
 +// сохраняем изменения в файле
 +
 +fclose(fsave);​ fsave = fopen(fn_save,"​wb"​);​ fwrite(gamebuf,​ 1, c, fsave);
 +
 +
 +
 +printf("​============================================================\n"​\
 +
 + "​now see BASES > RESEARCH > Add New Project > Cydonia/​L'​Tech\n"​);​
 +
 +}
 +
 +Листинг 17 программа,​ дающая Cydonia/​L'​Tech в UFO-2
 +
 +===== >>>​ врезка баг в UFO и его последствия =====
 +
 +─ SU.GAME.STRATEGY (2:​5060/​8.13) ──────────────────────────── SU.GAME.STRATEGY ─
 +
 + ​Msg ​ : 5 of 694  ​
 +
 + From : Alexander Volkov ​ 2:​5020/​168.7 ​ Срд 04 Июн 97 11:59 
 +
 + ​To ​ : Kostya Shapovalov ​ Пнд 16 Июн 97 14:58 
 +
 + Subj : UFO II  ​
 +
 +────────────────────────────────────────────────────────────────────────────────
 +
 +Hello Kostya!
 +
 +Thursday May 22 1997 09:06, Kostya Shapovalov wrote to All:
 +
 + ​KS>​ Левиафан чертов!!!!!!! Не хочет изобретаться,​ хоть убей! Я уже две базы
 +
 + ​KS>​ загpохал,​ брал Commander'​ов в рюкзаки и уматывал - не появляются!
 +
 +В смысле берешь в рюкзак живого лобстеp-командеpа,​ эвакуируешься с колонии
 +
 +- а командеp на твоей базе не появляется?​ странно...
 +
 + ​KS>​ Alien Contaiment y меня пустая (продал и от'​transfer'​ил всех).
 +
 + ​KS>​ А без Левиафана T'Leth не проявляется,​ сволочь!
 +
 + ​KS>​ Может, расу определенную надо?!
 +
 +Лобстеp-командеpа.
 +
 +В непатченной,​ правда,​ был глюк - если сначала замучать тасот-командеpа,​ то
 +
 +левиафана в pесече не будет никогда.
 +
 +===== >>>​ врезка насколько законно патчить игры =====
 +
 +Мыщъху часто приходится слышать,​ что модификация игр незаконна и хачить можно только сэйвы. В корне неверное утверждение! По российскому законодательству,​ на своем компьютере с программой можно делать все, что угодно,​ в том числе и заменять DEC на NOP. А вот распространять хакнутый файл без согласия правообладателя — нельзя,​ что логично и правильно.
 +
 +===== заключение =====
 +
 +Форматы файлов-сэйвов многих игрушек уже давно исследованы и могут быть найдены в сети. На их основе создаются хакерские редакторы и прочие читерские средства,​ имеющие хождение в народе. За ними стоит огромный объем исследовательской работы и далеко не всегда тривиального сравнения оказывается достаточно. Без помощи отладчика и дизассемблера здесь не обойтись! Все правильно:​ геймеры — играют,​ хакеры — ковыряются в двоичных файлах,​ отлаживают,​ дизассемблируют,​ в общем хакерстуют.
 +
 +Приобретенные навыки оказываются очень полезны при расшифровке сетевых протоколов и реконструкции недокументированных форматов файлов и файловых систем. Квалифицированных специалистов единицы на них всегда присутствует устойчивый спрос, так что бессмертие в играх — это совсем не забава! Это очень и очень серьезно! Многие выдающиеся хакеры начинали именно с бессмертия. Осваивали hex-редактор,​ терзали отладчик,​ понемногу изучали ассемблер и вот так постепенно шаг за шагом двигались к тому кем они стали сейчас.
 +
 +В общем, вы меня поняли. Вливайтесь!
 +
 +
 +
 +{{for-ever_Image_17.png?​553}}
 +
 +Рисунок 18
 +
 +