Как вы говорите, это код, используемый для защиты от переполнения буфера. Компилятор генерирует эту «канарскую проверку стека» для функций, которые имеют локальные переменные, которые могут быть буферами, которые могут быть переполнены. Обратите внимание на инструкции непосредственно над и под строкой, о которой вы спрашиваете:
sub $0x40, %rsp
mov %fs:0x28, %rax
mov %rax, -0x8(%ebp)
xor %eax, %eax
sub
выделяет 64 байта пространства в стеке, что достаточно для хотя бы одного небольшого массива. Затем секретное значение копируется из %fs:0x28
в верхнюю часть этого пространства, сразу под указателем предыдущего кадра и адресом возврата, и затем оно стирается из файла регистров.
Тело функции что-то делает с массивами; если он пишет достаточно далеко за концом массива, он перезапишет секретное значение. В конце функции будет код, аналогичный
mov -0x8(%rbp), %rax
xor %fs:28, %rax
jne 1
mov %rbp, %rsp
pop %rbp
ret
1:
call __stack_chk_fail # does not return
Это подтверждает, что секретное значение не изменилось, и вылетает из программы, если оно изменилось. Идея состоит в том, что кто-то, пытающийся использовать простую уязвимость переполнения буфера, как вы, когда вы используете gets
, не сможет изменить адрес возврата без изменения секретного значения.
Компилятор имеет несколько различных эвристик , выбираемых с помощью параметров командной строки, для решения, когда необходимо сгенерировать код защиты стека и канареек.
Вы не можете написать код C, соответствующий этому языку ассемблера, потому что он использует необычный режим адресации% fs: nnnn; Канарский код стека преднамеренно использует режим адресации, на который не опирается никакая другая генерация кода, чтобы противнику было как можно сложнее узнать секретное значение.