Làm thế nào để bạn gỡ lỗi một định dạng nhị phân?


11

Tôi muốn có thể gỡ lỗi xây dựng một trình xây dựng nhị phân. Ngay bây giờ tôi về cơ bản đang in dữ liệu đầu vào sang trình phân tích cú pháp nhị phân, sau đó đi sâu vào mã và in ánh xạ của đầu vào sang đầu ra, sau đó lấy ánh xạ đầu ra (số nguyên) và sử dụng dữ liệu đó để xác định số nguyên tương ứng trong nhị phân. Khá cồng kềnh và yêu cầu tôi phải sửa đổi mã nguồn sâu để có được ánh xạ giữa đầu vào và đầu ra.

Có vẻ như bạn có thể xem nhị phân trong các biến thể khác nhau (trong trường hợp của tôi, tôi muốn xem nó ở dạng khối 8 bit dưới dạng số thập phân, vì nó khá gần với đầu vào). Trên thực tế, một số số là 16 bit, một số 8, một số 32, v.v. Vì vậy, có thể sẽ có một cách để xem nhị phân với mỗi số khác nhau được tô sáng trong bộ nhớ theo một cách nào đó.

Cách duy nhất tôi có thể thấy là có thể là nếu bạn thực sự xây dựng một trình hiển thị cụ thể cho định dạng / bố cục nhị phân thực tế. Vì vậy, nó biết vị trí của các số 32 bit và vị trí của các số 8 bit, v.v ... Đây là rất nhiều công việc và loại khó khăn trong một số tình huống. Vì vậy, tự hỏi nếu có một cách chung để làm điều đó.

Tôi cũng đang tự hỏi cách gỡ lỗi chung của loại điều này hiện nay là gì, vì vậy có lẽ tôi có thể có một số ý tưởng về những gì cần thử từ đó.


75
Bạn có một câu trả lời rằng "sử dụng hexdump trực tiếp, và làm điều này và điều đó nữa" - và câu trả lời đó đã nhận được rất nhiều sự ủng hộ. Và câu trả lời thứ hai, 5 giờ sau (!), Chỉ nói "sử dụng hexdump". Sau đó, bạn chấp nhận cái thứ hai có lợi cho cái thứ nhất? Nghiêm túc?
Doc Brown

4
Mặc dù bạn có thể có lý do chính đáng để sử dụng định dạng nhị phân, hãy xem xét liệu bạn có thể chỉ sử dụng định dạng văn bản hiện có như JSON không. Khả năng đọc của con người rất nhiều, và máy móc và mạng thường đủ nhanh để sử dụng định dạng tùy chỉnh để giảm kích thước là không cần thiết hiện nay.
jpmc26

4
@ jpmc26 vẫn còn rất nhiều sử dụng cho các định dạng nhị phân và sẽ luôn như vậy. Khả năng đọc của con người thường là thứ yếu đối với hiệu suất, yêu cầu lưu trữ và hiệu suất mạng. Và vẫn còn nhiều lĩnh vực mà đặc biệt là hiệu suất mạng kém và lưu trữ bị hạn chế. Ngoài ra, đừng quên tất cả các hệ thống phải giao tiếp với các hệ thống cũ (cả phần cứng và phần mềm) và phải hỗ trợ các định dạng dữ liệu của chúng.
jwenting

4
@jwenting Không, thực ra, thời gian dành cho nhà phát triển thường là phần đắt nhất của một ứng dụng. Chắc chắn, đó có thể không phải là trường hợp nếu bạn làm việc tại Google hoặc Facebook, nhưng hầu hết các ứng dụng không hoạt động ở quy mô đó. Và khi các nhà phát triển của bạn dành thời gian cho công cụ là tài nguyên đắt nhất, khả năng đọc của con người sẽ cao hơn rất nhiều so với 100 mili giây thêm cho chương trình để phân tích nó.
jpmc26

3
@ jpmc26 Tôi không thấy bất cứ điều gì trong câu hỏi gợi ý cho tôi rằng OP là người xác định định dạng.
JimmyJames

Câu trả lời:


76

Đối với kiểm tra đột xuất, chỉ cần sử dụng hexdump tiêu chuẩn và tìm hiểu nhãn cầu.

Nếu bạn muốn thực hiện một cuộc điều tra thích hợp, tôi thường viết một bộ giải mã riêng trong một cái gì đó như Python - lý tưởng là điều này sẽ được điều khiển trực tiếp từ một tài liệu đặc tả thông báo hoặc IDL, và càng tự động càng tốt (vì vậy không có cơ hội giới thiệu thủ công cùng một lỗi trong cả hai bộ giải mã).

Cuối cùng, đừng quên bạn nên viết bài kiểm tra đơn vị cho bộ giải mã của mình, sử dụng đầu vào đóng hộp đã biết chính xác.


2
"chỉ cần sử dụng một hexdump tiêu chuẩn và học cách đánh cầu nó." Vâng Theo kinh nghiệm của tôi, nhiều phần của bất cứ thứ gì lên tới 200 bit có thể được viết ra trên một bảng trắng để so sánh được nhóm lại, điều này đôi khi giúp với loại điều này để bắt đầu.
Cột

1
Tôi thấy một bộ giải mã riêng đáng giá cho nỗ lực nếu dữ liệu nhị phân đóng một phần quan trọng trong ứng dụng (hoặc hệ thống, nói chung). Điều này đặc biệt đúng nếu định dạng dữ liệu là biến: dữ liệu trong các bố cục cố định có thể được phát hiện trong một hexdump với một chút thực hành nhưng nhanh chóng đạt được một bức tường khả thi. Chúng tôi đã gỡ lỗi lưu lượng USB và CAN bằng bộ giải mã gói thương mại và tôi đã viết một bộ giải mã PROFIBus (trong đó các biến trải đều trên byte, hoàn toàn không thể đọc được trong kết xuất hex) và thấy cả ba trong số chúng đều hữu ích.
Peter - Tái lập Monica

10

Bước đầu tiên để làm điều này là bạn cần một cách để tìm hoặc xác định một ngữ pháp mô tả cấu trúc của dữ liệu tức là một lược đồ.

Một ví dụ về điều này là một tính năng ngôn ngữ của COBOL, được gọi một cách không chính thức là copybook. Trong các chương trình COBOL, bạn sẽ xác định cấu trúc của dữ liệu trong bộ nhớ. Cấu trúc này ánh xạ trực tiếp đến cách các byte được lưu trữ. Điều này là phổ biến đối với các ngôn ngữ trong thời đại đó trái ngược với các ngôn ngữ đương đại phổ biến trong đó bố cục vật lý của bộ nhớ là mối quan tâm triển khai được trừu tượng hóa khỏi nhà phát triển.

Một tìm kiếm google cho ngôn ngữ lược đồ dữ liệu nhị phân xuất hiện một số công cụ. Một ví dụ là Apache DFDL . Có thể đã có UI cho điều này là tốt.


2
Tính năng này không dành riêng cho các ngôn ngữ thời đại 'cổ đại'. Các cấu trúc và liên kết C và C ++ có thể được căn chỉnh theo bộ nhớ. C # có StructLayoutAttribution, mà tôi đã sử dụng để truyền dữ liệu nhị phân.
Kasper van den Berg

1
@KaspervandenBerg Trừ khi bạn nói rằng C và C ++ đã thêm những thứ này gần đây, tôi coi đó là cùng thời đại. Vấn đề là các định dạng này không chỉ đơn giản là để truyền dữ liệu, mặc dù chúng được sử dụng cho điều đó, chúng ánh xạ trực tiếp đến cách mã làm việc với dữ liệu trong bộ nhớ và trên đĩa. Nói chung, đó không phải là cách các ngôn ngữ mới hơn có xu hướng hoạt động mặc dù chúng có thể có các tính năng như vậy.
JimmyJames

@KaspervandenBerg C ++ không làm điều đó nhiều như bạn nghĩ. Có thể sử dụng công cụ dành riêng cho việc triển khai để căn chỉnh và loại bỏ phần đệm (và, thừa nhận, ngày càng tăng tiêu chuẩn là thêm các tính năng cho loại điều này) và thứ tự thành viên là xác định (nhưng không nhất thiết giống như trong bộ nhớ!).
Các cuộc đua nhẹ nhàng trong quỹ đạo

6

ASN.1 , Ký hiệu cú pháp trừu tượng Một, cung cấp một cách chỉ định định dạng nhị phân.

  • DDT - Phát triển bằng cách sử dụng dữ liệu mẫu và kiểm tra đơn vị.
  • Một bãi chứa văn bản có thể hữu ích. Nếu trong XML, bạn có thể thu gọn / mở rộng các chế độ con.
  • ASN.1 không thực sự cần thiết nhưng dựa trên ngữ pháp, đặc tả tệp khai báo dễ dàng hơn.

6
Nếu cuộc diễu hành không bao giờ kết thúc của các lỗ hổng bảo mật trong trình phân tích cú pháp ASN.1 là bất kỳ dấu hiệu nào, việc áp dụng nó chắc chắn sẽ cung cấp bài tập tốt trong việc gỡ lỗi các định dạng nhị phân.
Đánh dấu

1
@Mark nhiều mảng byte nhỏ (và trong các cây phân cấp khác nhau) thường không được xử lý đúng (an toàn) trong C (ví dụ: không sử dụng ngoại lệ). Không bao giờ đánh giá thấp mức độ thấp, tính không an toàn vốn có của C. ASN.1 trong - ví dụ - java không phơi bày vấn đề này. Vì một phân tích cú pháp theo ngữ pháp ASN.1 có thể được thực hiện một cách an toàn, thậm chí C có thể được thực hiện với một cơ sở mã nhỏ và an toàn. Và một phần của các lỗ hổng vốn có của chính định dạng nhị phân: người ta có thể khai thác các cấu trúc "hợp pháp" của ngữ pháp định dạng, có ngữ nghĩa tuyệt vọng.
Eggen

3

Các câu trả lời khác đã mô tả việc xem kết xuất hex hoặc viết ra các cấu trúc đối tượng trong JSON. Tôi nghĩ rằng kết hợp cả hai điều này là rất hữu ích.

Sử dụng một công cụ có thể hiển thị JSON trên đỉnh kết xuất hex thực sự hữu ích; Tôi đã viết một công cụ nguồn mở phân tích cú pháp nhị phân .NET được gọi là dotNetBytes , đây là một ví dụ về một ví dụ DLL .

Ví dụ dotNetBytes


1

Tôi không chắc là tôi hoàn toàn hiểu, nhưng có vẻ như bạn có một trình phân tích cú pháp cho định dạng nhị phân này và bạn kiểm soát mã cho nó. Vì vậy, câu trả lời này được xây dựng trên giả định đó.

Trình phân tích cú pháp theo một cách nào đó sẽ lấp đầy các cấu trúc, lớp hoặc bất kỳ cấu trúc dữ liệu nào mà ngôn ngữ của bạn có. Nếu bạn thực hiện một ToStringcho tất cả mọi thứ được phân tích cú pháp, thì bạn sẽ kết thúc bằng một phương pháp rất dễ sử dụng và dễ dàng duy trì để hiển thị dữ liệu nhị phân đó ở định dạng có thể đọc được.

Về cơ bản bạn sẽ có:

byte[] arrayOfBytes; // initialized somehow
Object obj = Parser.parse(arrayOfBytes);
Logger.log(obj.ToString());

Và đó là nó, từ quan điểm của việc sử dụng nó. Tất nhiên, điều này đòi hỏi bạn phải thực hiện / ghi đè ToStringhàm cho Objectlớp / struct / bất cứ thứ gì của bạn và bạn cũng sẽ phải làm như vậy đối với bất kỳ lớp / cấu trúc / trình tự lồng nhau nào.

Ngoài ra, bạn có thể sử dụng một câu lệnh có điều kiện để ngăn ToStringchức năng được gọi trong mã phát hành để bạn không lãng phí thời gian vào thứ gì đó sẽ không được ghi lại bên ngoài chế độ gỡ lỗi.

Bạn ToStringcó thể trông như thế này:

return String.Format("%d,%d,%d,%d", int32var, int16var, int8var, int32var2);

// OR

return String.Format("%s:%d,%s:%d,%s:%d,%s:%d", varName1, int32var, varName2, int16var, varName3, int8var, varName4, int32var2);

Câu hỏi ban đầu của bạn có vẻ như bạn đã cố gắng thực hiện điều này và bạn nghĩ rằng phương pháp này rất nặng nề, nhưng đôi khi bạn cũng đã thực hiện phân tích cú pháp định dạng nhị phân và tạo các biến để lưu trữ dữ liệu đó. Vì vậy, tất cả những gì bạn phải làm là in các biến hiện có ở mức độ trừu tượng thích hợp (lớp / struct của biến nằm trong).

Đây là điều bạn chỉ nên làm một lần và bạn có thể làm điều đó trong khi xây dựng trình phân tích cú pháp. Và nó sẽ chỉ thay đổi khi định dạng nhị phân thay đổi (điều này sẽ nhắc thay đổi đối với trình phân tích cú pháp của bạn).

Theo cách tương tự: một số ngôn ngữ có các tính năng mạnh mẽ để biến các lớp thành XML hoặc JSON. C # đặc biệt tốt ở đây. Bạn không phải từ bỏ định dạng nhị phân của mình, bạn chỉ cần thực hiện XML hoặc JSON trong một câu lệnh ghi nhật ký gỡ lỗi và để lại mã phát hành của bạn.

Cá nhân tôi khuyên bạn không nên đi theo đường kết xuất hex, bởi vì nó dễ bị lỗi (bạn đã bắt đầu ở byte bên phải chưa, bạn có chắc chắn khi bạn đọc từ trái sang phải mà bạn đang "nhìn thấy" độ chính xác, v.v.) .

Ví dụ: Nói các ToStringsbiến nhổ của bạn a,b,c,d,e,f,g,h. Bạn chạy chương trình của mình và nhận thấy có lỗi g, nhưng vấn đề thực sự bắt đầu với c(nhưng bạn đang gỡ lỗi, vì vậy bạn chưa tìm ra điều đó). Nếu bạn biết các giá trị đầu vào (và bạn nên), bạn sẽ thấy ngay đó clà vấn đề bắt đầu.

So với một bãi chứa hex chỉ cho bạn biết 338E 8455 0000 FF76 0000 E444 ....; nếu các trường của bạn có kích thước khác nhau, cbắt đầu từ đâu và giá trị là gì - trình soạn thảo hex sẽ cho bạn biết nhưng quan điểm của tôi là đây dễ bị lỗi và mất thời gian. Không chỉ vậy, nhưng bạn không thể dễ dàng / nhanh chóng tự động hóa một bài kiểm tra thông qua trình xem hex. In ra một chuỗi sau khi phân tích dữ liệu sẽ cho bạn biết chính xác chương trình của bạn đang 'nghĩ gì' và sẽ là một bước trong quá trình kiểm tra tự động.

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.