Forum: PC-Programmierung Speicherdump mithilfe von C unter DOS erstellen


von Asterix (Gast)


Lesenswert?

Liebes Forum,

mit dem folgenden Programm habe ich versucht unter MS-DOS einen 
Speicherdump des ersten Megabyte zu erstellen. – Sinn des ganzen ist es 
PnP Geräte zu finden, die sich laut PnP BIOS Spezifikation im 
Speicherbereich 0xF0000 - 0xFFFFF finden lassen und durch einen $PnP 
String an ihrem Anfang, das es zu suchen gilt, erkennen lassen. – Nur 
ist es nun so, dass mein Programm (kompiliert und ausgeführt mit Turbo 
C++ 1.01 unter FreeDOS 1.2) nur ein einziges Speichersegment zu lesen 
scheint.

Die Ausgabe sagt zwar, dass alle Segmente addresiert würden, im Dump 
wiederholt sich allerdings alles. Zu erkennen war es daran das, dass 
sich ein stehendes Bild ergab, wenn ich im Hexeditor nach "pnp" gesucht 
habe und die Taste festgehelten habe.
1
#include <stdlib.h>
2
#include <stdio.h>
3
4
int main (int argc, char **argv) {
5
 long i;
6
 unsigned char far *mem;
7
 long start = 0x00000;
8
 long stop = 0xfffff;
9
 FILE *file;
10
11
 file = fopen ("memory.dmp", "wb");
12
13
 for (i = start; i < stop; i += 16) {
14
  mem = (void far *) i;
15
  printf ("%p\n", mem);
16
  fwrite (mem, 16, 1, file);
17
 }
18
19
 fclose (file);
20
 return 0;
21
}

Da viele online Resourcen über DOS heutzutage down sind und nur noch 
durch tote Links in Erinnerung geblieben sind, würde ich mich freuen, 
falls mir jemand erklären könnte wo das Problem in meinem Code ist. Das 
Huge Model hab ich in den Optionen schon gewählt und zwei "far" habe ich 
auch. Nur gehen tut es nicht.

Vielen Dank

von Mario M. (thelonging)


Lesenswert?

WIMRE wird der Segment-Teil der Adresse in der oberen Hälfte des 
Pointers gespeichert. Die physische Adresse 0xfabcd wird also als 
0xfabc000d geschrieben (oder auch 0xf000abcd).

von foobar (Gast)


Lesenswert?

> WIMRE wird der Segment-Teil der Adresse in der oberen Hälfte des
> Pointers gespeichert.

War das nicht der Unterschied zwischen far und huge?  Far: 16 bit 
segment plus 16 bit offset in 32bit.  Huge: 20 bit address in 32bit, 
splitten nach seg/offset bei Zugriff.

Alles lange her ... ich vermute, fwrite kommt mit den Pointern nicht 
klar, kann nur 16 bit.  Mal nen huge-Pointer probiert, selbst 
dereferenziert und per fputc ausgegeben?

von Rolf M. (rmagnus)


Lesenswert?

Man muss denke ich auch darauf achten, dass das Programm mit dem 
richtigen Speichermodell compiliert wird.

Mario M. schrieb:
> WIMRE wird der Segment-Teil der Adresse in der oberen Hälfte des
> Pointers gespeichert. Die physische Adresse 0xfabcd wird also als
> 0xfabc000d geschrieben (oder auch 0xf000abcd).

Eigentlich war es üblich, Segment- und Offsetadresse durch Doppelpunkt 
getrennt zu schreiben, da die beiden ja nicht einfach aneinander gehängt 
werden. Die physische Adresse ergibt sich aus
Segmentadresse*16 + Offsetadresse. Dann ist auch logisch, dass sich 
alles wiederholt, weil bei Erhöhung der Segmentadresse um 1 alles 
nochmal durchlaufen wird - lediglich um 16 Bytes verschoben.

: Bearbeitet durch User
von Cppbert3 (Gast)


Lesenswert?

hier gibt es ein Pascal Tool unter Option 7 gibt es ein Memory snapshot 
für das 1. Mbyte

https://github.com/BenjaminSoelberg/dos-DumpExe

von Asterix (Gast)


Lesenswert?

foobar schrieb:
>> WIMRE wird der Segment-Teil der Adresse in der oberen Hälfte des
>> Pointers gespeichert.
>
> War das nicht der Unterschied zwischen far und huge?  Far: 16 bit
> segment plus 16 bit offset in 32bit.  Huge: 20 bit address in 32bit,
> splitten nach seg/offset bei Zugriff.
>
> Alles lange her ... ich vermute, fwrite kommt mit den Pointern nicht
> klar, kann nur 16 bit.  Mal nen huge-Pointer probiert, selbst
> dereferenziert und per fputc ausgegeben?

Danke, das wusste ich noch gar nicht, dass es auch ein huge 
Schlüsselwort gibt. Wobei ich das Huge Model schon eingestellt hatte.

Der Dump funktioniert und hat es wohl sogar schon vorher. Bei genauerem 
hinsehen am Segmentbeginn Stelle ich fest, dass sie schon 
unterschiedlich sind. Allerdings ist da immernoch ein stehendes Bild bei 
der Suche nach pnp (Das Programm hat pnp im Namen). Es liegt scheinbar 
etwas so 10x im Speicher.

Rolf M. schrieb:
> Man muss denke ich auch darauf achten, dass das Programm mit dem
> richtigen Speichermodell compiliert wird.
>
> Mario M. schrieb:
>> WIMRE wird der Segment-Teil der Adresse in der oberen Hälfte des
>> Pointers gespeichert. Die physische Adresse 0xfabcd wird also als
>> 0xfabc000d geschrieben (oder auch 0xf000abcd).
>
> Eigentlich war es üblich, Segment- und Offsetadresse durch Doppelpunkt
> getrennt zu schreiben, da die beiden ja nicht einfach aneinander gehängt
> werden. Die physische Adresse ergibt sich aus
> Segmentadresse*16 + Offsetadresse. Dann ist auch logisch, dass sich
> alles wiederholt, weil bei Erhöhung der Segmentadresse um 1 alles
> nochmal durchlaufen wird - lediglich um 16 Bytes verschoben.

Die Programausgabe sagt das auch so, scheint richtig zu sein:
1
0000:0000
2
0000:0010
3
0000:0020
4
5
000F:FFD0
6
000F:FFE0
7
000F:FFF0

Um zu meinem eigentlichen Anliegen zu kommen – Plug 'n' Play – einen 
$PnP String habe ich nicht im Speicher gefunden. Dabei ist doch heute 
alles PnP.
Fragen zu einem PCI Bus Scan gehören wohl eher in einen neuen Thread. 
Trotzdem wäre ich euch dankbar, falls jemand der in der DOS Ära schon 
aktiv war, den ein oder anderen Buchtipp zum Thema hätte.

von Rolf M. (rmagnus)


Lesenswert?

Asterix schrieb:
> Die Programausgabe sagt das auch so, scheint richtig zu sein:
> 0000:0000
> 0000:0010
> 0000:0020
> …
> 000F:FFD0
> 000F:FFE0
> 000F:FFF0

Ja, aber 0001:0000 ist nicht die Adresse bei 64k, also 0x10000, sondern 
bei 16 Byte, also 0x10. Es ist ein und die selbe Speicherstelle wie 
0000:0010.

von Asterix (Gast)


Angehängte Dateien:

Lesenswert?

Rolf M. schrieb:
> Asterix schrieb:
>> Die Programausgabe sagt das auch so, scheint richtig zu sein:
>> 0000:0000
>> 0000:0010
>> 0000:0020
>> …
>> 000F:FFD0
>> 000F:FFE0
>> 000F:FFF0
>
> Ja, aber 0001:0000 ist nicht die Adresse bei 64k, also 0x10000, sondern
> bei 16 Byte, also 0x10. Es ist ein und die selbe Speicherstelle wie
> 0000:0010.

Danke, jetz funktioniert das Programm. In der Segmentadresse*16 + 
Offsetadresse Konfiguration bekomm ich zwar leider in meiner for 
Schleife einen Overflow. Einen größeren Integer hab ich nicht und eine 
do while will ich nicht, deswegen hab ich die Segmentadresse soweit 
verkleinert, dass ich sie mit 65536 multiplizieren kann. Leider habe ich 
das Gefühl das Ganze fundamental falsch zu verwenden, da es ja 
eigentlich eine Optimierung auf kurze Datentypen ist.

Hier noch der Vollständigkeit halber das neue Programm:
1
#include <stdlib.h>
2
#include <stdio.h>
3
4
int main (int argc, char **argv) {
5
 unsigned long i, j, i_0 = 0, j_0 = 0, i_1 = 0xffff, j_1 = 0xf000;
6
 unsigned long di = 0x10, dj = 0x1000;
7
 unsigned char *mem;
8
 FILE *file;
9
10
 file = fopen ("memory.dmp", "wb");
11
 for (j = j_0; j <= j_1; j += dj) {
12
  for (i = i_0; i <= i_1; i += di) {
13
   mem = (void *) (j * 0x10000 + i);
14
   printf ("%p = %lu = %lx, %lx, %lx\n", mem, mem, mem, i, j * 0x10000);
15
   fwrite (mem, di, 1, file);
16
  }
17
 }
18
19
 fclose (file);
20
 return 0;
21
}

Jetzt kann ich im spezifizierten Bereich auch zweimal den String $PnP 
finden. Bin schon gespannt was das wohl für Geräte sind, schlißlich hat 
der Laptop mehr als zwei Komponenten.

foobar schrieb:
>> WIMRE wird der Segment-Teil der Adresse in der oberen Hälfte des
>> Pointers gespeichert.
>
> War das nicht der Unterschied zwischen far und huge?  Far: 16 bit
> segment plus 16 bit offset in 32bit.  Huge: 20 bit address in 32bit,
> splitten nach seg/offset bei Zugriff.
>
> Alles lange her ... ich vermute, fwrite kommt mit den Pointern nicht
> klar, kann nur 16 bit.  Mal nen huge-Pointer probiert, selbst
> dereferenziert und per fputc ausgegeben?

Aus dem was in der Turbo C++ Hilfe zum Large und Huge Model steht, werd 
ich auch noch nicht ganz schlau. Bedeutet das, dass ich im Large Model 
Funktionspointer verwenden müsste und im Huge Model tuts auch ein 
Voidpointer? Hab von der Hilfe mal zwei Screenshots angehängt.

von rbx (Gast)


Lesenswert?

Man kann auch das Debug Programm in FreeDos benutzen. Das kann man 
umschalten zwischen 16 und 32 Bit und sich außerdem MMX/SSE/FPU Register 
anzeigen lassen.

Mit dem Watcom-Compiler kann man "Protected-Mode"-Programme erstellen, 
wenn man die "386"-Version des Compilers aufruft.
https://en.wikipedia.org/wiki/Watcom_C/C%2B%2B
https://en.wikipedia.org/wiki/Open_Watcom_Assembler

Da das Vi-Programm auch in einer 32-Bit-Protected-Mode-Variante 
vorliegt, kann man es direkt in der Windows(8)konsole benutzen, man 
braucht keinen Emulator.

Technisch gesehen - also wenn man weiß, was man im Hexeditor 
hinzuschreiben hat, kann man in FreeDos das kleinste "Speichermodell" 
nehmen (nur ein Segment, Sprünge (möglichst) nur innerhalb von 100h. 
(z.B.)

von turbo (Gast)


Lesenswert?

In Turbo Pascal gibt es das Array Mem[] mit dem du jede beliebige 
Adresse erreichen kannst.

von Dirk B. (dirkb2)


Lesenswert?

Im Real Mode werden zum Zugriff auf den Speicher die Segmentregister 
benötigt.

Abhängig vom Speichermodell werden die teilweise auf konstante Werte 
gesetzt um das umkopieren zu sparen.

Im Large Model darf ein Datenobjekt (Array, Struct, Pointer) nur 64 kB 
groß sein.
Beim Zugriff wird nur der Index geändert, Data Segment Register (DS) 
zeigt auf den Anfang.

Im Huge Model wird auch das DS geändert.

Das braucht mehr Code, daher verzichtet man darauf gerne.

von Rolf M. (rmagnus)


Lesenswert?

Dirk B. schrieb:
> Das braucht mehr Code, daher verzichtet man darauf gerne.

Vor allem können Segmentregister nicht direkt beschrieben/bearbeitet 
werden. Man muss immer den Umweg über ein anderes Register oder den 
Stack gehen. Das bei jedem Speicherzugriff machen zu müssen, ist schon 
aufwändig. Bei Large kann man wenigstens durch ein Array iterieren, ohne 
das bei jeder einzelnen Iteration machen zu müssen.

von rbx (Gast)


Lesenswert?

Ich habe auf stackoverflow noch etwas gefunden, das hilft vielleicht 
(noch, ein wenig) weiter:
https://stackoverflow.com/questions/56084263/how-can-i-access-full-memory-space-in-freedos-with-c-application/56085619

von georg (Gast)


Lesenswert?

Wozu überhaupt der ganze Aufwand? DEBUG genügt doch für den angefragten 
Zweck. Und für viele andere.

Georg

von Dirk B. (dirkb2)


Lesenswert?

georg schrieb:
> Wozu überhaupt der ganze Aufwand? DEBUG genügt doch für den angefragten
> Zweck. Und für viele andere.

Wozu Fahrrad fahren lernen, es gibt Bus/Papa/Mama.

: Bearbeitet durch User
von Asterix (Gast)


Angehängte Dateien:

Lesenswert?

Vielen Dank für die vielen Antworten. Der PnP Scanner ist jetzt fertig. 
In der DosBox findet er zwar leider nichts, auf meinem Laptop bekomm ich 
jedoch die folgende Ausgabe. Hab das Prgramm und den Quellcode mal in 
den Anhang gepackt.
1
PnP Hardware Scanner 1.0 Alpha
2
Copyright (R) 2019  Asterix
3
Released to the Public Domain
4
5
PnP Device Found at F000:0940
6
Signature: $PnP
7
Version: 10
8
Length: 33
9
Control Field: 0
10
Checksum: 85
11
Event Notification Flag Address: b7a0000
12
Real Mode 16-Bit Offset to Entry Point: 91
13
Real Mode 16-Bit Code Segment Address: 0
14
16-Bit Protected Mode Offset to Entry Point: 68
15
16-Bit Protected Mode Code Segment Base Address: b7a0000
16
OEM Device Identifier: b7a0000
17
Real Mode 16-Bit Data Segment Address: 0
18
16-Bit Protected Mode Data Segment Base Address: b7a0000

Bleibt nur die Frage was das für ein Gerät ist. Die OEM hat ja soweit 
ich weiß was mit EISA zu tun.

georg schrieb:
> Wozu überhaupt der ganze Aufwand? DEBUG genügt doch für den angefragten
> Zweck. Und für viele andere.
>
> Georg

Storytime – Es war einmal ein Computernutzer mit seiner x86 kompatiblen 
Maschine und ihrem Linux. Der dachte sich eines Tages: "I'd prefer 
starting my Linux by typing LIN on a DOS prompt over all those 
bootloaders." Und alsbald war auch schon DOS installiert. Ein kleines 
Skriptchen mit dem wunderschönen Namen LIN.BAT war auch gleich 
geschrieben. Und So nahmen die Dinge ihren Lauf.

Es kam der Tag, da blieb der Computernutzer für eine Weile im DOS bevor 
er LIN eingab. Er hat ein paar Treiber installiert. Fürs CD-Laufwerk und 
die Maus ja sogar für die Netzwerkkarte gabs einen. Damit stand dem 
ersten alten Spiel auch nichts mehr im Wege, was dagegen gesprochen 
hätte, es nicht auch zu installieren. –

Ja und so kommt das, dass man im 21. Jahrhundert versucht PC Hardware im 
Real Mode zu finden. Das Progrämmchen kann vermutlich nur ISA. PCIe ist 
soweit ich weiß unter 0xE0000000 - 0xF0000000 zu finden. Schön Memory 
Mapped. Werd mal versuchen da mit DEBUG hinzuschauen.

Kennt jemand noch Webseiten zum Thema? Sowas wie vogons oder osdev zum 
Beispiel.

von Dirk B. (dirkb2)


Lesenswert?

Geräte werden aber meist über den IO-Bus angesprochen.

von S. R. (svenska)


Lesenswert?

Asterix schrieb:
> Kennt jemand noch Webseiten zum Thema?
> Sowas wie vogons oder osdev zum Beispiel.

Die deutsche Variante von osdev wäre auf 
http://www.lowlevel.eu/wiki/Hauptseite zu finden. :-)

Asterix schrieb:
> Storytime

Kann LOADLIN überhaupt noch aktuelle Kernel starten?

von cppbert3 (Gast)


Lesenswert?

https://superuser.com/questions/727226/how-to-list-hardware-in-dos

Hier werden ein paar dos tools aufgefuehrt hwinfo sieht ganz gut aus

von Asterix (Gast)


Lesenswert?

S. R. schrieb:
> Asterix schrieb:
>> Kennt jemand noch Webseiten zum Thema?
>> Sowas wie vogons oder osdev zum Beispiel.
>
> Die deutsche Variante von osdev wäre auf
> http://www.lowlevel.eu/wiki/Hauptseite zu finden. :-)

Dankeschön, die kannte ich noch nicht.

S. R. schrieb:
> Asterix schrieb:
>> Storytime
>
> Kann LOADLIN überhaupt noch aktuelle Kernel starten?

Nein, da hast Du recht LOADLIN hat da Probleme. Mit LINLD von hier 
https://busybox.net/~vda/linld und ein klein wenig concat Magic für die 
Microcodeupdates gehts. Die LIN.BAT sieht ungefähr so aus:
1
@echo=off
2
del minitrd.img
3
copy /b intel.img + initram.img minitrd.img
4
linld image=vmlinuz initrd=minitrd.img cl=@linux.txt

cppbert3 schrieb:
> https://superuser.com/questions/727226/how-to-list-hardware-in-dos
>
> Hier werden ein paar dos tools aufgefuehrt hwinfo sieht ganz gut aus

Das stimmt, HWINFO ist gut. Einzige Schattenseite ist, dass es Unmengen 
konventionellen Speicher braucht.

Als nächstes werde ich mich mal dem ACPI widmen. Damit kann man die 
Basisadresse der PCI(e) Memory Map finden. Soweit ich das verstanden 
habe, ist mit dem PCIe Interface ja auch Zugriff auf PCI Geräte möglich. 
Hier noch jemand der das mal umreißt 
https://www.waste.org/~winkles/hardware/pci.htm und da die ACPI 
Spezifikation 
https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf 
fürs Tabellenlayout.

Leider habe ich Turbo C++ schon wieder zu gunsten von NASM und SmallerC 
verlassen. Damit lassen sich Unreal Mode Programme leichter schreiben. 
Die DSDT hab ich damit schon gefunden.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.