WinDbg : 在Win7X64中调试x86应用层程序

主机环境: win7X64Sp1

装了WDK后, 默认的WinDbg是X64版本.

当WinDbg -I后, 是将X64程序的异常处理交给了WinDbgX64.

我写了个Demo, 搞了个野指针, 向野指针中写内容时, Demo挂了, WinDbgX64捕获不到.

在WDK安装光盘(GRMWDK_EN_7600_1.ISO)中, 有X86版WinDbg的安装程序 : \Debuggers\dbg_x86.msi

装好WinDbgX86, 切到安装后的目录: C:\Program Files (x86)\Debugging Tools for Windows (x86)\

运行WinDbg -I, 就可以捕获到x86版应用程序的异常.


假设挂掉的x86应用层程序是我们自己的, 有源码.

#include "stdafx.h"
#include <Windows.h>
#include <tchar.h>

int Add(int a, int b, int * pI);

int _tmain(int argc, _TCHAR* argv[])
{
    int x   =   1;
    int y   =   2;
    int * pI    = NULL; ///< 模拟野指针

    _tprintf(L"Add(%d, %d) = %d\r\n", x, y, Add(x, y, pI));

    getchar();
    return 0;
}

int Add(int a, int b, int * pI)
{
    int c   =   0;

    c = a + b;
    *pI = c;    ///< 模拟异常

    return c;
}


将程序编译成x86Rlease带调式符号, 拷贝工程目录之外运行.


运行Demo, 挂了之后, 被WinDbgX86版捕获.

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

*** wait with pending attach
Symbol search path is: SRV*D:\WinDbgSysSymbolsWin7X86*http://msdl.microsoft.com/download/symbols/
Executable search path is: 
ModLoad: 00f10000 00f16000   D:\MyWorkDir\dump\TestConsole.exe
ModLoad: 774f0000 77670000   C:\Windows\SysWOW64\ntdll.dll
ModLoad: 74d50000 74e60000   C:\Windows\syswow64\kernel32.dll
ModLoad: 75380000 753c7000   C:\Windows\syswow64\KERNELBASE.dll
ModLoad: 71ff0000 72093000   C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_50934f2ebcb7eb57\MSVCR90.dll
ModLoad: 72500000 7254c000   C:\Windows\system32\apphelp.dll
(18dc.18f4): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=0038f5a4 ecx=00000000 edx=00000000 esi=00000001 edi=00f1337c
eip=775115de esp=0038f590 ebp=0038fa5c iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!ZwRaiseException+0x12:
775115de 83c404          add     esp,4


运行命令 !analyze -v, 居然直接看到了分析后的源代码错误行数, 

0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


FAULTING_IP: 
TestConsole!Add+1a [d:\myworkdir\testconsole\testconsole.cpp @ 24]
00f1107a 8911            mov     dword ptr [ecx],edx

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00f1107a (TestConsole!Add+0x0000001a)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000001
   Parameter[1]: 00000000
Attempt to write to address 00000000

FAULTING_THREAD:  000018f4

DEFAULT_BUCKET_ID:  NULL_POINTER_READ

PROCESS_NAME:  TestConsole.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx

EXCEPTION_PARAMETER1:  00000001

EXCEPTION_PARAMETER2:  00000000

WRITE_ADDRESS:  00000000 

FOLLOWUP_IP: 
TestConsole!Add+1a [d:\myworkdir\testconsole\testconsole.cpp @ 24]
00f1107a 8911            mov     dword ptr [ecx],edx

MOD_LIST: <ANALYSIS/>

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  0038f5f4 -- (.cxr 0x38f5f4)
eax=00000003 ebx=00000000 ecx=00000000 edx=00000003 esi=00000001 edi=00f1337c
eip=00f1107a esp=0038fa58 ebp=0038fa5c iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
TestConsole!Add+0x1a:
00f1107a 8911            mov     dword ptr [ecx],edx  ds:002b:00000000=????????
Resetting default scope

LAST_CONTROL_TRANSFER:  from 00f1102c to 00f1107a

PRIMARY_PROBLEM_CLASS:  NULL_POINTER_READ

BUGCHECK_STR:  APPLICATION_FAULT_NULL_POINTER_READ_NULL_POINTER_WRITE

STACK_TEXT:  
0038fa5c 00f1102c 00000001 00000002 00000000 TestConsole!Add+0x1a [d:\myworkdir\testconsole\testconsole.cpp @ 24]
0038fa7c 00f111ec 00000001 00103ea8 00105d98 TestConsole!wmain+0x2c [d:\myworkdir\testconsole\testconsole.cpp @ 13]
0038fac0 74d633aa 7efde000 0038fb0c 77529ef2 TestConsole!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]
0038facc 77529ef2 7efde000 77c2b3f1 00000000 kernel32!BaseThreadInitThunk+0xe
0038fb0c 77529ec5 00f11334 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
0038fb24 00000000 00f11334 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b


FAULTING_SOURCE_CODE:  
    20: {
    21:     int c   =   0;
    22: 
    23:     c = a + b;
>   24:     *pI = c;    ///< ?¡ê?a¨°¨¬3¡ê
    25: 
    26:     return c;
    27: }


SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  testconsole!Add+1a

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: TestConsole

IMAGE_NAME:  TestConsole.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  51b41328

STACK_COMMAND:  .cxr 0x38f5f4 ; kb

FAILURE_BUCKET_ID:  NULL_POINTER_READ_c0000005_TestConsole.exe!Add

BUCKET_ID:  APPLICATION_FAULT_NULL_POINTER_READ_NULL_POINTER_WRITE_testconsole!Add+1a

Followup: MachineOwner
---------


我还没有设置windbg参数(符号路径, 源代码目录, 映像目录)!

可能和我在本机编译有关系, EXE中有这些调试信息的指示.

看到报错信息: TestConsole!Add+1a [d:\myworkdir\testconsole\testconsole.cpp @ 24]

可以看出报错的函数为 TestConsole!Add, 反汇编这个函数进行分析.

0:000> uf TestConsole!Add
TestConsole!Add [d:\myworkdir\testconsole\testconsole.cpp @ 20]:
   20 00f11060 55              push    ebp
   20 00f11061 8bec            mov     ebp,esp
   20 00f11063 51              push    ecx
   21 00f11064 c745fc00000000  mov     dword ptr [ebp-4],0
   23 00f1106b 8b4508          mov     eax,dword ptr [ebp+8]
   23 00f1106e 03450c          add     eax,dword ptr [ebp+0Ch]
   23 00f11071 8945fc          mov     dword ptr [ebp-4],eax
   24 00f11074 8b4d10          mov     ecx,dword ptr [ebp+10h]
   24 00f11077 8b55fc          mov     edx,dword ptr [ebp-4]
   24 00f1107a 8911            mov     dword ptr [ecx],edx
   26 00f1107c 8b45fc          mov     eax,dword ptr [ebp-4]
   27 00f1107f 8be5            mov     esp,ebp
   27 00f11081 5d              pop     ebp
   27 00f11082 c3              ret
分析函数反汇编内容

0:000> uf TestConsole!Add
TestConsole!Add [d:\myworkdir\testconsole\testconsole.cpp @ 20]:
   20 00f11060 55              push    ebp                          ///< 保存现场
   20 00f11061 8bec            mov     ebp,esp                      ///< 保存栈针
   20 00f11063 51              push    ecx                          ///< 保存现场
   21 00f11064 c745fc00000000  mov     dword ptr [ebp-4],0          ///< 函数内的临时变量清0
   23 00f1106b 8b4508          mov     eax,dword ptr [ebp+8]        ///< 函数第一个参数赋值到eax
   23 00f1106e 03450c          add     eax,dword ptr [ebp+0Ch]      ///< 函数第二个参数累加到eax
   23 00f11071 8945fc          mov     dword ptr [ebp-4],eax        ///< 将eax赋值到临时变量
   24 00f11074 8b4d10          mov     ecx,dword ptr [ebp+10h]      ///< 将参数3赋值到ecx
   24 00f11077 8b55fc          mov     edx,dword ptr [ebp-4]        ///< 将临时变量赋值到dex
   24 00f1107a 8911            mov     dword ptr [ecx],edx          ///< 将临时变量(累加和)给出参3, 报错了
   26 00f1107c 8b45fc          mov     eax,dword ptr [ebp-4]
   27 00f1107f 8be5            mov     esp,ebp
   27 00f11081 5d              pop     ebp
   27 00f11082 c3              ret

说明参数3指针为空, 引起错误

现在要找到ecx为啥为空?

看栈调用链

0038fa5c 00f1102c 00000001 00000002 00000000 TestConsole!Add+0x1a [d:\myworkdir\testconsole\testconsole.cpp @ 24]
0038fa7c 00f111ec 00000001 00103ea8 00105d98 TestConsole!wmain+0x2c [d:\myworkdir\testconsole\testconsole.cpp @ 13]
看到 TestConsole!wmain 调用 TestConsole!Add

反汇编 TestConsole!wmain

0:000> uf TestConsole!wmain
TestConsole!wmain [d:\myworkdir\testconsole\testconsole.cpp @ 8]:
    8 00f11000 55              push    ebp
    8 00f11001 8bec            mov     ebp,esp
    8 00f11003 83ec0c          sub     esp,0Ch
    9 00f11006 c745f801000000  mov     dword ptr [ebp-8],1
   10 00f1100d c745fc02000000  mov     dword ptr [ebp-4],2
   11 00f11014 c745f400000000  mov     dword ptr [ebp-0Ch],0
   13 00f1101b 8b45f4          mov     eax,dword ptr [ebp-0Ch]
   13 00f1101e 50              push    eax
   13 00f1101f 8b4dfc          mov     ecx,dword ptr [ebp-4]
   13 00f11022 51              push    ecx
   13 00f11023 8b55f8          mov     edx,dword ptr [ebp-8]
   13 00f11026 52              push    edx
   13 00f11027 e834000000      call    TestConsole!Add (00f11060)
   13 00f1102c 83c40c          add     esp,0Ch
   13 00f1102f 50              push    eax
   13 00f11030 8b45fc          mov     eax,dword ptr [ebp-4]
   13 00f11033 50              push    eax
   13 00f11034 8b4df8          mov     ecx,dword ptr [ebp-8]
   13 00f11037 51              push    ecx
   13 00f11038 68f420f100      push    offset TestConsole!GS_ExceptionPointers+0x8 (00f120f4)
   13 00f1103d ff15a420f100    call    dword ptr [TestConsole!_imp__wprintf (00f120a4)]
   13 00f11043 83c410          add     esp,10h
   15 00f11046 ff159c20f100    call    dword ptr [TestConsole!_imp__getchar (00f1209c)]
   16 00f1104c 33c0            xor     eax,eax
   17 00f1104e 8be5            mov     esp,ebp
   17 00f11050 5d              pop     ebp
   17 00f11051 c3              ret

分析 TestConsole!wmain 反汇编结果

0:000> uf TestConsole!wmain
TestConsole!wmain [d:\myworkdir\testconsole\testconsole.cpp @ 8]:
    8 00f11000 55              push    ebp                          ///< 保存现场
    8 00f11001 8bec            mov     ebp,esp                      ///< 保存栈针
    8 00f11003 83ec0c          sub     esp,0Ch                      ///< 开辟3个DWORD
    9 00f11006 c745f801000000  mov     dword ptr [ebp-8],1          ///< dw1 = 1
   10 00f1100d c745fc02000000  mov     dword ptr [ebp-4],2          ///< dw2 = 2
   11 00f11014 c745f400000000  mov     dword ptr [ebp-0Ch],0        ///< dw3 = 0
   13 00f1101b 8b45f4          mov     eax,dword ptr [ebp-0Ch]      ///< push dw3, 参数3为NULL~
   13 00f1101e 50              push    eax
   13 00f1101f 8b4dfc          mov     ecx,dword ptr [ebp-4]        ///< push dw2
   13 00f11022 51              push    ecx
   13 00f11023 8b55f8          mov     edx,dword ptr [ebp-8]        ///< push dw1
   13 00f11026 52              push    edx
   13 00f11027 e834000000      call    TestConsole!Add (00f11060)
   13 00f1102c 83c40c          add     esp,0Ch
   13 00f1102f 50              push    eax
   13 00f11030 8b45fc          mov     eax,dword ptr [ebp-4]
   13 00f11033 50              push    eax
   13 00f11034 8b4df8          mov     ecx,dword ptr [ebp-8]
   13 00f11037 51              push    ecx
   13 00f11038 68f420f100      push    offset TestConsole!GS_ExceptionPointers+0x8 (00f120f4)
   13 00f1103d ff15a420f100    call    dword ptr [TestConsole!_imp__wprintf (00f120a4)]
   13 00f11043 83c410          add     esp,10h
   15 00f11046 ff159c20f100    call    dword ptr [TestConsole!_imp__getchar (00f1209c)]
   16 00f1104c 33c0            xor     eax,eax
   17 00f1104e 8be5            mov     esp,ebp
   17 00f11050 5d              pop     ebp
   17 00f11051 c3              ret
可以看出Add函数参数3为空的原因是: Add函数调用前, 参数3硬编码赋值成NULL, 引起.


IDA分析反汇编结果

在WinDbg中经过 !analyze -v 分析后, 得到报错地址为 00f1107a, 段地址为0x00f1, 偏移地址为0x107a

用IDA反汇编PE文件, 看到main函数所在的地址为.text:0040xxxx, 按下 g 键, 输入地址为0040107A, 

前面是段地址和IDA代码中相同即可, 后面的偏移地址输入要和WinDbg中报错地址的偏移地址相同.

.text:00401060 sub_401060      proc near               ; CODE XREF: _wmain+27p
.text:00401060
.text:00401060 var_4           = dword ptr -4
.text:00401060 arg_0           = dword ptr  8
.text:00401060 arg_4           = dword ptr  0Ch
.text:00401060 arg_8           = dword ptr  10h
.text:00401060
.text:00401060                 push    ebp
.text:00401061                 mov     ebp, esp
.text:00401063                 push    ecx
.text:00401064                 mov     [ebp+var_4], 0
.text:0040106B                 mov     eax, [ebp+arg_0]
.text:0040106E                 add     eax, [ebp+arg_4]
.text:00401071                 mov     [ebp+var_4], eax
.text:00401074                 mov     ecx, [ebp+arg_8]
.text:00401077                 mov     edx, [ebp+var_4]
.text:0040107A                 mov     [ecx], edx      ; 报错
.text:0040107C                 mov     eax, [ebp+var_4]
.text:0040107F                 mov     esp, ebp
.text:00401081                 pop     ebp
.text:00401082                 retn
.text:00401082 sub_401060      endp

ecx 是从[ebp + arg_8]中来的, 是函数调用的第三个参数

在IDA中查找 sub_401060 函数调用(按下 x 键), 只有一处调用了此函数

.text:00401000 _wmain          proc near               ; CODE XREF: ___tmainCRTStartup+10Ap
.text:00401000
.text:00401000 var_C           = dword ptr -0Ch
.text:00401000 var_8           = dword ptr -8
.text:00401000 var_4           = dword ptr -4
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 sub     esp, 0Ch
.text:00401006                 mov     [ebp+var_8], 1
.text:0040100D                 mov     [ebp+var_4], 2
.text:00401014                 mov     [ebp+var_C], 0  ; 这里将参数3清0了
.text:0040101B                 mov     eax, [ebp+var_C]
.text:0040101E                 push    eax
.text:0040101F                 mov     ecx, [ebp+var_4]
.text:00401022                 push    ecx
.text:00401023                 mov     edx, [ebp+var_8]
.text:00401026                 push    edx
.text:00401027                 call    sub_401060      ; 调用报错的函数
.text:0040102C                 add     esp, 0Ch
.text:0040102F                 push    eax
在IDA中结合WinDbg中得到的报错地址, 进行分析出错的原因, 比只用WinDbg方便.

在WinDbg中有种方法可以看到调用前的代码.

经过 !analyze -v, 可以知道报错地址为T_estConsole+0x107a, 这句所在函数的返回地址为0134102c

0037fca4 0134102c 00000001 00000002 00000000 T_estConsole+0x107a

用WinDbg看看, 返回地址前的汇编代码是什么. 如果是硬编码引起的错误. 很快就能发现问题.

u 0134102c L-0x40
T_estConsole+0xfec:
01340fec 0000            add     byte ptr [eax],al
01340fee 0000            add     byte ptr [eax],al
01340ff0 0000            add     byte ptr [eax],al
01340ff2 0000            add     byte ptr [eax],al
01340ff4 0000            add     byte ptr [eax],al
01340ff6 0000            add     byte ptr [eax],al
01340ff8 0000            add     byte ptr [eax],al
01340ffa 0000            add     byte ptr [eax],al
01340ffc 0000            add     byte ptr [eax],al
01340ffe 0000            add     byte ptr [eax],al
01341000 55              push    ebp
01341001 8bec            mov     ebp,esp
01341003 83ec0c          sub     esp,0Ch
01341006 c745f801000000  mov     dword ptr [ebp-8],1
0134100d c745fc02000000  mov     dword ptr [ebp-4],2
01341014 c745f400000000  mov     dword ptr [ebp-0Ch],0 ///< 硬编码将参数3搞成0了
0134101b 8b45f4          mov     eax,dword ptr [ebp-0Ch]
0134101e 50              push    eax
0134101f 8b4dfc          mov     ecx,dword ptr [ebp-4]
01341022 51              push    ecx
01341023 8b55f8          mov     edx,dword ptr [ebp-8]
01341026 52              push    edx
01341027 e834000000      call    T_estConsole+0x1060 (01341060)
用WinDbg从调用返回处往上看, 可以知道参数的来源~








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值