Showing posts with label windbg. Show all posts
Showing posts with label windbg. Show all posts

Sunday, July 10, 2011

Windbg: oleaut32!DllMain+0x76 除錯筆記

最近看到了一個令人匪夷所思的 Crash Dump,Call Stack 是長這樣:

0c 078efe7c 770d15a8 oleaut32!DllMain+0x76 (Unloaded)
0d 078efe9c 7c94118a oleaut32!_DllMainCRTStartup+0x52 (Unloaded)
0e 078efebc 7c953a23 ntdll!LdrpCallInitRoutine+0x14
0f 078eff34 7c80c126 ntdll!LdrShutdownThread+0xd7
10 078eff6c 7813299f kernel32!ExitThread+0x3e
11 078eff74 781329c0 msvcr80!_endthreadex+0x1f
12 078effac 78132a47 msvcr80!_callthreadstartex+0x20
13 078effb4 7c80b713 msvcr80!_threadstartex+0x66

在程式要回到 oleaut32!DllMain+0x76 的時候,就發生 Access Violation,原因是因為 oleaut32.dll 已經被 Unloaded 了。

花了一些時間追 oleaut32.dll 的實作,發現它在 InitAppData() 裡面會去呼叫 CoCreateInstanceEx(),這邊有個神祕的 Error Handling:

如果 Calling Thread 沒有執行過 CoInitialize(),它會回傳 0x800401F0h,也就是 COM 沒有初始化的錯誤碼。

在此時,oleaut32.dll 會在這個 Calling Thread 的 APP_DATA 中留下一個 Flag,當在 Thread 結束的時候,oleaut32.dll 就會在 DLL_THREAD_DETACH 裡面去檢查這個 Flag。如果 Flag 有被標記,那麼 oleaut32.dll 就會呼叫 ole32!CoSetState(0) 去重置 COM Thread  的狀態。

(APP_DATA 是放在 Thread Local Storage 的一個 Structure,每個有跑過 OLE 的 Thread 都應該會有一個。)

然而,CoSetState() 有個行為,如果有 State 被重置,就會把 oleaut32.dll 給 Unload 掉,可是此時 oleaut32.dll 就是 CoSetState() 的 Caller,當 CoSetState() 一回去之後,就 Access Violation。

(這裡有個更匪夷所思的實作,ole32.dll 是用 global variable 去存放 oleaut32.dll 的 handle。)

好啦,這些都是微軟的 Binary,遇到了這個問題該怎麼辦?根據分析,至少我們知道這一切都是起因於 OLE Automation 遇到了 COM 初始化的問題,所以至少可以先朝這個方向先 Troubleshooting。

[這篇文章是針對 Windows XP SP3 的環境做分析,Windows 7 的 ole32.dll 和 oleaut32.dll 已經拿掉了 CoSetState(),也許新的平台已經不會再有這種問題。]


參考資料:

Saturday, April 30, 2011

WinDbg: CreateProcessAsUser 回傳 ACCESS_DENIED 之案例分析

最近手邊處理了一個案例。因為一些特殊需求,想要試試看在 Administrator 帳號下,呼叫 CreateProcessAsUser 建立在別的 Logon Session 的進程。這並不是一個典型的應用。你可以照著 MSDN 上的說明處理,幫 Administrator 加上需要的權限,看起來可以在 Console Session 上正常運作。

事情沒那麼簡單,在 Windows Server 2003 下面,CreateProcessAsUser 沒辦法在 Terminal Service 的 Logon Session 下面建立進程,永遠會得到一個 ACCESS_DENIED (5) 的錯誤碼。不過,MSDN 並沒有描述這個錯誤碼的原因。

看來,要有把手弄髒的心理準備。

首先,嘗試用 WinDbg 逐步執行看看是哪裡丟出了這個錯誤碼。結果發現,CreateProcessAsUser 裡面最終會用 CreateFile 去開一個 Terminal Server 的 Named Pipe,這個地方是讓 CreateProcessAsUser 呼叫失敗的點。

0:000> kvn
 # ChildEBP RetAddr  Args to Child              
00 002cce10 00525227 002cee90 c0000000 00000000 kernel32!CreateFileW (FPO: [Non-Fpo])
01 002ceef4 00515fd4 00000001 00000000 000003bc ADVAPI32!CreateRemoteSessionProcessW+0xbd (FPO: [Non-Fpo])
02 002cef44 0040433c 000003bc 00000000 002cf1a8 ADVAPI32!CreateProcessAsUserW+0xb0 (FPO: [Non-Fpo])

0:000> du 002cee90 
002cee90  "\\.\Pipe\TerminalServer\ozUBFyty"
002ceed0  "9LQoprqROq\1"

看來問題似乎是 Administrator 並沒有權限去讀寫這個 Named Pipe,於是乎,接下來的方向轉為確認這個 Name Piped 的 Security Context。

C:\>PsExec.exe -s c:\accesschk.exe \Pipe\TerminalServer\ozUBFyty9LQoprqROq\1

\\.\Pipe\TerminalServer\ozUBFyty9LQoprqROq\1
  RW NT AUTHORITY\SYSTEM
  RW NT AUTHORITY\LOCAL SERVICE
  RW NT AUTHORITY\NETWORK SERVICE
c:\accesschk.exe exited on 6BD2PFN1Z with error code 0.

結果發現 Administrator 果然不行開 Named Pipe。只有System、Local Service、和 Network Service 這幾個帳戶才有能力讀寫這個 Named Pipe。

在 Windows Server 2003 有 Terminal Service 的 System 帳戶下,呼叫 CreateProcessAsUser 在別的 Remote Logon Session 建立新進程,會發現新進程的 Parent Process 並不是我們自己的 Caller Process,而是 Winlogon.exe 這個系統進程。

這裡可以做個猜測,這個 Named Pipe 的作用就是讓 Caller Process 可以送出建立新進程的指令給 Winlogon.exe。打開 Process Explorer,確認一下果然發現 Named Pipe 是由 Winlogon.exe 所建立。


追到這邊,大概心裡有數了。在 Windows 2003 Server 上面,CreateProcessAsUser 是沒辦法跑在非系統內建的幾個重要帳戶裡面。Winlogon.exe 已經限制了 Named Pipe 的讀寫權限,若是沒有符合的帳戶,就是沒有辦法執行 CreateProcessAsUSer。

結論:好奇心除了可以殺死一隻貓,也可以殺掉你寶貴的時間。:P

[Updated]

  • CodeProject 裡有個小工具 RunAsEx,你可以用它來調整各種組合,實驗看看各種不同情境下的 CreateProcessAsUser,可以很方便的達到 PoC 的效果。

Saturday, April 23, 2011

WinDbg:手把手教你看C++ Exception

不管是用WinDbg打開一個Dump File或是Live Debug,如果遇到的是C++ Exception的話,WinDbg就會丟出C++ EH exception的訊息。

(abc.2dc): C++ EH exception - code e06d7363 (first/second chance not available)
eax=073bee80 ebx=03bb12f0 ecx=00000000 edx=00000000 esi=073bef08 edi=0000005c
eip=7c812afb esp=073bee7c ebp=073beed0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
kernel32!RaiseException+0x53:
7c812afb 5e              pop     esi
0:055> kn
 # ChildEBP RetAddr  
00 073beed0 78158e89 kernel32!RaiseException+0x53
01 073bef08 647050a6 msvcr80!_CxxThrowException+0x46 [f:\dd\vctools\crt_bld\self_x86\crt\prebuild\eh\throw.cpp @ 161]

如果丟出來的exception不是標準函式庫裡面的exception object的話,用!analyze -v也沒辦法幫你對到exception的資訊。這時候,你可以試著按照以下的步驟操作,找出exception class。

0:055> .exr -1
ExceptionAddress: 7c812afb (kernel32!RaiseException+0x00000053)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3
   Parameter[0]: 19930520
   Parameter[1]: 073bef28
   Parameter[2]: 647261ac

首先,先用".exr -1"印出目前EXCEPTION_RECORD的資訊。根據_CxxThrowException丟出的例外結構,Parameter[0]會是一個0x19930520的Magic Number,表示這是一個C++ exception。而Parameter[1]會是exception object address。至於Parameter[2],存放了_s__ThrowInfo結構的address,裡面會有C++ exception class的資訊,我們需要先看看exception object到底是什麼class type。

0:055> dt msvcr80!_s__ThrowInfo 647261ac
   +0x000 attributes       : 0
   +0x004 pmfnUnwind       : 0x64705cf0     void  testDLL!TestDbException::~TestDbException+0
   +0x008 pForwardCompat   : (null) 
   +0x00c pCatchableTypeArray : 0x647261a0 _s__CatchableTypeArray

知道class type後,就可以傾印出exception object的資訊,看看到底是什麼原因丟出C++ exception。

0:055> dt testDLL!TestDbException 073bef28  
   +0x000 __VFN_table : 0x64722524 
   +0x004 m_nType         : 1
   +0x008 m_iErrorCode     : 0n-536570191

參考資料:

Monday, August 17, 2009

[Windbg] How to Brake at Child Process Creation

有時候,想要從進程(Process)一建立起來就開始除錯,一種方式是直接用 Windbg 直接用 .create 去把進程跑起來。可是在 Google Chrome 這種多進程的程式,想要從某個子進程開始除錯,一定要等主進程把子進程帶起來時才可以除錯。此時,就會需要用一些技巧讓 Windbg 去中斷子進程。
首先,先執行 Google Chrome,它是一個多進程架構的瀏覽器,適合作為這次的範例。
image
然後,在用命令列或GUI的方式 Attach 到主進程 Chrome.exe (1508)。
1) 用 .childdbg 1 命令 Windbg 中斷在子進程的建立。
0:013> .childdbg 1    
Processes created by the current process will be debugged
2) 用 g 命令 Windbg 繼續執行程式,並且去 Chrome 瀏覽個網頁讓它另外建立子進程。
image
3) 接下來會發現 Windbg 已經中斷在子進程當中,可以注意到提示字元已經從 0:013> 切換到 1:016> 。
0:013> g    
Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols;SRV*c:\GoogleSyms*http://build.chromium.org/buildbot/symsrv     
Executable search path is:     
ModLoad: 00400000 004d6000   chrome.exe     
eax=0044092a ebx=7ffde000 ecx=7c9363bb edx=7c99e178 esi=0012dc28 edi=00189318     
eip=7c810705 esp=0012fffc ebp=00000000 iopl=0         nv up ei pl nz na po nc     
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000200     
7c810705 ??              ???     
1:016> |     
   0    id: 5e4    attach    name: C:\Documents and Settings\Administrator\Local Settings\Application Data\Google\Chrome\Application\chrome.exe     
.  1    id: c40    child    name: chrome.exe
4) 此時只需要用 |0s 切換到原本的主進程,然後用 .detach 放掉主進程。
1:016> |0s    
eax=7c930250 ebx=00000000 ecx=000e1714 edx=00000c88 esi=7c99e420 edi=7c99e440     
eip=7c92e514 esp=049eff70 ebp=049effb4 iopl=0         nv up ei ng nz na pe nc     
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000286     
ntdll!KiFastSystemCallRet:     
7c92e514 c3              ret     
0:015> .detach     
eax=0044092a ebx=7ffde000 ecx=7c9363bb edx=7c99e178 esi=0012dc28 edi=00189318     
eip=7c810705 esp=0012fffc ebp=00000000 iopl=0         nv up ei pl nz na po nc     
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000200     
7c810705 ??              ???     
Detached     
1:016> |     
.  1    id: c40    child    name: chrome.exe
5) 此時就大功告成,接下來就可以開始對子進程除錯了。