调用指令时C ++程序崩溃(C0000005访问冲突)

时间:2010-12-30 05:19:31

标签: c++ visual-c++ crash-dumps disassembly

我遇到了令我困惑的崩溃,到目前为止我发现不可能一直重现。该代码使用Visual Studio 2008编译。

(当然简化)源代码如下所示:

class AbstractParentClass 
{
private:
    /* data members */
public:
    AbstractParentClass();
    /* 
       virtual functions ...
     */
}; 

class ChildClass : public AbstractParentClass
{
private:
    /* data members */
public:
    ChildClass();
    /* 
       overridden/implemented virtual functions ...
     */
};

void DifferentClass::func(const char ** strs)
{
     ChildClass child_class;
     int i = 0;
     [...]
}

崩溃转储的反汇编如下所示:

Library!DifferentClass::func:
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10  
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]

将func()的源映射到反汇编,它看起来像这样:

Library!DifferentClass::func:
void DifferentClass::func(const char ** strs)
{
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
     ChildClass child_class;
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10 
     int i = 0;
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]
}

在成功的运行中(不同的机器,即使在同一台机器上,崩溃也不能可靠地重现),反汇编的唯一区别是调用指令,它将映射正确地映射到默认构造函数的地址。 ChildClass,像这样:

00404e8b  call        ChildClass::ChildClass (40a3d0h)

而非喜欢:

612cab2b  call        a06cff10

因此在崩溃运行中,作为调用指令参数的a06cff10地址似乎来自who-know-where,并且没有特别映射到任何东西。因此,可以预见,尝试访问该地址(以获取ChildClass默认构造函数)会导致访问冲突:

EXCEPTION_RECORD:  0012f688 -- (.exr 0x12f688)
ExceptionAddress: a06cff10
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: a06cff10
Attempt to read from address a06cff10

任何在崩溃转储中查看该地址的尝试确实表明该地址超出了该进程的范围。

更新:所以在从zvrba读取下面的响应并进一步查看它之后,有问题的调用似乎是静态库中的十几个函数调用中的第一个(由DLL加载)功能偏移不正确。它们不是同一类中的所有功能。虽然所有类(包括调用和被调用)都存在于同一个静态库中,但有三个或四个不同的类会影响函数。在第一次崩溃的事情中,指令是e8e053403f,该指令中的3F4053E0偏移应仅为53E0的偏移量。所有其他实例都具有相同的偏移问题。指令中的偏移量为3F40XXXX,当它应该只是XXXX时。额外的3F400000当然会把东西送到Never Never Land。到目前为止,我还没有找到关于反汇编中哪些函数地址有效以及是否有效的模式。库中的DifferentClass的一个成员函数将其对ChildClass的所有调用都视为坏,而另一个成员函数DifferentClass将对ChildClass进行不同的调用,看起来很好。

有没有人见过这样的事情/对可能的原因有任何想法?

3 个答案:

答案 0 :(得分:2)

您是否可以在另一个DLL中实现子类构造函数?我怀疑发生的是,在崩溃运行中,DLL被加载到另一个地址而不是其首选地址 - 您可以在VS调试器的模块窗口中检查它。这反过来导致调用目标被错误计算(该特定调用指令是相对的)。程序集中的偏移量(E8操作码之后的4个字节)也非常奇怪,看起来更像是一个尚未修复的重定位而不是有效的偏移量。你如何加载该DLL?

答案 1 :(得分:2)

很难理解大部分源代码被省略的情况,尽管从你的注释和反汇编中看来,它看起来像是ChildClass vtable的地址被破坏了。这可能有几个可能的原因,例如

  • 正如提到的@contactmatt,数组/缓冲区溢出
  • 使用已销毁的对象
  • 使用单位变量
  • 错误地转换/转换变量/指针
  • 将数据从缓冲区加载到对象中,而不打包/检查字节对齐

首先,找到vtable地址并尝试单步执行调试器,检查vtable内存何时被覆盖。它可能比您指出的位置(40a3d0h)高几个字节:

  call        ChildClass::ChildClass (40a3d0h)

然后,查找可能在该时间点执行的代码。

警告:因为原因几乎未知,找到实际修复意味着要阅读大量代码/通过源代码控制版本来查看任何可能的危险。 根据经验,导致损坏的问题(例如提到的问题之一)可能甚至不会在访问冲突的代码行附近。

答案 2 :(得分:0)

这不是有帮助的,但在我工作的地方,我正在通过访问C程序中的越界数组来修复访问冲突错误结果。我只能偶尔在我的机器上重现它。我们唯一能解决的问题就是将大量的“数组越界”检查到位。

相关问题