Типично ДОС выделяет стартуемой задаче всю доступную оперативную память.
По выгрузке завершившейся задачи ДОС освобождает прежде занимаемую ей память.
Однако некоторая часть памяти постоянно занята ядром ДОСа и программами сервиса ввода/вывода.
Для поддержки их неприкосновенности ДОС имеет средства рапределения памяти и способы
оставить память неприкосновенной после выполнения задачи. В этом случае программа,
завершаеясь специальным образом, оставляет указанную ею область памяти помеченной как
используемая. Эта область памяти урезается из общедоступного объема памяти и в дальнейшем
ДОС не использует ее. Поскольку в ДОС принята линейная модель памяти, средства свопирования
или переноса кода не предусмотрены, и программы занимают память от нижней границы доступного
пространтства. Таким образом, прочие программы просто начинают грузиться выше границы
занятого устойчивыми модулями пространства. Для корректной обработки памяти и файлов ДОС держит
специальные системные таблицы, из которых для креков интерес представляют MCB (memory
control block) и PSP (program segment prefix). MCB создается ДОСом для каждого запроса выделения
памяти (при старте программы ДОС сам производит такой запрос), и удаляется при запросе
освободить память (опять же ДОС сам вызывает запрос освободить память по завершенни задачи).
В случае завершения программы специальным образом TSR (terminate & stay resident) запрос
освобождения не производится, и созданный
MCB остается в памяти. MCB организованы таким образом, что каждый блок имеет в специальном поле
ссылку на следующий блок или же признак, что он последний в цепочке. Это позволяет просмотреть
всю цепочку блоков, и узнать, чем занята память ДОС в данный момент.
PSP создается для загружаемой задачи и собержит некоторую полезную как для нее, так и для ДОСа
информацию, типа указателя на командную строку, переданную программе. Имеет длину 256 байт
и предшествует любой программе. При старте программы на него указывает DS:0000.
Структуру таблиц смотрите в "таблицы вспомогательные"
.
2. Система прерываний. Перехват перываний.
Система прерываний компьютеров IBM PC представляет из себя программное средсво
переключения контекста задачи для вызова обслуживающих подпрограмм на фоне работы
задачи, так, что она этого и не замечает. Прерывания бывают аппаратные и программные. Аппаратные
возникают вследствие запросов оборудования на обслуживание процессором. Например, прерывание
IRQ 1 возбуждается клавиатурой при нажатии/отпускании клавиши. Для арбитража аппаратных прерываний,
если их случилось сразу несколько, запросы от оборудования проходят через контроллер прерываний,
который запрограммирован на определенный приоритет (порядок) их выполнения. Контроллер
также может совсем отменять реагирование на некотрорые из них (маскирование). Аппаратные
прерывания IRQ преводятся в программные INT (смари таблицы), и таким образом, обрабатываются
неотличимо от программных. Программные прерывания инициируюся выполняемой инструкцией процессора
INT X , где Х - номер прерывания. Этот номер указывает на строку таблицы векторов прерываний,
хранящуюся в первом К памяти. Каждая строка таблицы содержит два слова: сегмент и смещение, и имеет,
таким образом, длину в 4 байта. Сегмент и смещение указывают на точку входа подпрограммы, обслуживающей
данное прерывание. Таким образом, для получения адреса в таблице (вектора) процессор просто умножает
номер прерывания на 4 (сдвигая его на 2 бита влево).
Обработка происходит следующим образом: получив инструкцию INT X, или сигнал от контроллера прерываний,
процессор сохраняет в стеке флаги, текуший сегмент кода CS и указатель IP, читает в таблице
прерываний по адресу Х * 4 сегмент и смещение обработчика, загружает их в CS и IP и выполняет
код обработчика. Обработчик заканчивается инструкцией IRET, по которой процессор достает
из стека старые IP, CS и флаги и, зангузив их, возвращается к выполнению преравнной задачи.
Первые 32 прерывания зарезервированы для обслуживания оборудования, а остальные используются
программами на свое усмотрение. При загрузке компьютера BIOS инициализирет таблицу прерываний
таким образом, что вектора обслуживания оборудования указывают на процедуры в BIOS. При этом некоторые
запросы не обслуживаются, и их вектора указывают на заглушку (инструкция IRET), например,
прерывание таймера 1Сh. Программы могут установить свой обработчик того или иного прерывания,
заменив имеющийся ветор в таблице на свой, указывающий на свою процедуру обработки.
При этом возможны 3 варианта:
Сначала наш обработчик, потом возвращаем прежний
Сначала прежний обработчик, потом наш.
Только наша обработка.
process:
;...
;...
;...
jmp old_int
process:
pushf
call old_int
;...
;...
;...
iret
process:
;...
;...
;...
iret
Если написать программу, резидентно оставляющую в памяти участок кода с обработчиком, то
такой обработчик прерывания будет действовать и после завершения установившей его программы.
Установка резидентной части программы наглядно показана в примерах, помещенных далее.
Основная стратегия установки/перехвата: прочитать вектор перехватывемого прерывания и
сохранить в переменной для будущего использования (old_int), прописать вектор, указывающий
на наш обработчик, завершиться, оставив резидентную часть (обработчик).
Замечание: контроллер прерываний, обрабатывающий аппаратные прерывания, должен
знать, что прерывание принято и пущено в обработку, для правильной работы своей схемы
приоритетов. Поэтому, когда вы обрабатываете аппаратные прерывания, следует послать
в контроллер сигнал подтвержения, типа такого:
mov ah,20h
out 20h,ah
- в противном случае обработка этого прерывания будет нарушена. Если же вы передаете управление
стандартному обработчику, то он сам выдаст такое подвердение, и вам оно не нужно.
3. Съем дампов памяти. Анализ результатов.
Для крекования игрулек или защищенных программ наиболее часто используется
способ заненсения в определенную ячейку памяти желаемого значения. Например, это может быть
количество жизней или признак введенности правильного пароля. Как говорилось выше, ДОС отводит
стартующим программам всю доступную на данный момент память. В ней и размещается код
программы и данные, которые она может загружать вместе с кодом или подкачивать с диска.
В каком-то месте памяти хранятся и интересующие крекера значения. Для обнаружения алресов
этих ячеек используется метод съема дампов памяти в характерные моменты, когда хранимое значение
изменяется, и последующего сравнения дампов между собой с целью отыскать места этих различий.
Например, вы снимаете дампы памяти в моменты, когда у вас было 4, 3, 2 и 1 жизни. Запустив
программу - компаратор, вы получаете файл, содержащий все различия межу этими дампами.
Среди них надо отыскать значения, похожие на целевые. Например, вы находите строку:
XXXX:XXXX 4 3 2 1
Это напоминает количество жизней в момент снятия каждого дампа. Стало быть, адрес ячейки,
хранящей количество жизней, равен XXXX:XXXX.
Следует учесть, что количество совпадений может быть больше одного по причине различных
эховых переменных. Найти среди них действительно управляющую - дело эксперимента. Кроме
того, по ходу игры меняется так много параметров, что зачастую отличаются до половины всего
содержимого дампов, так что используйте несложную программу, помогающую найти ожидаемые
результаты (в пакете Immortal Player отсутствует, к сожалению). Вы можете
скачать мой вариант программы или написать свою. При этом даже программа на интерпретируемом
бейсике работает куда быстрее человека.
Далее пишем простую прогу, которая вешает резидентный обработчик на прерывание
таймера, происходящее примерно 18 раз в секунду, и с каждым разом обработчик будет записывать
по указанному нами адресу указанное значение. Таким образом ваша "жизнь", не успев уменьшиться,
будет тут же восполняться. Некоторые игры терпят даже превышение обычных для них жизней до
небывалых количеств (скажем, вместо четырех - аж 65535 штук!).
4. Заготовки крекеров и тяжелые случаи
В конце этого параграфа помещен для скачки архив заготовок крекеров. Там
есть самые простые ломалки и более хитроумные варианты. Я снадбил их комментариями,
помогающими разобраться в назначении инструкций. Тем не менее пару слов о хитрых приемах,
когда простые способы не работают или вешают систему. Это случается тогда, когда игрушка
не держит все данные сразу в памяти, а подгружает их с диска в виде фалов данных или
оверлеев. На счастье, в осносном попадаются игрушки, имеющие простую структуру типа
ИНТРО/ИГРА/ФИНАЛ. Соответственно был написан крекер, активирующийся только после прохождения
интро-заставки по нажатию кнопки ALT. Кроме того, он предохранен от случайной активации, проверяя
наличие атакуемой программы в памяти, и, таким обрахом, может быть загружен и сидеть в памяти,
дожидаясь игрушку, сколь угодно долго. Проверку он производит, просматривая обсуждавшиеся
МСВ, проверяя, является ли владельцем МСВ программа. Это он делает, "надкусывая" краешек кода,
на который указывает МСВ, и проверяя, похож ли он на PSP. Если код "на вкус" как PSP, то берется и
сравнивается с указанным имя программы (для простоты я не проверяю всю строку, а беру первый
символ, именуя прогу так, чтобы ее имя начиналась на какую-нибудь кракозяблу типа @). Если прога, начинающаяся
с такой масямбы, есть в памяти, крекер активируется. В примерах есть крекер для "принца персии 2",
файл которого prince.exe следует переименовать в _prince.exe. Ключевой символ "_", в данном случае.
К другим тяжелым случаям относится проверка перехватов. Обычно все игрушки ставят свой
обработчик прерывания клавиатуры, позволяющий им лучше реагировать на клавиши (и игнорировать
нажатия типа CTRL+C). Вот иногда игрушка проверяет, остался на месте ее обработчик,
и если это не так, ставит его заново. Это не дает нам буквально никакой возможности отнять
у нее прерывание без нарушения ее нормальной работы. Даже если мы предусмотрим свою проверку
и тоже будем переустанавливать свой обработчик, то прерывание будет каждый раз переходить
из рук в руки :). Выход из этого положения - каджый раз ставить свой обработчик, а потом возвращать
игрушкин так, чтобы она ничего не заметила. На самом деле я встречал такой трабл не игушке,
а в одной из систем защит...