이진 형식을 어떻게 디버깅합니까?


11

이진 빌더 빌드를 디버그 할 수 있기를 원합니다. 지금은 기본적으로 입력 데이터를 이진 파서에 인쇄 한 다음 코드에 대해 깊이 들어가서 출력에 대한 입력 매핑을 인쇄 한 다음 출력 매핑 (정수)을 사용하여 해당 정수를 찾습니다. 이진에서. 꽤 어색하고 입력과 출력 사이의 매핑을 얻기 위해 소스 코드를 깊이 수정해야합니다.

바이너리를 다른 변형으로 볼 수있는 것처럼 보입니다 (제 경우에는 입력에 매우 가깝기 때문에 8 비트 청크로 10 진수로 보려고합니다). 실제로 일부 숫자는 16 비트, 일부 8, 일부 32 등입니다. 따라서 메모리에서이 서로 다른 숫자가 강조된 방식으로 바이너리를 보는 방법이있을 수 있습니다.

내가 볼 수있는 유일한 방법은 실제로 이진 형식 / 레이아웃에 특정한 시각화 프로그램을 작성하는 것입니다. 따라서 시퀀스에서 32 비트 숫자의 위치와 8 비트 숫자의 위치 등을 알고 있습니다. 이는 일부 상황에서 많은 작업과 까다로운 작업입니다. 일반적인 방법이 있는지 궁금합니다.

또한이 유형의 것을 디버깅하는 일반적인 방법이 무엇인지 궁금합니다. 그래서 시도해 볼 수있는 아이디어를 얻을 수 있습니다.


75
"16 진수 덤프를 직접 사용하고이 작업을 추가로 수행하십시오"라는 답이 하나 있습니다. 그 답은 많은 찬사를 받았습니다. 그리고 5 시간 후 (!) 두 번째 답변은 "16 진 덤프 만 사용"이라고 말합니다. 그럼 당신은 첫 번째에 찬성하여 두 번째 것을 받아들입니까? 진심이야?
Doc Brown

4
이진 형식을 사용해야 할 충분한 이유가있을 수 있지만 대신 JSON과 같은 기존 텍스트 형식 만 사용할 수 있는지 고려하십시오. 사람의 가독성은 매우 중요하며, 일반적으로 컴퓨터와 네트워크는 크기를 줄이기 위해 사용자 지정 형식을 사용하는 것이 불필요 할 정도로 빠릅니다.
jpmc26

4
@ jpmc26 이진 형식에는 여전히 많이 사용되며 항상 사용됩니다. 사람의 가독성은 일반적으로 성능, 스토리지 요구 사항 및 네트워크 성능보다 중요합니다. 특히 네트워크 성능이 떨어지고 스토리지가 제한되는 영역이 여전히 많습니다. 또한 레거시 시스템 (하드웨어 및 소프트웨어 모두)과 인터페이스하고 데이터 형식을 지원해야하는 모든 시스템을 잊지 마십시오.
jwenting

4
@jwenting 아니요, 실제로 개발자 시간은 일반적 으로 가장 비싼 응용 프로그램입니다. 물론 Google 또는 Facebook에서 작업하는 경우에는 해당되지 않지만 대부분의 앱은 그 규모로 작동하지 않습니다. 개발자가 물건에 시간을 소비하는 것이 가장 비싼 자원 인 경우, 사람의 가독성은 프로그램이 구문 분석하는 데 100 밀리 초가 넘는 시간보다 더 중요합니다.
jpmc26

3
@ jpmc26 질문에 OP가 형식을 정의하는 것임을 제안하는 내용이 없습니다.
JimmyJames

답변:


76

임시 점검의 경우 표준 16 진 덤프를 사용하고 시선을 사로 잡는 방법을 배우십시오.

적절한 조사를 위해 도구를 만들고 싶다면 일반적으로 Python과 같은 별도의 디코더를 작성합니다. 이상적으로는 메시지 사양 문서 또는 IDL에서 직접 구동되며 가능한 한 자동화됩니다 (따라서 수동으로 소개 할 가능성이 없습니다) 두 디코더에서 동일한 버그).

마지막으로, 알려진 올바른 입력을 사용하여 디코더에 대한 단위 테스트를 작성해야한다는 것을 잊지 마십시오.


2
"표준 hexdump를 사용하고 그것을 안구하는 법을 배우십시오." 예. 내 경험상 그룹화 된 비교를 위해 화이트 보드에 최대 200 비트의 여러 섹션을 쓸 수 있으며 때로는 이러한 종류의 작업을 시작하는 데 도움이됩니다.
Mast

1
바이너리 데이터가 응용 프로그램 (또는 일반적으로 시스템)에서 중요한 역할을하는 경우 노력할 가치가있는 별도의 디코더를 찾습니다. 데이터 형식이 가변적 인 경우 특히 그렇습니다. 고정 된 레이아웃의 데이터는 약간의 연습만으로 16 진 덤프에서 발견 할 수 있지만 실행 가능성이 빠릅니다. 상용 패킷 디코더로 USB 및 CAN 트래픽을 디버깅했으며, 변수가 바이트에 분산되어 16 진 덤프에서 읽을 수없는 PROFIBus 디코더를 작성했으며이 세 가지 모두가 매우 유용하다는 것을 알았습니다.
피터-복원 모니카

10

이를위한 첫 번째 단계는 데이터의 구조, 즉 스키마를 설명하는 문법을 찾거나 정의하는 방법이 필요하다는 것입니다.

이에 대한 예는 COBOL의 언어 기능으로 비공식적으로 카피 북이라고합니다. COBOL 프로그램에서는 메모리의 데이터 구조를 정의합니다. 이 구조는 바이트가 저장된 방식에 직접 매핑되었습니다. 이것은 메모리의 물리적 레이아웃이 개발자로부터 추상화 된 구현 관심사 인 일반적인 현대 언어와는 반대로 그 시대의 언어에 공통적입니다.

이진 데이터 스키마 언어에 대한 Google 검색 은 많은 도구를 제공합니다. 예는 Apache DFDL 입니다. 이미 UI가있을 수 있습니다.


2
이 기능은 '고대'시대 언어로 예약되지 않았습니다. C 및 C ++ 구조체와 공용체는 메모리 정렬 될 수 있습니다. C #에는 StructLayoutAttribute가 있는데 이진 데이터를 전송하는 데 사용했습니다.
카스퍼 반 덴 버그

1
@KaspervandenBerg C와 C ++이 최근에 이것을 추가했다고 말하지 않는 한, 나는 같은 시대를 고려합니다. 요점은 이러한 형식은 단순히 데이터 전송을위한 것이 아니라 메모리와 디스크의 데이터에서 코드가 작동하는 방식에 직접 매핑 된 것입니다. 일반적으로 새로운 언어가 그러한 기능을 가지고 있지만 어떻게 작동 하는가는 아닙니다.
JimmyJames

@KaspervandenBerg C ++은 생각만큼 그렇게하지 않습니다. 구현 별 툴링을 사용하여 패딩을 정렬하고 제거 할 수 있으며 (그리고 점점 더 표준이 이런 종류의 기능을 추가하고 있음) 멤버 순서는 결정적입니다 (그러나 반드시 메모리와 동일하지는 않습니다).
궤도에서 가벼움 경주

6

ASN.1 (Abstract Syntax Notation One)은 이진 형식을 지정하는 방법을 제공합니다.

  • DDT-샘플 데이터 및 단위 테스트를 사용하여 개발합니다.
  • 텍스트 덤프가 도움이 될 수 있습니다. XML 인 경우 하위 계층 구조를 축소 / 확장 할 수 있습니다.
  • ASN.1은 실제로 필요하지는 않지만 문법 기반의보다 선언적인 파일 지정이 더 쉽습니다.

6
ASN.1 파서에서 끝없는 보안 취약점의 퍼레이드가 어떤 징후 일 경우이를 채택하면 바이너리 형식 디버깅에 좋은 연습이 될 것입니다.
Mark

1
@ 많은 작은 바이트 배열 (및 다양한 계층 트리에서)은 종종 C에서 올바르게 처리되지 않습니다 (예 : 예외를 사용하지 않음). 예를 들어, java에서 C. ASN.1의 저수준 고유의 안전을 과소 평가하지 마십시오. ASN.1 문법 지정 구문 분석이 안전하게 수행 될 수 있으므로 C조차도 작고 안전한 코드 기반으로 수행 될 수 있습니다. 취약점의 일부는 이진 형식 자체에 내재되어 있습니다.이 형식 형식 문법의 "법적인"구성을 악용 할 수 있습니다.
Joop Eggen

3

다른 답변은 16 진수 덤프를 보거나 JSON에서 객체 구조를 작성하는 것에 대해 설명했습니다. 두 가지를 결합하는 것이 매우 도움이된다고 생각합니다.

16 진 덤프 위에 JSON을 렌더링 할 수있는 도구를 사용하면 실제로 유용합니다. 나는 dotNetBytes 라는 .NET 바이너리를 파싱하는 오픈 소스 도구를 작성했습니다 . 여기 에는 DLL 예제가 있습니다.

dotNetBytes 예제


1

완전히 이해하지는 못했지만이 바이너리 형식에 대한 파서가 있고 코드를 제어하는 ​​것처럼 들립니다. 따라서이 답변은 그 가정에 기반을두고 있습니다.

파서는 어떤 식 으로든 구조체, 클래스 또는 언어가 가진 모든 데이터 구조를 채울 것입니다. ToString파싱되는 모든 것에 대해 를 구현하면 , 이진 데이터를 사람이 읽을 수있는 형식으로 표시하는 매우 사용하기 쉽고 유지 관리가 쉬운 방법으로 끝납니다.

당신은 본질적으로 :

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

그리고 그것을 사용하는 관점에서, 그것입니다. 물론 이것은 클래스 / 구조체 / 무엇이든에 ToString대한 기능을 구현 / 재정의 Object해야하며 중첩 된 클래스 / 구조체 / 기타에 대해서도 그렇게해야합니다.

또한 조건문을 사용하여 ToString릴리스 코드에서 함수가 호출되는 것을 방지하여 디버그 모드 외부에서 로그되지 않는 항목에 시간을 낭비하지 않도록 할 수 있습니다.

귀하의 ToString이 같은 힘의 모양을

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);

원래의 질문은이 작업을 다소 시도한 것처럼 들리며이 방법이 부담 스럽다고 생각하지만 어느 시점에서 이진 형식 구문 분석을 구현하고 해당 데이터를 저장하는 변수를 만들었습니다. 따라서 기존 변수를 적절한 추상화 수준 (변수가있는 클래스 / 구조)으로 인쇄하기 만하면됩니다.

이 작업은 한 번만 수행하면되며 파서를 작성하는 동안 수행 할 수 있습니다. 그리고 바이너리 형식이 변경 될 때만 변경됩니다 (이미 파서에 대한 변경을 프롬프트합니다).

비슷한 맥락에서 : 일부 언어에는 클래스를 XML 또는 JSON으로 변환하는 강력한 기능이 있습니다. C #이 특히 좋습니다. 이진 형식을 포기할 필요는 없습니다. 디버그 로깅 문에서 XML 또는 JSON을 수행하고 릴리스 코드 만 남겨 두십시오.

16 진수 덤프 경로는 오류가 발생하기 쉽기 때문에 개인적으로 권장하지 않습니다 (오른쪽 바이트에서 시작 했습니까? 왼쪽에서 오른쪽을 읽을 때 올바른 엔디안 등을보고 있는지 확인하십시오) .

예 : ToStrings뱉어 낸 변수를 말하십시오 a,b,c,d,e,f,g,h. 프로그램을 실행하고의 버그를 발견 g했지만 문제가 실제로 시작되었습니다 c(그러나 디버깅 중이므로 아직 파악하지 못했습니다). 입력 값을 알고 있으면 c문제가 시작되는 위치를 즉시 알 수 있습니다 .

단지 당신에게 알려주는 16 진 덤프와 비교된다 338E 8455 0000 FF76 0000 E444 ....; 필드의 크기가 다양 c하고 시작 위치와 값은 무엇입니까-16 진수 편집기가 알려 주지만 내 요점은 오류가 발생하기 쉽고 시간이 많이 걸리는 것입니다. 뿐만 아니라 16 진 뷰어를 통해 테스트를 쉽고 빠르게 자동화 할 수는 없습니다. 데이터를 파싱 한 후 문자열을 인쇄하면 프로그램이 '생각하는 것'을 정확하게 알 수 있으며 자동화 된 테스트 경로를 따라 한 단계가됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.