地址0000000C是一个特殊地址吗?


32

编程时有时会中断。您犯了一个错误,程序尝试从错误的地址读取。

在我眼前一亮的是,这些例外通常是:

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

现在,我看到了很多错误日志,而对我而言突出的是:0000000C。这是“特殊”地址吗?我看到了其他读取错误的访问冲突,但地址似乎是随机的,但是这种情况在完全不同的情况下会不断出现。


1
我也注意到,0000000C就是这样比更常见00000008,但没有一个答案似乎地址,在所有:/
鸣叫鸭

2
也许这System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData就是12=0x0C为什么这种偏移更为普遍的原因。
Mark Hurd

1
@MarkHurd太可怕了。您是否真的认为有如此多的非托管应用程序故意读取/写入.NET字符串,这将是访问冲突的主要根源?
a安2015年

Answers:


57

00000000是一个特殊地址(空指针)。0000000C就是在向空指针添加偏移量12时得到的结果,最可能的原因是有人试图z通过实际上为空的指针来获取以下结构的成员。

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;
}

29
或可能是因为某个小整数值被错误地取消了引用,就好像它是一个指针一样。小值比大值普遍得多,因此,它倾向于产生非法地址,例如0X0000000C,而不是例如0x43FCC893。
Kilian Foth,2015年

3
我问这个问题的原因是,与其他地址相比,0000000C回来的频率很高。为什么偏移量12比偏移量4、8或16更常见?
Pieter B

5
经过进一步调查,这个答案是完全正确的。在我的资料中,类的“ tag”属性被广泛使用(无论好坏,我都必须处理它)。在我的情况下,tag属性是低级基类的一部分,并且始终在该偏移量处创建。
Pieter B

1
优点。可能涵盖了空指针的情况,但是空指针++只是一个普通的地址(在这种情况下为无效),因此仅在访问它时才会失败。
尼尔

8
@Leushenko是的,内存保护通常适用于整个页面,即使可能只捕获0,也最好也保护以下地址,因为如果使用空指针的指针算术发生,则很可能会访问以下地址(如OP的案例)。

11

在Windows中它是非法的解引用所述整个第一页和最后一页,换句话说,第一个或最后64 KIB进程内存的(范围0x00000000,以0x0000ffff0xffff00000xffffffff在32位应用程序)。

这是为了捕获将空指针或索引解引用为空数组的不确定行为。而且页面大小为64 KiB,因此Windows仅需防止将第一页或最后一页分配给有效范围。

这不能防止可能具有任何值(包括有效地址)的未初始化指针。


7
Windows无法真正做到这一点。页表是x86定义和要求的结构,小页固定为4KB。它镶嵌在石头上(更确切地说,镶嵌在硅上)。64KB可能是为了方便。
ElderBug

12
在这种情况下,我宁愿写64 KiB而不是65 kB,因为2的幂是很重要的。
CodesInChaos

4
64KB范围是NT的Aplha版本遗留下来的。它不是页面大小,而是分配粒度。 blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301,2015年

3
@CodesInChaos:虽然大写的“ M”,“ G”和“ T”是模棱两可的,但我认为没有理由反对将“ k”用于10 ^ 3,将“ K”用于2 ^ 10。
2015年

2
@MooingDuck是的,这就是为什么我缩小了小页面的原因。大多数x64 CPU还支持1GiB页面。据我所知,除非分配了特殊的API,否则Windows总是以4KB页面作为页面。
ElderBug 2015年

2

至于为什么0x0C看起来比0x08(真的吗?我不知道;在哪种应用程序中?)更常见,所以这可能与虚方法表指针有关。这实际上是一个注释(大量猜测:),但是它更大一些,所以这里...如果您有一个带有虚方法的类,则其自身的字段将被移位0x04。例如,从另一个虚拟类继承的类可能具有这样的内存布局:

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

这是常见的情况,还是接近?我不确定。但是,请注意,在64位应用程序中,这可能更有趣地转向该0x0C值:

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

因此,实际上在很多情况下,应用程序在空指针偏移量上可能存在大量重叠。它可能是子类中的第一个字段,或者是其虚拟方法表指针-每当您在实例上调用任何虚拟方法时都需要它,因此,如果您在null指针上调用虚拟方法,则会在其实例上遇到访问冲突VMT偏移量。然后,此特定值的普遍性可能与某些通用API有关,该API提供具有相似继承模式或更可能是特定接口的类(对于某些类的应用程序,例如DirectX游戏,完全可能)。可能可能会跟踪到这样的一些简单的常见原因,但是我倾向于摆脱一些应用程序,这些应用程序会非常快地执行空取消引用,因此...


1
如果您浏览注释,则可以大大减少猜测。
Deduplicator 2015年

@Deduplicator好吧,我发现在不安全的代码中使用托管的.NET字符串以及手动指针操作的想法令人恐惧,而且我认为这将是导致访问冲突的主要原因。“是的,这完全是内存安全的,不用担心,我们使用了C#。我们只是从C ++手动修改内存,但是在C#中是安全的。”
a安2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.