• WinDbg 解决Font.ToLogFont AccessViolationExcetion


    有个程序总是在windows 2003 server 异常退出. 并且,  查看调用栈也肯奇怪, 应该是很正常的调用. 怀疑是堆溢出.

    开启heap trace :

    C:Program FilesDebugging Tools for Windows (x86)>gflags -i app.exe +ust +hpa

    发现在Font.ToLogFont函数遇到: {"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."} 

    如下图:

    用Windbg打开exe后发现:

    (12e8.13e4): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=07362e9c ebx=00000001 ecx=00000003 edx=4dd77680 esi=07362f4c edi=09b03000
    eip=4ddccc1c esp=0012e894 ebp=0012e8e8 iopl=0 nv up ei pl nz na pe nc
    cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
    gdiplus!GpFont::GetLogFontW+0x12d:
    4ddccc1c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

    汇编代码:

    4ddccc11 8b7004          mov     esi,dword ptr [eax+4]
    4ddccc14 6a10            push    10h
    4ddccc16 03f0            add     esi,eax
    4ddccc18 83c71c          add     edi,1Ch
    4ddccc1b 59              pop     ecx
    4ddccc1c f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

    重新运行程序,在4ddccc1b处打断点, 

    0:007> bp 4ddccc1b
    0:007> g
    Breakpoint 0 hit
    eax=07362e9c ebx=00000001 ecx=5d5be675 edx=4dd77680 esi=07362f18 edi=080b2fdc
    eip=4ddccc1b esp=0012e890 ebp=0012e8e8 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    gdiplus!GpFont::GetLogFontW+0x12c:
    4ddccc1b 59              pop     ecx

    查看edi所在堆还有多少可写空间,

    0:000> !heap -p -a edi
        address 080b2fdc found in
        _DPH_HEAP_ROOT @ 151000
        in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                     9bf6380:          80b2fc0               3c -          80b2000             2000
        7c83d7f0 ntdll!RtlAllocateHeap+0x00000e9f
        776bcfde ole32!CRetailMalloc_Alloc+0x00000016
        776bcf4b ole32!CoTaskMemAlloc+0x00000013
        79ac4920 mscorlib_ni+0x00244920
        79aca07b mscorlib_ni+0x0024a07b
        79ab516a mscorlib_ni+0x0023516a
        7b1effc4 System_Drawing_ni+0x0004ffc4
        7b1e46c0 System_Drawing_ni+0x000446c0
        7b1e452d System_Drawing_ni+0x0004452d
    
     
    0:000> ? 80b2fc0+ 0x3c - edi
    Evaluate expression: 32 = 00000020

    剩余32字节.

    而ecx指示需要写入0x10 * 4=0x40字节:

    0:000> p
    eax=07362e9c ebx=00000001 ecx=00000010 edx=4dd77680 esi=07362f18 edi=080b2fdc
    eip=4ddccc1c esp=0012e894 ebp=0012e8e8 iopl=0 nv up ei pl nz na po nc
    cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
    gdiplus!GpFont::GetLogFontW+0x12d:
    4ddccc1c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

    显然不够用, 执行后抛出AV:

    (6d8.1310): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=07362e9c ebx=00000001 ecx=00000007 edx=4dd77680 esi=07362f3c edi=080b3000
    eip=4ddccc1c esp=0012e894 ebp=0012e8e8 iopl=0 nv up ei pl nz na po nc
    cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
    gdiplus!GpFont::GetLogFontW+0x12d:
    4ddccc1c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
    0:000> ?(3c - 20)/4
    Evaluate expression: 7 = 00000007

    ecx 还剩7个DWORD写不下.

    原因是什么??

    LOGFONT在C#中定义是:

     [StructLayout(LayoutKind.Sequential)]
            private class LOGFONT
            {
                public int lfHeight = 0;
                public int lfWidth = 0;
                public int lfEscapement = 0;
                public int lfOrientation = 0;
                public int lfWeight = 0;
                public byte lfItalic = 0;
                public byte lfUnderline = 0;
                public byte lfStrikeOut = 0;
                public byte lfCharSet = 0;
                public byte lfOutPrecision = 0;
                public byte lfClipPrecision = 0;
                public byte lfQuality = 0;
                public byte lfPitchAndFamily = 0;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32) ] 
                public string lfFaceName = "";
            }

    WinGDI.h定义:

    /* Logical Font */
    #define LF_FACESIZE         32
    
    typedef struct tagLOGFONTA
    {
        LONG      lfHeight;
        LONG      lfWidth;
        LONG      lfEscapement;
        LONG      lfOrientation;
        LONG      lfWeight;
        BYTE      lfItalic;
        BYTE      lfUnderline;
        BYTE      lfStrikeOut;
        BYTE      lfCharSet;
        BYTE      lfOutPrecision;
        BYTE      lfClipPrecision;
        BYTE      lfQuality;
        BYTE      lfPitchAndFamily;
        CHAR      lfFaceName[LF_FACESIZE];
    } LOGFONTA, *PLOGFONTA, NEAR *NPLOGFONTA, FAR *LPLOGFONTA;
    

    看上去没有任何问题啊!!

    但是 gdiplus并没有按照CHAR来处理传入进去的字符串, 相反, 是按照WCHAR来处理的. 

    WinGDI.h对于宽字符的LOGFONT定义是:

    typedef struct tagLOGFONTW
    {
        LONG      lfHeight;
        LONG      lfWidth;
        LONG      lfEscapement;
        LONG      lfOrientation;
        LONG      lfWeight;
        BYTE      lfItalic;
        BYTE      lfUnderline;
        BYTE      lfStrikeOut;
        BYTE      lfCharSet;
        BYTE      lfOutPrecision;
        BYTE      lfClipPrecision;
        BYTE      lfQuality;
        BYTE      lfPitchAndFamily;
        WCHAR     lfFaceName[LF_FACESIZE];
    } LOGFONTW, *PLOGFONTW, NEAR *NPLOGFONTW, FAR *LPLOGFONTW;

    因此是C#中的定义lfFaceName长度只有一半!

    解决方法就是C#中定义 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

  • 相关阅读:
    二维莫队的一个细节
    错失AK良机的测试48T3 Walk
    枚举二进制子集
    又是一次值得纪念的考试
    测试46
    值得纪念的测试43
    点分治模板理解
    牛客多校第三场 G Removing Stones(分治+线段树)
    牛客多校第三场 F Planting Trees
    HDU6621 K-th Closest Distance HDU2019多校训练第四场 1008(主席树+二分)
  • 原文地址:https://www.cnblogs.com/huigll/p/3245631.html
Copyright © 2020-2023  润新知