Co robi ((void (*) ()) buf) (); oznaczać?

59

Rozwiązuję binarne wyzwanie dotyczące eksploatacji na picoCTF i natrafiłem na następujący fragment kodu:

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

gdzie bufjest tablica znaków.

Rozwiązałem wyzwanie, ale nie rozumiem, co dokładnie robi. Spojrzałem na ten wątek, ale nie mogłem go zrozumieć.

Co ((void (*)())buf)();znaczy

sh.3.ll
źródło
14
Co ((void (*)())buf)();znaczy Oznacza to, że autor nie rozumie typedef. typedef void (*voidFuncPtrType)();wyjaśniłby ten kod.
Andrew Henle
33
@AndrewHenle w projektowaniu wyzwań CTF, klarowność nie jest tak naprawdę najważniejszym celem, a nawet część zaciemnienia można się spodziewać w ramach wyzwania. Bardziej prawdopodobne niż to, że autor zdawał sobie sprawę, że nie jest to najbardziej czytelny sposób robienia rzeczy.
ManfP
2
Oznacza to, że twój program ma UB.
R .. GitHub ZATRZYMAJ LÓD
4
Oznacza to, że reguła deklaracji typu „spiralna” C jest zbyt skomplikowana. Istnieje powód praktycznie każdego innego języka o typie statycznym, który nie pochodzi bezpośrednio od C, zamiast tego używa reguł od lewej do prawej.
Mason Wheeler,
2
@MasonWheeler „Spirala” to miejski mit. Deklaracja ma tyle samo lub mniej „spirali”, co odpowiadające jej wyrażenie. Operatory są po prostu stosowane w kolejności i kolejności od lewej do prawej (oczywiście nie mówię tu nic nowego): „Muszę to wyrejestrować, a następnie wywołać, a wynik ma typ void”: voila, wskaźnik do funkcji void .
Peter - Przywróć Monikę

Odpowiedzi:

129

void (*)() jest typem, typ jest „wskaźnikiem do funkcji, który pobiera nieokreślone argumenty i nie zwraca żadnej wartości”.

(void (*)()) jest rzutowaniem typu na powyższy typ.

(void (*)())bufrzutuje bufna powyższy typ.

((void (*)())buf)() wywołuje funkcję (nie przekazuje żadnych argumentów).

W skrócie: nakazuje kompilatorowi, aby traktował go bufjako wskaźnik do funkcji i wywołał tę funkcję.

Jakiś koleś programisty
źródło
15
Uważam, że cdeclnarzędzie (lub strona internetowa ) jest pomocne w tłumaczeniu bardziej złożonych wyrażeń C na angielski.
bta
3
@bta cdecl nie jest tu przydatny, ponieważ składnia nie jest deklaracją. Jest to wywołanie funkcji za pośrednictwem rzutowania na wcześniej zadeklarowany symbol
bolov
3
@bolov - Na całej wypowiedzi, nie, ale to nie wyjaśnia najbardziej skomplikowanych części z niego. Stamtąd dekodowanie reszty jest dość proste.
bta
5
@AvD Jeśli gdziekolwiek buflub gdzie copyznajduje się adres wykonywalny, a sam kod jest niezależny od pozycji, to zadziała. Jest oczywiście tak mało przenośny, jak to tylko możliwe, ale powinno to działać w wielu środowiskach bez systemu operacyjnego, a także w starszych systemach x86, które nie ustawiają bitu no-execute (NX) na stosie.
wrtlprnft
4
@AvD: To niekoniecznie się zawiesi. O ile obszar danych nie jest chroniony przed wykonaniem (zależy to od architektury i środowiska wykonawczego), możesz użyć tej sztuczki do skompilowania funkcji w tablicę w czasie wykonywania i wywoływania jej w locie. Po raz pierwszy użyłem tej sztuczki 35 lat temu na DEC Vax do skompilowania maszyn Turinga do nieudanego eksperymentu w ewolucji maszyn Turinga.
TonyK
11

wskaźnik bufjest konwertowany na wskaźnik w celu unieważnienia funkcji, przyjmując nieokreśloną liczbę parametrów, a następnie usuwając odwołanie (tj. wywoływana funkcja).

P__J__
źródło
9

To typecast, po którym następuje wywołanie funkcji. Po pierwsze, bufjest rzutowany na wskaźnik do funkcji, która zwraca void. Ostatnia para nawiasów oznacza, że ​​funkcja jest wówczas wywoływana.

lukeg
źródło
7

Rzuca tablicę znaków na wskaźnik do funkcji nie przyjmującej argumentów i zwracającej ją void, a następnie wywołuje ją. Dereferencje wskaźnika nie są wymagane ze względu na działanie wskaźników funkcji.

Wyjaśnienie:

Ta „tablica znaków” jest w rzeczywistości tablicą kodu maszynowego. Kiedy rzutujesz tablicę na a void (*)()i wywołujesz ją, uruchamia ona kod maszynowy wewnątrz tablicy. Jeśli podałeś zawartość tablicy, mógłbym ją dla ciebie zdemontować i powiedzieć, co ona robi.

SS Anne
źródło