ทำไมพอพยายาม break-in เข้าเคอร์เนล NT 3.1 แล้วเครื่อง 486DX4 ของผมถึงรีบูต?
(retrocomputing.stackexchange.com)- บน Windows NT 3.1 เมื่อสั่ง break-in ด้วย Ctrl-C ผ่าน
i386kdระบบเป้าหมาย 486DX4 จะรีบูตแทนที่จะแสดงพรอมป์kd>โดยสาเหตุมาจากปัญหาเรื่อง ความเข้ากันได้กับ enhanced 486 - ใน NTOSKRNL.EXE ฟังก์ชัน
KiSaveProcessorControlStateและKiRestoreProcessorControlStateใช้การเปรียบเทียบแบบ word ตอนตรวจชนิด CPU ทำให้เข้าใจผิดว่า 486 ที่รองรับ CPUID เป็น Pentium ขึ้นไป - ค่า
CpuTypeและCpuIDใน KPRCB ถูกอ่านพร้อมกัน ทำให้หมายเลขรุ่นของ 486 ที่รองรับ CPUID ถูกตีความว่าสูงขึ้น 256 และส่งผลให้มีการเข้าถึง รีจิสเตอร์ CR4 ที่ไม่มีอยู่บน 486 - ทั้ง 486DX-33 เดิมและ Am486DX4-NV8T แบบ write-through ที่ไม่มี SMM สามารถทำ kernel debugging ได้ตามปกติ จึงชี้ว่าปัญหาเกิดเฉพาะกับ enhanced 486 ที่มีคำสั่ง CPUID
- หากแพตช์ตำแหน่ง
cmp ds:word_FFDFF138, 5สองแห่งใน NTOSKRNL.EXE ให้เป็น byte compare ก็จะแก้ได้ทั้งบน NT 3.1 Advanced Server รุ่นต้นฉบับและ NT 3.1 SP3
สภาพแวดล้อมที่ใช้ทดสอบและอาการ
- ติดตั้ง Windows NT 3.1 บน Compaq ProSignia 3080 แล้วทดลองทำ kernel debugging
- เครื่องนี้เคยใช้งาน Windows NT 3.1 จริง และดูเป็นหนึ่งในเครื่องที่ Windows NT ตั้งใจรองรับอย่างชัดเจน
- ขยาย RAM เป็น 128MB และเปลี่ยน Intel 486DX-33 แบบซ็อกเก็ตเป็น AMD enhanced 486DX4-SV8B
- CPU ตัวนี้รองรับ write-back cache และ SMM และติดตั้งผ่านซ็อกเก็ตอะแดปเตอร์แปลงแรงดัน
- การรองรับ 486DX4 ของ BIOS ยังไม่ได้จัดการ จึงตั้งจัมเปอร์ CPU เป็น ตัวคูณ 2x
- แนวคิดคือถ้าชิปเซ็ตไม่รองรับ L1 write-back ก็ให้ตั้งเป็น 2x เพื่อให้เข้ากันได้ทางซอฟต์แวร์กับ Intel 80486DX2-66
- Intel 80486DX2-66 เป็นตัวเลือกที่ระบบนี้รองรับอยู่แล้ว
- การติดตั้ง Windows NT 3.1 เสร็จสมบูรณ์ตามปกติ
- แผ่น CD ของ Windows NT 3.1 มี debugging symbols แบบครบ จึงลองทำ kernel debugging
- จุดประสงค์คือสืบหาสาเหตุที่ NetDDE ทิ้งข้อผิดพลาดไว้ใน event log
- อีกปัญหาหนึ่งคือระบบล่มกับการ์ดอีเธอร์เน็ต EISA บางใบ และยังเป็นไปได้ว่าเกิดจากฮาร์ดแวร์เสีย
- ต้องใช้
i386kdที่มากับ Windows NT 3.1 แทนkdหรือntkdจาก Windows 10 development kit รุ่นใหม่ จึงจะตรงกับการตั้งค่า kernel debugging ของระบบนี้ - เมื่อกด Ctrl-C เพื่อ break-in ใน
i386kdเครื่องเป้าหมายจะ รีบูต แทนที่จะให้พรอมป์kd>
สิ่งที่ตัดออกไปแล้วว่าไม่ใช่สาเหตุ
- หน่วยความจำตรวจสอบแล้วว่าปกติ
- ไม่ใช่ความเสียหายของไฟล์ระบบ
- ไม่มีการเปิดใช้งานฮาร์ดแวร์ watchdog ที่จะรีบูตเครื่องเมื่อเคอร์เนลหยุดเพื่อการดีบัก
- อะแดปเตอร์ USB-to-serial ฝั่งโฮสต์สื่อสารได้ปกติ
- แม้จะดูเหมือนเป็น PL2301 ปลอม แต่ไม่ได้ส่งคำสั่ง debugger ผิดจนกลายเป็นคำสั่ง “reboot system”
- ในโปรโตคอล KD มีคำสั่ง reboot system อยู่จริง
- ไม่เกี่ยวกับตัวเลือก remote management หรือการแจ้งเตือนของเมนบอร์ด
สาเหตุจริง: enhanced 486 กับเคอร์เนล NT 3.1 ไม่เข้ากัน
- เคอร์เนล Windows NT 3.1 ไม่รองรับโปรเซสเซอร์ enhanced 486
- ให้เจาะจงกว่านั้นคือมีปัญหากับ 486 ที่รองรับคำสั่ง
CPUID - kernel debugging ทำงานได้ตามปกติบน CPU ต่อไปนี้
- 486DX-33 เดิมที่ติดมากับเครื่อง
- Am486DX4-NV8T แบบ write-through บนคอร์รุ่นเก่าที่ไม่ใช่ enhanced และไม่มี SMM
- ถ้าเพียงต้องการทดสอบ kernel debugging ของ NT 3.1 ก็ควรใช้ CPU ที่เข้ากันได้กับ Windows NT 3.1 โดยตรง
- ถ้าจะซ่อม NT เอง ต้องแพตช์บั๊กการตรวจชนิด CPU ใน NTOSKRNL.EXE
เส้นทางโค้ดในเคอร์เนลที่ทำให้เกิดปัญหา
- สาเหตุโดยตรงของความไม่เข้ากันคือบั๊กใน
KiSaveProcessorControlState- ฟังก์ชันคู่กันอย่าง
KiRestoreProcessorControlStateก็มีบั๊กคล้ายกัน
- ฟังก์ชันคู่กันอย่าง
KiSaveProcessorControlStateถูกเรียกจากสามตำแหน่งภายใน NTOSKRNL.EXE- เมื่อ exception ถูกส่งต่อเข้า kernel debugger ผ่าน
KdpTrap- สำหรับการ break-in ด้วย Ctrl-C จะเกิด breakpoint exception จากฟังก์ชัน polling สำหรับ break-in ภายใน timer tick interrupt
- เมื่อมีการเรียก
KeBugCheckExหรือก็คือสถานการณ์ “หน้าจอฟ้า” - เมื่อมีการเรียก
KiSaveProcessorState- หากการวิเคราะห์ control flow ของ NTOSKRNL.EXE โดย IDA ถูกต้องครบถ้วน ฟังก์ชันนี้ไม่ได้ export และภายใน NTOSKRNL เองก็ไม่มีการเรียกใช้ จึงดูเหมือนว่าในทางปฏิบัติจะไม่เกิดเส้นทางนี้
- เมื่อ exception ถูกส่งต่อเข้า kernel debugger ผ่าน
KiSaveProcessorControlStateจะบันทึกสถานะควบคุมของโปรเซสเซอร์ลงในโครงสร้างCONTEXTแบบขยาย- บันทึก CR0, CR2, CR3
- บน Pentium ขึ้นไปจะบันทึก CR4 ด้วย
- บันทึกรีจิสเตอร์ดีบัก DR0~DR3, DR6, DR7
- รวมถึงค่าระดับ global protected mode เช่น ที่อยู่ GDT, ที่อยู่ IDT, active TSS selector และ LDT selector
การตีความฟิลด์ KPRCB ผิดพลาด
- การระบุชนิดโปรเซสเซอร์อาศัยค่าจาก KPRCB
- KPRCB เป็นส่วนหนึ่งของ KPCR
- KPRCB ของ boot processor หรือระบบ single-processor อยู่ที่ virtual address
FFDFF120และเมธอดนี้ฮาร์ดโค้ดค่านั้นไว้
- ฟิลด์ KPRCB ที่เกี่ยวข้องใน NT 3.1 ตามที่ Geoff Chappell สรุปไว้มีดังนี้
+018 CHAR CpuType+019 CHAR CpuID+01A UShort CpuStep
- ฟิลด์เหล่านี้ถูกกำหนดค่าเริ่มต้นใน
KiSetProcessorType- สำหรับโปรเซสเซอร์ 486 ค่า byte ที่ offset 18 จะถูกตั้งเป็น 4
- โปรเซสเซอร์ Pentium จะถูกตั้งเป็น 5
- โปรเซสเซอร์ Pentium Pro และ Pentium II/III จะถูกตั้งเป็น 6
- byte ที่ offset 19 เป็น boolean flag ที่บอกว่าโปรเซสเซอร์รองรับ CPUID และทำงานอย่าง “สมเหตุสมผล” หรือไม่
- คำสั่งเปรียบเทียบที่เป็นปัญหาอ่าน word ที่ address
FFDFF138ไม่ใช่ byteFFDFF138อยู่ห่างจากจุดเริ่มต้นของ KPRCB 18h ไบต์- ดังนั้นมันจึงตีความไม่เฉพาะ
CpuTypeแต่รวม byte ถัดไปคือCpuIDเป็นส่วนหนึ่งของหมายเลขรุ่นด้วย
- โปรเซสเซอร์ที่รองรับ CPUID จะถูกมองว่ามีหมายเลขรุ่นมากกว่าความจริง 256
- 80-4-86 ที่รองรับ CPUID จะถูก Windows NT 3.1 มองเป็น 80-260-86
- ค่า 260 มากกว่าเกณฑ์ Pentium ที่ 5 อย่างมาก เคอร์เนลจึงสรุปว่าโปรเซสเซอร์นั้น ควรมี CR4
- แต่ enhanced 486 ไม่มี CR4 จึงทำให้เกิดปัญหาในเส้นทาง break-in
วิธีแพตช์
- เมื่อยืนยันบั๊กได้แล้ว การแก้ไขทำได้ง่าย
- ใน NTOSKRNL.EXE คำสั่ง
cmp ds:word_FFDFF138, 5ปรากฏอยู่เพียงสองครั้งKiSaveProcessorControlStateKiRestoreProcessorControlState
- ทั้งสองตำแหน่งต้องเปลี่ยนจาก word compare เป็น byte compare
- แพตช์ชุดไบต์ต่อไปนี้สองครั้งด้วย hex editor
- เดิม:
66 83 3D 38 F1 DF FF 05 - เปลี่ยนเป็น:
90 80 3D 38 F1 DF FF 05
- เดิม:
- การแก้ไขนี้ใช้ได้ทั้งกับ NTOSKRNL.EXE ของ NT 3.1 Advanced Server รุ่นดั้งเดิมและ NT 3.1 SP3
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ครั้งหนึ่งผมเคยทำ port check เพื่อดูว่าเซอร์วิสยังทำงานอยู่ไหม แต่แค่เปิด TCP port ไม่กี่พอร์ตบนเครื่องต่าง ๆ ก็ทำให้บริษัทครึ่งหนึ่งล่มไปเลย
เป็นยุคที่เหลือเชื่อจริง ๆ
เรามาไกลมากแล้วจริง ๆ
https://en.wikipedia.org/wiki/Ping_of_death
บางเครื่องยังมี ช่องโหว่ SMB ที่ไม่ได้แพตช์อยู่นานหลายปีด้วย ตอนนั้นมี Metasploit แล้ว แค่ไม่กี่คำสั่งก็ฉีด VNC เข้าไปในโฮสต์ Windows ส่วนใหญ่ในเครือข่ายโลคัลได้แล้ว ทุกวันนี้อย่างน้อยความเร็วในการแพตช์ก็เร็วขึ้นมาก
อา หลุมกระต่ายของ retro computing แม้จะแยกออกจากผลกระทบในโลกจริงอย่างไม่เป็นอันตราย แต่ก็ยังให้ความพึงพอใจมากจริง ๆ
เป็นกับดักน้ำผึ้งของพวกเนิร์ดโดยแท้ สุดท้ายผมก็กดปุ่มขยาย “ดูอีก 6 ความคิดเห็น” จนได้
วันนี้ผมอาจจะได้ทำงานบ้างก็ได้
แต่ 486 นี่นะ? มันทำให้นึกถึงความทรงจำมากมายเกี่ยวกับจอฟ้า และการรอไม่รู้จบเพราะหน่วยความจำไม่พอจนต้อง swap ลงดิสก์ สำหรับผมคงยังเร็วเกินไป
“ทางแก้มันชัดเจนอยู่แล้ว” นี่พูดถูกจริง ๆ
เดาว่าน่าจะอ่าน disassembly ของโค้ด trap ไปเรื่อย ๆ จนเจอปัญหา ไม่น่าจะใช้ QEMU หรือวิธีดีบักอื่น ๆ ได้
ต้องอ่านให้ถึง วิธีการ ที่ถูกฝังอยู่ในคอมเมนต์ที่ซ่อนอยู่ช่วงท้ายด้วย
ไม่ได้ซ่อนคอมเมนต์ที่คะแนนต่ำสุด ไม่ได้ซ่อนตามลำดับใหม่สุดหรือเก่าสุดด้วย รู้สึกเหมือนสุ่มและไม่จำเป็น
ถ้าปัญหาคือพื้นที่ ผมว่าควรใส่ การแบ่งหน้า มากกว่าซ่อน
ของอย่าง slotket adapter ที่บอร์ด Abit, Asus และอาจจะ MSI รุ่นเก่า ๆ ใช้เพื่อความเข้ากันได้นั้น มีความสามารถด้านโอเวอร์คล็อกจริง ๆ
ประเด็นคือความเร็ว RAM เท่าไร และเมนบอร์ดจะรับความเร็วได้แค่ไหนจากการตั้งค่าตัวคูณ นั่นเป็นยุคโอเวอร์คล็อกค่อนข้างแรก ๆ ที่มี Celeron กับ Pentium II
ช่วงหนึ่งเพราะเรื่องความเข้ากันได้ ผมเสียบ Celeron 600 เข้ากับอะแดปเตอร์ แล้วเสียบต่อเข้า Slot II to Socket 370 adapter อีกที เป็นคอนฟิกแบบ [1] แล้วรันที่ 1.2GHz ได้สบาย ๆ ผมว่าถึงขั้นดันไป 1.4GHz บน Windows ME ด้วยซ้ำ และสุดท้ายก็ทำ CPU ตัวนั้นไหม้
[1]http://krick.3feetunder.com/370mod/
คำตอบที่อยู่ใต้คำถามนั้นยอดเยี่ยมจริง ๆ
หัวข้อก็ดี คำตอบก็ยอดเยี่ยมมาก
เท่มากที่คนถามมาตอบเองด้วย
ผมชอบอะไรแบบนี้ ดีใจจริง ๆ ที่ผู้คนสนใจ retro computing
การบันทึกปัญหาและวิธีแก้แบบนี้ไว้ให้ทุกคนเป็นเรื่องดี
ผมรู้อยู่แล้วว่าเจ้า CPUID นี่เป็นไอเดียที่ไม่ดี
ทำไม timestamp ของคำถามกับคำตอบถึงเหมือนกัน?
เขาคงคิดว่าการเอาข้อมูลนี้ไปลงในรูปแบบถาม-ตอบของ SO จะอยู่ได้นานกว่าบล็อกโพสต์ที่ไม่มีใครหาเจอ
แย่ไหมที่ผมรู้ว่าเป็นเรื่องอะไรตั้งแต่ยังไม่เปิดดู?
ช่างเถอะ ผมต้องไปตะโกนไล่เด็กสมัยนี้ออกจากสนามหญ้าหน้าบ้านแล้ว
สิ่งที่พึ่งได้มีแค่ไหวพริบของตัวเอง และถ้าจำเป็นก็มี เซียน ที่โทรตามทางโทรศัพท์บ้านได้ ไม่มีอินเทอร์เน็ตสุดวิเศษที่เต็มไปด้วยคำตอบซึ่งมีประโยชน์ไม่เท่ากันแบบทุกวันนี้