Что делает ((void (*) ()) buf) (); жадный?

59

Я решаю проблему бинарной эксплуатации на picoCTF и наткнулся на этот фрагмент кода:

((void (*)())buf)();

где bufмассив символов.

Я решил проблему, но не могу понять, что именно он делает. Я посмотрел на эту ветку, но не смог разобрать.

Что ((void (*)())buf)();значит?

sh.3.ll
источник
14
Что ((void (*)())buf)();значит? Это означает, что автор не понимает typedef. typedef void (*voidFuncPtrType)();сделает этот код понятным.
Эндрю Хенле
33
@ AndrewHenle в разработке задач CTF, ясность не является главной целью, и некоторая запутанность может даже ожидаться как часть проблемы. Скорее всего, автор знал, что это не самый читаемый способ ведения дел.
ManfP
2
Это означает, что ваша программа имеет UB.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
4
Это означает, что правило объявления типа "спираль" в C слишком сложное. Существует причина, по которой практически любой другой статически типизированный язык, который не происходит непосредственно от C, вместо этого использует правила слева направо.
Мейсон Уилер
2
@MasonWheeler "Спираль" - это городской миф. Декларация является такой же или столь же маленькой «спиралью», как и соответствующее выражение. Операторы просто применяются в порядке приоритета и в порядке слева направо (конечно, ничего нового здесь не говорится): «Мне нужно разыменовать его, затем вызвать его, и результат имеет тип void»: вуаля, указатель на функцию void ,
Питер - Восстановить Монику

Ответы:

129

void (*)() является типом, типом которого является «указатель на функцию, которая принимает неопределенные аргументы и не возвращает значения».

(void (*)()) является приведением типа к вышеуказанному типу.

(void (*)())bufприведение bufк вышеуказанному типу.

((void (*)())buf)() вызывает функцию (без аргументов).

Вкратце: он говорит компилятору обращаться с ним bufкак с указателем на функцию и вызывать эту функцию.

то программист чувак
источник
15
Я нахожу cdeclутилиту (или веб-сайт ) полезной для перевода более сложных выражений Си на английский язык.
ВТ
3
@bta cdecl здесь бесполезен, так как синтаксис не является объявлением. Это вызов функции с помощью гипса на ранее объявленный символе
Болы
3
@bolov - На всем заявлении, нет, но это делает объяснить наиболее сложную часть его. Оттуда расшифровка всего остального довольно проста.
ВТ
5
@AvD Если где bufили где copyнаходится по адресу исполняемого файла и сам код не зависит от позиции, это будет работать. Он, конечно, настолько непереносим, ​​насколько это возможно, но это должно работать во многих чистых средах, а также в старых операционных системах x86, которые не устанавливают бит no-execute (NX) в стеке и куче.
wrtlprnft
4
@AvD: Это не обязательно потерпит крах. Если область данных не защищена от выполнения (что зависит от архитектуры и среды выполнения), вы можете использовать этот прием для компиляции функции в массив во время выполнения и вызова ее на лету. Впервые я использовал этот прием 35 лет назад на DEC Vax для компиляции машин Тьюринга для неудачного эксперимента по эволюции машин Тьюринга.
TonyK
11

Указатель bufпреобразуется в указатель на функцию void, принимающую неопределенное количество параметров, а затем разыменовывается (то есть вызывается функция).

P__J__
источник
9

Это типизация, сопровождаемая вызовом функции. Во-первых, bufприведен указатель на функцию, которая возвращает void. Последняя пара скобок означает, что функция вызывается.

lukeg
источник
7

Он преобразует массив символов в указатель на функцию, не принимающую аргументов и возвращающую void, а затем вызывает ее. Разыменование указателя не требуется из-за того, как работают указатели функций.

Объяснение:

Этот «массив символов» на самом деле является массивом машинного кода. Когда вы приводите массив к a void (*)()и вызываете его, он запускает машинный код внутри массива. Если бы вы предоставили содержимое массива, я мог бы разобрать его для вас и рассказать, что он делает.

СС Энн
источник