Contents

เริ่มต้นใช้งาน GNU Debugger บน Linux: A Crash Course

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

GNU Debugger หรือที่เรียกกันทั่วไปว่า GDB เป็นยูทิลิตี้การดีบักที่ยั่งยืนซึ่งได้รับความไว้วางใจและใช้งานโดยนักพัฒนาเมื่อเวลาผ่านไป ในส่วนนี้ เราจะสำรวจแอปพลิเคชันในบริบทของระบบ Linux

การเตรียมโปรแกรมตัวอย่าง

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

คุณมีทางเลือกในการใช้คอมไพลเลอร์ C ทางเลือกใดๆ โดยมีเงื่อนไขว่าคุณต้องไม่ลบข้อมูลเมตาของไฟล์ปฏิบัติการซึ่งมีข้อมูลเกี่ยวกับที่มาของโปรแกรมและการแก้ไขที่เกิดขึ้นระหว่างการคอมไพล์

/th/images/compiling-and-executing-the-sample-programs.jpg

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

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

การรันโปรแกรมใน GDB

/th/images/executing-a-program-in-gdb.jpg

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

เพื่อให้โปรแกรมทำงานได้อย่างถูกต้องเมื่อต้องการอาร์กิวเมนต์บรรทัดคำสั่ง จำเป็นต้องผนวกอาร์กิวเมนต์เหล่านี้ต่อท้ายชื่อโปรแกรมเมื่อเรียกใช้ โครงสร้างทางไวยากรณ์ที่ใช้โดย GNU Debugger (GDB) สำหรับการอัพโหลดและรันแอปพลิเคชันพร้อมกับอาร์กิวเมนต์สามารถสรุปได้ดังนี้:

 gdb <program>
run <args> 

หรือ:

 gdb
file <program>
run <args> 

การตั้งค่าเบรกพอยต์ด้วย GDB

/th/images/setting-and-deleting-breakpoints.jpg

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

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

 break main
break 47 

หากต้องการเข้าถึงรายการเบรกพอยต์ทั้งหมดที่ใช้งานอยู่ในระหว่างเซสชันการแก้ไขข้อบกพร่องนี้ โปรดป้อนคำสั่งต่อไปนี้:

 info breakpoints 

หากต้องการลบเบรกพอยต์ปัจจุบันบางส่วนหรือทั้งหมดในโค้ดของคุณ คุณสามารถใช้คำสั่ง “delete ” (หากลบอันใดอันหนึ่งออก) หรือเพียงแค่ “delete” (เพื่อกำจัดทั้งหมด)

 delete 2
delete 3-5 

GDB จัดเตรียมความสามารถในการสร้างจุดพักแบบมีเงื่อนไข ซึ่งจำเป็นต้องตรงตามเงื่อนไขเฉพาะเพื่อให้โปรแกรมหยุดชั่วคราวระหว่างการดำเนินการ เงื่อนไขนี้อาจเกี่ยวข้องกับการเปลี่ยนแปลงค่าของตัวแปร การเรียกใช้ฟังก์ชันที่ไม่เกิดผล หรือเกณฑ์อื่นๆ ที่คุณต้องการ ต่อไปนี้แสดงถึงไวยากรณ์ที่ใช้ในการกำหนดจุดพักแบบมีเงื่อนไข:

 break <location> if n == 2 

หากคุณต้องการให้โปรแกรมกลับมาทำงานต่อเมื่อพบจุดพัก กรุณาป้อนคำสั่ง"ดำเนินการต่อ"

 continue 

ก้าวผ่านรหัส

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

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

คำสั่งดังกล่าวกำหนดให้ GNU Debugger (GDB) ดำเนินการกับบรรทัดถัดไปของไฟล์ต้นฉบับที่เกี่ยวข้อง ซึ่งจะทำให้สามารถข้ามผ่านฐานโค้ดได้อย่างครอบคลุมเป็นรายบุคคล

คำสั่ง “next” ใน REPL ของ Pycharm อนุญาตให้เรียกใช้บรรทัดโค้ดถัดไปภายในฟังก์ชันที่ใช้งานอยู่ในปัจจุบัน หลังจากที่การดำเนินการสิ้นสุดลง ต่างจากคำสั่ง “step” ซึ่งดำเนินการกับแต่ละคำสั่งหรือคำสั่ง “next” ถือว่าฟังก์ชันทั้งหมดเป็นบล็อกโค้ดที่ต่อเนื่องกัน ดังนั้นจึงทำให้ผู้ใช้สามารถดำเนินการผ่านหลายบรรทัดได้โดยไม่หยุดชะงัก

คำสั่ง finish เสร็จสิ้นการดำเนินการของบล็อกโค้ดที่ค้างอยู่ใดๆ ที่ยังคงอยู่ในฟังก์ชันปัจจุบันให้เสร็จสิ้นอย่างมีประสิทธิภาพ ตามด้วยการหยุดกระบวนการ

การตรวจสอบตัวแปร

/th/images/using-the-display-command.jpg

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

 print <variable> 

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

 display <variable> 

การตั้งค่าจุดเฝ้าระวัง

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

 watch <variable_name> 

การดีบักเฉพาะเธรดด้วย GDB

/th/images/thread-specific-debugging-in-gdb.jpg

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

หากต้องการรับข้อมูลเกี่ยวกับเธรดที่ใช้งานอยู่ในแอปพลิเคชันของคุณ ให้ใช้คำสั่ง “info” ตามด้วยบรรทัดว่าง

 info threads 

ในการเข้าถึงและโต้ตอบกับเธรดใดรายการหนึ่งในรายการ อาจใช้ตำแหน่งตัวเลขหรือดัชนีภายในอาร์เรย์ของตัวเลือกที่มีอยู่ ซึ่งช่วยให้สามารถเลือกและจัดการเธรดที่ต้องการตามตัวระบุที่กำหนด

 thread 2 

เมื่อเลือกเธรดที่เฉพาะเจาะจง เราอาจค่อยๆ ข้ามลำดับการดำเนินการโดยใช้คำสั่ง stepping"ขั้นตอน"“ถัดไป"และ"เสร็จสิ้น"ดังที่แสดงไว้ก่อนหน้านี้

การดีบักระยะไกลด้วย GDB

/th/images/connecting-to-remote-gbdserver.jpg

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

ในการติดตั้ง gdbserver บนระบบที่ใช้ Ubuntu หรือ Debian เป็นรากฐาน เราสามารถใช้ Advanced Package Tool (APT) เพื่อจุดประสงค์นี้ได้

 sudo apt install gdbserver 

เมื่อดำเนินการกระบวนการติดตั้ง ให้นำทางไปยังไดเร็กทอรีที่มีไบนารี่และดำเนินการคำสั่งเฉพาะภายในตำแหน่งนั้นเพื่อเริ่มการทำงานของโปรแกรม gdbserver

 gdbserver <ip>:<port> <binary> 

แอปพลิเคชัน"gdbserver"คาดว่าจะแสดงข้อความที่ระบุว่าได้เริ่มต้นแล้วและกำลังรับฟังการเชื่อมต่อขาเข้าบนพอร์ตที่ระบุ จากนั้น บนอุปกรณ์อื่น ให้เริ่มต้นอินสแตนซ์ของซอฟต์แวร์ “GDB” และสร้างการเชื่อมต่อกับเซิร์ฟเวอร์ระยะไกลโดยใช้คำสั่ง “เป้าหมาย” ตามด้วยการกำหนดเฉพาะสำหรับวัตถุที่ต้องการสอบถาม

 target remote <server_ip>:<port> 

การเขียนสคริปต์ GDB เพื่อทำการดีบักอัตโนมัติ

/th/images/gdb-scripting-example.jpg

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

นี่คือตัวอย่าง:

 set logging enabled on
set logging file sample.out
break main
  command 1
  backtrace
  print N
  continue
end
quit

ในข้อมูลโค้ดที่ให้มา ได้รับการแนะนำว่าเครื่องมือแก้ไขจุดบกพร่อง GDB ควรเปิดใช้งานฟังก์ชันการบันทึกและจัดเก็บบันทึกผลลัพธ์ไว้ในไฟล์ชื่อ"sample.out"นอกจากนี้ จุดขัดจังหวะถูกกำหนดไว้ภายในฟังก์ชันหลักของโปรแกรม

เพื่อดำเนินการเบรกพอยต์หนึ่งซึ่งอยู่ที่จุดเริ่มต้นของฟังก์ชัน’main’โปรดทำตามขั้นตอนเหล่านี้โดยใช้ GNU Debugger (GDB): ขั้นแรก เริ่มต้น backtrace โดยการป้อนคำสั่ง’backtrace’ประการที่สอง แสดงค่าของตัวแปร’N’ด้วยคำสั่ง’print N’ประการที่สาม ดำเนินการโปรแกรมต่อโดยพิมพ์’ดำเนินการต่อ’ดีบักเกอร์จะดำเนินการย้อนรอยเพื่อกำหนดสถานะปัจจุบันของโฟลว์โปรแกรม จากนั้นจะแสดงค่าของตัวแปร’N’หลังจากนั้นก็จะกลับมาดำเนินการตามกระบวนการอีกครั้ง สุดท้ายนี้ เมื่อมีการแสดงข้อมูลที่เกี่ยวข้องทั้งหมดแล้ว GDB จะหยุดการดำเนินการอย่างสง่างาม

ในการรันสคริปต์นี้ ให้ใช้:

 gdb -x <script> <binary> 

ตอนนี้คุณรู้วิธีดีบักโปรแกรมของคุณด้วย GDB แล้ว!

การดีบักเป็นความสามารถที่สำคัญ และการเชี่ยวชาญการใช้ GDB เพื่อวัตถุประสงค์ในการดีบั๊กจะช่วยเพิ่มความสามารถรอบด้านในฐานะนักพัฒนาได้อย่างมาก ฟังก์ชันการทำงานต่างๆ ที่ GDB มอบให้ เช่น การนำทางทีละขั้นตอนผ่านโค้ด การวางจุดพัก การดีบักเธรดแบบกำหนดเป้าหมาย และอื่นๆ ทำให้ GDB เป็นเครื่องมือที่ทรงพลังเมื่อตรวจสอบไฟล์ไบนารี่ภายในสภาพแวดล้อมระบบปฏิบัติการ Linux

สำหรับผู้ที่ต้องการวิเคราะห์และแก้ไขปัญหาโปรแกรมซอฟต์แวร์ภายในระบบปฏิบัติการ Windows การสำรวจความรู้เพิ่มเติมเกี่ยวกับ Windbg ซึ่งเป็นเครื่องมือแก้ไขจุดบกพร่องที่ผสานรวมในตัวสำหรับ Windows อาจถือเป็นตัวเลือกที่คุ้มค่าแก่การพิจารณา