Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #90, ИЮНЬ 2006 г.

Трюки от Криса

Крис Касперски

Хакер, номер #090, стр. 090-128-2


Хак №4 – скрытые возможности цикла for

Вот еще один типичный пример кода, написанный "правильным" программистом:

Инициализация переменной перед входом в цикл (широко распространенный вариант)

x = 0;

for (a = 0; a < n; a++)

{

...

if (f()) x++;

...

}

Инициализация переменной x перед входом в цикл режет глаза, занимает целую строку и придает листингу некоторую небрежность. И это притом, что в каждом учебнике написано, что си допускает множественную инициализацию в циклах. Ну и что с того, что x не является параметром цикла? Компилятору ведь все равно, и никакой хакер не успокоится, пока не перепишет этот код так:

Инициализация переменной перед входом в цикл (хакерский вариант)

for (a = 0, x = 0; a < n; a++)

{

...

if (f()) x++;

...

}

При большом количестве переменных это здорово выручает! А вот еще более конкретный пример. В "каноническом" виде он выглядит так:

Подсчет суммы ("канонический" вариант)

sum = 0;

for (a = 0; a < n; a++) sum += f(a);

А что, если избавиться от тела цикла, поместив весь код в заголовок? Любой хакер без труда сделает это:

for (a = 0, sum = 0; a < n; sum += f(a), a++);

А вот еще более оптимизированный вариант (инкремент, совмещенный с передачей аргумента):

for (a = 0, sum = 0; a < n; sum += f(a++));

Хак №5 – со знаком или без

Общеизвестно, что ((unsigned char) 0xFF == (signed char) -1). Соответственно, на 32-разрядных платформах ((unsigned int) 0xFFFFFFFF == (signed int) -1). Естественно, написать -1 намного быстрее и надежнее, чем пересчитывать F'ы, рискуя пропустить один из них или переборщить.

Вот конкретный пример хакерского кода:

Гибридный хакерский цикл со знаковыми и без знаковых параметров

unsigned int a;

for (a = 0; a < -1UL; a++) printf("%x\n",a);

С точки зрения "нормального" прикладного программиста, этот код вообще не должен работать, поскольку (0 > -1), и цикл ни разу не выполнится. Но ведь это не обычный -1, а с суффиксом UL, что равносильно конструкции ((unsigned int) -1), после преобразования которой мы получим 0xFFFFFFFF.

Развиваю мысль дальше: можно не только сократить исходный текст, но и оптимизировать машинный код.

Возьмем конструкцию вида ("правильный" вариант проверки диапазона):

signed char x, y;

if ((x > 0) && (x < y)) ...

Для хакеров очевидно, что проверка на положительное значение x избыточна и от нее легко избавиться, переписав код так:

if ((unsigned int) x < y)) ...

Хак №6 – лишние аргументы

Си не требует соблюдения прототипов функций, что открывает большие возможности для трюкачества. Поскольку аргументы заносятся справа налево (то есть на вершине стека оказывается крайний левый аргумент), а стек чистит материнская функция, то лишние аргументы попросту игнорируются. И вот тут начинается самое интересное. Поскольку в качестве аргументов допускается использовать выражения (а выражением в си, как уже говорилось, может быть практически все что угодно), мы получаем мощный инструмент для борьбы с фигурными скобками.

Вот, например:

if (a)

{

x += f(a); if (n < MAX) n++;

}

Первым делом освобождаемся от оператора if, преобразуя его в выражение: ((n < MAX) && n++), и передаем его функции f() как "сверхплановый" аргумент:

Назад на стр. 090-128-1  Содержание  Вперед на стр. 090-128-3
Hosted by uCoz