Warum startet meine 486DX4-Maschine neu, wenn ich versuche, in den NT-3.1-Kernel einzubrechen?
(retrocomputing.stackexchange.com)- Wenn man unter Windows NT 3.1 mit
i386kdein Ctrl-C-Break-in auslöst, erhält man statt deskd>-Prompts einen Neustart des Zielsystems mit 486DX4; Ursache war ein Problem mit der enhanced-486-Kompatibilität KiSaveProcessorControlStateundKiRestoreProcessorControlStatein NTOSKRNL.EXE verwenden beim Prüfen des CPU-Typs einen Word-Vergleich, wodurch ein 486 mit CPUID-Unterstützung fälschlich als Pentium oder neuer erkannt wird- Da
CpuTypeundCpuIDim KPRCB gemeinsam gelesen werden, wird die Modellnummer eines CPUID-fähigen 486 um 256 zu groß interpretiert; dadurch erfolgt ein Zugriff auf das auf 486 nicht vorhandene CR4-Register - Mit dem ursprünglich eingebauten 486DX-33 und einem write-through Am486DX4-NV8T ohne SMM funktionierte Kernel-Debugging korrekt, wodurch sich das Problem auf enhanced 486 mit CPUID-Befehl eingrenzen ließ
- Wenn man die beiden Stellen
cmp ds:word_FFDFF138, 5in NTOSKRNL.EXE auf einen Byte-Vergleich patcht, lässt sich sowohl das originale NT 3.1 Advanced Server als auch NT 3.1 SP3 korrigieren
Reproduktionsumgebung und Symptom
- Auf einem Compaq ProSignia 3080 wurde Windows NT 3.1 installiert und anschließend Kernel-Debugging versucht
- Dieses System lief tatsächlich produktiv mit Windows NT 3.1 und scheint eine der Maschinen gewesen zu sein, auf die Windows NT ausdrücklich abzielte
- Der RAM wurde auf 128 MB erweitert, und der gesockelte Intel 486DX-33 wurde durch einen AMD enhanced 486DX4-SV8B ersetzt
- Diese CPU unterstützt write-back cache und SMM und ist in einem Spannungsadapter-Sockel montiert
- Die BIOS-Unterstützung für den 486DX4 wurde zunächst ignoriert und die CPU per Jumper auf 2x-Multiplikator gesetzt
- Ohne L1-write-back-Unterstützung des Chipsatzes und mit 2x-Multiplikator wurde erwartet, dass die Software-Kompatibilität einem Intel 80486DX2-66 entspricht
- Der Intel 80486DX2-66 ist eine unterstützte Option für dieses System
- Die Installation von Windows NT 3.1 selbst verlief problemlos
- Da die Windows-NT-3.1-CD vollständige Debug-Symbole enthält, wurde Kernel-Debugging ausprobiert
- Ziel war es, herauszufinden, warum NetDDE Fehler ins Ereignisprotokoll schreibt
- Zusätzlich gab es ein Problem, bei dem das System mit einer bestimmten EISA-Ethernet-Karte abstürzte, sodass ein Hardwaredefekt nicht ausgeschlossen war
- Statt
kdoderntkdaus dem aktuellen Windows-10-Entwicklungskit musste das mit Windows NT 3.1 geliefertei386kdverwendet werden, damit die Kernel-Debugging-Konfiguration passt - Wenn in
i386kdein Ctrl-C-Break-in versucht wurde, lieferte die Zielmaschine keinenkd>-Prompt, sondern startete neu
Dinge, die als Ursache ausgeschlossen wurden
- Der Speicher wurde als fehlerfrei bestätigt
- Es lag keine Beschädigung von Systemdateien vor
- Es war kein Hardware-Watchdog aktiv, der das System neu startet, wenn der Kernel für Debugging angehalten wird
- Der USB-Seriell-Adapter auf Host-Seite kommunizierte korrekt
- Das Gerät scheint zwar ein gefälschter PL2301 zu sein, hat aber nicht versehentlich einen Debugger-Befehl falsch übertragen und dadurch „reboot system“ gesendet
- Im KD-Protokoll gibt es tatsächlich einen Befehl zum Neustart des Systems
- Auch mit Remote-Management- oder Warnoptionen des Mainboards hing das Problem nicht zusammen
Die eigentliche Ursache: Inkompatibilität zwischen enhanced 486 und NT-3.1-Kernel
- Der Windows-NT-3.1-Kernel ist nicht kompatibel mit enhanced-486-Prozessoren
- Genauer gesagt tritt das Problem bei 486-Prozessoren auf, die den Befehl
CPUIDbereitstellen - Kernel-Debugging funktioniert korrekt auf folgenden CPUs
- dem ursprünglich eingebauten 486DX-33
- einem write-through Am486DX4-NV8T mit älterem non-enhanced core ohne SMM
- Wenn es nur darum geht, NT-3.1-Kernel-Debugging zu testen, ist es sinnvoller, eine CPU zu verwenden, die von Windows NT 3.1 standardmäßig unterstützt wird
- Wenn man NT selbst korrigieren will, muss der Fehler in der CPU-Typ-Erkennung von NTOSKRNL.EXE gepatcht werden
Der Kernel-Codepfad, auf dem das Problem auftritt
- Die direkte Ursache der Inkompatibilität ist ein Bug in
KiSaveProcessorControlState- Die zugehörige Funktion
KiRestoreProcessorControlStateenthält einen ähnlichen Bug
- Die zugehörige Funktion
KiSaveProcessorControlStatewird an drei Stellen innerhalb von NTOSKRNL.EXE aufgerufen- wenn eine Ausnahme über
KdpTrapan den Kernel-Debugger reflektiert wird- Bei Ctrl-C-Break-in entsteht dabei eine Breakpoint-Ausnahme in der Break-in-Polling-Funktion innerhalb des Timer-Tick-Interrupts
- wenn
KeBugCheckExaufgerufen wird, also im „Blue Screen“-Fall - wenn
KiSaveProcessorStateaufgerufen wird- Wenn IDAs Kontrollflussanalyse von NTOSKRNL.EXE vollständig ist, wird diese Funktion nicht exportiert und auch intern von NTOSKRNL nicht aufgerufen, sodass dieser Fall praktisch nicht eintritt
- wenn eine Ausnahme über
KiSaveProcessorControlStatespeichert den Prozessor-Steuerzustand in einer erweitertenCONTEXT-Struktur- Es speichert CR0, CR2 und CR3
- Auf Pentium oder neuer wird auch CR4 gespeichert
- Es speichert die Debug-Register DR0~DR3, DR6 und DR7
- Außerdem speichert es globale Protected-Mode-Einstellungen wie GDT-Adresse, IDT-Adresse, aktiven TSS-Selektor und LDT-Selektor
Fehlinterpretation von KPRCB-Feldern
- Zur Bestimmung des Prozessortyps werden Werte aus dem KPRCB verwendet
- KPRCB ist Teil des KPCR
- Das KPRCB des Boot-Prozessors oder eines Single-Processor-Systems liegt bei der virtuellen Adresse
FFDFF120, was in dieser Methode hartkodiert ist
- Die von Geoff Chappell zusammengestellten relevanten KPRCB-Felder in NT 3.1 sind:
+018 CHAR CpuType+019 CHAR CpuID+01A UShort CpuStep
- Diese Felder werden in
KiSetProcessorTypeinitialisiert- Bei 486-Prozessoren wird das Byte an Offset 18 auf 4 gesetzt
- Bei Pentium-Prozessoren auf 5
- Bei Pentium Pro sowie Pentium II/III auf 6
- Das Byte an Offset 19 ist ein boolesches Flag dafür, ob der Prozessor
CPUIDunterstützt und sich „vernünftig“ verhält
- Die problematische Vergleichsinstruktion liest kein Byte an Adresse
FFDFF138, sondern ein WordFFDFF138liegt 18h Byte vom Anfang des KPRCB entfernt- Dadurch wird nicht nur
CpuType, sondern auch das direkt folgende ByteCpuIDgemeinsam als Teil der Modellnummer interpretiert
- Prozessoren mit
CPUID-Unterstützung werden dadurch so behandelt, als wäre ihre Modellnummer um 256 höher als tatsächlich- Ein CPUID-fähiger 80-4-86 wird unter Windows NT 3.1 wie ein 80-260-86 behandelt
- 260 ist deutlich größer als der Pentium-Grenzwert 5, daher nimmt der Kernel an, dass dieser Prozessor ein CR4 besitzen muss
- Da enhanced 486 kein CR4 haben, tritt der Fehler auf dem Break-in-Pfad auf
Patch-Methode
- Ist der Bug einmal bestätigt, ist die Korrektur einfach
- In NTOSKRNL.EXE kommt die Instruktion
cmp ds:word_FFDFF138, 5genau zweimal vorKiSaveProcessorControlStateKiRestoreProcessorControlState
- An beiden Stellen muss der Word-Vergleich auf einen Byte-Vergleich geändert werden
- In einem Hex-Editor wird dazu zweimal die folgende Bytefolge gepatcht
- Alt:
66 83 3D 38 F1 DF FF 05 - Neu:
90 80 3D 38 F1 DF FF 05
- Alt:
- Diese Änderung funktioniert sowohl für die originale NT-3.1-Advanced-Server-Ausgabe von NTOSKRNL.EXE als auch für NT 3.1 SP3
1 Kommentare
Hacker-News-Kommentare
Ich hatte früher einmal einen Port-Check gebaut, um zu prüfen, ob ein Dienst noch lief, und habe allein dadurch, dass ich ein paar TCP-Ports auf den Maschinen geöffnet habe, die halbe Firma lahmgelegt.
Das waren absurde Zeiten.
Wir sind wirklich weit gekommen.
https://en.wikipedia.org/wiki/Ping_of_death
Auf manchen Maschinen gab es außerdem SMB-Schwachstellen, die über Jahre nicht gepatcht wurden. Das war bereits zu Zeiten von Metasploit, sodass man mit ein paar Befehlen auf den meisten Windows-Hosts im lokalen Netzwerk VNC einschleusen konnte. Heute geht das Patchen zumindest enorm schnell.
Ah, das Kaninchenloch des Retro-Computing. Harmlos von realweltlichen Konsequenzen getrennt, aber trotzdem ungeheuer befriedigend.
Eine echte Honigfalle für Nerds. Am Ende habe ich doch auf den Button „6 weitere Kommentare anzeigen“ geklickt.
Vielleicht bekomme ich heute doch noch etwas Arbeit erledigt.
Aber ein 486? Da kommen zu viele Erinnerungen an Bluescreens und endloses Warten hoch, weil der Rechner den Speicher nicht bewältigen konnte und ständig auf die Platte geswappt hat. Dafür ist es für mich wohl noch zu früh.
„Die Lösung ist offensichtlich“ trifft es wirklich.
Vermutlich hat jemand immer weiter die Disassemblierung des Trap-Codes gelesen, bis das Problem auffiel. Ich glaube nicht, dass QEMU oder andere Debugging-Mittel zur Verfügung standen.
Man sollte unbedingt auch die Methode lesen, die in den versteckten Kommentaren am Ende vergraben ist.
Es werden weder die Kommentare mit den wenigsten Stimmen versteckt, noch die neuesten oder ältesten. Es wirkt zufällig und unnötig.
Wenn Platz das Problem ist, fände ich Paginierung besser als Ausblenden.
Diese Slotket-Adapter, wie sie früher auf Abit-, Asus- und vielleicht MSI-Boards aus Kompatibilitätsgründen genutzt wurden, hatten wirkliches Overclocking-Potenzial.
Entscheidend waren die RAM-Geschwindigkeit und die Frage, wie weit das Mainboard bei den Multiplikator-Einstellungen den Takt mitmachte. Das waren die relativ frühen Overclocking-Zeiten mit Celeron und Pentium II.
Ich hatte aus Kompatibilitätsgründen einmal einen Celeron 600 in einen Adapter gesteckt und diesen wiederum in einen Slot-II-auf-Socket-370-Adapter, in einer Konfiguration wie [1], und er lief problemlos mit 1,2 GHz. Ich glaube sogar, dass ich ihn unter Windows ME auf 1,4 GHz gebracht habe; am Ende habe ich die CPU dann durchgebrannt.
[1]http://krick.3feetunder.com/370mod/
Die Antwort auf die Frage war wirklich hervorragend.
Gutes Thema und eine sehr gute Antwort.
Schön, dass der Fragesteller auch gleich selbst die Antwort gepostet hat.
So etwas mag ich. Es freut mich wirklich, dass sich Leute für Retro-Computing interessieren.
Es ist gut, solche Probleme und ihre Lösungen für alle zu dokumentieren.
Ich wusste doch, dass dieses CPUID keine gute Idee ist.
Warum hatten Frage und Antwort denselben Zeitstempel?
Vermutlich hielt er es für besser, diese Information im Frage-und-Antwort-Format von SO zu veröffentlichen, wo sie länger erhalten bleibt, als in einem Blogpost, den niemand findet.
Ist es schlimm, dass ich wusste, worum es geht, ohne es zu öffnen?
Egal, ich muss jetzt die Kids anschreien, sie sollen von meinem Rasen runter.
Man hatte nur seinen Verstand und, falls nötig, ein paar Gurus, die man per Festnetztelefon anrufen konnte. Es gab kein wunderbares Internet voller Antworten von höchst unterschiedlicher Nützlichkeit.