· int noguest=1;
· char pass[16];
· int a=1;
· for (;a«argc;a++)
· {
· if (argv[a][0]-'/')
· {
· if (!strcmp( amp;argv[a][0],"/GUEST:ON")) noguest=0;
·}
· else
· {
· if (strlen(argv[a])»16)
· printf("Too long arg: %s\n",argv[a]);
· else
· strcpy( amp;pass[0],argv[a]);
·}
·}
· if ((!strcmp("KPNC++\n", amp;pass[0])) || (!noguest))
· printf("Password ok\n");
· else
· printf("Wrong password\n");
·
·
·}
Дизассемблирование позволяет установить, что переменная “noguest” расположена в «хвосте» буфера buff и может быть искажена при его переполнении. Поскольку, при проверке длины строки допущена ошибка «if (strlen(argv[a])»16)…», завершающий ноль шестнадцатисимвольной строки обнулит значение переменной “noguest” и откроет злоумышленнику путь в систему. Это демонстрирует следующий эксперимент:
· buff.var.exe 1234567890123456
· Password ok
Но если увеличить длину строки хотя бы на один байт, программа отбросит ее как неправильную:
· buff.var.exe 12345678901234567
· Too long arg: 12345678901234567
· Wrong password
Конечно, вероятность возникновения подобной ситуации на практике очень мала. Для атаки необходимо неблагоприятное стечение многих маловероятных обстоятельств. Размер буфера должен быть кратен величие выравнивания, иначе переполняющий байт запишется в «черную дыру» [321] и ничего не произойдет. Следующая за буфером переменная должна быть критична к обнулению, т.е. если программист открывал бы доступ на машину при ненулевом значении флага guest, опять бы ничего не произошло. Поэтому, в большинстве случаев несанкционированного доступа к машине получить не удастся, а вот «завесить» ее гораздо вероятнее.
Например, следующий код (на, диске, прилагаемом к книге, он находится в файле “/SRC/buff.var.2.c”), в отличие от предыдущего, трудно назвать искусственным и «притянутым за уши»:
· #include «stdio.h»
· #include «string.h»
·
· main (int argc,char **argv)
· {
·
· char pass[16];
· int a=1;
· for (;a«argc;a++)
· {
· if (argv[a][0]-'/')
· {
· if (!strcmp( amp;argv[a][0],"/GUEST:ON"))
· {
· printf("Guest user ok\n");
· break;
·}
·}
· else
· {
· if (strlen(argv[a])»16)
· printf("Too long arg: %s\n",argv[a]);
· else
· strcpy( amp;pass[0],argv[a]);
·}
·}
· if ((!strcmp("KPNC++\n", amp;pass[0])))
· printf("Password ok\n");
· else
· printf("Wrong password\n");
·
·}
Переполнение буфера вызовет запись нуля в счетчик цикла ‘a’, в результате чего цикл никогда не достигнет своего конца, а программа «зависнет». А если буфер окажется расположенным в вершине стека, то «вылетевший» за его пределы ноль исказит значение регистра EBP. Большинство компиляторов генерируют код, использующий для адресации локальных переменных регистр EBP, поэтому искажение его значения приведет к нарушению работы вызывающей процедуры.
Такую ситуацию демонстрирует следующий пример (на диске, прилагаемом к книге, он расположен в файле “/SRC/buff.ebp.c”):
· #include «stdio.h»
· #include «string.h»
·
· int Auth()
· {
· char pass[16];
· printf("Passwd:");fgets( amp;pass[0],17,stdin);
· return!strcmp("KPNC++\n", amp;pass[0]);
·}
·
· main (int argc,char **argv)
· {
·
· int guest=0;
· if (argc»2) if (!strcmp( amp;argv[1][0],"/GUEST:ON")) guest=1;
·
· if (Auth() || guest) printf("Password ok\n");
· else
· printf("Wrong password\n");
·
·}
Ввод строки наподобие “1234567890123456123” затрет сохраненное значение регистра EBP, в результате чего при попытке прочитать значение переменной guest произойдет обращение к совсем другой области памяти, которая, скорее всего, содержит ненулевое значение. В результате злоумышленник сможет несанкционированно войти в систему.
Модификация сохраненного значения регистра EBP имеет побочный эффект - вместе с регистром EBP изменяется и регистр-указатель верхушки стека. Большинство компиляторов генерируют приблизительно следующие прологи и эпилоги функций (в листинге они выделены жирным шрифтом):
·.text:00401040 Main proc near; CODE XREF: start+AFp
·.text:00401040
·.text:00401040 var_4 = dword ptr -4
·.text:00401040
·.text:00401040 push ebp
·.text:00401041 mov ebp, esp
·.text:00401043 push ecx
·.text:00401044 push offset aChahgeEbp; "Chahge EBP\n"
·.text:00401049 call sub_0_401214
·.text:0040104E add esp, 4
·.text:00401051 call Auth
·.text:00401056 mov [ebp+var_4], eax
·.text:00401059 cmp [ebp+var_4], 0
·.text:0040105D jz short loc_0_40106E
·.text:0040105F push offset aPasswordOk; "Password ok\n"
·.text:00401064 call sub_0_401214