Lỗi khoogn đọc được file pdb trên visual năm 2024

Khi có những lỗi hệ thống xảy ra ( vd như truy cập bất hợp pháp ) thì sẽ có một ngắt cứng được máy sinh ra. Hệ điều hành sẽ bắt lấy ngắt này để xử lý. Tiếp theo, hệ điều hành sẽ tìm xem process có cài đặt SEH hay không bằng cách đọc vào bảng con trỏ, nếu các con trỏ trỏ tới vị trí khác null tức là đã có SEH. Khi đó hệ điều hành sẽ chuyển điều khiển sang địa chỉ được trỏ bởi SEH và đoạn code bắt lỗi của process sẽ được chạy.

Tất cả các đoạn code thực thi được bảo vệ bởi SEH để ngăn ngừ những ngoại lệ là crack chương trình.SEH thường được sinh ra ở cấu trúc exception của C/C++

SEH thì được thực hiện như là mỗi thread của danh sách liên kết.1 con trỏ bắt đầu của danh sách được lấy ra từ DWORD đầu tiên của TIB(fs:00)Xem đoạn code sau :

PHP Code:

`push addr _exception_handler ; địa chỉ exception handler push dword ptr fs:[0] ; địa chỉ handler trước đó mov fs:[0], esp ; thêm vào list

;

code tại đây

pop dword ptr fs

:[0] ; Remove handler add esp, 4 ; Clean stack `

Khi 1 ngoại lệ xảy ra,OS sẽ gọi _exception_handler routine.1 cách đơn giản,chỉ có 2 DWORD(trong cấu trúc của EXCEPTION_REGISTRATION) sẽ được push vào stack.Và ta có thể thêm 1 số trường vào cấu trúc này.Xem ví dụ dưới đây,ta sẽ add thêm 2 trường vào SEH frame : giá trị của EBP và địa chỉ sẽ nhảy đến khi có exception.

Code:

`; Set a new SEH frame push ebp ; EBP at safe-place (needed for ENTER/LEAVE) push addr _resume_at_safe_place ; Addr. of safe-place push addr _exception_handler ; Addr. of our exception handler push dword ptr fs:[0] ; Addr. of previous handler mov fs:[0], esp ; Install new SEH handler

; ... try code ...

_resume_at_safe_place: ; Remove SEH frame pop dword ptr fs:[0] ; Remove SEH handler add esp, 3*4 ; Remove additional data from stack

`

Đoạn mã trên C/C++

Code:

`EXCEPTION_DISPOSITION __cdecl _exception_handler( struct _EXCEPTION_RECORD *ExceptionRecord, struct _EXTENDED_EXCEPTION_REGISTRATION *EstablisherFrame, struct _CONTEXT *ContextRecord, void *DispatcherContext) { ContextRecord->cx_Eax = ExceptionRecord->ExceptionCode; ContextRecord->cx_Eip = EstablisherFrame->SafeExit; ContextRecord->cx_Ebp = EstablisherFrame->SafeEBP; ContextRecord->cx_Esp = EstablisherFrame;

return ExceptionContinueExecution; }

`

_exception_handler lưu trữ thanh ghi EBP,set EAX đến code chứa ngoại lệ

Tôi dịch lại bài viết Effective Exception Handling in Visual C++ ,hy vọng giúp các bạn có cái nhìn sâu hơn về exception handling trên VC++ :

Bài viết này miêu tả về các handling exception và lỗi trong 1 ứng dụng VC++ trên windows

Một exception(có thể gọi là crash) là việc làm dừng chương trình và dừng các bước thực thi hiện tại trong ứng dụng.Ví dụ,có 1 exception có thể gây ranếu chương trình truy xuất vào 1 vùng nhớ không hợp lê(như con trỏ NULL).bộ nhớ không được cấp phát,lúc này C run-time(CRT) sẽ phát hiện tra lỗi này và trả về 1 quyết định thực thi nào đó

1 chương trình C++ có thể có vài loại exception : SEH exception cung cấp thông qua OS,CRT cung cấp thông qua C runtimr library …Mỗi kiểu lỗi yêu cầu phải cài đặt 1 hàm exception handler mà nó chặn thực thi khi có exception và vài lỗi có thể tự khôi phục.

Nếu ứng dụng của bạn có nhiều thread thực thi,có thể có vài cái được hoàn thành.Vài exception handler làm việc cho mọi process,mọi luồng,cũng có các exception handler thực hiện trên thread.Vì thế,trong trường hợp này bạn cần phải cài đặt exception handler trong mỗi thread.

Với mỗi module(exe or dll) trong ứng dụng của bạn đượx linked đến CRT library.Exception handler phụ thuộc vào kiểu CRT link tới

Cuối bài viết sẽ là 1 demo nho nhỏ về Exception Handler.

Background

Thời gian trước đây,tôi dùng 1 cách để tìm ra các ngoại lệ cho 1 trong các project của tôi(CrashRpt – A crash reporting library for Windows applications) Thư viện CrashRpt hỗ trợ chươgn trình của bạn lúc xảy ra ngoại lệ,nó sẽ gửi thông tin lỗi qua internet hoặc minidump,tạo log …

Lỗi khoogn đọc được file pdb trên visual năm 2024

Tìm trên trang MSDN cho ta hàm SetUnhandledExceptionFilter().Và vì 1 số lý do nào đó thì ngoại lệ đã được xử lý và cửa sổ của Dr.Watson vẫn hiện ra thay vì cửa sổ của CrashRpt

Tôi tiếp tục tìm kiếm trên MSDN và tìm vài hàm được cung cấp bởi CRT có thể dùng để lấy hanle của lỗi.Ví dụ như các hàm : set_terminate(), _set_invalid_parameter_handler(), _set_purecall_handler()

Để tìm hiểu cách hoạt động của exception handling ta sẽ đi chi tiết hơn qua các ví dụ và phân tích sau :

About Exceptions Như bạn biết,1 exception hay 1 ritical error nghĩa là 1 chương trình bị tạm dừng thực thi trong 1 thời điểm nào đó. Ví dụ,1 exception có thể gây lỗi vì các lý do sau : – Truy cập vùng nhớ không hợp lệ(con trỏ Null chẳn hạn) – Tràn stack vì infinite recursion(có thể hiểu là do ta viết code đệ quy ko đúng, hàm ko trả được giá trị, nó gọi chính nó sau đó cứ gọi hoài mà ko thoát ra được) – Write 1 khối dữ liệu lớn mà không chia nhỏ thành các buffer – 1 phương thức ảo trong C++ class được gọi – tham số truyền sai vào hàm CRT library sẽ phát hiện lỗi và kết thúc quá trình thực thi này.Có 2 loại exception : SEH(sinh tự nhiên của hệ điều hành) và exception của C++. Bạn có thể tham khảo bài viết sau để biết cách Compiler biên dịch 1 exception-handling như thế nào : http://www.codeproject.com/Articles/2126…n-handling

SEH được cung cấp bởi hệ điều hành.SEH ecxception được thiết kế cho C nhưng ta cũng có thể dùng trên C++

SEH exceptions sử dung __try{}__except(){} construction.Hàm main của chương trình ta cũng được bao bọc bởi 1 construction.Vì thế mặc định SEH exception đã được gọi.

Ví dụ :

PHP Code:

int* p = NULL; // pointer to NULL __try { // code được bảo vệ *p = 13; // violation exception } __except(EXCEPTION_EXECUTE_HANDLER) // Nơi xử lý exception { ExitProcess(1); }

Trong khía cạnh khác,exception C++ được cung cấp bởi CRT bibrary(chỉ có chương trình dịch = C++ mới tạo ra dạng exceptions này). Ví dụ :

PHP Code:

`// exceptions

include <iostream>

using namespace std; int main () { try { throw 20; } catch (int e) { cout << "An exception occurred. Exception Nr. " << e << endl; } return 0; } `

Structured Exception Handling(SEH)

Khi 1 SEH exception xảy ra,bạn sẽ thấy cửa sổ của Dr. Watson(có nút send error report).Bạn cũng có thể sinh ra SEH exception của chính bạn bằng cách dùng hàm RaiseException()

Mỗi SEH exception có 1 code exception tương ứng.Bạn có thể extract code exception này trong mệnh đề __except khi sử dụng hàm GetExceptionCode() :

Ví dụ

PHP Code:

int seh_filter(unsigned int code, struct _EXCEPTION_POINTERS* ep) { // SInh error report // thực thi exception handler return EXCEPTION_EXECUTE_HANDLER; } void main() { __try { // gây ra ngoại lệ ở đây } __except(seh_filter(GetExceptionCode(), GetExceptionInformation())) { // dừng chương trình ExitProcess(1); } }

__try{}__except(){} là của C. Tuy nhiên,bạn cũng có thể dùng SEH exception này trên C++ và xử lý nó như làm với kiểu exception của C++.Có thể dùng hàm _set_se_translator() của CRT librrary như sau(code từ MSDN) :

PHP Code:

`// crt_settrans.cpp // compile with: /EHa

include <stdio.h>

include <windows.h>

include <eh.h>

void SEFunc(); void trans_func( unsigned int, EXCEPTION_POINTERS* ); class SE_Exception { private: unsigned int nSE; public: SE_Exception() {} SE_Exception( unsigned int n ) : nSE( n ) {} ~SE_Exception() {} unsigned int getSeNumber() { return nSE; } }; int main( void ) { try { _set_se_translator( trans_func ); SEFunc(); } catch( SE_Exception e ) { printf( "Caught a __try exception with SE_Exception.\n" ); } } void SEFunc() { __try { int x, y=0; x = 5 / y; } __finally { printf( "In finally\n" ); } } void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp ) { printf( "In trans_func.\n" ); throw SE_Exception(); } `

Tuy nhiên,điểm bất lợi của __try{}__catch(Expression){} là bạn có thể quên bảo vệ các đoạn code tiềm tàng lỗi có thể gây ra 1 exception.Một SEH exception không hợp lệ có thể được gây ra từ việc sử dụng các bộ lọc exception k hợp lệ(do hàm SetUnhandledExceptionFilter() tạo ra.Để hiểu về SEH các bạn có thể xem bài viết antidebug,kỹ thuật dùng SetUnhandledExceptionFilter tại link sau : ……)

Thông tin exception sẽ được đưa vào exception handler qua cấu trúc EXCEPTION_POINTERS

Ví dụ :

PHP Code:

LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionPtrs) { // tạo ra error report vào đây //.. // Thực thi exception handler tiếp theo ... return EXCEPTION_EXECUTE_HANDLER; } void main() { SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); }

Nếu hàm exception handler được load từ DLL,bạn nên cẩn thận khi sử dụng hàm SetUnhandledExceptionFilter .Nếu DLL không được unload trong lúc chương trình bị crack,thì sẽ khó lường trước những gì sẽ xảy ra tiếp theo

Chú ý,trong Windows 7,có 1 hàm mới là RaiseFailFastException.Hàm này chấp nhận bỏ qua mọi cài đặt exception handlers (không tính SEH và vectored) và bỏ qua ngoại lệ đến thẳng màn hình report.Tiêu biểu là,bạn gọi hàm này nếu ứng dụng bạn đang ở 1 trạng thái không tốt và bạn muốn tạo ra bảng Windows Error Report(chương trình quá nặng,chưa load kịp các bước tiếp theo chẳn hạn).

Vectored Exception Handling

Vectored Exception Handling (VEH) là 1 phần mở rộng của cấu trúc exception handling.Nó được giới thiệu đầu tiên ở windows xp.Để thêm 1 Vectored Exception Handling,ta cần dùng hàm AddVectoredExceptionHandler.Điểm bất lợi của VEH là nó chỉ dùng cho windows xp trở về sau.

Để gỡ bỏ,ta dùng hàm RemoveVectoredExceptionHandler.

VE chấp nhận theo dõi hoặc thực thi mọi SEH exceptions của ứng dụng.Để bảo đảm sự tương thích,khi 1 SEH exception gây ra lỗi trong phần nào đó của chương trình,hệ thống sẽ gọi cài đặt VEH,sau đó mới đến SEH/

1 điểm thuận lợi của VEH nữa là nó có thể liên kết các exception handlers,để nếu khi ai đó cài VEH trên exception của bạn,bạn vẫn chặn được các exception này

VEH thích hợp khi bạn cần theo dõi các SEH exceptions,như debugger làm.Nhưng có 1 vấn đề ở đây đó là khi bạn quyết định exceptions nào cần bỏ qua.Trong code của ứng dụng,vài exceptions có thể được bảo vệ bằng try-catch,và VEH không tự động trong các frame-based SEH.

Bản thân tôi nghĩ hàm SetUnhandledExceptionFilter thì hay hơn.Vì nó là top-level.Nghĩa là nếu ta không làm gì với ngoại lệ thì SEH vẫn được gọi.

CRT Error Handling

Trong trường hợp SEH exceptions và C++ exceptions,CRT cung cấp những error.Khi 1 lỗi của CRT xảy ra,bạn có thể thấy 1 message sau :

Terminate Handler Khi CRT gặp 1 unhandled exception trong C++,nó sẽ gọi hàm terminate().để nó chặn các lời gọi và thực thi các hành động đúng,bạn nên thiết lập error hanler bằng hàm set_terminate(). Ví dụ :

PHP Code:

void my_terminate_handler() { // Abnormal program termination (terminate() function was called) // Do something here // Finally, terminate program exit(1); } void main() { set_terminate(my_terminate_handler); terminate(); }

Trong môi trường đa luồng thì các hàm unexpected và terminate không liên quan với nhau.Vì thế cần chú ý đến từng luồng

Pure Call Handler

Sử dụng hàm _set_purecall_handler() để xử lý ngoại lệ của việc gọi 1 hàm thuần ảo.Hàm này dùng cho VC7 trở về sau.

Ví dụ

PHP Code:

`// _set_purecall_handler.cpp // compile with: /W1

include <tchar.h>

include <stdio.h>

include <stdlib.h>

class CDerived; class CBase { public: CBase(CDerived derived): m_pDerived(derived) {}; ~CBase(); virtual void function(void) = 0; CDerived m_pDerived; }; class CDerived : public CBase { public: CDerived() : CBase(this) {}; // C4355 virtual void function(void) {}; }; CBase::~CBase() { m_pDerived -> function(); } void myPurecallHandler(void) { printf("In _purecall_handler."); exit(0); } int _tmain(int argc, _TCHAR* argv[]) { _set_purecall_handler(myPurecallHandler); CDerived myDerived; } `

New Operator Fault Handler

_set_new_handler() xử lý lỗi memory allocation faults(từ VC7,dùng cho mọi thread của process). hàm _set_new_mode() tìm ra lỗi của hàm malloc()

Ví dụ :

PHP Code:

`; Set a new SEH frame push ebp ; EBP at safe-place (needed for ENTER/LEAVE) push addr _resume_at_safe_place ; Addr. of safe-place push addr _exception_handler ; Addr. of our exception handler push dword ptr fs:[0] ; Addr. of previous handler mov fs:[0], esp ; Install new SEH handler

; ... try code ...

_resume_at_safe_place: ; Remove SEH frame pop dword ptr fs:[0] ; Remove SEH handler add esp, 3*4 ; Remove additional data from stack

`0

Invalid Parameter Handler

hàm _set_invalid_parameter_handler() xử lý sai tham số đầu vào,cho mọi thread,từ VC7 trờ lên

Ví dụ :

PHP Code:

`; Set a new SEH frame push ebp ; EBP at safe-place (needed for ENTER/LEAVE) push addr _resume_at_safe_place ; Addr. of safe-place push addr _exception_handler ; Addr. of our exception handler push dword ptr fs:[0] ; Addr. of previous handler mov fs:[0], esp ; Install new SEH handler

; ... try code ...

_resume_at_safe_place: ; Remove SEH frame pop dword ptr fs:[0] ; Remove SEH handler add esp, 3*4 ; Remove additional data from stack

`1

C++ Signal Handling

C++ chung cấp cho 1 chương trình sử dụng 1 yêu cầu ngắt.Bạn có thể xử lý tín hiệu ngắt ngày với hàm signal()

CÓ 6 tín hiệu ngắt trên VC++

SIGABRT Abnormal termination SIGFPE Floating-point error SIGILL Illegal instruction SIGINT CTRL+C signal SIGSEGV Illegal storage access SIGTERM Termination request

Theo MSDN thì SIGILL, SIGSEGV, và SIGTERM không được sinh ra và tương tự với ANSI.Tuy nhiên,nếu ta thiết lập SIGSEGV trong thread chính,nó sẽ được gọi bởi CRT thay vì SEH exception handler với hàm SetUnhandledExceptionFilter() và biến _pxcptinfoptrs chứa con trỏ đến exception information.

Biến _pxcptinfoptrs cũng được dùng trong SIGFPE handler.Trong các signal khác nó bằng NULL

SIGFPE signal handler được gọi khi floating point lỗi ,như chia cho 0 chẳn hạn.Ta có thể sinh ra 6 signals bằng cách dùng hàm raise

Ví dụ :

PHP Code:

`; Set a new SEH frame push ebp ; EBP at safe-place (needed for ENTER/LEAVE) push addr _resume_at_safe_place ; Addr. of safe-place push addr _exception_handler ; Addr. of our exception handler push dword ptr fs:[0] ; Addr. of previous handler mov fs:[0], esp ; Install new SEH handler

; ... try code ...

_resume_at_safe_place: ; Remove SEH frame pop dword ptr fs:[0] ; Remove SEH handler add esp, 3*4 ; Remove additional data from stack

`2

Chú ý: Vì không phải là tài liệu chuẩn trên MSDN,nhưng dường như bạn cũng nên cài SIGFPE, SIGILL, and SIGSEGV signal handlers cho 1 thread mới trong chương trình.Còn SIGABRT, SIGINT, and SIGTERM signal handlers làm việc cho mọi thread,vì thế ta nên cài 1 cái vào hàm main.

Retrieving Exception Information

Khi 1 exception xảy ra,bạn muốn xem tình trạng của CPU để xác định nơi nào trong code gây ra vấn đề này,ta có thể dùng hàm MiniDumpWriteDump(). Trong 1 SEH exception handler được thiết lập bởi hàm SetUnhandledExceptionFilter() ,thông tin của exception này được lấy ra từ cấu trúc EXCEPTION_POINTERS. Trong try-catch,bạn lấy thông tin của exception bằng hàm intrinsic GetExceptionInformation() và đưa vào bộ quản lý các SEH exception như 1 tham số.

Trong SIGFPE và SIGSEGV signal handlers,ta có thể lấy thông tin của exception từ biến _pxcptinfoptrs và được khai báo ở <signal.h>.Biến này là 1 undocument.

Nhưng signal handlers khác ở CRT error handlers,bạn không thể dễ dàng extract thông tin exception.Tôi tìm 1 cách khắc phục(xem source file đính kèm,dong 104)

Đoạn code sau sẽ chỉ ra cách lấy trang thái CPU được sử dụng như 1 thông tin của exception :

PHP Code:

`; Set a new SEH frame push ebp ; EBP at safe-place (needed for ENTER/LEAVE) push addr _resume_at_safe_place ; Addr. of safe-place push addr _exception_handler ; Addr. of our exception handler push dword ptr fs:[0] ; Addr. of previous handler mov fs:[0], esp ; Install new SEH handler

; ... try code ...

_resume_at_safe_place: ; Remove SEH frame pop dword ptr fs:[0] ; Remove SEH handler add esp, 3*4 ; Remove additional data from stack

`3

Exception Handling and CRT Linkage

Mỗi module(exe,dll) trong ứng dụng của bạn đều được liên kết đến CRT.Bạn có thể liên kết CRT như a thư viện tĩnh hoặc 1 thư viện động.Khi thiết lập CRT error handlers,như terminate handler, unexpected handler, pure call handler, invalid parameter handler .. thì 1 toán tử error handler hay còn gọi là 1 signal handler sẽ làm việc cho CRT và lời gọi module được liên kết đến và sẽ không chặn các exception ỏ các CRT module khác nhau(không bị nhầm lẫn và chặn thực thi lẫn nhau),vì mỗi CRT module có một tín hiệu trạng thái riêng của nó của nó.

Vài module có thể share 1 DLL.Điều này giảm đi tối thiểu kích thước của CRT code.Đó là lý do vì sao multi-threaded CRT DLL được đề xuất thay cho CRT tĩnh.Tuy nhiên,nhiều lập trình viên vẫn thích cách dùng CRT tĩnh thông dụng vì nó dễ để phân phối các module đơn lẻ hơn là phân phối các module thực thi dựa trên cùng 1 module.Để có thêm thông tin,xem bài viết sau đây http://www.codeproject.com/Articles/1915…ATL-and-CR

Nếu bạn có dự định chỉ dùng 1 thư viện CRT tĩnh và muốn dùng các hàm exception handling 1 cách đơn thuần,bạn phải build các hàm này thành 1 thư viện này với cờ /NODEFAULTLIB,và sau đó link đến file exe mà dll của chương trình bạn.Bạn cũng có thể phải cài đặt CRT error handler cho từng module cho từng ứng dụng

Visual C++ Compiler Flags

Có vài trình dịch VC++ chuyển đổi giữa các exception handling.Trên VS2008 thì các kiểu exception nằm ở mục Properties->Configuration Properties->C/C++->Code Generation.Ta sẽ phân tích từng field trong đó.

Exception Handling Model

Bạn có thể thiết lập 1 kiểu exception handling với các option là /EHs (or EHsc) để đồng bộ với các kiểu exception handling,hay /EHa để thực hiện các exception handling không đồng bộ.Các kiểu không đồng bộ có thể được sử dụng trong try-catch để bắt exception của của SEH và của exception của C++.Nếu thiết lập kiểu đồng bộ thì try-catch sẽ không gây ra SEH exceptions

Floating Point Exceptions

Bạn có thể bật floating point exceptions bằng cách chọn cờ /fp:except.Option này mặc định bị tắt,vì thế floating point exceptions sẽ không hoạt động.

Buffer Security Checks

Mặc định thì ta có option /GS (Buffer Security Check),cờ này giúp chèn code để kiểm tra về các buffer khi việc đọc ghi các khối nhớ lớn xảy ra,làm xảy ra tràn các buffer

Chú ý : trong VC++ 7.1 trở lên,bạn có thể dùng hàm _set_security_error_handler() để gọi khi 1 buffer overrun xuất hiện.Tuy nhiên,hàm này đã bị bỏ ở các version sau của CRT

TỪ CRT 8.0 trở đi,bạn không thể chặn lỗi buffer overrun trong code.Khi buffer overrun xảy ra,CRT sẽ hiện cửa sổ Dr.Watson thay vì gọiđến bộ quản lý exception.Để biết thêm thông tin,xem link sau: