Dmitry
Dmitry

Reputation: 3107

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?

Upvotes: 1

Views: 152

Answers (1)

blabb
blabb

Reputation: 9007

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)

Upvotes: 4

Related Questions