This command invokes VS debugger for test.vbs script file.
C:\Temp>wscript //D //X test.vbs
or
C:\Temp>cscript //D //X test.vbs
[F10] key executes the VB script line by line.
class Animal { public: Animal() {} protected: string name; }; class Dog : public Animal { public: Dog() {} protected: string petOwner; }; 0:000:x86> dt Dog MyTest!Dog +0x000 name : std::basic_string<char,std::char_traits<char>,std::allocator<char> > +0x020 petOwner : std::basic_string<char,std::char_traits<char>,std::allocator<char> > That one is easy. What if we have virtual function? Does it affect object layout? Let's see another example. The below example has 2 virtual function in base class and overrode by subclass. #include <iostream> #include <string> using namespace std; class Animal { public: Animal() { name = "Animal"; } virtual void DisplayInfo() { cout << name << endl; } virtual void Run() {} protected: string name; }; class Dog : public Animal { public: Dog() { name = "Dog"; petOwner = "N/A"; } void DisplayInfo() { cout << name << ":" << petOwner << endl; } void Run() { cout << "Run" << endl; } protected: string petOwner; }; int _tmain(int argc, _TCHAR* argv[]) { Dog* pDog = new Dog(); Dog* pDog2 = new Dog(); Animal* pA = pDog; // <== breakpoint pA->DisplayInfo(); pA = pDog2; pA->Run(); return 0; } If we check the Dog object layout, we can see there is 4 byte vtable pointer in the first position. 0:000:x86> dt Dog MyTest!Dog +0x000 __VFN_table : Ptr32 +0x004 name : std::basic_string<char,std::char_traits<char>,std::allocator<char> > +0x024 petOwner : std::basic_string<char,std::char_traits<char>,std::allocator<char> > Now to investigate a little more, I set a breakpoint in main(). When the debugger broke into the breakpoint, two Dog object can be found using dv command. This is easiest way of finding Dog object in the current process. 0:000:x86> dv /i prv param argc = 0n1 prv param argv = 0x00585170 prv local pA = 0xcccccccc prv local pDog2 = 0x00585288 prv local pDog = 0x00589f78 But what if the application is very complex and we're in the middle of nowhere but want to find all Dog objects in memory? Well, one way we can try is to search vtable in the whole memory. Since vtable comes first in the object layout, we can look for it in memory and find a clue for object instance. It is brute force search but sometimes can be useful. So in order to do that, first, we find vftable by examining (x command) Dog class. 0:000:x86> x MyTest!Dog::* 012729b0 MyTest!Dog::Dog (void) 01271a90 MyTest!Dog::DisplayInfo (void) 012720a0 MyTest!Dog::Run (void) 01271d50 MyTest!Dog::~Dog = <no type information> 01279680 MyTest!Dog::'RTTI Base Class Array' = <no type information> 01279670 MyTest!Dog::'RTTI Class Hierarchy Descriptor' = <no type information> 01279658 MyTest!Dog::'RTTI Complete Object Locator' = <no type information> 01278854 MyTest!Dog::'vftable' = <no type information> 01279690 MyTest!Dog::'RTTI Base Class Descriptor at (0,-1,0,64)' = <no type information> Then search (s command) the memory space for the vtable value. 0:000:x86> s -d 0 L?0xffffffff 01278854 00585288 01278854 00585308 00676f44 cd006c61 T.'..SX.Dog.al.. 00589f78 01278854 005851f8 00676f44 cd006c61 T.'..QX.Dog.al.. Above result shows 2 Dog objects found. Now we can examine the Dog objects by using dt command. The second dt /b command shows the content of name field in Dog object. 0:000:x86> dt 00585288 Dog MyTest!Dog +0x000 __VFN_table : 0x01278854 +0x004 name : std::basic_string<char,std::char_traits<char>,std::allocator<char> > +0x024 petOwner : std::basic_string<char,std::char_traits<char>,std::allocator<char> > 0:000:x86> dt /b 00585288+4 std::basic_string<char,std::char_traits<char>,std::allocator<char> > MyTest!std::basic_string<char,std::char_traits<char>,std::allocator<char> > +0x000 _Myproxy : 0x00585308 +0x004 _Bx : std::_String_val<char,std::allocator<char> >::_Bxty +0x000 _Buf : "Dog" [00] 68 'D' [01] 111 'o' [02] 103 'g' [03] 0 '' [04] 97 'a' [05] 108 'l' [06] 0 '' [07] -51 '' [08] -51 '' [09] -51 '' [10] -51 '' [11] -51 '' [12] -51 '' [13] -51 '' [14] -51 '' [15] -51 '' +0x000 _Ptr : 0x00676f44 "--- memory read error at address 0x00676f44 ---" +0x000 _Alias : "Dog" [00] 68 'D' [01] 111 'o' [02] 103 'g' [03] 0 '' [04] 97 'a' [05] 108 'l' [06] 0 '' [07] -51 '' [08] -51 '' [09] -51 '' [10] -51 '' [11] -51 '' [12] -51 '' [13] -51 '' [14] -51 '' [15] -51 '' +0x014 _Mysize : 3 +0x018 _Myres : 0xf +0x01c _Alval : std::allocator<char> =012788f4 npos : 0xffffffff
0:004> kL ChildEBP RetAddr 00c9e5b0 77175e6c ntdll!KiFastSystemCallRet 00c9e5b4 7532179c ntdll!ZwWaitForSingleObject+0xc 00c9e620 75a0f003 KERNELBASE!WaitForSingleObjectEx+0x98 00c9e638 75a0efb2 kernel32!WaitForSingleObjectExImplementation+0x75 00c9e64c 75ca88df kernel32!WaitForSingleObject+0x12 00c9e670 75dca819 ole32!GetToSTA+0xad 00c9e6a0 75dcc05f ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x140 00c9e780 75cbd0e5 ole32!CRpcChannelBuffer::SendReceive2+0xef 00c9e7fc 75cbcb09 ole32!CAptRpcChnl::SendReceive+0xaf 00c9e850 75dcbf75 ole32!CCtxComChnl::SendReceive+0x1c5 00c9e86c 76c5178b ole32!NdrExtpProxySendReceive+0x49 00c9e878 76cc5744 RPCRT4!NdrpProxySendReceive+0xe 00c9ec90 75dcba02 RPCRT4!NdrClientCall2+0x1a6 00c9ecb0 75cbc8b5 ole32!ObjectStublessClient+0xa2 00c9ecc0 6b23f6a3 ole32!ObjectStubless+0xf 00c9ecf8 0027d9d9 FastProx!CWbemSvcWrapper::XWbemServices::CreateInstanceEnumAsync+0x6e 00c9ed28 00263778 wmiprvse!CServerObject_StaThread::CreateInstanceEnumAsync+0x92 00c9ed68 002635dc wmiprvse!CInterceptor_IWbemSyncProvider::Helper_CreateInstanceEnumAsync+0x159 00c9edac 76c5fc8f wmiprvse!CInterceptor_IWbemSyncProvider::CreateInstanceEnumAsync+0xf4 00c9edd4 76cc4c53 RPCRT4!Invoke+0x2a 00c9f1dc 75dcd936 RPCRT4!NdrStubCall2+0x2d6 00c9f224 6b234f55 ole32!CStdStubBuffer_Invoke+0xb6 00c9f238 75dcd9c6 FastProx!CBaseStublet::Invoke+0x29 00c9f280 75dcdf1f ole32!SyncStubInvoke+0x3c 00c9f2cc 75ce213c ole32!StubInvoke+0xb9 00c9f3a8 75ce2031 ole32!CCtxComChnl::ContextInvoke+0xfa 00c9f3c4 75dca754 ole32!MTAInvoke+0x1a 00c9f3f4 75dcdcbb ole32!AppInvoke+0xab 00c9f4d4 75dca773 ole32!ComInvokeWithLockAndIPID+0x372 00c9f520 76c5f34a ole32!ThreadInvoke+0x302 00c9f55c 76c5f4da RPCRT4!DispatchToStubInCNoAvrf+0x4a 00c9f5b4 76c5f3c6 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x16c 00c9f5dc 76c60cef RPCRT4!RPC_INTERFACE::DispatchToStub+0x8b 00c9f614 76c5f882 RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0xb2 00c9f660 76c5f7a4 RPCRT4!LRPC_SCALL::DispatchRequest+0x23b 00c9f680 76c5f763 RPCRT4!LRPC_SCALL::QueueOrDispatchCall+0xbd 00c9f69c 76c5f5ff RPCRT4!LRPC_SCALL::HandleRequest+0x34f 00c9f6d0 76c5f573 RPCRT4!LRPC_SASSOCIATION::HandleRequest+0x144 00c9f708 76c5ee4f RPCRT4!LRPC_ADDRESS::HandleRequest+0xbd 00c9f780 76c5ece7 RPCRT4!LRPC_ADDRESS::ProcessIO+0x50a 00c9f78c 76c61357 RPCRT4!LrpcServerIoHandler+0x16 00c9f79c 7715d3a3 RPCRT4!LrpcIoComplete+0x16 00c9f7c4 77160748 ntdll!TppAlpcpExecuteCallback+0x1c5 00c9f92c 75a11194 ntdll!TppWorkerThread+0x5a4 00c9f938 7718b3f5 kernel32!BaseThreadInitThunk+0xe 00c9f978 7718b3c8 ntdll!__RtlUserThreadStart+0x70 00c9f990 00000000 ntdll!_RtlUserThreadStart+0x1b 0:004> kpL ChildEBP RetAddr 00c9e5b0 77175e6c ntdll!KiFastSystemCallRet(void) 00c9e5b4 7532179c ntdll!ZwWaitForSingleObject(void)+0xc ....... 00c9e670 75dca819 ole32!GetToSTA(class OXIDEntry * pOXIDEntry = 0x00333918, class CMessageCall * pCall = 0x0036af18)+0xad .......As you see above, GetToSTA takes 2 parameters. One for OXIDEntry object pointer and the other for CMessageCall object pointer. If we look into the first parameter, it provides some useful information.
0:004> dt OXIDEntry 0x00333918
ole32!OXIDEntry
+0x000 _pNext : 0x75dd68f8 OXIDEntry
+0x004 _pPrev : 0x00333898 OXIDEntry
+0x008 _dwPid : 0x204
+0x00c _dwTid : 0x16bc
+0x010 _moxid : _GUID {9feeca0e-6667-eb70-3925-cbaa316f4a29}
+0x020 _mid : 0x294a6f31`aacb2539
+0x028 _ipidRundown : _GUID {0000380d-0204-16bc-3099-d83e8ae297f6}
+0x038 _dwFlags : 0x303
+0x03c _hServerSTA : 0x0c4400f0 HWND__
+0x040 _pParentApt : 0x003498e0 CComApartment
+0x044 _pRpc : (null)
+0x048 _pAuthId : (null)
+0x04c _pBinding : (null)
+0x050 _dwAuthnHint : 1
+0x054 _dwAuthnSvc : 0xffffffff
+0x058 _pMIDEntry : 0x003336f0 MIDEntry
+0x05c _pRUSTA : 0x0035255c IRemUnknown
+0x060 _cRefs : 0n13
+0x064 _hComplete : (null)
+0x068 _cCalls : 0n1
+0x06c _cResolverRef : 0n0
+0x070 _dwExpiredTime : 0
......
dwPid and dwTid indicate the process id and thread id to which the COM call is made. In this case thread 4 is making a COM call to thread 7 (PID 0x204, TID 0x16bc) where the STA COM object resides.0:007> ~
.......
. 7 Id: 204.16bc Suspend: 1 Teb: 7ffd8000 Unfrozen
.......
When a COM call is made, STA thread receives the call, translates it to window message and put it into hidden window message queue. STA thread processes the message from the queue and set event when the COM method is done. This will release Wait function in caller thread (id=4 in above case).
int Calc(int a, int b, int c, int d, int e) { // <= breakpoint 1 int result = 0; // <= breakpoint 2 for(int i=0; i<10; i++) { result += a*i + b - c + d * 2 + e; printf("%d : %d\n", i, result); } result += a - b + c -d + e; return result; } int _tmain(int argc, _TCHAR* argv[]) { int s1,s2,s3,s4,s5; scanf("%d %d %d %d %d", &s1, &s2, &s3, &s4, &s5); int result = Calc(s1,s2,s3,s4,s5); // <= breakpoint 0 printf("Result = %d", result); return 0; }I set 3 breakpoints as marked above.
0:000> bl 0 e 00000001`3f5e10ed 0001 (0001) 0:**** Simple!wmain+0x3d 1 e 00000001`3f5e1000 0001 (0001) 0:**** Simple!Calc 2 e 00000001`3f5e1016 0001 (0001) 0:**** Simple!Calc+0x16Right before calling a function at breakpoint 0, we can inspect the assembly code to see how the parameters are passed. Basically what it does is to pass first 4 parameters (I entered 1,2,3,4,5 for scanf()) to ECX, EDX, R8D, R9D registers. (Since passing parameters are int32, ECX register is used instead of RCX). The last 5th parameter is passed to stack (rsp+20h).
0:000> u . Simple!wmain+0x3d [c:\temp\simple\simple.cpp @ 18]: 00000001`3fd910ed 8b442434 mov eax,dword ptr [rsp+34h] 00000001`3fd910f1 89442420 mov dword ptr [rsp+20h],eax //5th param: 5 00000001`3fd910f5 448b4c2440 mov r9d,dword ptr [rsp+40h] // 4 00000001`3fd910fa 448b442430 mov r8d,dword ptr [rsp+30h] // 3 00000001`3fd910ff 8b542438 mov edx,dword ptr [rsp+38h] // 2 00000001`3fd91103 8b4c243c mov ecx,dword ptr [rsp+3Ch] //1st param: 1 00000001`3fd91107 e8f4feffff call Simple!Calc (00000001`3fd91000)Now let's continue to reach breakpoint 1 at the begining of Calc() function. This is the point where we can check prolog assembly code of the function. For non-optimzition build, here you can see that those registers for parameters are copied to stack home area.
0:000> uf . Simple!Calc [c:\temp\simple\simple.cpp @ 4]: 4 00000001`3f5d1000 44894c2420 mov dword ptr [rsp+20h],r9d 4 00000001`3f5d1005 4489442418 mov dword ptr [rsp+18h],r8d 4 00000001`3f5d100a 89542410 mov dword ptr [rsp+10h],edx 4 00000001`3f5d100e 894c2408 mov dword ptr [rsp+8],ecxOnce those function prolog codes are executed, that is, when we move to breakpoint 2, the stack has correct 5 parameters and thus kP call stack command or dv command displays correct parameter values. Below we can check 5 parameters in stack address 00000000`0026feb0 ~ 00000000`0026fed0. Stack slot 00000000`0026fed8 has garbage value, just for 16 bytes alignment.
0:000> p
Breakpoint 2 hit
Simple!Calc+0x16:
00000001`3f5d1016 c744242000000000 mov dword ptr [rsp+20h],0
0:000> dq /c 1 @rsp
00000000`0026fe70 00000000`00000000
00000000`0026fe78 00000000`5fca10b1
00000000`0026fe80 00000000`00000001
00000000`0026fe88 00000000`00000000
00000000`0026fe90 00000000`00000000
00000000`0026fe98 00000001`3f5d11ac
00000000`0026fea0 00000001`3f5d2150
00000000`0026fea8 00000001`3f5d110c //return address
00000000`0026feb0 00000001`00000001 //param 1
00000000`0026feb8 00000000`00000002
00000000`0026fec0 00000000`00000003
00000000`0026fec8 00000000`00000004
00000000`0026fed0 00000000`00000005 //param 5
00000000`0026fed8 00000000`0026fee4 //for alignment
And here is what I got when running kP and dv command.0:000> kP Child-SP RetAddr Call Site 00000000`0026fe70 00000001`3f5d110c Simple!Calc( int a = 0n1, int b = 0n2, int c = 0n3, int d = 0n4, int e = 0n5)+0x16 [c:\temp\simple\simple.cpp @ 5] 0:000> dv /i /V prv param 00000000`0026feb0 @rsp+0x0040 a = 0n1 prv param 00000000`0026feb8 @rsp+0x0048 b = 0n2 prv param 00000000`0026fec0 @rsp+0x0050 c = 0n3 prv param 00000000`0026fec8 @rsp+0x0058 d = 0n4 prv param 00000000`0026fed0 @rsp+0x0060 e = 0n5 prv local 00000000`0026fe90 @rsp+0x0020 result = 0n0Now what if we have optimized build? I recompiled the source code with Maxmimum Speed optimization (/O2). For optimized build, the prolog of Calc() function starts like this.
0:000> uf Simple!Calc Simple!Calc [c:\temp\simple\simple.cpp @ 4]: 4 00000001`3ff51000 48895c2408 mov qword ptr [rsp+8],rbx 4 00000001`3ff51005 48896c2410 mov qword ptr [rsp+10h],rbp 4 00000001`3ff5100a 4889742418 mov qword ptr [rsp+18h],rsi 4 00000001`3ff5100f 57 push rdi 4 00000001`3ff51010 4154 push r12 4 00000001`3ff51012 4155 push r13 4 00000001`3ff51014 4156 push r14 4 00000001`3ff51016 4157 push r15 4 00000001`3ff51018 4883ec20 sub rsp,20hAs you can see here, there is no mov command for parameter copy. By the time I reached breakpoint 2 where prolog codes are all executed, the first 4 parameter values were not copied at all and only registers held the parameter values.
0:000> p Breakpoint 2 hit Simple!Calc+0x1c: 00000001`3ff5101c 448b6c2470 mov r13d,dword ptr [rsp+70h] ss:00000000`0022f8f0=00000005 0:000> kP L1 Child-SP RetAddr Call Site 00000000`0022f880 00000001`3ff510e1 Simple!Calc( int a = 0n1, int b = 0n0, int c = 0n0, int d = 0n2291968, int e = 0n5)+0x1c [c:\temp\simple\simple.cpp @ 5] 0:000> dv /i prv param a = 0n1 prv param b = 0n0 prv param c = 0n0 prv param d = 0n2291968 prv param e = 0n5 0:000> r rcx rcx=0000000000000001 0:000> r rdx rdx=0000000000000002 0:000> r r8 r8=0000000000000003 0:000> r r9 r9=0000000000000004As you might already notice, this behavior of optimized build can cause a lot of headache for 64 bit debugging. The behavior means that the call stack parameter information in 64 bit optimization build is completely useless. It will be much painful if we need to analyze regular dump file or Watson dump file which has less debugging information. So then how can we find correct parameter values? We know from the previous inspection that only registers hold those 4 parameter values. Starting from this point, we can think we have to trace down what parameter values were entered from previous call frame. When caller calls a function, it saves 4 parameters to registers. Since we can see this in assembly code, we unassmeble the code and can track down the parameter value. But what if the caller doesn't pass constant value as a parameter? Well, then, it will be much more tedious investigation since we have to dig into the history of the registers or stack area. For unfortunate cases, we might need to inspect many call stack frames and the assmebly codes to figure out how the parameters were passed all the way up to current stack frame.
C> cdb -pv –pn myApp.exe -c ".dump /ma /u c:\tmp\myApp.dmp;q" C> cdb -pv –p 500 -c ".dump /ma c:\tmp\myApp.dmp;q"
Create Dump File From Task Manager |
0:004> ~0s
eax=0012ed84 ebx=00000000 ecx=0012ed84 edx=779764f4 esi=0012ed84 edi=77399442
eip=779764f4 esp=0012ec80 ebp=0012ec9c 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:
779764f4 c3 ret
0:000> !teb
TEB at 7ffdf000
ExceptionList: 0012fa98
StackBase: 00130000
StackLimit: 0012a000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdf000
EnvironmentPointer: 00000000
……In TEB above, we see there is user mode stack base and upper limit of the stack. That is, the stack ranges from 0x00130000 – 0x0012a000 (stack grows from high to low memory).
lkd> !process 0 4 calc.exe
PROCESS 86ac47e8 SessionId: 1 Cid: 0558 Peb: 7ff
DirBase: ce18ea00 ObjectTable: a3b59140 HandleC
Image: calc.exe
THREAD 85b92938 Cid 0558.1db8 Teb: 7ffdf000
THREAD 85cf19b0 Cid 0558.1768 Teb: 7ffdd000
THREAD 86b3f128 Cid 0558.1a3c Teb: 7ffdc000
THREAD 880b1d48 Cid 0558.1bf0 Teb: 7ffdb000
THREAD 871c05c8 Cid 0558.1e90 Teb: 7ffda000
lkd> dt nt!_KTHREAD 85b92938
+0x000 Header : _DISPATCHER_HEADER
+0x010 CycleTime : 0x14b1bef8
+0x018 HighCycleTime : 0
+0x020 QuantumTarget : 0x174fbc90
+0x028 InitialStack : 0x8f7f0fd0 Void
+0x02c StackLimit : 0x8f7ee000 Void
+0x030 KernelStack : 0x8f7f09b0 Void
+0x034 ThreadLock : 0
……
+0x086 SpecialApcDisable : 0n0
+0x084 CombinedApcDisable : 0
+0x088 Teb : 0x7ffdf000 Void
+0x090 Timer : _KTIMER
……
lkd> !teb 0x7ffdf000
TEB at 7ffdf000
ExceptionList: 00078914
StackBase: 00080000
StackLimit: 00069000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdf000
EnvironmentPointer: 00000000
ClientId: 00000ea8 . 0000131c
RpcHandle: 00000000
Tls Storage: 7ffdf02c
PEB Address: 7ffd6000
LastErrorValue: 0
LastStatusValue: c0000139
Count Owned Locks: 0
HardErrorMode: 0
lkd> .thread /p /r 85b92938
Implicit thread is now 85b92938
Implicit process is now 86ac47e8
Loading User Symbols
................................
lkd> !teb 0x7ffdf000
TEB at 7ffdf000
ExceptionList: 0012fa98
StackBase: 00130000
StackLimit: 0012a000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdf000
EnvironmentPointer: 00000000
ClientId: 00000558 . 00001db8
RpcHandle: 00000000
Tls Storage: 7ffdf02c
PEB Address: 7ffde000
LastErrorValue: 0
LastStatusValue: c0150008
Count Owned Locks: 0
HardErrorMode: 0Plesae note that the thread object (ex:85b92938) points to an executive thread block (ETHREAD) which includes its kernel thread block (KTHREAD) as its first member of the ETHREAD structure. KTHREAD contains TEB pointer in its structure.
lkd> !thread 85b92938
THREAD 85b92938 Cid 0558.1db8 Teb: 7ffdf000 Win32Thread: fe5a34f8 WAIT: (Suspended) KernelMode Non-Alertable
SuspendCount 1
FreezeCount 1
85b92b00 Semaphore Limit 0x2
Not impersonating
DeviceMap bf992858
Owning Process 86ac47e8 Image: calc.exe
Attached Process N/A Image: N/A
Wait Start TickCount 13525527 Ticks: 79995 (0:00:20:47.929)
Context Switch Count 1580
UserTime 00:00:00.031
KernelTime 00:00:00.078
Win32 Start Address 0x00959768
Stack Init 8f7f0fd0 Current 8f7f09b0 Base 8f7f1000 Limit 8f7ee000 Call 0
Priority 11 BasePriority 8 UnusualBoost 0 ForegroundBoost 2 IoPriority 2 PagePriority 5
ChildEBP RetAddr Args to Child
8f7f09c8 82a71c15 85b92938 00000000 807c8120 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
8f7f0a00 82a704f3 85b929f8 85b92938 85b92b00 nt!KiSwapThread+0x266 (CONV: fastcall)
8f7f0a28 82a6a3cf 85b92938 85b929f8 00000000 nt!KiCommitThreadWait+0x1df (CONV: stdcall)
8f7f0aa4 82aad0d6 85b92b00 00000005 00000000 nt!KeWaitForSingleObject+0x393 (CONV: stdcall)
8f7f0abc 82aab117 00000000 00000000 00000000 nt!KiSuspendThread+0x18 (FPO: [3,0,0]) (CONV: stdcall)
8f7f0b04 82a71bfd 00000000 00000000 00000000 nt!KiDeliverApc+0x17f (CONV: stdcall)
8f7f0b48 82a704f3 85b929f8 85b92938 87965ff0 nt!KiSwapThread+0x24e (CONV: fastcall)
8f7f0b70 82a6a3cf 85b92938 85b929f8 00000000 nt!KiCommitThreadWait+0x1df (CONV: stdcall)
8f7f0be8 9af10d75 87965ff0 0000000d 00000001 nt!KeWaitForSingleObject+0x393 (CONV: stdcall)
WARNING: Frame IP not in any known module. Following frames may be wrong.
8f7f0d1c 82a4647a 0012ed84 00000000 00000000 0x9af10d75
8f7f0d1c 00000000 0012ed84 00000000 00000000 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 8f7f0c
60)
8f7f0ce8 00000000 9af152a2 001b0b8e 0000000f 0x0More topics to come:
0:000> ~1s
0:001> !token -n
Thread is not impersonating. Using process token...
TS Session ID: 0x1
User: S-1-5-21-2127521184-1604012920-1887927527-570548 (User: TDomain\yongslee)
Groups:
00 S-1-5-21-2127521184-1604012920-1887927527-513 (Group: TDomain\Domain Users)
Attributes - Mandatory Default Enabled
01 S-1-1-0
Attributes - Mandatory Default Enabled
.....
.....
Impersonation Level: Anonymous
TokenType: Primary
Is restricted token: no.As bold-face text says, the thread is not impersonating any and using primary access token which is the process token.
=> Whenever SQLCM is launched, new SQL WMI provider process (wmiprvse) will be created (if already doesn’t exist)
=> Run tlist.exe to find SQL WMI provider
C> tlist –m sqlmgmprovider.dll
0:011> bp sqlmgmprovider!SqlServiceAdvancedProperty::EnumerateInstances
0:011> g(4) In SQLCM, select SQL Server Services -> doubleclick SQL Server (MSSQLSERVER) to bring up the Properties page -> Click Advanced tab to display advanced properties. (This will call SqlServiceAdvancedProperty:: EnumerateInstances method in SQL WMI provider)
Breakpoint 0 hit
eax=541c1d58 ebx=80041024 ecx=54214588 edx=6d599bc9 esi=54214588 edi=0095e3c8
eip=541def70 esp=00deefb8 ebp=00deefc8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
sqlmgmprovider!SqlServiceAdvancedProperty::EnumerateInstances:
541def70 8bff mov edi,edi
0:007> !token -n
TS Session ID: 0x1
User: S-1-5-21-2127521184-1604012920-1887927527-570548 (User: TDomain\yongslee)
Groups:
00 S-1-5-21-2127521184-1604012920-1887927527-513 (Group: TDomain\Domain Users)
Attributes - Mandatory Default Enabled
....
Primary Group: S-1-5-21-2127521184-1604012920-1887927527-513 (Group: TDomain\Domain Users)
Privs:
…
15 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
19 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
20 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
Auth ID: 0:5eeba
Impersonation Level: Impersonation
TokenType: Impersonation
Is restricted token: no.The thread token here is impersonating and acts as TDomain\yongslee, not using Network Service. Please note this user is the same one that invoked SQLCM process. So even if the SQL WMI provider process is run as Network Service, actual worker thread is using the client user principal that makes WMI request. If the client application is run in low privilege account and the account cannot access system resource such as registry, the WMI request accessing registry resource won’t be successful.
0:010> bp framedyn!WbemCoImpersonateClient
0:010> g
framedyn!WbemCoImpersonateClient:
0:007> !token –n //Check token before impersonation
Thread is not impersonating. Using process token...
TS Session ID: 0
User: S-1-5-20 (Well Known Group: NT AUTHORITY\NETWORK SERVICE)
.....
Impersonation Level: Anonymous
TokenType: Primary
Is restricted token: no.
0:007> gu // Execute WbemCoImpersonateClient()
framedyn!CWbemProviderGlue::CheckImpersonationLevel+0x39:
0:007> !token –n // Check token after impersonation
TS Session ID: 0x1
User: S-1-5-21-2127521184-1604012920-1887927527-570548 (User: TDomain\yongslee)
.....
Impersonation Level: Impersonation
TokenType: Impersonation