Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #64, МАРТ 2006 г.

защита игр от взлома

КРИС КАСПЕРСКИ АКА МЫЩЪХ

Спецвыпуск: Хакер, номер #064, стр. 064-066-2


Сокрытие алгоритма в машинном коде — широко распространенный, но неэффективный защитный примем. Понятно, что дизассемблерный листинг — это не исходный код и одна строка на языке высокого уровня может транслироваться в десятки машинных команд, часто перемешанных. К тому же в двоичном файле нет комментариев, зато все остальное очень даже встречается: структура классов, имена функций/переменных...

Текстовые строки

ASCII- и UNICODE-строки несут в себе очень богатую информацию: и текстовые сообщения, выводимые на экран при регистрации/окончании trial-срока/неправильном вводе пароля, и ветви реестра, и имена ключевых файлов, иногда и сами серийные номера/пароли. С паролями все ясно. Хранить их в открытом виде нельзя, нужно хэшировать их. А что плохого в текстовых сообщениях? Обнаружив их в теле программы, хакер по перекрестным ссылкам очень быстро найдет тот код, выводящий их, — вот что плохо. То же самое относится и к именам ключевых файлов с ветвями реестра.

Листинг

текстовая строка «wrong s/n» с перекрестной ссылкой, ведущей к процедуре sub_401000

.text:00401016 call sub_401000

.text:0040101B add esp, 4

.text:0040101E test eax, eax

.text:00401020 jz short loc_40102F

.text:00401022 push offset aWrongSN ; «wrong s/n\n» ; указатель на строку

.text:00401027 call _printf

...

.data:00406030 aWrongSN db 'wrong s/n',0Ah,0 ; DATA XREF: 00401022h^o

Чтобы затруднить анализ, необходимо либо зашифровать все строки, расшифровывая только перед непосредственным употреблением (если расшифровать сразу же после запуска программы, хакер просто снимет дамп и увидит их в виде прямого текста), либо поместить их в ресурсы и грузить через LoadString — это программируется легче, но в то же время легче ломается. Хакеру достаточно открыть файл в любом редакторе ресурсов, найти нужную строку, запомнить ее идентификатор, запустить отладчик и установить условную точку останова. Далее — дождаться вызова LoadString с нужным uID, определить адрес буфера lpBuffer, установить на него точку останова и... если lpBuffer выводится не сразу, а передается через цепочку промежуточных буферов, хакер матерится, но ничего не делает, потому что не может сделать ничего. Аппаратных точек останова всего четыре, и если защита использует не последний буфер в цепочке, то отследить момент реального обращения к строке становится невозможно (на самом деле возможно: с помощью секретного хакерского оружия NO_ACCESS на страницу, только об этом не все знают).

В дизассемблере же отследить перемещение строки по локальным буферам практические невозможно (во всяком случае, автоматически). Глобальные буфера (в которые компилятор пихает константные строки, в том числе зашифрованные) отслеживаются сразу, так что LoadString по некоторым позициям очень даже рулит!

Символьная информация

отладочная информация

По умолчанию компилятор генерирует файл без отладочной информации, и она попадет туда только в исключительном случае, но все-таки попадет. Такая участь настигает не только начинающих программистов, не знающих, чем отличается Debug от Release, но и «маститые» фирмы, выпускающие достойные программные продукты. Допустим, у нас имеется глобальная программа IsRegistered, тогда смысл пары машинных команд будет ясен и без комментариев.

Назад на стр. 064-066-1  Содержание  Вперед на стр. 064-066-3