如何对没有符号的转储中指向 dll 函数的零指针进行故障排除

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

我正在尝试对笔记本电脑上崩溃的旧应用程序进行故障排除。

查看转储文件,我发现它因以下指令而崩溃: 0044e381 ff1538b57b00 call dword ptr [appname+0x3bb538 (007bb538)]

因为一个微不足道的原因: 0:000> dp 0x007bb538 007bb538 00000000 00000000 00000000 00000000

如果我没理解错的话,它是对一个dll函数的调用,但由于某种原因无法加载。如果我有符号,我可能会看到类似 _impl_!blahblahblah 的东西来帮助我猜测那是什么。唉,不是这样的。

所以问题是:如何确定它试图调用哪个 dll?

带符号反汇编

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

让我们卸载符号

0:000> .reload /u calc
Unloaded calc

无符号反汇编

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

0:000> $$ 你想知道 8124c is/maybe 指向什么
你假设它可能是一个 import
0:000> $$ 以免获取导入 table 地址和大小并查看该假设是否正确

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> $$ 检查 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>$$ 所以IMAGE_IMPORT_DESCRIPTORS如下

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> $$ 你可以在 IID 的第 4 个双字中找到各自的 dll(它是 rva 添加图像库)

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> $$ 第五个双字(第一个 thunk)将保留每个 dll 的导入开始

0:000> $$ 所以 124c 位于 1144 和 12a8 之间 即 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是第43次导入

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

它匹配符号

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"

---------------------------------------- ----------
更新
-------------------------------------------- ----

内置 !imports 命令在没有符号的情况下无法工作
所以我一直在使用一些类似于上面 posted
的脚本 自从出现这个问题后,我试图从
中删除硬编码黑客 脚本添加了一些用于按序号和 post 导入的技巧,这可能很有用

脚本如下

$$ 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; 
    }
}

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

应该导致

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

---------------------------------------- ---------------------------------------------- ------------
第二个更新
-------------------------------------------- ---------------------------------------------- ------

使用c++表达式和数组下标手动解析导入地址table的新脚本
使用数组下标,我们可以访问每个 IMAGE_IMPORT_DESCRIPTOR
中的五个双字中的每一个 所以这个脚本也可以用第 5 个双字
中的符号打印实际地址 因此我们也可以看到序号的实际导入是什么

脚本如下

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"
}

用法和结果如下

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)