Không ((void (*) ()) buf) (); nghĩa là?


59

Tôi đang giải quyết một thách thức khai thác nhị phân trên picoCTF và tình cờ thấy đoạn mã này:

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

trong đó buflà một mảng ký tự.

Tôi đã giải quyết thử thách nhưng dường như không thể hiểu chính xác nó đang làm gì. Tôi đã xem chủ đề này nhưng tôi không thể làm được.

((void (*)())buf)();nghĩa là gì?


14
((void (*)())buf)();nghĩa là gì? Nó có nghĩa là tác giả không hiểu typedef. typedef void (*voidFuncPtrType)();sẽ làm cho mã này rõ ràng.
Andrew Henle

33
@AndrewHenle trong việc thiết kế các thách thức CTF, sự rõ ràng không thực sự là mục tiêu hàng đầu, và một số obfuscation thậm chí có thể được dự kiến ​​là một phần của thách thức. Nhiều khả năng hơn là không, tác giả đã nhận ra rằng đây không phải là cách làm dễ đọc nhất.
ManfP

2
Nó có nghĩa là chương trình của bạn có UB.
R .. GitHub DỪNG GIÚP ICE

4
Nó có nghĩa là quy tắc khai báo kiểu "xoắn ốc" của C quá phức tạp. Có một lý do hầu như mọi ngôn ngữ gõ tĩnh khác không trực tiếp từ C sử dụng các quy tắc từ trái sang phải thay vào đó.
Mason Wheeler

2
@MasonWheeler "Xoắn ốc" là một huyền thoại đô thị. Khai báo là "xoắn ốc" nhiều hoặc ít như biểu thức tương ứng. Các toán tử được áp dụng đơn giản theo thứ tự ưu tiên và từ trái sang phải (tất nhiên không cho bạn biết bất kỳ điều gì mới ở đây): "Tôi cần phải hủy đăng ký nó, sau đó gọi nó và kết quả có kiểu void": voila, con trỏ đến hàm void .
Peter - Tái lập lại

Câu trả lời:


129

void (*)() là một kiểu, kiểu "con trỏ tới hàm lấy các đối số không xác định và không trả về giá trị".

(void (*)()) là một kiểu đúc cho loại trên.

(void (*)())bufphôi bufđến loại trên.

((void (*)())buf)() gọi hàm (không có đối số).

Tóm lại: Nó báo cho trình biên dịch coi buflà một con trỏ tới một hàm và gọi hàm đó.


15
Tôi thấy cdecltiện ích (hoặc trang web ) hữu ích cho việc dịch các biểu thức C phức tạp hơn sang tiếng Anh.
bta

3
@bta cdecl không hữu ích ở đây vì cú pháp không phải là khai báo. Đó là một cuộc gọi chức năng thông qua một biểu tượng trên một biểu tượng đã khai báo trước đó
bolov

3
@bolov - Trên toàn bộ tuyên bố, không có, nhưng nó không giải thích một phần phức tạp nhất của nó. Từ đó, giải mã phần còn lại khá đơn giản.
bta

5
@AvD Nếu bất cứ nơi nào bufhoặc copyđược đặt tại một địa chỉ thực thi và bản thân mã là độc lập với vị trí, điều này sẽ hoạt động. Tất nhiên nó không phải là di động, nhưng nó sẽ hoạt động trong nhiều môi trường kim loại trần cũng như các hệ điều hành x86 cũ hơn không đặt bit không thực thi (NX) trên stack và heap.
wrtlprnft

4
@AvD: Nó sẽ không nhất thiết phải sụp đổ. Trừ khi khu vực dữ liệu được bảo vệ chống thực thi (phụ thuộc vào kiến ​​trúc và môi trường thời gian chạy), bạn có thể sử dụng thủ thuật này để biên dịch một hàm thành một mảng trong thời gian chạy và gọi nó một cách nhanh chóng. Lần đầu tiên tôi đã sử dụng thủ thuật này 35 năm trước trên máy DEC Vax để biên dịch máy Turing cho một thử nghiệm thất bại trong quá trình tiến hóa máy Turing.
TonyK

11

con trỏ bufđược chuyển đổi thành con trỏ để vô hiệu hóa hàm lấy số lượng tham số không xác định và sau đó được hủy đăng ký (tức là hàm được gọi).


9

Đó là một kiểu chữ, theo sau là một hàm gọi. Đầu tiên, bufđược truyền tới con trỏ tới hàm trả về void. Cặp dấu ngoặc cuối cùng có nghĩa là hàm được gọi.


7

Nó đưa mảng ký tự thành một con trỏ tới một hàm không có đối số và trả về void, sau đó gọi nó. Hủy bỏ hội nghị con trỏ là không cần thiết do cách con trỏ chức năng hoạt động.

Một lời giải thích:

"Mảng ký tự" đó thực sự là một mảng mã máy. Khi bạn truyền mảng cho a void (*)()và gọi nó, nó sẽ chạy mã máy bên trong mảng. Nếu bạn cung cấp nội dung của mảng, tôi có thể tháo rời nó cho bạn và cho bạn biết nó đang làm gì.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.