空指针带来的AV异常.

来源:互联网 发布:汽车电脑编程工资高吗 编辑:程序博客网 时间:2024/05/21 08:53

故名思意, 如果一个指针是NULL, (NullPtr == NULL), 则 NullPtr->Method() 会产生异常.
   
    但是根据被调用函数不同, 分为 NullPtr->Member_Method() 和 NullPtr->Virtual_Method()

    //   
    // 例子
    //
    class AA
    {
    public:
        virtual void SayHello(int dwDummy)
        {
            cout << "Hello" << endl;
        }

        void SayHello(void)
        {
            m_nValue = 0;
            cout << "Hello" << endl;
        }

    private:
        int m_nValue;
    };

    int main ( )
    {
        SaySomething();
        return 0;
    }
   
    1) 【虚函数】
    void SaySomething(void)
    {
        AA * pAA = NULL;
        pAA->SayHello(0); // 虚函数.
    }
   
    DUMP 文件:
   
    FAULTING_IP:
    ds!SaySomething+2c [f:\my project\1111\ds\ds.cpp @ 121]
    0019d91c 8b10            mov     edx,dword ptr [eax]

    EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
        ExceptionAddress: 0019d91c (ds!SaySomething+0x0000002c)
        ExceptionCode: c0000005 (Access violation)
        ExceptionFlags: 00000000
    NumberParameters: 2
        Parameter[0]: 00000000
        Parameter[1]: 00000000
        Attempt to read from address 00000000

    FOLLOWUP_IP:
    ds!SaySomething+2c [f:\my project\1111\ds\ds.cpp @ 121]
    0019d91c 8b10            mov     edx,dword ptr [eax]

    STACK_TEXT: 
    0033fa60 0019d983 00000000 00000000 7ffdb000 ds!SaySomething+0x2c [f:\my project\1111\ds\ds.cpp @ 121]
    0033fb34 0019f887 00000001 00411be8 00411c50 ds!main+0x23 [f:\my project\1111\ds\ds.cpp @ 128]
    0033fb80 0019f75f 0033fb94 76951114 7ffdb000 ds!__tmainCRTStartup+0x117 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 266]
    0033fb88 76951114 7ffdb000 0033fbd4 773bb299 ds!mainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 182]
    0033fb94 773bb299 7ffdb000 421f5b46 00000000 kernel32!BaseThreadInitThunk+0xe

    >> 在调用 NullPtr->Virtual_Method() 的函数内产生异常. <<
   
    原因
   
    pAA->SayHello(0);
    008DD915  mov         esi,esp
    008DD917  push        0   
    008DD919  mov         eax,dword ptr [pAA]
    008DD91C  mov         edx,dword ptr [eax] <------- AV
    008DD91E  mov         ecx,dword ptr [pAA]
    008DD921  mov         eax,dword ptr [edx]
    008DD923  call        eax 
   
    因为 指针为 0,  0 地址的 虚函数表指针也是 0. 当访问虚函数表的时候, 就会出现异常。


    2) 【成员函数】
    void SaySomething(void)
    {
        AA * pAA = NULL;
        pAA->SayHello(); // 成员函数.
    }

    DUMP 文件:
   
    FAULTING_IP:
    ds!AA::SayHello+26 [f:\my project\1111\ds\ds.cpp @ 109]
    01256996 c7400400000000  mov     dword ptr [eax+4],0

    EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
    ExceptionAddress: 01256996 (ds!AA::SayHello+0x00000026)
        ExceptionCode: c0000005 (Access violation)
        ExceptionFlags: 00000000
    NumberParameters: 2
        Parameter[0]: 00000001
        Parameter[1]: 00000004
    Attempt to write to address 00000004

    FOLLOWUP_IP:
    ds!AA::SayHello+26 [f:\my project\1111\ds\ds.cpp @ 109]
    01256996 c7400400000000  mov     dword ptr [eax+4],0

    STACK_TEXT: 
    0016f964 0125693d 0016fb18 00000000 7ffdc000 ds!AA::SayHello+0x26 [f:\my project\1111\ds\ds.cpp @ 109]
    0016fa44 01256a73 00000000 00000000 7ffdc000 ds!SaySomething+0x2d [f:\my project\1111\ds\ds.cpp @ 122]
    0016fb18 01263567 00000001 004c1be8 004c1c50 ds!main+0x23 [f:\my project\1111\ds\ds.cpp @ 128]
    0016fb64 0126343f 0016fb78 76951114 7ffdc000 ds!__tmainCRTStartup+0x117 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 266]
    0016fb6c 76951114 7ffdc000 0016fbb8 773bb299 ds!mainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 182]
    0016fb78 773bb299 7ffdc000 4239a0bf 00000000 kernel32!BaseThreadInitThunk+0xe

    >> 在 被调用的 NullPtr->SayHello() 函数内产生异常 <<
   
    原因
   
    pAA->SayHello();
   
    因为是调用成员函数, 所以是 "静态绑定" 但是在 SayHello() 的内部:
   
    m_nValue = 0;
    013F6993  mov         eax,dword ptr [this]
    013F6996  mov         dword ptr [eax+4],0    <--- 异常
   
    因为 this 指针来源于 pAA (ECX), 因为 this 指针指向 0, 所以成员变量的地址是非法值 (0~64K), 所以AV.
   
    PS : 如果成员函数不需要访问成员变量, 那么 NULL->Member_Method() 不会有问题.
   
    同理:
    class Mgr
    {
        ......
        ......
        AA m_myAAObj;
    }
   
    g_pMgr->m_myAAObj.SayHello();
   
    SayHello() 要访问成员变量, 那么要知道 this 指针 (即该对象在内存中的位置).
    ECX = g_pMgr + offsetof (m_myAAObj);
        = g_pMgr 的值 + m_myAAObj 在 Mgr 中的偏移.
   
    如果 g_pMgr 指向了 NULL, 那么 ECX 的值只是偏移; 反之, 当发现 ECX 的值是个很小的值, 如果该值是偏移, 说明 g_pMgr 值 NULL.
   
    【All in all】
   
    NULL->虚函数()   是访问虚函数表时引起AV.
    NULL->成员函数() 是该成员函数访问成员变量引起的AV.

原创粉丝点击