คุณจะดีบักรูปแบบไบนารีอย่างไร


11

ฉันต้องการที่จะสามารถแก้ปัญหาการสร้างตัวสร้างไบนารี ตอนนี้ฉันกำลังพิมพ์ข้อมูลอินพุตไปยังตัวแยกวิเคราะห์ไบนารี่จากนั้นก็ลึกเข้าไปในโค้ดและพิมพ์การแม็พของอินพุตไปยังเอาต์พุตจากนั้นใช้การแม็พเอาต์พุต (จำนวนเต็ม) และใช้เพื่อค้นหาจำนวนเต็มที่สอดคล้องกัน ในไบนารี ค่อนข้างงอแงและต้องการให้ฉันแก้ไขซอร์สโค้ดอย่างลึกซึ้งเพื่อให้ได้การแม็พระหว่างอินพุตและเอาต์พุต

ดูเหมือนว่าคุณสามารถดูไบนารีในรูปแบบที่แตกต่างกัน (ในกรณีของฉันฉันต้องการดูมันในหน่วยจำนวน 8 บิตเป็นตัวเลขทศนิยมเพราะมันค่อนข้างใกล้กับอินพุต) ที่จริงแล้วตัวเลขบางตัวเป็น 16 บิตบาง 8 หรือ 32 เป็นต้นดังนั้นอาจมีวิธีดูไบนารีด้วยตัวเลขที่แตกต่างกันเหล่านี้ที่เน้นในหน่วยความจำในบางวิธี

วิธีเดียวที่ฉันจะเห็นว่าเป็นไปได้คือถ้าคุณสร้าง visualizer เฉพาะกับรูปแบบ / เค้าโครงไบนารีจริง ดังนั้นจึงรู้ได้ว่าลำดับ 32 บิตควรอยู่ที่ใดและควรใช้เลข 8 บิตที่ไหนเป็นต้นนี่เป็นงานที่ต้องทำมากมายและมีเล่ห์เหลี่ยมในบางสถานการณ์ ดังนั้นสงสัยว่ามีวิธีทั่วไปที่จะทำ

ฉันยังสงสัยด้วยว่าวิธีทั่วไปในการแก้ไขข้อบกพร่องของประเภทนี้ในปัจจุบันคืออะไรดังนั้นบางทีฉันอาจได้รับแนวคิดบางอย่างเกี่ยวกับสิ่งที่ต้องลองจากสิ่งนั้น


75
คุณมีหนึ่งคำตอบที่บอกว่า "ใช้ hexdump โดยตรงและทำสิ่งนี้และที่เพิ่มเติม" - และคำตอบนั้นมี upvotes มากมาย และคำตอบที่สอง 5 ชั่วโมงต่อมา (!) โดยพูดว่า "ใช้ hexdump" เท่านั้น ถ้าอย่างนั้นคุณยอมรับคนที่สองเพื่อเป็นคนแรกไหม? อย่างจริงจัง?
Doc Brown

4
แม้ว่าคุณอาจมีเหตุผลที่ดีที่จะใช้รูปแบบไบนารีให้พิจารณาว่าคุณสามารถใช้รูปแบบข้อความที่มีอยู่เช่น JSON แทนได้หรือไม่ ความสามารถในการอ่านของมนุษย์นั้นมีจำนวนมากและโดยปกติแล้วเครื่องและเครือข่ายจะเร็วพอที่การใช้รูปแบบที่กำหนดเองเพื่อลดขนาดไม่จำเป็นในปัจจุบัน
jpmc26

4
@ jpmc26 ยังคงมีการใช้งานมากสำหรับรูปแบบไบนารีและจะเป็น ความสามารถในการอ่านของมนุษย์มักเป็นเรื่องรองกับประสิทธิภาพความต้องการพื้นที่เก็บข้อมูลและประสิทธิภาพของเครือข่าย และยังมีอีกหลายพื้นที่ที่ประสิทธิภาพของเครือข่ายโดยเฉพาะแย่และพื้นที่จัดเก็บ จำกัด นอกจากนี้อย่าลืมทุกระบบที่มีการเชื่อมต่อกับระบบเดิม (ทั้งฮาร์ดแวร์และซอฟต์แวร์) และต้องสนับสนุนรูปแบบข้อมูลของพวกเขา
jwenting

4
@jwenting ไม่จริงๆแล้วเวลานักพัฒนาซอฟต์แวร์มักจะเป็นแอปพลิเคชั่นที่แพงที่สุด แน่นอนว่าอาจไม่ใช่กรณีที่คุณทำงานที่ Google หรือ Facebook แต่แอพส่วนใหญ่ไม่ทำงานในระดับนั้น และเมื่อนักพัฒนาซอฟต์แวร์ของคุณใช้เวลากับสิ่งต่าง ๆ เป็นทรัพยากรที่มีราคาแพงที่สุดความสามารถในการอ่านของมนุษย์นับได้มากกว่า 100 มิลลิวินาทีเป็นพิเศษสำหรับโปรแกรมที่จะแยกวิเคราะห์
jpmc26

3
@ jpmc26 ฉันไม่เห็นอะไรเลยในคำถามที่แนะนำให้ฉันรู้ว่า OP เป็นตัวกำหนดรูปแบบ
JimmyJames

คำตอบ:


76

สำหรับการตรวจสอบเฉพาะกิจเพียงใช้ hexdump มาตรฐานและเรียนรู้ที่จะมองมัน

หากคุณต้องการรวมเครื่องมือสำหรับการตรวจสอบที่เหมาะสมฉันมักจะเขียนตัวถอดรหัสแยกต่างหากในรูปแบบของ Python ซึ่งจะเป็นการขับเคลื่อนโดยตรงจากเอกสารข้อมูลจำเพาะข้อความหรือ IDL และเป็นไปโดยอัตโนมัติที่สุด (ดังนั้นจึงไม่มีโอกาสแนะนำด้วยตนเอง ข้อผิดพลาดเดียวกันในตัวถอดรหัสทั้งสอง)

สุดท้ายอย่าลืมว่าคุณควรเขียนการทดสอบหน่วยสำหรับตัวถอดรหัสโดยใช้อินพุตกระป๋องที่ถูกต้อง


2
"เพียงใช้ hexdump มาตรฐานและเรียนรู้ที่จะมองมัน" ได้. จากประสบการณ์ของผมสามารถเขียนอะไรก็ได้มากถึง 200 บิตลงบนกระดานไวท์บอร์ดเพื่อเปรียบเทียบกลุ่มซึ่งบางครั้งช่วยในการเริ่มต้นสิ่งนี้
เสา

1
ฉันพบว่าตัวถอดรหัสแยกต่างหากคุ้มค่ากับความพยายามหากข้อมูลไบนารีมีบทบาทสำคัญในแอปพลิเคชัน (หรือระบบโดยทั่วไป) นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งถ้ารูปแบบข้อมูลเป็นตัวแปร: ข้อมูลในเลย์เอาต์คงที่สามารถเห็นได้ใน hexdump ด้วยการฝึกฝนเล็กน้อย เรา debugged การรับส่งข้อมูล USB และ CAN ด้วยตัวถอดรหัสแพ็คเก็ตเชิงพาณิชย์และฉันได้เขียนตัวถอดรหัส PROFIBus (ที่ตัวแปรแพร่กระจายข้ามไบต์ไม่สามารถอ่านได้อย่างสมบูรณ์ในฐานสิบหก) และพบว่าทั้งสามนั้นมีประโยชน์อย่างมาก
Peter - Reinstate Monica

10

ขั้นตอนแรกในการทำเช่นนี้คือคุณต้องการวิธีการค้นหาหรือกำหนดไวยากรณ์ที่อธิบายถึงโครงสร้างของข้อมูลเช่น schema

ตัวอย่างนี้เป็นคุณสมบัติภาษาของภาษาโคบอลซึ่งเป็นที่รู้จักกันอย่างไม่เป็นทางการในชื่อหนังสือ ในโปรแกรมภาษาโคบอลคุณจะต้องกำหนดโครงสร้างของข้อมูลในหน่วยความจำ โครงสร้างนี้แมปโดยตรงกับวิธีที่เก็บไบต์ นี่เป็นเรื่องธรรมดาสำหรับภาษาในยุคนั้นเมื่อเทียบกับภาษาร่วมสมัยทั่วไปที่รูปแบบทางกายภาพของหน่วยความจำเป็นข้อกังวลเกี่ยวกับการใช้งานที่แยกออกจากนักพัฒนา

การค้นหา google สำหรับภาษาสคีมาข้อมูลไบนารีจะมีเครื่องมือหลายอย่าง ตัวอย่างคือApache DFDL อาจมี UI สำหรับสิ่งนี้เช่นกัน


2
คุณลักษณะนี้ไม่ได้สงวนไว้กับภาษายุค 'โบราณ' C และ C ++ structs และสหภาพสามารถจัดหน่วยความจำ C # มี StructLayoutAttribute ซึ่งฉันใช้เพื่อส่งข้อมูลไบนารี
Kasper van den Berg

1
@ KaspervandenBerg นอกจากคุณจะบอกว่า C และ C ++ ได้เพิ่มสิ่งเหล่านี้เมื่อเร็ว ๆ นี้ฉันคิดว่าในยุคเดียวกัน ประเด็นก็คือรูปแบบเหล่านี้ไม่ได้มีไว้สำหรับการส่งข้อมูลเท่านั้นแม้ว่าจะถูกใช้ในการทำเช่นนั้นพวกเขาก็แมปโดยตรงกับวิธีการทำงานของรหัสกับข้อมูลในหน่วยความจำและบนดิสก์ โดยทั่วไปไม่ได้ว่าภาษาใหม่จะทำงานอย่างไรแม้ว่าพวกเขาอาจมีคุณสมบัติดังกล่าว
JimmyJames

@ KaspervandenBerg C ++ ไม่ได้ทำเท่าที่คุณคิด มันเป็นไปได้ที่จะใช้เครื่องมือเฉพาะการนำไปใช้เพื่อจัดตำแหน่งและกำจัดการขยาย (และเป็นที่ยอมรับมาตรฐานที่เพิ่มมากขึ้นคือการเพิ่มฟีเจอร์สำหรับสิ่งประเภทนี้) และการสั่งซื้อสมาชิกนั้นเป็นสิ่งที่กำหนด (แต่ไม่จำเป็นต้องเหมือนกัน
การแข่งขัน Lightness ใน Orbit

6

ASN.1 , สัญลักษณ์ไวยากรณ์นามธรรมหนึ่งจัดเตรียมวิธีการระบุรูปแบบไบนารี

  • DDT - พัฒนาโดยใช้ข้อมูลตัวอย่างและการทดสอบหน่วย
  • การถ่ายโอนข้อมูลแบบข้อความจะมีประโยชน์ หากอยู่ใน XML คุณสามารถยุบ / ขยายลำดับชั้นย่อยได้
  • ASN.1 ไม่จำเป็นต้องใช้จริง ๆ แต่เป็นไปตามหลักไวยากรณ์ข้อมูลจำเพาะไฟล์ที่เปิดเผยเพิ่มเติมนั้นง่ายขึ้น

6
หากขบวนพาเหรดที่ไม่มีที่สิ้นสุดของช่องโหว่ความปลอดภัยในตัวแยกวิเคราะห์ ASN.1 เป็นตัวบ่งชี้ใด ๆ การใช้มันจะช่วยให้มีการออกกำลังกายที่ดีในการดีบักรูปแบบไบนารี
Mark

1
@ ทำเครื่องหมายอาร์เรย์ขนาดเล็กจำนวนมาก (และในต้นไม้ลำดับชั้นที่แตกต่างกัน) มักจะไม่ถูกต้อง (ปลอดภัย) ใน C (ตัวอย่างเช่นไม่ใช้ข้อยกเว้น) ไม่ดูถูกดูแคลนระดับต่ำ, ความไม่ปลอดภัยโดยธรรมชาติของ C. ASN.1 ใน - ตัวอย่างเช่น - java ไม่ได้เปิดเผยปัญหานี้ เนื่องจากการแยกวิเคราะห์ไวยากรณ์ ASN.1 โดยตรงสามารถทำได้อย่างปลอดภัยแม้ C สามารถทำได้ด้วยรหัสฐานขนาดเล็กและปลอดภัย และส่วนหนึ่งของช่องโหว่นั้นอยู่ในรูปแบบไบนารี่เอง: เราสามารถใช้ประโยชน์จากโครงสร้างทางกฎหมายที่ถูกต้องตามหลักไวยากรณ์ของรูปแบบที่มีความหมายที่น่ารังเกียจ
Joop Eggen

3

คำตอบอื่น ๆ ได้อธิบายถึงการดูการถ่ายโอนฐานสิบหกหรือเขียนโครงสร้างของวัตถุในเช่น JSON ฉันคิดว่าการรวมทั้งสองอย่างนี้มีประโยชน์มาก

การใช้เครื่องมือที่สามารถสร้างการแสดงผล JSON ที่ด้านบนของ hex dump นั้นมีประโยชน์จริงๆ ผมเขียนเป็นเครื่องมือที่มาเปิดที่แจง .NET ไบนารีที่เรียกว่าdotNetBytesนี่เป็นมุมมองของตัวอย่าง DLL

ตัวอย่าง dotNetBytes


1

ฉันไม่แน่ใจว่าฉันเข้าใจอย่างถ่องแท้ แต่ดูเหมือนว่าคุณมีตัวแยกวิเคราะห์สำหรับรูปแบบไบนารีนี้และคุณสามารถควบคุมรหัสได้ ดังนั้นคำตอบนี้ถูกสร้างขึ้นบนสมมติฐานนั้น

ตัวแยกวิเคราะห์จะใช้วิธีเติมข้อมูลในคลาสคลาสหรือโครงสร้างข้อมูลใด ๆ ที่ภาษาของคุณมี หากคุณใช้ a ToStringสำหรับทุกอย่างที่วิเคราะห์คำแล้วคุณจะจบลงด้วยวิธีที่ใช้งานง่ายและบำรุงรักษาง่ายในการแสดงข้อมูลไบนารีนั้นในรูปแบบที่มนุษย์อ่านได้

คุณต้องมี:

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

และนั่นคือจากมุมมองของการใช้งาน ของหลักสูตรนี้คุณต้องใช้ / แทนที่ToStringฟังก์ชั่นสำหรับObjectชั้นเรียนของคุณ/ struct / อะไรก็ตามและคุณจะต้องทำเช่นนั้นสำหรับชั้นเรียนซ้อน / structs / whatevers ใด ๆ

คุณสามารถใช้คำสั่งแบบมีเงื่อนไขเพิ่มเติมเพื่อป้องกันไม่ให้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);

คำถามเดิมของคุณทำให้ดูเหมือนว่าคุณพยายามทำเช่นนี้และคุณคิดว่าวิธีนี้เป็นภาระ แต่คุณก็ยังนำไปใช้ในการแยกวิเคราะห์รูปแบบไบนารีและสร้างตัวแปรเพื่อเก็บข้อมูลนั้น ดังนั้นสิ่งที่คุณต้องทำคือพิมพ์ตัวแปรที่มีอยู่เหล่านั้นในระดับที่เหมาะสมของนามธรรม (class / struct ตัวแปรอยู่ใน)

นี่คือสิ่งที่คุณควรทำเพียงครั้งเดียวและคุณสามารถทำได้ในขณะที่สร้างเครื่องมือแยกวิเคราะห์ และมันจะเปลี่ยนเฉพาะเมื่อรูปแบบไบนารีเปลี่ยนแปลง (ซึ่งจะแจ้งให้มีการเปลี่ยนแปลงตัวแยกวิเคราะห์ของคุณอยู่แล้ว)

ในหลอดเลือดดำที่คล้ายกัน: บางภาษามีคุณสมบัติที่แข็งแกร่งสำหรับการเปลี่ยนคลาสเป็น XML หรือ JSON C # ดีมากในเรื่องนี้ คุณไม่จำเป็นต้องเลิกใช้รูปแบบไบนารีของคุณเพียงแค่ทำ XML หรือ JSON ในคำสั่งการบันทึกการดีบักและปล่อยรหัสการปล่อยของคุณเพียงอย่างเดียว

ฉันเองไม่แนะนำให้ไปตามเส้นทางการถ่ายโอนข้อมูลแบบ hex เพราะมีแนวโน้มที่จะเกิดข้อผิดพลาด (คุณเริ่มจากไบต์ที่ถูกต้องคุณแน่ใจหรือไม่ว่าคุณกำลังอ่านจากซ้ายไปขวาว่าคุณ "เห็น" endianness ที่ถูกต้อง ฯลฯ ) .

ตัวอย่าง: สมมติว่าคุณคายออกตัวแปรToStrings a,b,c,d,e,f,g,hคุณเรียกใช้โปรแกรมของคุณและสังเกตเห็นข้อบกพร่องด้วยgแต่ปัญหาเริ่มต้นด้วยจริง ๆc(แต่คุณกำลังดีบั๊กดังนั้นคุณยังไม่พบปัญหานั้น) หากคุณทราบค่าอินพุต (และคุณควร) คุณจะเห็นทันทีว่าcเกิดจากปัญหาใด

เมื่อเทียบกับการถ่ายโอนข้อมูลฐานสิบหกที่เพิ่งจะบอกคุณ338E 8455 0000 FF76 0000 E444 ....; ถ้าเขตข้อมูลของคุณมีขนาดแตกต่างกันจะcเริ่มต้นที่ไหนและมีค่าอะไร - ตัวแก้ไขฐานสิบหกจะบอกคุณ แต่ประเด็นของฉันคือข้อผิดพลาดง่ายและใช้เวลานาน ไม่เพียงแค่นั้น แต่คุณไม่สามารถทำการทดสอบอัตโนมัติได้อย่างง่ายดายผ่านมุมมองฐานสิบหก การพิมพ์สตริงหลังจากวิเคราะห์ข้อมูลจะบอกคุณว่าโปรแกรมของคุณ 'คิด' และจะเป็นหนึ่งในเส้นทางการทดสอบอัตโนมัติ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.