2010-03-04 27 views
7

tôi đi qua các đoạn lạ sau code.Imagine bạn có typedef sau:con trỏ hàm và số không rõ các đối số trong C++

typedef int (*MyFunctionPointer)(int param_1, int param_2); 

Và sau đó, trong một chức năng, chúng tôi đang cố gắng để chạy một chức năng từ một DLL theo cách sau:

LPCWSTR DllFileName; //Path to the dll stored here 
LPCSTR _FunctionName; // (mangled) name of the function I want to test 

MyFunctionPointer functionPointer; 

HINSTANCE hInstLibrary = LoadLibrary(DllFileName); 
FARPROC functionAddress = GetProcAddress(hInstLibrary, _FunctionName); 

functionPointer = (MyFunctionPointer) functionAddress; 

//The values are arbitrary 
int a = 5; 
int b = 10; 
int result = 0; 

result = functionPointer(a, b); //Possible error? 

vấn đề là, rằng không có cách nào để biết nếu functon có địa chỉ chúng tôi đã nhận với LoadLibrary mất hai số nguyên arguments.The tên dll được cung cấp bởi người sử dụng tại thời gian chạy, sau đó tên của các hàm đã xuất được liệt kê và người dùng se lects một để kiểm tra (một lần nữa, tại thời gian chạy: S: S). Vì vậy, bằng cách thực hiện cuộc gọi hàm ở dòng cuối cùng, chúng ta có đang mở cánh cửa để ngăn chặn sự tham nhũng có thể không? Tôi biết rằng điều này biên dịch, nhưng những gì sắp xếp của thời gian chạy lỗi sẽ xảy ra trong trường hợp chúng tôi đang đi sai đối số cho chức năng chúng tôi đang chỉ đến?

+0

Nếu bạn không chắc chắn chức năng chính xác nào được xuất bởi dll (loại chính xác, nhưng * không * quy ước gọi), hãy sử dụng Dependency Walker. –

Trả lời

4

Có ba lỗi tôi có thể nghĩ nếu mong đợi và sử dụng số hoặc loại các thông số và quy ước gọi khác nhau:

  • nếu quy ước gọi là khác nhau, giá trị tham số sai sẽ được đọc
  • nếu chức năng thực sự mong đợi nhiều tham số hơn, giá trị ngẫu nhiên sẽ được sử dụng làm tham số (tôi sẽ cho bạn hình dung hậu quả nếu con trỏ có liên quan)
  • trong mọi trường hợp, địa chỉ trả về sẽ hoàn thành. dữ liệu sẽ được chạy ngay sau khi hàm trả về.

Trong hai từ: Undefined behavior

4

Tôi e rằng không có cách nào để biết - người lập trình được yêu cầu biết nguyên mẫu trước khi nhận con trỏ hàm và sử dụng nó.

Nếu bạn không biết nguyên mẫu trước thì tôi đoán bạn cần triển khai một số loại giao thức với DLL nơi bạn có thể liệt kê bất kỳ tên hàm và tham số nào bằng cách gọi các hàm đã biết trong tệp DLL. Tất nhiên, DLL cần được viết để tuân thủ giao thức này.

+0

Đó không phải là quan điểm của tôi: Tôi biết tôi không nên gọi chức năng như thế, nhưng chính xác thì nó sẽ gây ra lỗi gì? Có cái gì đó có thể được xử lý, hoặc tôi chỉ sẽ mess lên ngăn xếp cuộc gọi của tôi và sụp đổ các ứng dụng. –

+1

Bạn không thể xử lý nó. Bạn có thể sẽ không làm lộn xộn ngăn xếp nếu hàm này là hàm C nhưng các tham số sẽ sai, tất nhiên có thể gây ra sự cố. – Cthutu

0

Nói chung nếu bạn đang gọi LoadLibrary và GetProcByAddrees bạn có tài liệu cho bạn biết nguyên mẫu. Thậm chí phổ biến hơn như với tất cả các windows.dll bạn được cung cấp một tập tin tiêu đề. Trong khi điều này sẽ gây ra một lỗi nếu sai nó thường rất dễ dàng để quan sát và không phải là loại lỗi sẽ lẻn vào sản xuất.

+0

Tôi có lý do để tin rằng người đã viết điều này không có bất kỳ thông tin nào về chức năng được gọi, ngoài tên của nó. Vì vậy, trong trường hợp chúng tôi đang thực sự vượt qua số lượng các đối số sai, điều gì sẽ xảy ra? –

+0

Mọi thứ đều có thể xảy ra. Điều gì xảy ra nếu hàm này lấy ba arg hoặc một chuỗi và một int? Việc gọi một hàm có sai số/kiểu đối số là không xác định. – lilburne

1

Tôi không nghĩ vậy, đó là một câu hỏi hay, chỉ có bạn phải biết các tham số là gì cho con trỏ hàm hoạt động, nếu bạn không và mù quáng nhồi nhét các tham số và gọi nó, sẽ sụp đổ hoặc nhảy vào rừng không bao giờ được nhìn thấy một lần nữa ... Nó là lập trình để truyền đạt thông điệp về những gì các chức năng mong đợi và các loại tham số, may mắn bạn có thể tháo rời nó và tìm ra từ nhìn vào ngăn xếp con trỏ và địa chỉ dự kiến ​​bằng cách 'con trỏ ngăn xếp' (sp) để tìm ra loại tham số.

Sử dụng PE Explorer ví dụ, bạn có thể tìm hiểu những gì các chức năng được sử dụng và kiểm tra các bãi chứa tháo rời ...

Hope this helps, Trân trọng, Tom.

0

Hầu hết các trình biên dịch C/C++ đều có người gọi thiết lập ngăn xếp trước cuộc gọi và điều chỉnh lại con trỏ ngăn xếp sau đó. Nếu hàm được gọi không sử dụng con trỏ hoặc tham số tham chiếu, sẽ không có tham nhũng bộ nhớ, mặc dù kết quả sẽ vô giá trị. Và khi chạy lại, con trỏ/lỗi tham chiếu hầu như luôn xuất hiện với một chút thử nghiệm.

+0

Hmm, tôi nghĩ rằng với stdcall đó là trách nhiệm của callee để điều chỉnh stack sau khi cuộc gọi chức năng.Vì vậy, nếu chúng ta vượt qua số lượng sai của args, sẽ không phải là ném ra khỏi SP? –

+0

No. Tôi bình thường C/C + + trình biên dịch, người gọi là chịu trách nhiệm. Đó là cách các hàm variadic như printf có thể làm việc với mức độ an toàn hợp lý. –

+0

Có, nhưng sau đó một lần nữa, quy ước gọi mặc định cho hầu hết các trình biên dịch C/C++ là _cdecl, không _stdcall, theo như tôi biết –

1

Nó sẽ bị lỗi trong mã DLL (vì nó đã được truyền dữ liệu bị hỏng) hoặc: Tôi nghĩ Visual C++ thêm mã trong quá trình gỡ lỗi để phát hiện loại sự cố này. Nó sẽ nói một cái gì đó như: "Giá trị của ESP đã không được lưu trên một cuộc gọi chức năng", và sẽ trỏ đến mã gần cuộc gọi. Nó giúp nhưng không phải là hoàn toàn mạnh mẽ - Tôi không nghĩ rằng nó sẽ dừng lại bạn đi qua trong các đối số sai nhưng cùng kích thước (ví dụ int thay vì một tham số char * trên x86). Như câu trả lời khác nói, bạn chỉ cần biết, thực sự.

+0

Điều đó sẽ khá hữu ích. Có cách nào để phát hiện vấn đề và không nhận được màn hình lỗi ? Bởi vì nếu đó là trường hợp, tôi sẽ có thể vá lỗi vấn đề phần nào.Tôi không thực sự lo lắng về việc đi qua các thông số sai của cùng một kích thước, miễn là tôi tránh ngăn xếp tham nhũng bên ngoài chức năng. –

+0

Tôi không biết tự mình phát hiện ra nó; dự đoán tốt nhất của tôi là tìm hiểu xem công cụ gỡ lỗi nào thực hiện và thêm bản lắp ráp nội tuyến của riêng bạn để thực hiện kiểm tra tương tự trong bản dựng bản phát hành. Âm thanh khó khăn mặc dù. – AshleysBrain

3

Nếu đó là một chức năng __stdcall họ đã để lại tên mangling nguyên vẹn (cả IFS lớn, nhưng chắc chắn có thể dù sao) tên sẽ có @nn ở cuối, nơi nn là một con số. Số đó là số byte mà hàm mong đợi dưới dạng đối số và sẽ xóa ngăn xếp trước khi nó trả về. Vì vậy, nếu đó là một mối quan tâm lớn, bạn có thể xem tên thô của hàm và kiểm tra xem lượng dữ liệu bạn đang đưa vào ngăn xếp có khớp với lượng dữ liệu mà nó sẽ xóa khỏi ngăn xếp hay không.

Lưu ý rằng đây chỉ là sự bảo vệ chống lại Murphy chứ không phải Machiavelli. Khi bạn tạo một DLL, bạn có thể sử dụng một tệp xuất để thay đổi tên của các hàm. Điều này thường được sử dụng để loại bỏ tên mangling - nhưng tôi khá chắc chắn nó cũng sẽ cho phép bạn đổi tên một hàm từ xxx @ 12 thành xxx @ 16 (hoặc bất kỳ thứ gì) để đánh lừa người đọc về các tham số mà nó mong đợi.

Chỉnh sửa: (chủ yếu để trả lời nhận xét của msalters): đúng là bạn không thể áp dụng __stdcall cho chức năng thành viên, nhưng bạn chắc chắn có thể sử dụng nó trên những thứ như chức năng toàn cục, cho dù chúng được viết bằng C hoặc C++.

Đối với những thứ như chức năng của thành viên, tên được xuất của hàm sẽ bị xáo trộn. Trong trường hợp đó, bạn có thể sử dụng UndecorateSymbolName để có chữ ký đầy đủ của nó. Sử dụng điều đó có phần không náo nhiệt, nhưng cũng không phức tạp lắm.

+0

Câu hỏi được gắn thẻ C++, chứ không phải C. Nhưng bạn mô tả tên mangling của hàm C. – MSalters

1

Không có câu trả lời chung. Tiêu chuẩn quy định rằng một số trường hợp ngoại lệ nhất định được đưa ra trong một số trường hợp nhất định, nhưng ngoài việc mô tả cách thức chương trình phù hợp sẽ được thực hiện và đôi khi nói rằng một số vi phạm nhất định phải dẫn đến chẩn đoán. (Có thể có một cái gì đó cụ thể hơn ở đây hoặc ở đó, nhưng tôi chắc chắn không nhớ một.)

Mã đang làm gì không theo tiêu chuẩn, và vì có một trình biên dịch, trình biên dịch có quyền hãy tiếp tục và làm bất cứ điều ngu ngốc nào mà lập trình viên muốn mà không có khiếu nại. Điều này do đó sẽ là một vấn đề thực hiện.

Bạn có thể kiểm tra tài liệu triển khai của mình, nhưng có thể không có tài liệu đó. Bạn có thể thử nghiệm hoặc nghiên cứu cách thực hiện các cuộc gọi chức năng khi triển khai.

Thật không may, câu trả lời là rất có khả năng là nó sẽ vít một cái gì đó lên mà không được ngay lập tức rõ ràng.

Các vấn đề liên quan