2 điểm bởi GN⁺ 2023-09-29 | 1 bình luận | Chia sẻ qua WhatsApp
  • Trên Windows NT 3.1, khi dùng i386kd để break-in bằng Ctrl-C, hệ thống 486DX4 đích sẽ khởi động lại thay vì hiện dấu nhắc kd>, và nguyên nhân là do vấn đề tương thích enhanced 486
  • KiSaveProcessorControlStateKiRestoreProcessorControlState trong NTOSKRNL.EXE dùng so sánh word khi kiểm tra loại CPU, nên nhận nhầm 486 có hỗ trợ CPUID là Pentium trở lên
  • CpuTypeCpuID trong KPRCB bị đọc cùng lúc, khiến số model của 486 hỗ trợ CPUID bị diễn giải lớn hơn 256, và kết quả là phát sinh truy cập vào thanh ghi CR4 vốn không tồn tại trên 486
  • Trên 486DX-33 nguyên bản và Am486DX4-NV8T write-through không có SMM, kernel debugging hoạt động bình thường, nên vấn đề được thu hẹp vào enhanced 486 có cung cấp lệnh CPUID
  • Có thể sửa cả NT 3.1 Advanced Server nguyên bản và NT 3.1 SP3 bằng cách vá hai vị trí cmp ds:word_FFDFF138, 5 trong NTOSKRNL.EXE thành byte compare

Môi trường tái hiện và triệu chứng

  • Đã cài Windows NT 3.1 lên Compaq ProSignia 3080 rồi thử kernel debugging
    • Hệ thống này thực tế từng chạy Windows NT 3.1, và có vẻ là một trong những máy mà Windows NT nhắm tới rõ ràng
    • RAM được nâng lên 128MB, và CPU Intel 486DX-33 dạng socket được thay bằng AMD enhanced 486DX4-SV8B
    • CPU này hỗ trợ write-back cache và SMM, được gắn vào socket adapter điện áp
  • Việc xử lý hỗ trợ 486DX4 trong BIOS được để lại sau, còn CPU được đặt jumper ở hệ số nhân 2x
    • Với chipset không hỗ trợ L1 write-back mà đặt 2x, kỳ vọng là tương thích phần mềm với Intel 80486DX2-66
    • Intel 80486DX2-66 là một tùy chọn được hỗ trợ trên hệ thống này
  • Bản thân việc cài Windows NT 3.1 hoàn tất bình thường
  • Đĩa CD Windows NT 3.1 có kèm đầy đủ symbol debug nên đã thử kernel debugging
    • Mục đích là điều tra vì sao NetDDE ghi lỗi vào event log
    • Cũng có vấn đề hệ thống bị crash với một số card Ethernet EISA nhất định, và vẫn còn khả năng lỗi phần cứng
  • Phải dùng i386kd đi kèm Windows NT 3.1 thay vì kd hay ntkd từ bộ công cụ phát triển Windows 10 mới hơn thì cấu hình kernel debugging mới khớp
  • Khi thử break-in bằng Ctrl-C trong i386kd, máy đích khởi động lại thay vì đưa ra dấu nhắc kd>

Những khả năng đã được loại trừ

  • Bộ nhớ đã được xác nhận là bình thường
  • Không phải do hỏng file hệ thống
  • Không có watchdog phần cứng nào được bật để khởi động lại máy khi kernel dừng vì debugging
  • USB-serial adapter phía máy host giao tiếp bình thường
    • Thiết bị này có vẻ là PL2301 giả, nhưng không phải do gửi nhầm lệnh debugger thành lệnh “reboot system”
    • Giao thức KD thực sự có lệnh reboot system
  • Cũng không liên quan đến tùy chọn quản trị từ xa hay cảnh báo của mainboard

Nguyên nhân thực sự: enhanced 486 không tương thích với kernel NT 3.1

  • Kernel Windows NT 3.1 không tương thích với bộ xử lý enhanced 486
  • Cụ thể hơn, vấn đề xảy ra trên các CPU 486 có cung cấp lệnh CPUID
  • Kernel debugging hoạt động bình thường trên các CPU sau
    • 486DX-33 gốc ban đầu
    • Am486DX4-NV8T write-through dùng core non-enhanced đời cũ, không có SMM
  • Nếu chỉ muốn thử kernel debugging trên NT 3.1 thì nên dùng CPU vốn tương thích mặc định với Windows NT 3.1
  • Nếu muốn sửa chính NT, cần vá lỗi nhận diện loại CPU trong NTOSKRNL.EXE

Đường đi mã kernel nơi lỗi phát sinh

  • Nguyên nhân trực tiếp của sự không tương thích là lỗi trong KiSaveProcessorControlState
    • Hàm đối ứng KiRestoreProcessorControlState cũng có lỗi tương tự
  • KiSaveProcessorControlState được gọi ở ba vị trí trong NTOSKRNL.EXE
    • Khi một exception được phản ánh tới kernel debugger qua KdpTrap
      • Với break-in Ctrl-C, một breakpoint exception xảy ra trong chức năng polling break-in bên trong timer tick interrupt
    • Khi KeBugCheckEx được gọi, tức là tình huống “màn hình xanh”
    • Khi KiSaveProcessorState được gọi
      • Nếu phân tích control flow của NTOSKRNL.EXE bằng IDA là đầy đủ, hàm này không được export và cũng không được gọi ở đâu trong nội bộ NTOSKRNL, nên trên thực tế có vẻ không xảy ra
  • KiSaveProcessorControlState lưu trạng thái điều khiển bộ xử lý vào cấu trúc CONTEXT mở rộng
    • Lưu CR0, CR2, CR3
    • Trên Pentium trở lên thì lưu cả CR4
    • Lưu các thanh ghi debug DR0~DR3, DR6, DR7
    • Cả các thiết lập protected mode toàn cục như địa chỉ GDT, địa chỉ IDT, TSS selector đang hoạt động, và LDT selector

Lỗi diễn giải các trường KPRCB

  • Việc xác định loại bộ xử lý dùng các giá trị trong KPRCB
    • KPRCB là một phần của KPCR
    • KPRCB của bộ xử lý khởi động hoặc hệ thống đơn xử lý nằm tại địa chỉ ảo FFDFF120, và địa chỉ này được hardcode trong phương thức đó
  • Theo tổng hợp của Geoff Chappell, các trường KPRCB liên quan trong NT 3.1 là:
    • +018 CHAR CpuType
    • +019 CHAR CpuID
    • +01A UShort CpuStep
  • Các trường này được khởi tạo trong KiSetProcessorType
    • Trên bộ xử lý 486, byte ở offset 18 được đặt thành 4
    • Bộ xử lý Pentium được đặt thành 5
    • Pentium Pro và Pentium II/III được đặt thành 6
    • Byte ở offset 19 là cờ boolean cho biết bộ xử lý có hỗ trợ CPUID và hoạt động “hợp lý” hay không
  • Lệnh so sánh gây vấn đề đọc word ở địa chỉ FFDFF138 thay vì byte
    • FFDFF138 là vị trí cách điểm bắt đầu KPRCB 18h byte
    • Vì vậy không chỉ CpuType mà cả byte kế tiếp là CpuID cũng bị diễn giải chung như một phần của số model
  • Các bộ xử lý hỗ trợ CPUID bị coi là có số model lớn hơn thực tế 256
    • 80-4-86 có hỗ trợ CPUID bị Windows NT 3.1 coi như 80-260-86
    • 260 lớn hơn rất nhiều so với mốc Pentium là 5, nên kernel kết luận rằng CPU đó phải có CR4
    • Enhanced 486 không có CR4, vì vậy đường break-in phát sinh lỗi

Cách vá

  • Sau khi xác nhận lỗi, cách sửa khá đơn giản
  • Trong NTOSKRNL.EXE, lệnh cmp ds:word_FFDFF138, 5 chỉ xuất hiện đúng hai lần
    • KiSaveProcessorControlState
    • KiRestoreProcessorControlState
  • Cả hai vị trí đều phải đổi word compare thành byte compare
  • Dùng hex editor để vá chuỗi byte sau ở cả hai chỗ
    • Cũ: 66 83 3D 38 F1 DF FF 05
    • Mới: 90 80 3D 38 F1 DF FF 05
  • Bản sửa này áp dụng được cho cả NTOSKRNL.EXE của bản phân phối NT 3.1 Advanced Server nguyên bản và NT 3.1 SP3

1 bình luận

 
GN⁺ 2023-09-29
Các ý kiến trên Hacker News
  • Trước đây tôi từng làm một công cụ kiểm tra cổng để xem dịch vụ còn sống không, và chỉ bằng cách mở vài cổng TCP trên các máy, tôi đã làm chết một nửa công ty
    Đó là một thời kỳ thật vô lý

    • Đã từng có lúc Win95 có thể chết, thậm chí bị chiếm quyền, chỉ bằng cách ping theo một kiểu nhất định
      Chúng ta đã đi được một chặng đường rất xa
      https://en.wikipedia.org/wiki/Ping_of_death
      Một số máy còn có lỗ hổng SMB không được vá trong nhiều năm. Đó đã là thời có Metasploit, nên chỉ với vài lệnh là có thể tiêm VNC vào hầu hết các máy Windows trong mạng nội bộ. Ngày nay, ít nhất tốc độ vá lỗi đã nhanh hơn rất nhiều
    • Tôi nghĩ là nhiều hơn “vài” cái rất nhiều
  • À, cái hang thỏ của retro computing. Nó được tách biệt một cách vô hại khỏi các hệ quả trong thế giới thực, nhưng vẫn cực kỳ thỏa mãn
    Đúng là một cái bẫy mật cho dân geek. Cuối cùng tôi vẫn bấm nút mở rộng “xem thêm 6 bình luận”

    • Tôi thấy con trỏ đang hướng tới nút mở rộng đó và kịp kéo nó ra
      Có lẽ hôm nay tôi vẫn làm được chút việc
    • Nhìn những máy tính có trước thời của tôi, như PET chẳng hạn, tôi thấy chắc sẽ rất vui nếu được nghịch thử. Một máy IRIX cũ cũng ổn
      Nhưng 486 ư? Nó gợi lại quá nhiều ký ức về màn hình xanh và những lần chờ đợi vô tận vì máy không gánh nổi bộ nhớ nên phải swap ra đĩa. Có vẻ với tôi vẫn còn quá sớm
    • Nội dung bài viết dường như vẫn còn liên quan ở mức nào đó cho đến ngày nay
  • Câu “giải pháp thì hiển nhiên” thật sự rất đúng

    • Giáo sư toán của tôi từng nói có hai loại bài toán: bài dễ và bài mình chưa hiểu
    • Đúng là vậy sau khi bug đã được xác định. Nhưng tôi tò mò họ tìm ra những bug kiểu này bằng cách nào
      Có lẽ là cứ đọc disassembly của mã trap cho đến khi phát hiện ra vấn đề. Tôi không nghĩ khi đó có thể dùng QEMU hay phương tiện debug nào khác
  • Nhất định phải đọc cả cách làm bị chôn trong phần bình luận bị ẩn ở cuối

    • Tôi không bao giờ hiểu được cơ chế tự động thu gọn bình luận của Stack Overflow
      Nó không phải là ẩn bình luận có ít phiếu nhất, cũng không phải ẩn theo mới nhất hay cũ nhất. Nó có cảm giác ngẫu nhiên và không cần thiết
      Nếu vấn đề là không gian, tôi nghĩ nên dùng phân trang thay vì ẩn đi
  • Những thứ như adapter slotket từng được dùng để tương thích trên các bo mạch Abit, Asus cũ, và có lẽ cả MSI, thật sự có khả năng ép xung
    Mấu chốt là tốc độ RAM là bao nhiêu và bo mạch chủ chịu được xung đến mức nào ở thiết lập hệ số nhân. Đó là thời kỳ ép xung tương đối sơ khai, thời của Celeron và Pentium II
    Có lúc vì lý do tương thích, tôi cắm một con Celeron 600 vào adapter, rồi lại cắm vào adapter Slot II sang Socket 370, chạy ổn ở 1.2GHz với cấu hình kiểu [1]. Thậm chí hình như tôi còn đẩy lên 1.4GHz trên Windows ME, và cuối cùng đã đốt cháy con CPU đó
    [1]http://krick.3feetunder.com/370mod/

    • Ép xung từng rất vui, nhưng việc CPU hiện đại có thể điều khiển xung và điện áp rất chính xác để tận dụng biên an toàn ngày xưa cũng là điều tốt
  • Câu trả lời cho câu hỏi đó thật sự xuất sắc
    Chủ đề hay, câu trả lời cũng rất hay

    • Thật ra là người đó tự trả lời câu hỏi của mình. Nhưng vẫn xuất sắc
  • Thật tuyệt khi người đặt câu hỏi tự đăng cả câu trả lời
    Tôi thích những thứ như thế này. Rất vui khi mọi người quan tâm đến retro computing
    Việc ghi chép lại cả những vấn đề và giải pháp như thế này cho tất cả mọi người là một điều tốt

  • Tôi biết cái CPUID này là một ý tưởng tệ mà

    • Thật buồn cười khi các lập trình viên Windows cảm thấy cần đưa một phần “trustworthiness” vào struct
  • Vì sao timestamp của câu hỏi và câu trả lời lại giống nhau?

    • Là để chia sẻ thông tin. Có thể thấy người viết câu hỏi và người viết câu trả lời là cùng một người
      Có lẽ họ cho rằng đăng thông tin này theo dạng hỏi đáp trên SO sẽ tồn tại lâu hơn một bài blog mà không ai tìm thấy
  • Có tệ không khi tôi biết nó là gì mà không cần mở ra?
    Thôi được rồi, tôi phải đi hét với bọn trẻ thời nay rằng hãy ra khỏi bãi cỏ nhà tôi đây

    • Việc giải quyết vấn đề thời chúng tôi lớn lên rất khác
      Thứ có thể dựa vào chỉ là sự lanh trí, và nếu cần thì vài cao thủ có thể gọi bằng điện thoại cố định. Không có một Internet tuyệt vời đầy những câu trả lời hữu ích ở mức độ rất khác nhau như bây giờ