Tôi cứ tự hỏi làm thế nào để một trình sửa lỗi hoạt động? Một phần có thể được 'đính kèm' để chạy thực thi. Tôi hiểu rằng trình biên dịch dịch mã sang ngôn ngữ máy, nhưng làm thế nào để trình gỡ lỗi 'biết' nó đang được gắn vào cái gì?
Tôi cứ tự hỏi làm thế nào để một trình sửa lỗi hoạt động? Một phần có thể được 'đính kèm' để chạy thực thi. Tôi hiểu rằng trình biên dịch dịch mã sang ngôn ngữ máy, nhưng làm thế nào để trình gỡ lỗi 'biết' nó đang được gắn vào cái gì?
Câu trả lời:
Các chi tiết về cách trình gỡ lỗi hoạt động sẽ phụ thuộc vào những gì bạn đang gỡ lỗi và hệ điều hành là gì. Để gỡ lỗi gốc trên Windows, bạn có thể tìm thấy một số chi tiết về MSDN: API gỡ lỗi Win32 .
Người dùng cho trình gỡ lỗi biết quy trình nào sẽ đính kèm, theo tên hoặc theo ID tiến trình. Nếu đó là một tên thì trình gỡ lỗi sẽ tra cứu ID tiến trình và bắt đầu phiên gỡ lỗi thông qua một cuộc gọi hệ thống; trong Windows, đây sẽ là DebugActiveProcess .
Sau khi được đính kèm, trình gỡ lỗi sẽ vào một vòng lặp sự kiện giống như bất kỳ giao diện người dùng nào, nhưng thay vì các sự kiện đến từ hệ thống cửa sổ, HĐH sẽ tạo ra các sự kiện dựa trên những gì xảy ra trong quá trình được gỡ lỗi - ví dụ: xảy ra ngoại lệ. Xem WaitForDebugEvent .
Trình gỡ lỗi có thể đọc và ghi bộ nhớ ảo của tiến trình đích và thậm chí điều chỉnh các giá trị đăng ký của nó thông qua các API do HĐH cung cấp. Xem danh sách các chức năng sửa lỗi cho Windows.
Trình gỡ lỗi có thể sử dụng thông tin từ các tệp ký hiệu để dịch từ địa chỉ sang tên biến và vị trí trong mã nguồn. Thông tin tệp biểu tượng là một bộ API riêng biệt và không phải là một phần cốt lõi của HĐH. Trên Windows, điều này thông qua SDK truy cập giao diện gỡ lỗi .
Nếu bạn đang gỡ lỗi một môi trường được quản lý (.NET, Java, v.v.), quy trình thường sẽ trông tương tự, nhưng các chi tiết thì khác, vì môi trường máy ảo cung cấp API gỡ lỗi thay vì HĐH cơ bản.
Như tôi hiểu nó:
Đối với các điểm dừng phần mềm trên x86, trình gỡ lỗi thay thế byte đầu tiên của lệnh bằng CC
( int3
). Điều này được thực hiện với WriteProcessMemory
trên Windows. Khi CPU nhận được lệnh đó và thực thi lệnh int3
này, điều này khiến CPU tạo ra ngoại lệ gỡ lỗi. HĐH nhận được ngắt này, nhận ra quá trình đang được gỡ lỗi và thông báo cho quá trình gỡ lỗi mà điểm dừng bị tấn công.
Sau khi điểm dừng được nhấn và quá trình dừng lại, trình gỡ lỗi sẽ xem danh sách các điểm dừng của nó và thay thế CC
bằng byte đã có ở đó. Trình gỡ lỗi thiết lập TF
, Cờ bẫy trong EFLAGS
(bằng cách sửa đổi CONTEXT
) và tiếp tục quá trình. Cờ bẫy khiến CPU tự động tạo ngoại lệ một bước ( INT 1
) cho lệnh tiếp theo.
Khi quá trình được gỡ lỗi dừng lại vào lần tiếp theo, trình gỡ lỗi lại thay thế byte đầu tiên của lệnh breakpoint CC
và quá trình tiếp tục.
Tôi không chắc liệu đây có chính xác là cách nó được triển khai bởi tất cả các trình gỡ lỗi hay không, nhưng tôi đã viết một chương trình Win32 để tự gỡ lỗi bằng cơ chế này. Hoàn toàn vô dụng, nhưng giáo dục.
Trong Linux, gỡ lỗi một quy trình bắt đầu bằng lệnh gọi hệ thống ptrace (2) . Bài viết này có một hướng dẫn tuyệt vời về cách sử dụng ptrace
để thực hiện một số cấu trúc gỡ lỗi đơn giản.
(2)
cho chúng tôi biết một cái gì đó nhiều hơn (hoặc ít hơn) so với "ptrace là một cuộc gọi hệ thống"?
(2)
là số phần thủ công. Xem en.wikipedia.org/wiki/Man_page#Manual_sections để biết mô tả về các phần thủ công.
ptrace
là một cuộc gọi hệ thống.
(2)
cho chúng ta biết rằng chúng ta có thể gõ man 2 ptrace
và lấy đúng trang chủ - không quan trọng ở đây vì không có gì khác ptrace
để định hướng, nhưng để so sánh man printf
với man 3 printf
Linux.
Nếu bạn đang dùng HĐH Windows, một tài nguyên tuyệt vời cho việc này sẽ là "Gỡ lỗi ứng dụng cho Microsoft .NET và Microsoft Windows" của John Robbins:
(hoặc thậm chí phiên bản cũ hơn: "Ứng dụng gỡ lỗi" )
Cuốn sách có một chương về cách một trình gỡ lỗi hoạt động bao gồm mã cho một vài trình gỡ lỗi đơn giản (nhưng hoạt động).
Vì tôi không quen thuộc với các chi tiết về gỡ lỗi Unix / Linux, nên công cụ này hoàn toàn không thể áp dụng cho các hệ điều hành khác. Nhưng tôi đoán rằng như là một giới thiệu về một chủ đề rất phức tạp, các khái niệm - nếu không phải là chi tiết và API - nên 'chuyển' sang hầu hết mọi hệ điều hành.
Một nguồn có giá trị khác để hiểu gỡ lỗi là hướng dẫn sử dụng CPU Intel (Hướng dẫn dành cho nhà phát triển phần mềm kiến trúc Intel® 64 và IA-32). Trong tập 3, chương 16, nó đã giới thiệu phần hỗ trợ gỡ lỗi, chẳng hạn như các ngoại lệ đặc biệt và các thanh ghi gỡ lỗi phần cứng. Sau đây là từ chương đó:
Cờ T (bẫy), TSS - Tạo ngoại lệ gỡ lỗi (#DB) khi thực hiện một nỗ lực để chuyển sang một tác vụ với cờ T được đặt trong TSS của nó.
Tôi không chắc liệu Window hay Linux có sử dụng cờ này hay không, nhưng rất thú vị khi đọc chương đó.
Hy vọng điều này sẽ giúp được ai đó.
Tôi nghĩ có hai câu hỏi chính để trả lời ở đây:
1. Làm thế nào trình gỡ lỗi biết rằng một ngoại lệ xảy ra?
Khi một ngoại lệ xảy ra trong một quá trình đang được gỡ lỗi, trình gỡ lỗi sẽ được HĐH thông báo trước khi bất kỳ trình xử lý ngoại lệ người dùng nào được xác định trong quy trình đích sẽ có cơ hội phản hồi ngoại lệ đó. Nếu trình gỡ lỗi chọn không xử lý thông báo ngoại lệ (cơ hội đầu tiên) này, trình tự gửi ngoại lệ sẽ tiếp tục và luồng đích sẽ có cơ hội xử lý ngoại lệ nếu nó muốn làm như vậy. Nếu ngoại lệ SEH không được xử lý bởi quy trình đích, trình gỡ lỗi sau đó được gửi một sự kiện gỡ lỗi khác, được gọi là thông báo cơ hội thứ hai, để thông báo cho nó rằng một ngoại lệ chưa được xử lý xảy ra trong quy trình đích. Nguồn
2. Làm thế nào trình gỡ lỗi biết làm thế nào để dừng trên một điểm dừng?
Câu trả lời đơn giản là: Khi bạn đặt điểm dừng vào chương trình, trình gỡ lỗi sẽ thay thế mã của bạn tại thời điểm đó bằng lệnh int3 là phần mềm bị gián đoạn . Như một hiệu ứng, chương trình bị đình chỉ và trình gỡ lỗi được gọi.
Hiểu biết của tôi là khi bạn biên dịch một ứng dụng hoặc tệp DLL, bất cứ thứ gì nó biên dịch để chứa các ký hiệu đại diện cho các hàm và các biến.
Khi bạn có bản dựng gỡ lỗi, các biểu tượng này chi tiết hơn nhiều so với khi đó là bản dựng phát hành, do đó cho phép trình gỡ lỗi cung cấp cho bạn thêm thông tin. Khi bạn đính kèm trình gỡ lỗi vào một tiến trình, nó sẽ xem xét các hàm nào hiện đang được truy cập và giải quyết tất cả các biểu tượng gỡ lỗi có sẵn từ đây (vì nó biết các phần bên trong của tệp được biên dịch trông như thế nào, nó có thể xác định được những gì có thể có trong bộ nhớ , với nội dung của ints, float, chuỗi, v.v.). Giống như poster đầu tiên đã nói, thông tin này và cách các biểu tượng này hoạt động phụ thuộc rất nhiều vào môi trường và ngôn ngữ.