Ist die Adresse 0000000C eine spezielle Adresse?

32

Beim Programmieren geht es manchmal kaputt. Sie haben einen Fehler gemacht und Ihr Programm versucht, von einer falschen Adresse zu lesen.

Eine Sache, die mir besonders auffällt, ist, dass diese Ausnahmen häufig wie folgt lauten:

Access violation at address 012D37BC in module 'myprog.exe'. Read of address 0000000C.

Jetzt sehe ich viele Fehlerprotokolle und was mir auffällt, ist das: 0000000C. Ist das eine "besondere" Adresse? Ich sehe andere Zugriffsverletzungen mit schlechten Lesezugriffen, aber die Adressen scheinen zufällig zu sein, aber diese kommen immer wieder in ganz anderen Situationen zurück.

Pieter B
quelle
1
Ich habe auch bemerkt , dass 0000000Cist Art und Weise als häufiger 00000008, aber keine der Antworten auf Adresse erscheinen , dass überhaupt: /
Mooing Ente
2
Vielleicht System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringDataist 12=0x0Cein Grund , warum dieser Offset häufiger ist.
Mark Hurd
1
@ MarkHurd Das ist beängstigend. Glauben Sie wirklich, dass es so viele nicht verwaltete Anwendungen gibt, die absichtlich .NET-Zeichenfolgen lesen / schreiben, dass dies eine Hauptursache für Zugriffsverletzungen ist?
Luaan

Antworten:

57

00000000ist eine spezielle Adresse (der Nullzeiger). 0000000Cist genau das, was Sie erhalten, wenn Sie dem Nullzeiger einen Versatz von 12 hinzufügen, höchstwahrscheinlich, weil jemand versucht hat, das zElement einer Struktur wie der folgenden durch einen Zeiger zu erhalten, der tatsächlich null war.

struct Foo {
    int w, x, y; // or anything else that takes 12 bytes including padding
    // such as: uint64_t w; char x;
    // or: void *w; char padding[8];
    // all assuming an ordinary 32 bit x86 system
    int z;
}

quelle
29
Oder vielleicht, weil ein kleiner ganzzahliger Wert fälschlicherweise dereferenziert wurde, als wäre er ein Zeiger. Kleine Werte sind weitaus häufiger als große Werte, daher führt dies eher zu ungültigen Adressen wie 0X0000000C als z. B. 0x43FCC893.
Kilian Foth
3
Der Grund, warum ich diese Frage gestellt habe, ist, dass 0000000C im Vergleich zu anderen Adressen so oft zurückkommt. Warum ist der Versatz 12 um eine Größenordnung häufiger als der Versatz 4, 8 oder 16?
Pieter B
5
Nach weiteren Untersuchungen ist diese Antwort völlig richtig. In meiner Quelle wird die "Tag" -Eigenschaft von Klassen häufig verwendet (entweder gut oder schlecht, ich muss mich damit auseinandersetzen). Die Tag-Eigenschaft in meinem Fall ist Teil einer Basisklasse auf niedriger Ebene und wurde immer an diesem Offset erstellt.
Pieter B
1
Hervorragender Punkt. Vielleicht wurde die Nullzeiger-Groß- und Kleinschreibung behandelt, aber Nullzeiger ++ ist nur eine normale (und in diesem Fall ungültige) Adresse, daher schlägt sie nur beim Zugriff fehl.
Neil
8
@Leushenko Ja, der Speicherschutz funktioniert normalerweise auf ganzen Seiten, und selbst wenn nur 0 abgefangen werden konnte, ist es vorzuziehen, auch die folgenden Adressen zu schützen, da wahrscheinlich auf sie zugegriffen wird, wenn Zeigerarithmetik mit einem Nullzeiger erfolgt (wie in OPs Fall).
11

In Windows ist es illegal , die dereferenzieren gesamte erste und die letzte Seite , mit anderen Worten die erste oder letzte 64 KiB des Prozesses Speicher (die Bereiche 0x00000000auf 0x0000ffffund 0xffff0000zu 0xffffffffin einer 32-Bit - Anwendung).

Dies dient zum Auffangen des undefinierten Verhaltens, einen Nullzeiger oder Index in ein Null-Array zu dereferenzieren. Die Seitengröße beträgt 64 KB, sodass Windows nur verhindern muss, dass der ersten oder letzten Seite ein gültiger Bereich zugewiesen wird.

Dies schützt nicht vor nicht initialisierten Zeigern, die irgendeinen Wert haben könnten (einschließlich gültiger Adressen).

Ratschenfreak
quelle
7
Windows kann das nicht wirklich. Die Seitentabelle ist eine Struktur, die von x86 definiert und benötigt wird, und kleine Seiten sind auf 4 KB festgelegt. Es ist in Stein gemeißelt (genauer gesagt in Silizium). Die 64KB sind wahrscheinlich der Einfachheit halber.
ElderBug
12
In diesem Fall schreibe ich lieber 64 KB anstelle von 65 KB, da die Größe der Zweierpotenz relevant ist.
CodesInChaos
4
Der 64-KB-Bereich ist ein Überbleibsel der Aplha-Version von NT. Und es ist nicht die Seitengröße, sondern die Zuordnungsgranularität. blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301
3
@CodesInChaos: Obwohl die Großbuchstaben "M", "G" und "T" nicht eindeutig sind, sehe ich keinen Grund, die Verwendung von "k" für 10 ^ 3 und "K" für 2 ^ 10 zu missbilligen.
Superkatze
2
@MooingDuck Ja, deshalb habe ich kleine Seiten präzisiert. Die meisten x64-CPUs unterstützen auch die 1GiB-Seiten. Soweit ich weiß, hat Windows immer Seiten mit 4 KB, es sei denn, es wurden spezielle APIs zugewiesen.
ElderBug,
2

Warum 0x0Cscheint häufiger als 0x08(ist es wirklich? Ich weiß nicht; und in welchen Arten von Anwendungen?), Könnte dies mit Tabellenzeigern für virtuelle Methoden zu tun haben. Das ist wirklich eher ein Kommentar (wilde Massenraten :), aber es ist etwas größer, also geht es weiter ... Wenn Sie eine Klasse mit virtuellen Methoden haben, werden ihre eigenen Felder um verschoben 0x04. Beispielsweise könnte eine Klasse, die von einer anderen virtuellen Klasse erbt, ein Speicherlayout wie das folgende haben:

0x00 - VMT pointer for parent
0x04 - Field 1 in parent
0x08 - VMT pointer for child
0x0C - Field 1 in child

Ist das ein häufiges Szenario oder sogar nahe? Ich bin mir nicht sicher. Beachten Sie jedoch, dass dies in einer 64-Bit-Anwendung noch interessanter in Richtung des 0x0CWerts verschoben werden kann:

0x00 - VMT parent
0x08 - Field 1 parent
0x0C - VMT child
0x14 - Field 2 child

Es gibt also tatsächlich viele Fälle, in denen sich Anwendungen in Nullzeiger-Offsets möglicherweise erheblich überschneiden. Dies ist möglicherweise das erste Feld in einer untergeordneten Klasse oder der Tabellenzeiger für die virtuelle Methode, der benötigt wird, wenn Sie eine virtuelle Methode für eine Instanz aufrufen. Wenn Sie also eine virtuelle Methode für einen nullZeiger aufrufen , wird die Zugriffsverletzung für die betreffende Methode angezeigt VMT-Offset. Die Prävalenz dieses bestimmten Werts hat dann möglicherweise etwas mit einer allgemeinen API zu tun, die eine Klasse mit einem ähnlichen Vererbungsmuster bereitstellt, oder eher mit einer bestimmten Schnittstelle (für einige Anwendungsklassen wie DirectX-Spiele durchaus möglich). Es könnte möglich sein, eine einfache häufige Ursache wie diese aufzuspüren, aber ich neige dazu, Anwendungen, die keine Dereferenzierung durchführen, schnell loszuwerden, also ...

Luaan
quelle
1
Wenn Sie die Kommentare durchsehen, können Sie das Raten erheblich reduzieren.
Deduplizierer
@ Deduplicator Nun, ich finde die Idee, dass verwaltete .NET-Zeichenfolgen in unsicherem Code mit manuellen Zeigeroperationen verwendet werden, beängstigend, und der Gedanke, dass dies die Hauptursache für Zugriffsverletzungen sein würde, umso mehr. "Ja, das ist absolut speichersicher, keine Sorge, wir haben C # verwendet. Wir haben den Speicher nur manuell in C ++ geändert, aber es ist in C # sicher."
Luaan