WinDbg的程序例子

Example 1
這個例子是說明有一個 thread 拿了 critical section 後並沒有釋放掉,造成另一條 thread 想要拿這個 critical section 的使用拿不到,並且無限的等待。

#include <Windows.h>

CRITICAL_SECTION cs;

DWORD WINAPI newThreadProc(LPVOID lpParameter)
{
InitializeCriticalSection(&cs);
EnterCriticalSection(&cs);
return 0;
}
void Example1()
{
DWORD tid = 0;
HANDLE hThread = CreateThread(NULL, 0, newThreadProc, NULL, 0, &tid); // let the thread execute immediately

WaitForSingleObject(hThread, INFINITE);
EnterCriticalSection(&cs);
}
void main()
{
Example1();
}


首先在 windbg 中打入~*kb,會有下面的資料出在畫面上:

  • ~: Lists all threads

  • *: Specify process id

  • kb: Displays stack trace of current thread. Kb causes the display to include the first three parameters passed to each function.

0:001> ~*kb

0 Id: 1208.115c Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr Args to Child
002bf978 776e5620 776be16a 00000024 00000000 ntdll!KiFastSystemCallRet
002bf97c 776be16a 00000024 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
002bf9e0 776be04d 00000000 00000000 7ffd8000 ntdll!RtlpWaitOnCriticalSection+0x155
*** WARNING: Unable to verify checksum for C:/Users/derek.chen/Desktop/WinDebugTest/Debug/WinDebugTest.exe
002bfa08 00c014ba 00c07140 002bfbcc 00000000 ntdll!RtlEnterCriticalSection+0x152
002bfaf8 00c01553 00000000 00000000 7ffd8000 WinDebugTest!Example1+0x6a [c:/users/derek.chen/desktop/windebugtest/example1.cpp @ 18]
002bfbcc 00c01af8 00000001 007b1a88 007b1db0 WinDebugTest!main+0x23 [c:/users/derek.chen/desktop/windebugtest/example1.cpp @ 24]
002bfc1c 00c0193f 002bfc30 7716d0e9 7ffd8000 WinDebugTest!__tmainCRTStartup+0x1a8 [f:/dd/vctools/crt_bld/self_x86/crt/src/crtexe.c @ 586]
002bfc24 7716d0e9 7ffd8000 002bfc70 776c19bb WinDebugTest!mainCRTStartup+0xf [f:/dd/vctools/crt_bld/self_x86/crt/src/crtexe.c @ 403]
002bfc30 776c19bb 7ffd8000 76dad664 00000000 kernel32!BaseThreadInitThunk+0xe
002bfc70 776c198e 00c0111d 7ffd8000 00000000 ntdll!__RtlUserThreadStart+0x23
002bfc88 00000000 00c0111d 7ffd8000 00000000 ntdll!_RtlUserThreadStart+0x1b

# 1 Id: 1208.1318 Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr Args to Child
009df974 7770c9a0 766cd3b0 00000000 00000000 ntdll!DbgBreakPoint
009df9a4 7716d0e9 00000000 009df9f0 776c19bb ntdll!DbgUiRemoteBreakin+0x3c
009df9b0 776c19bb 00000000 766cd3e4 00000000 kernel32!BaseThreadInitThunk+0xe
009df9f0 776c198e 7770c964 00000000 00000000 ntdll!__RtlUserThreadStart+0x23
009dfa08 00000000 7770c964 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b


我們可以發現 thread 0 停在 RtlEnterCriticalSection,合理的判斷是他拿不到 critical section,因此常試去解讀第一個 argument,裡頭帶有 critical section 的相關訊息。在 command 中打入 dt

  • Dt [dt module!typedef adr]: Dump type. Will dump the contents of the memory using typedef as a template.

0:001> dt 00c07140
cs
+0x000 DebugInfo : 0x77746640 _RTL_CRITICAL_SECTION_DEBUG
+0x004 LockCount : -6
+0x008 RecursionCount : 1
+0x00c OwningThread : 0x0000117c
+0x010 LockSemaphore : 0x00000024
+0x014 SpinCount : 0


我們發現此 critical section 被 0x0000117c thread 拿住了。然後再去查看目前所有的 thread id.

0:001> ~
. 0 Id: 1208.115c Suspend: 1 Teb: 7ffdf000 Unfrozen
. 1 Id: 1208.1318 Suspend: 1 Teb: 7ffde000 Unfrozen


最後發現目前的所有 thread 並不包括 0x0000117c,所以最後可以知道是一個生命週期已經結束的 thread 沒有正確釋放掉 critical section。


Example 2

第二個例子滿簡單的,就是常見 Divide by zero 的 exception

void Example2()
{
int y = 71;
y = y / (71 - y);
}

void main()
{
Example2();
}



根據我自己嘗試的結果,如果直接執行執行檔,然後在用 windbg 去 attatch to a process,無法直接看到此 exception 訊息,效果比較不好。如果透過 windbg 直接將此執行檔執行起來,可以很容易發現 divide by zero 的問題。

首先我們先透過 windbg 的 UI,File -> Open Executable,選擇 Example2 的執行檔。接著打入 g . 表示我們要開始跑這個程式,畫面如下:

  • g: The g command starts executing the given process or thread. Execution will halt at the end of the program, when BreakAddress is hit, or when another event causes the debugger to stop.

0:000> g
(1090.11a0): Integer divide-by-zero - code c0000094 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000047 ebx=7ffd5000 ecx=00000000 edx=00000000 esi=00000000 edi=0014fb68
eip=010f13d1 esp=0014fa90 ebp=0014fb68 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
*** WARNING: Unable to verify checksum for WinDebugTest.exe
WinDebugTest!Example2+0x31:
010f13d1 f7f9 idiv eax,ecx


我們可以發現訊息中就顯示了 Integer divide-by-zero 的問題,接著我們打入 u eip,來看看目前程式執行的 assembly code。

  • u (Unassemble): The u command displays an assembly translation of the specified program code in memory.

  • eip: The eip register points to the memory address which the processor will next attempt to execute

0:000> u eip
WinDebugTest!Example2+0x31 [c:/users/derek.chen/desktop/windebugtest/example2.cpp @ 4]:
010f13d1 f7f9 idiv eax,ecx
010f13d3 8945f8 mov dword ptr [ebp-8],eax
010f13d6 5f pop edi
010f13d7 5e pop esi
010f13d8 5b pop ebx
010f13d9 8be5 mov esp,ebp
010f13db 5d pop ebp
010f13dc c3 ret


大家如果對組語有一點熟悉的話,應該可以發現 idiv 就是整數除法的意思,接著來看看分母是多少,所以就要看 ecx 這個值,打入 r ecx

  • r: The r command displays or modifies registers, floating-point registers, flags, pseudo-registers, and fixed-name aliases.

0:000> r ecx
ecx=00000000


最後發現分母為0,在整數除法中是不允許的。


Example 3

這個例子是幫助大家熟悉 windbg 中使用 break point 的操作。基本上就是將 break point 設定在 k = rand(); 這行程式碼上,然後列印出 k 的值。

#include <stdlib.h>

void Example3()
{
int k = 0;
for (int i = 0; i < 5; ++i)
{
k = rand();
}
}

void main()
{
Example3();
}



首先,我們還是透過 File -> Open Executable 的方式來跑執行檔,然後打入 bp WinDebugTest!Example3,這樣我們就成功將 break point 設定在 Example3() 函式,接著打入 g, 讓執行檔開始跑。

  • bp: sets a new breakpoint at the address of the breakpoint location that is specified in the command.

  • WinDebugTest!Example3: 這個欄位放的是module name,如果沒有是唯一性,debugger不會在symbol上產生混淆,是可以不用給定。Example3這個是 function name。"!" 這個符號是連接 module name 和 function name。

0:000>0:000> bp WinDebugTest!Example3
*** WARNING: Unable to verify checksum for WinDebugTest.exe
0:000> g
Breakpoint 0 hit
eax=cccccccc ebx=7ffda000 ecx=00000000 edx=00000001 esi=00000000 edi=0024f774
eip=008213b0 esp=0024f6a4 ebp=0024f774 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
WinDebugTest!Example3:
008213b0 55 push ebp



接著很快就發現程式又停了下來,這就是我們剛剛的 break point 的功勞。如果有設定 source path 或者有時候 windbg 很聰明,會自動將執行檔位置的 source 自動讀取進來,現在應該可以發現 source 已經顯示在 windbg 上了。接著按 F10 三次,試著將程式執行的位置移動到 k = rand()。這時候我們打入 ~*kb 來觀察一下目前的 code stack,可以發現 k = rand() 這行程式的記憶體位置應該是 Example3+0x3d 。

0:000> ~*kb

. 0 Id: 12f4.11e4 Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr Args to Child
0024f6a0 00821453 00000000 00000000 7ffda000 WinDebugTest!Example3+0x3d [c:/users/derek.chen/desktop/windebugtest/example3.cpp @ 8]
0024f774 008219e8 00000001 00103308 00101e30 WinDebugTest!main+0x23 [c:/users/derek.chen/desktop/windebugtest/example3.cpp @ 15]
0024f7c4 0082182f 0024f7d8 7758d0e9 7ffda000 WinDebugTest!__tmainCRTStartup+0x1a8 [f:/dd/vctools/crt_bld/self_x86/crt/src/crtexe.c @ 586]
0024f7cc 7758d0e9 7ffda000 0024f818 77bb19bb WinDebugTest!mainCRTStartup+0xf [f:/dd/vctools/crt_bld/self_x86/crt/src/crtexe.c @ 403]
WARNING: Stack unwind information not available. Following frames may be wrong.
0024f7d8 77bb19bb 7ffda000 77237d52 00000000 kernel32!BaseThreadInitThunk+0x12
0024f818 77bb198e 00821113 7ffda000 00000000 ntdll!RtlInitializeExceptionChain+0x63
0024f830 00000000 00821113 7ffda000 00000000 ntdll!RtlInitializeExceptionChain+0x36



接著我們要先將原來的 break point 刪除,打入 bl 將所有的 break point 顯示出來。接著打bc0 將編號 0的 break point 刪除,為了確定真得已經刪除掉了,再次打入 bl 來做驗證,發現全部都不見了。

  • bl: The bl command lists information about existing breakpoints

  • bc: The bc command permanently removes previously set breakpoints from the system.

0:000> bl
0 e 008213b0 [c:/users/derek.chen/desktop/windebugtest/example3.cpp @ 4] 0001 (0001) 0:**** WinDebugTest!Example3
0:000> bc0
0:000> bl



然後我們要設定新的 break point,打入 bp WinDebugTest!Example3+0x3d "dd k L1; .echo hello world; g"。也就是希望將 k 值印出來,順便在印一句 hello world,然後再繼續跑。可以發現畫面上的訊息正如我們所期待的樣子,以第一行為例,0024f698是 k變數記憶體中的位置,00000029是他的value。

  • d*: The d* commands display the contents of memory in the given range.

  • dd: Double-word values (4 bytes)

  • L1: 從k variable開始列印1個DWORD

  • .echo: The .echo command displays a comment string.

  • ; : 用來表示此指令結束,然後可以承接下一個指令。

0:000> g
0024f698 00000029
hello world
0024f698 00004823
hello world
0024f698 000018be
hello world
0024f698 00006784
hello world
eax=57174340 ebx=7ffda000 ecx=571212c4 edx=571212c4 esi=00000000 edi=00000000
eip=77bd5e74 esp=0024f6e8 ebp=0024f6f8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
77bd5e74 c3 ret



Example 4

這個例子示範當我們將一個 constant value 指定給一個尚未初始化的指標時所產生的問題。

#include <stdio.h>

void Example4()
{
int* i = NULL;
*i = 80;
}

void main()
{
Example4();
}


首先就直接讓執行檔跑起來吧,打入g

0:000> g
(a40.e0c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7ffd3000 ecx=00000000 edx=00000001 esi=00000000 edi=0021fce4
eip=00a913c8 esp=0021fc0c ebp=0021fce4 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
*** WARNING: Unable to verify checksum for WinDebugTest.exe
WinDebugTest!Example4+0x28:
00a913c8 c70050000000 mov dword ptr [eax],50h ds:0023:00000000=????????


從 windbg 中就已經發現有 access violation的問題了。那我們來看看目前的組語吧。打入 u

0:000> u
WinDebugTest!Example4+0x28 [c:/users/derek.chen/desktop/windebugtest/example4.cpp @ 6]:
00a913c8 c70050000000 mov dword ptr [eax],50h
00a913ce 5f pop edi
00a913cf 5e pop esi
00a913d0 5b pop ebx
00a913d1 8be5 mov esp,ebp
00a913d3 5d pop ebp
00a913d4 c3 ret
00a913d5 cc int 3


看到第一行是一個 mov 的動作,大概就是常見的 eax裡面所含的記憶體位置是不合法的。打入 r eax,看一下他的內容。

0:000> r eax
eax=00000000



Example 5

第五個例子是展示我們不小心將同一個記憶體位置區段做了兩次的 delete動作。

#include <string.h>

void Example5()
{
char* str = new char[20];
delete [] str;
delete [] str; // error!
strcpy(str, "hi");
}

void main()
{
Example5();
}


首先先將執行檔跑起來,會跳出一個exception視窗警告你,這時候不要將視窗關掉,先用 windbg 做 attach 動作。然後直接打入 ~* 看一下目前所有的 process 資訊。

0:001> ~*
0 Id: 8a4.8e8 Suspend: 1 Teb: 7ffdf000 Unfrozen
Start: WinDebugTest!ILT+280(_mainCRTStartup) (00d7111d)
Priority: 0 Priority class: 32 Affinity: 3
. 1 Id: 8a4.15a0 Suspend: 1 Teb: 7ffde000 Unfrozen
Start: ntdll!DbgUiRemoteBreakin (77bfc964)
Priority: 0 Priority class: 32 Affinity: 3


應該可以發現 process 1 旁邊有個小句點,代表目前使用的是 process 1,但是我們的程式 WinDebugTest 卻是在 process 2。所以打入 ~0s 來做切換 thread 的動作。然後再重複打一次 ~* 看一下目前的情況

  • ~s (change current processor): The ~s command sets which processor is debugged on a multiprocessor system.

0:001>0:001> ~0s
eax=00290f00 ebx=00000000 ecx=00000000 edx=00000000 esi=00e27090 edi=00000000
eip=77bd5e74 esp=00165250 ebp=00165284 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
77bd5e74 c3 ret
0:000> ~*
. 0 Id: 8a4.8e8 Suspend: 1 Teb: 7ffdf000 Unfrozen
Start: WinDebugTest!ILT+280(_mainCRTStartup) (00d7111d)
Priority: 0 Priority class: 32 Affinity: 3
# 1 Id: 8a4.15a0 Suspend: 1 Teb: 7ffde000 Unfrozen
Start: ntdll!DbgUiRemoteBreakin (77bfc964)
Priority: 0 Priority class: 32 Affinity: 3


這個時候只要打 kb 觀察我們所在 thread 的 code stack 狀況。

0:000> kb
ChildEBP RetAddr Args to Child 
WARNING: Stack unwind information not available. Following frames may be wrong.
00165284 777c2dc0 00450b8c 00000000 00000001 ntdll!KiFastSystemCallRet
001652ac 777ecd48 77790000 00264170 00000000 USER32!SoundSentry+0x20f
0016534c 777ed2ca 00012012 0016fa28 00000000 USER32!SoftModalMessageBox+0x69d
0016549c 777ed3fc 001654a8 00000028 00000000 USER32!SoftModalMessageBox+0xc1f
001654f4 777ed678 00000000 001657e0 5e2a3438 USER32!MessageBoxTimeoutW+0x7f
00165514 777ed714 00000000 001657e0 5e2a3438 USER32!MessageBoxExW+0x1b
00165530 5e3734e6 00000000 001657e0 5e2a3438 USER32!MessageBoxW+0x45
00165584 5e35e75c 001657e0 5e2a3438 00012012 MSVCR90D!strerror_s+0x346
001677f0 5e366640 00000002 5e2a5b90 0016a860 MSVCR90D!CrtSetReportHookW2+0x71c
0016f890 5e35e392 00000002 5e2a5b90 00000034 MSVCR90D!VCrtDbgReportW+0x8e0
0016f8b0 5e35e7db 00000002 5e2a5b90 00000034 MSVCR90D!CrtSetReportHookW2+0x352
0016f8d8 5e363230 00000002 5e2a5b90 00000034 MSVCR90D!_CrtDbgReportW+0x2b
0016f920 00d71424 00812f60 0016fafc 00000000 MSVCR90D!operator delete+0xa0
0016fa28 00d71493 00000000 00000000 7ffd6000 WinDebugTest!Example5+0x64 [c:/users/derek.chen/desktop/windebugtest/example5.cpp @ 7]
0016fafc 00d71a38 00000001 00811a88 00811d60 WinDebugTest!main+0x23 [c:/users/derek.chen/desktop/windebugtest/example5.cpp @ 14]
0016fb4c 00d7187f 0016fb60 7758d0e9 7ffd6000 WinDebugTest!__tmainCRTStartup+0x1a8 [f:/dd/vctools/crt_bld/self_x86/crt/src/crtexe.c @ 586]
0016fb54 7758d0e9 7ffd6000 0016fba0 77bb19bb WinDebugTest!mainCRTStartup+0xf [f:/dd/vctools/crt_bld/self_x86/crt/src/crtexe.c @ 403]
0016fb60 77bb19bb 7ffd6000 76f35df6 00000000 kernel32!BaseThreadInitThunk+0x12
0016fba0 77bb198e 00d7111d 7ffd6000 00000000 ntdll!RtlInitializeExceptionChain+0x63
0016fbb8 00000000 00d7111d 7ffd6000 00000000 ntdll!RtlInitializeExceptionChain+0x36


可以發現目前正處於彈出一個 message box 的狀態,然後仔細在看一下,有一行 MSVCR90D!operator delete+0xa0 ,看起來好像就是這行出了問題。


Example 6

第六個例子是關於 stack over flow 的問題。

void Example6_2();

void Example6_1()
{
Example6_2();
}

void Example6_2()
{
Example6_1();
}

void main()
{
Example6_1();
}


首先就直接讓執行檔跑起來吧,打入g。

0:000> g
(10c4.934): Stack overflow - code c00000fd (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=cccccccc ebx=7ffdf000 ecx=00000000 edx=00000001 esi=00000000 edi=0013316c
eip=00f113b9 esp=00132fd8 ebp=00133098 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** WARNING: Unable to verify checksum for WinDebugTest.exe
WinDebugTest!Example6_1+0x9:
00f113b9 53 push ebx


windbg 上面就顯示了"Stack overflow"。

 

 

转载于:https://my.oschina.net/u/2246203/blog/322828

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值