Вы правы насчет третьей строки. Он принимает buf
, распадает его на указатель, а затем приводит этот указатель к указателю на функцию. Затем он вызывает эту функцию. Это работает, потому что void(*)()
означает «указатель на функцию, не принимающую параметров и возвращающую void
», и заключив ее в скобки, превращает ее в приведение. Теперь у вас есть указатель на функцию, которую вы можете вызвать, как если бы это было обычное имя функции.
Это не разрешено стандартом C, но в зависимости от того, как работают компиляторы, вероятно, это будет означать, что он обрабатывает адрес buf
как адрес функции в памяти и пытается вызвать эту функцию. Обычно это не сработает, потому что ОС скажет ЦПУ отметить область памяти, в которой buf
находится как неисполнимый, поэтому, когда вы пытаетесь притвориться, что это функция, ЦП выдает ошибку; давайте предположим, что ОС этого не делает или у вас достаточно старый процессор, который не поддерживает это.
Вы не показываете нам содержимое shellcode
. Поскольку все в компьютере представляет собой последовательность байтов, включая скомпилированный двоичный код, который запускает ваш компьютер, shellcode
может содержать исполняемый двоичный код. Если это так, то этот код запускается.
Например, если вы скомпилировали
void mycode()
{
puts("Hello, world!");
}
затем вы можете извлечь содержимое функции mycode
из результирующего объектного файла. Если вы поместите это содержимое в shellcode
, то код в вашем вопросе будет (при условии, что компилятор обрабатывает указатели на функции и указатели на данные и что ОС или ЦП не поддерживают память noexec) Hello, world!
на экран.