Вызывает переполнение буфера в C, чтобы вызвать истинное условие
Я пытаюсь решить эту проблему, где цель состоит в том, чтобы программа показала "ВЫ ПОБЕДИ!" Чтобы сделать это, я должен найти правильные значения переменных, чтобы сделать оба if
состояние и assert
оценивать как истинное
Первоначально я думал, что это будет относительно просто. Тем не менее, кажется, что переполнение буфера - это искусство, которое требует продвинутых навыков программирования (в том числе концепций с прямым и обратным порядком байтов), но у меня нет большого опыта в программировании на Си.
Любая помощь или совет будет принята с благодарностью.
#include<assert.h>
int
main(int argc, char*argv[]){
int size = 0;
int buf[1024];
read(0,&size,sizeof(size));
assert (size <= 1024);
read(0,buf,size*sizeof(int));
if (size > 0 && size < 100 && buf[999] == 'B')
printf("YOU WIN!\n");
return 0;
}
1 ответ
Забавная проблема.
Вам нужно что-то знать о машине, на которой вы работаете, поэтому я сделал следующие предположения:
32-битная машина с прямым порядком байтов.
Компилятор делает "классическую" компоновку стека, которая бы
size
рядом с концомbuf
,Данные выровнены до 4 байтов или меньше, поэтому между концом нет заполнения.
buf
а такжеsize
,
Классический компилятор на этой машине размещает ваши переменные в стеке вызовов в порядке их появления в коде, поэтому size
толкают сначала, а потом buf
, Ваш стек будет выглядеть так:
buf byte
top of stack offset offset
+-----------+ 0 * sizeof(int) = 0
| buf[ 0] |
+-----------+ 1 * sizeof(int) = 4
| |
...
| |
+-----------+ 1023 * sizeof(int) = 4092
| buf[1023] |
+-----------+ 1024 * sizeof(int) = 4096
| size |
+-----------+ 1025 * sizeof(int) = 4100
bottom of stack
Запись в buf[1024]
переписал бы size
, Если бы мы писали необработанные байты, то байты 4096, 4097, 4098 и 4099 соответствовали бы байтам в size
,
Мы знаем, что хотим, чтобы размер был отрицательным, чтобы передать assert
и мы хотим size * sizeof(int)
оценить до 4100. Потому что, если мы прочитаем 4100 байт, последние 4 байта будут перезаписаны size
,
size * 4 = 4100. Solving for size:
size = 1025 (or 0x401)
Теперь мы просто делаем это отрицательным, устанавливая самый значимый бит.
0x80000401
Таким образом, установка размера этого значения пройдет assert (size <= 1024)
, Потому что все с самым значимым битом установлено отрицательно, а все отрицательные числа меньше 1024.
Когда мы доберемся до буфера чтения, он будет оценен примерно так:
read(0, buf, 0x80000401 * sizeof(int));
read(0, buf, 0x80000401 * 4);
read(0, buf, 0x00001004) == read(0, buf, 4100)
Да, все верно, мы просто сказали, что нужно прочитать 4100 байт из-за 32-битной арифметики по модулю. Или, если это помогает, вы можете думать о умножении на 4 как о смещении влево на 2, так что смещение нашего магического значения влево на 2 избавляет от самого значимого бита.
Чтобы напечатать "ВЫ ВЫИГРЫВАЕТЕ!", Вы должны написать поток байтов, где buf[999] - это байт 3996, а байты 4096, 4097, 4098 и 4099 - байты size
, На этой гипотетической машине с прямым порядком байтов мы установили бы эти байты в 0, 0, 0 и 1-99 соответственно.