Search code examples
debuggingdllwindbgcrash-dumps

How to troubleshoot zero pointer to a dll function in a dump without symbols


I'm trying to troubleshoot an old application which crashes on my notebook.

Looking at the dump file I've found that it crashes with this instruction: 0044e381 ff1538b57b00 call dword ptr [appname+0x3bb538 (007bb538)]

because of a trivial reason: 0:000> dp 0x007bb538 007bb538 00000000 00000000 00000000 00000000

If I understand correctly, it is a call to a function of a dll which somehow failed to load. If I had symbols I would probably see something like _impl_!blahblahblah to help me guess what's that. Alas, that's not the case.

So the question is: how to determine which dll it tries to call?


Solution

  • disassembly with symbols

    0:000> u calc!AllocNumString+20 l1
    calc!AllocNumString+0x20:
    00083801 ff154c120800    call    dword ptr [calc!_imp__LocalAlloc (0008124c)]
    

    lets unload the symbols

    0:000> .reload /u calc
    Unloaded calc
    

    disassembly without symbols

    0:000> u 83801 l1
    00083801 ff154c120800    call    dword ptr ds:[8124Ch]
    

    0:000> $$ you want to know what 8124c is/maybe pointing to
    and you assume it might be an import
    0:000> $$ lest get the import table address and size and see if that assumption is true

    dt ntdll!_IMAGE_NT_HEADERS OptionalHeader -a OptionalHeader.DataDirectory[0n12].. 80000+poi(80000+3c)
       +0x018 OptionalHeader                       : _IMAGE_OPTIONAL_HEADER
          +0x060 DataDirectory                        : [12] 
             +0x000 VirtualAddress                       : 0x1000
             +0x004 Size                                 : 0x630
    0:000> $$ yes 8124c lies between 81000 and 81630 definately import
    

    0:000> $$ check the IAT

    0:000> dt ntdll!_IMAGE_NT_HEADERS OptionalHeader -a OptionalHeader.DataDirectory[0n1].. 80000+poi(80000+3c)
       +0x018 OptionalHeader                      : _IMAGE_OPTIONAL_HEADER
          +0x060 DataDirectory                       : [1] 
             +0x000 VirtualAddress                      : 0x51afc
             +0x004 Size                                : 0x154
    

    0:000> $$ so IMAGE_IMPORT_DESCRIPTORS ARE AS FOLLOWS

    0:000> dd /c 5 d1afc l154/4  51afc+imgbase(80000) 
    000d1afc  00051d20 ffffffff ffffffff 00051d14 00001000
    000d1b10  00051d38 ffffffff ffffffff 00051d08 00001018
    000d1b24  00051d40 ffffffff ffffffff 00051cfc 00001020
    000d1b38  00051da8 ffffffff ffffffff 00051cec 00001088
    000d1b4c  00051df0 ffffffff ffffffff 00051cdc 000010d0
    000d1b60  00051e0c ffffffff ffffffff 00051cd0 000010ec
    000d1b74  00051e14 ffffffff ffffffff 00051cc4 000010f4
    000d1b88  00051e24 ffffffff ffffffff 00051cb4 00001104
    000d1b9c  00051e4c ffffffff ffffffff 00051ca8 0000112c
    000d1bb0  **00051e64** ffffffff ffffffff 00051c98 00001144
    000d1bc4  00051fc8 ffffffff ffffffff 00051c8c 000012a8
    000d1bd8  00052150 ffffffff ffffffff 00051c80 00001430
    000d1bec  00052160 ffffffff ffffffff 00051c74 00001440
    000d1c00  00052168 ffffffff ffffffff 00051c68 00001448
    000d1c14  00052178 ffffffff ffffffff 00051c5c 00001458
    000d1c28  000521e8 ffffffff ffffffff 00051c50 000014c8
    000d1c3c  00000000 00000000 00000000 00000000 00000000
    

    0:000> $$ you can find the respective dlls on each of the 4th dword in IID (it is rva add imagebase to it)

    0:000> .foreach /pS 4 /ps 5 (place { dd /c 5 d1afc l154/4 }) { .printf "%x\t%ma\n" , place , 80000+ place }
    
    51d14   SHELL32.dll
    51d08   SHLWAPI.dll
    51cfc   gdiplus.dll
    51cec   ADVAPI32.dll
    51cdc   OLEAUT32.dll
    51cd0   UxTheme.dll
    51cc4   ole32.dll
    51cb4   COMCTL32.dll
    51ca8   ntdll.dll
    51c98   KERNEL32.dll **51e64 : d1e64 with imgbase**
    51c8c   USER32.dll
    51c80   RPCRT4.dll
    51c74   WINMM.dll
    51c68   VERSION.dll
    51c5c   GDI32.dll
    51c50   msvcrt.dll
    0   MZ
    

    0:000> $$ the fifth dword (first thunk) will hold the start of imports for each dll

    0:000> $$ so 124c lies between 1144 and 12a8 Ie in kernel32.dll

    0:000> .foreach /ps1 /pS1 (place { dpa d1e64  l?((12a8-1144)/4) } ) {da place + 80000 }
    000d27ea  "I.lstrlenA"
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    000d2cde  "D.LocalAlloc"
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    00080000  "MZ."
    

    124c is the 43rd import

    0:000> ? ((124c-1144) /4 ) + 1
    Evaluate expression: 67 = 00000043
    

    and it matches the symbol

    0:000> r $t0=0; .foreach /ps1 /pS1 (place { dpa d1e64  l?((12a8-1144)/4) } ) { r $t0 = @$t0+1; .if(@$t0==43){ da place + 80000 } }
    000d2cde  "D.LocalAlloc"
    

    -------------------------------------------------
    UPDATE
    -------------------------------------------------

    the inbuilt !imports <module> command doesn't work without symbols
    so i have been using some scripts similar to what was posted above
    since this question came up i tried to remove hard coded hacks from the
    script added some hacks for import by ordinal and post here may be it is useful

    Script as Follows

    $$ input to the script   ; r $t0=${$arg1}; .printf /D "<b>Input\t%x\n</b>",@$t0
    $$ mask  for ibase ; r $t1=@$t0&0xffff0000;.printf /D "<b>IBase\t%x\n</b>",@$t1
    $$ check dos_signature ; .printf /D "<b>IsDos\t%ma\n</b>",@$t1
    
    $$ get image_nt_headers to a pseudo register ;
    r? $t2 = (ntdll!_IMAGE_NT_HEADERS *) (@$t1 + (*(unsigned long *)( @$t1 + 0x3c)))
    
    .printf /D  "<b>\nImport Table  DataDirectory[0n12] \n\n</b>"
    ?? @$t2->OptionalHeader.DataDirectory[0xc]
    
    .printf /D  "<b>\nIAT  DataDirectory[0n01] \n\n</b>"
    ?? @$t2->OptionalHeader.DataDirectory[0x1]
    
    .printf /D "<b>\nDump IMAGE_IMPORT_DESCRIPTOR without 5 NULL DWORDS  \n\n</b>"
    
    r? $t3 = @@c++( @$t2->OptionalHeader.DataDirectory[0x1].VirtualAddress + @$t1 )
    r? $t4 = @@c++( @$t2->OptionalHeader.DataDirectory[0x1].Size / 4) - 5
    dd /c 5 @$t3 l?@$t4
    
    .printf /D  "<b>\nRespective dlls from 4th DWORD of IID\n\n</b>"
    .foreach /pS 4 /ps 5  (place {dd /c 5 @$t3 l?@$t4 }) { 
        .printf "%x + ibase \t %ma\n" , place , ( place + @$t1 )
    }
    
    .printf /D  "<b>\nFirst import from respective dlls 1st DWORD From IID \n\n</b>"
    .foreach /pS 1 /ps 5  (place {dd /c 5 @$t3 l?@$t4 }) {
        r $t5 = poi( place + @$t1 );
        .if( @$t5  & 0x80000000 )        {
            .printf /D "<b>Import By Ordinal 0n%d\n</b>" , (@$t5 & 0fffffff)
        } .else {
            .printf "poi(%x+ ib)+ib+2) %x\t%ma\n", place ,(@$t5+@$t1+2),(@$t5+@$t1+2)
        }
    }
    
    .printf /D  "<b>\nDumping All Imports From All dlls \n\n</b>"
    
    .for (r $t6 = 0 ; @$t6 < @$t4*4 ; r $t6 = @$t6+14) {
        r $t7=poi(@$t3+@$t6) ; r $t8 = @$t7+@$t1 ; 
        .printf /D "<b>\n%ma\n\n</b>" , (poi(@$t3+@$t6+0xc)+@$t1) 
        .while( poi(@$t8)!=0 )    {
            r $t9 = (poi(@$t8)+@$t1); 
            .if( @$t9  & 0x80000000 ) {
                .printf /D "<b>Import By Ordinal 0n%d\n</b>" , (@$t9 & 0000ffff)
            }.else {
                .printf "%ma\n", (poi(@$t8)+@$t1)+2
            }
            r $t8 = @$t8+4; 
        }
    }
    

    usage 0:000> $$>a< x:\getimp.txt f1124c

    should result in

    0:000> $$>a< e:\windbgscripts\getimp.txt f1124c
    Input   f1124c
    IBase   f10000
    IsDos   MZ
    
    Import Table  DataDirectory[0n12] 
    
    struct _IMAGE_DATA_DIRECTORY
       +0x000 VirtualAddress   : 0x1000
       +0x004 Size             : 0x630
    
    IAT  DataDirectory[0n01] 
    
    struct _IMAGE_DATA_DIRECTORY
       +0x000 VirtualAddress   : 0x51afc
       +0x004 Size             : 0x154
    
    Dump IMAGE_IMPORT_DESCRIPTOR without 5 NULL DWORDS  
    
    00f61afc  00051d20 ffffffff ffffffff 00051d14 00001000
    00f61b10  00051d38 ffffffff ffffffff 00051d08 00001018
    00f61b24  00051d40 ffffffff ffffffff 00051cfc 00001020
    00f61b38  00051da8 ffffffff ffffffff 00051cec 00001088
    00f61b4c  00051df0 ffffffff ffffffff 00051cdc 000010d0
    00f61b60  00051e0c ffffffff ffffffff 00051cd0 000010ec
    00f61b74  00051e14 ffffffff ffffffff 00051cc4 000010f4
    00f61b88  00051e24 ffffffff ffffffff 00051cb4 00001104
    00f61b9c  00051e4c ffffffff ffffffff 00051ca8 0000112c
    00f61bb0  00051e64 ffffffff ffffffff 00051c98 00001144
    00f61bc4  00051fc8 ffffffff ffffffff 00051c8c 000012a8
    00f61bd8  00052150 ffffffff ffffffff 00051c80 00001430
    00f61bec  00052160 ffffffff ffffffff 00051c74 00001440
    00f61c00  00052168 ffffffff ffffffff 00051c68 00001448
    00f61c14  00052178 ffffffff ffffffff 00051c5c 00001458
    00f61c28  000521e8 ffffffff ffffffff 00051c50 000014c8
    
    Respective dlls from 4th DWORD of IID
    
    51d14 + ibase    SHELL32.dll
    51d08 + ibase    SHLWAPI.dll
    51cfc + ibase    gdiplus.dll
    51cec + ibase    ADVAPI32.dll
    51cdc + ibase    OLEAUT32.dll
    51cd0 + ibase    UxTheme.dll
    51cc4 + ibase    ole32.dll
    51cb4 + ibase    COMCTL32.dll
    51ca8 + ibase    ntdll.dll
    51c98 + ibase    KERNEL32.dll
    51c8c + ibase    USER32.dll
    51c80 + ibase    RPCRT4.dll
    51c74 + ibase    WINMM.dll
    51c68 + ibase    VERSION.dll
    51c5c + ibase    GDI32.dll
    51c50 + ibase    msvcrt.dll
    
    First import from respective dlls 1st DWORD From IID 
    
    poi(51d20+ ib)+ib+2) f62352 SHGetSpecialFolderPathW
    Import By Ordinal 0n225
    poi(51d40+ ib)+ib+2) f623a0 GdipDrawLineI
    poi(51da8+ ib)+ib+2) f625a8 RegEnumKeyExW
    Import By Ordinal 0n2
    poi(51e0c+ ib)+ib+2) f626d0 IsThemeActive
    poi(51e14+ ib)+ib+2) f626e0 CoInitialize
    poi(51e24+ ib)+ib+2) f62716 ImageList_Destroy
    poi(51e4c+ ib)+ib+2) f6277c WinSqmAddToStreamEx
    poi(51e64+ ib)+ib+2) f627ec lstrlenA
    poi(51fc8+ ib)+ib+2) f62ea0 GetSysColor
    poi(52150+ ib)+ib+2) f6351a UuidCreate
    poi(52160+ ib)+ib+2) f6354a timeGetTime
    poi(52168+ ib)+ib+2) f63558 GetFileVersionInfoExW
    poi(52178+ ib)+ib+2) f6359e EqualRgn
    poi(521e8+ ib)+ib+2) f6376a wcsncmp
    
    Dumping All Imports From All dlls 
    
    
    SHELL32.dll
    
    SHGetSpecialFolderPathW
    SHGetFolderPathW
    ShellAboutW
    Import By Ordinal 0n165
    ShellExecuteExW
    
    SHLWAPI.dll
    
    Import By Ordinal 0n225
    
    gdiplus.dll
    
    GdipDrawLineI
    xxxxxxxxxxxx
    
    OLEAUT32.dll
    
    Import By Ordinal 0n2
    Import By Ordinal 0n7
    Import By Ordinal 0n8
    Import By Ordinal 0n150
    Import By Ordinal 0n6
    Import By Ordinal 0n9
    
    UxTheme.dll
    
    IsThemeActive
    
    xxxxxxxxxxxxxxx
    
    msvcrt.dll
    
    wcsncmp
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    _wcsrev
    

    ------------------------------------------------------------------------------------------------------
                                                 UPDATE NUMBER TWO
    ------------------------------------------------------------------------------------------------------

    A new script that uses c++ expressions and array subscripts to parse import address table manually
    with array subscripts we can access each of the five dwords in each IMAGE_IMPORT_DESCRIPTOR
    so this script can also print the actual address with symbols from te 5th dword
    and as such we can also see what the actual imports are for the ordinals

    Script as follows

    r $t0 = ( ${$arg1} & 0xffff0000 )
    .if(@$t0 != 0) { 
      .if( (poi(@$t0) & 0x0000ffff) == 5A4D ) {
        r $t1 = @$t0 + poi(@$t0+0x3c)
        .if( (poi(@$t1) & 0x0000ffff) == 4550 )  {
          r? $t2=@@c++((((ntdll!_IMAGE_NT_HEADERS *)@@masm(@$t1)))->OptionalHeader.DataDirectory)
          r? $t3=@@c++(((unsigned long *)( @$t2[1].VirtualAddress + @@masm(@$t0))))
          .for(r $t19 = 0; @$t19 < ((@@c++(@$t2[1].Size) /4 )-5) ; r $t19 = @$t19+0n5 ) {
            .printf /D "<b>\n                  Imports From %ma\n</b>",@@c++(@$t3[3 + @$t19 ] + @$t0)
            .printf /D "<b>Address\tNameFromIAT                         Symbol Resolved</b>\n\n"
            r? $t4 = ((unsigned long *) (@@c++(@$t3[0 + @$t19 ] + @@masm( @$t0 ))))
            r $t5 = 0
            r $t9 = 0
            .while ( @@c++(@$t4[@$t5]) != 0) {
              r $t6 = @@c++(@$t4[@$t5])
              .if(@$t6 & 0x80000000) {
                .printf /D "<b>0n%08d\tImport By Ordinal</b>" , @$t6 & 0x0000ffff
                .printf "                     "
                .printf "%y\n" , poi(@@c++(@$t3[4 + @$t19 ] + @@masm( @$t0 )) + @$t9)
                r $t9 = @$t9+4
              } .else {
                .printf "%08x\t" ,poi(@@c++(@$t3[4 + @$t19 ] + @@masm( @$t0 )) + @$t9)
                .printf "%-40ma" , ( @$t6 + @$t0  +2)
                .printf "%y\n" ,poi(@@c++(@$t3[4 + @$t19 ] + @@masm( @$t0 )) + @$t9)
                r $t9 = @$t9+4
              }
              r $t5 = @$t5+1
            }
          }
        } .else {
          .printf "IMAGE_NT_SIGNATURE PE NOT FOUND\n"
        }
      } .else {
        .printf "IMAGE_DOS_SIGNATURE MZ NOT FOUND\n"
      }
    }.else {
      .printf "provide an argument usage $$>a< <path to script file> <Address>\n"
    }
    

    usage and result as follows

    0:000> $$>a< e:\windbgscripts\gimp.txt calc+124c
    
                      Imports From SHELL32.dll
    Address NameFromIAT                         Symbol Resolved
    
    76060468    SHGetSpecialFolderPathW                 SHELL32!SHGetSpecialFolderPathW (76060468)
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    0n00000165  Import By Ordinal                     SHELL32!SHCreateDirectory (7614dd83)
    76061e46    ShellExecuteExW                         SHELL32!ShellExecuteExW (76061e46)
    
                      Imports From SHLWAPI.dll
    Address NameFromIAT                         Symbol Resolved
    
    0n00000225  Import By Ordinal                     SHLWAPI!SHStripMneumonicW (75ac417a)
    
                      Imports From ADVAPI32.dll
    Address NameFromIAT                         Symbol Resolved
    
    76de46c8    RegEnumKeyExW                           ADVAPI32!RegEnumKeyExWStub (76de46c8)
    76de468d    RegOpenKeyExW                           ADVAPI32!RegOpenKeyExWStub (76de468d)
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    7707d9dd    EventUnregister                         ntdll!EtwEventUnregister (7707d9dd)
    77085b0c    EventRegister                           ntdll!EtwEventRegister (77085b0c)
    
                      Imports From OLEAUT32.dll
    Address NameFromIAT                         Symbol Resolved
    
    0n00000002  Import By Ordinal                     OLEAUT32!SysAllocString (76c94642)
    0n00000007  Import By Ordinal                     OLEAUT32!SysStringLen (76c94680)
    0n00000008  Import By Ordinal                     OLEAUT32!VariantInit (76c93ed5)
    0n00000150  Import By Ordinal                     OLEAUT32!SysAllocStringByteLen (76c94731)
    0n00000006  Import By Ordinal                     OLEAUT32!SysFreeString (76c93e59)
    0n00000009  Import By Ordinal                     OLEAUT32!VariantClear (76c93eae)
    
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    
                      Imports From msvcrt.dll
    Address NameFromIAT                         Symbol Resolved
    
    76f7b05e    wcsncmp                                 msvcrt!wcsncmp (76f7b05e)
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    76fa048d    _wcsrev                                 msvcrt!_wcsrev (76fa048d)