Debugowanie jądra systemu Windows: Procesy

1. Wstęp

Jak debugować struktury przechowujące informacje o procesie w systemie operacyjnym Windows? W tym artykule znajdziesz odpowiedź na to pytanie. Zostaniesz zapoznany również z metodami manipulacji procesem. Ostatecznie zostanie przedstawiona metoda upodobnienia istniejącego procesu do uruchomionej instancji innego programu na przykładzie procesu notatnika oraz OneDrive.exe. Przygotuj herbatę lub kawę i zapraszam do lektury!

2. Proces w systemie Windows

Zanim zaczniemy przyglądać się istniejącym procesom, należy odpowiedzieć sobie na pytanie czym właściwie jest proces? Kiedy mówimy o procesie, rozumiemy przez to instancję uruchomionego programu. Każdy proces w systemie Windows posiada m.in.:

3. Wstępna analiza procesu w WinDbg

W tej części artykułu zostaną zaprezentowane metody pozyskania listy uruchomionych procesów oraz podstawowych informacji dotyczących tego procesu. Zostanie również zaprezentowana metoda prezentacji instancji struktury EPROCESS danego procesu.

W przygotowanym środowisku, należy uruchomić WinDBG lub kd.exe:

C:> "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\kd.exe" -k net:port=50000,key=Win.Kernel.Debug.Env

W celu prezentacji podstawowych poleceń debuggera, na analizowanej maszynie uruchomiłem program notepad.exe. Przyjrzyjmy się nowopowstałemu procesowi.

Jako pierwszy krok, należy zatrzymać system operacyjny: w WinDBG należy wybrać Break z menu Debug lub w sesji kd.exe nacisnąć kombinację klawiszy Ctrl + C.

W celu wypisania uruchomionych procesów należy użyć polecenia !dml_proc:

kd> !dml_proc
Address           PID  Image file name
ffffbf01`22260400 4    System
ffffbf01`2367c040 118  smss.exe
ffffbf01`243bf780 168  csrss.exe
ffffbf01`244b5780 1ac  smss.exe
ffffbf01`24468640 1b4  wininit.exe
...
ffffbf01`258c6780 11ac backgroundTask
ffffbf01`259a4780 11ec notepad.exe
ffffbf01`257b7780 1264 backgroundTask

Bardziej szczegółową listę procesów można pozyskać wykorzystując polecenie !process 0 0. Należy jednak uważać na dużą ilość danych dostarczonych na standardowe wyjście. Prawdopodobnie nie będziemy w stanie ich efektywnie wykorzystać. Czas oczekiwania na zakończenie tego polecenia może być bardzo długi, proporcjonalnie do liczby uruchomionych procesów.

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS ffffbf0122260400
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 001aa000  ObjectTable: ffffe10394002340  HandleCount: <Data Not Accessible>
    Image: System

PROCESS ffffbf012367c040
    SessionId: none  Cid: 0118    Peb: 7d6928c000  ParentCid: 0004
    DirBase: 76341000  ObjectTable: ffffe1039468b540  HandleCount: <Data Not Accessible>
    Image: smss.exe

...

Jeżeli istnieje faktyczna potrzeba wygenerowania szczegółowych danych dotyczących uruchomionych procesów (np. w celu ich parsowania i późniejszego wykorzystania), można wykorzystać polecenie !process 0 7, które wylistuje procesy wypisując większość dostępnych szczegółów.

Jeżeli jesteśmy zainteresowani tylko jednym, konkretnym procesem, to posługując się jego adresem, można wypisać szczegóły wyłącznie tego procesu. Takie podejście będzie dużo szybsze i bardziej efektywne.

Przyjrzyjmy się zatem procesowi notepad.exe. Jego adres to ffffbf01`259a4780. W tym celu wykorzystajmy polecenie !process i wypiszmy szczegóły tego procesu:

kd> !process ffffbf01`259a4780 5
PROCESS ffffbf01259a4780
    SessionId: 1  Cid: 11ec    Peb: 2a4e032000  ParentCid: 0ab8
    DirBase: 42280000  ObjectTable: ffffe1039aea1840  HandleCount: <Data Not Accessible>
    Image: notepad.exe
    VadRoot ffffbf012589d260 Vads 83 Clone 0 Private 459. Modified 22. Locked 0.
    DeviceMap ffffe103941a2710
    Token                             ffffe1039adff060
    ElapsedTime                       00:00:35.395
    UserTime                          00:00:00.000
    KernelTime                        00:00:00.000
    QuotaPoolUsage[PagedPool]         238848
    QuotaPoolUsage[NonPagedPool]      11536
    Working Set Sizes (now,min,max)  (3582, 50, 345) (14328KB, 200KB, 1380KB)
    PeakWorkingSetSize                3501
    VirtualSize                       2097271 Mb
    PeakVirtualSize                   2097276 Mb
    PageFaultCount                    3644
    MemoryPriority                    FOREGROUND
    BasePriority                      8
    CommitCharge                      593

        THREAD ffffbf01259a5080  Cid 11ec.11f0  Teb: 0000002a4e033000 Win32Thread: ffffbf012566e8c0 WAIT
        THREAD ffffbf01259de080  Cid 11ec.1200  Teb: 0000002a4e035000 Win32Thread: 0000000000000000 WAIT
        THREAD ffffbf0125a2e080  Cid 11ec.1204  Teb: 0000002a4e037000 Win32Thread: 0000000000000000 WAIT
        THREAD ffffbf0125a30080  Cid 11ec.1208  Teb: 0000002a4e039000 Win32Thread: 0000000000000000 WAIT
        THREAD ffffbf0125a31080  Cid 11ec.120c  Teb: 0000002a4e03b000 Win32Thread: 0000000000000000 WAIT

Informacje dotyczące danego procesu są przechowywane w przez system Windows w strukturze EPROCESS. W celu wypisania danych procesu zapisanych w strukturze EPROCESS, należy skorzystać z polecenia dt:

kd> dt _eprocess ffffbf01`259a4780
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   +0x2e0 RundownProtect   : _EX_RUNDOWN_REF
   +0x2e8 UniqueProcessId  : 0x00000000`00000b18 Void
   +0x2f0 ActiveProcessLinks : _LIST_ENTRY [ 0xffffb10f`5d7a9370 - 0xffffb10f`5da12a70 ]
   +0x300 Flags2           : 0x200d000
   ...
   +0x798 DiskIoAttribution : (null)
   +0x7a0 ReadyTime        : 0
   +0x7a8 DxgProcess       : 0x00000000`00000025 Void

4. Analiza Process Environment Block

W tej części artykułu zostaną przedstawione informacje dotyczące PEB oraz metoda jego odczytu.

Process Environment Block jest strukturą, która opisuje kontekst danego procesu.

W celu analizy PEB procesu notatnika, należy przełączyć kontekst debuggera na proces znajdujący się pod adresem ffffbf01`259a4780:

kd> .process /p ffffbf01`259a4780
Implicit process is now ffffbf01`259a4780
.cache forcedecodeuser done

Po przełączeniu kontekstu debuggera można wypisać informacje zawarte w PEB procesu notepad.exe.

kd> !peb
PEB at 0000002a4e032000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
    ImageBaseAddress:         00007ff6107d0000
    NtGlobalFlag:             0
    NtGlobalFlag2:            0
    Ldr                       00007ff925d933a0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 000001efa1382260 . 000001efa138e1d0
    Ldr.InLoadOrderModuleList:           000001efa13823d0 . 000001efa138e1b0
    Ldr.InMemoryOrderModuleList:         000001efa13823e0 . 000001efa138e1c0
                    Base TimeStamp                     Module
            7ff6107d0000 5789986a Jul 16 04:14:02 2016 C:\Windows\system32\notepad.exe
            7ff925c40000 59b0d03e Sep 07 06:51:10 2017 C:\Windows\SYSTEM32\ntdll.dll
            7ff924cc0000 5abdafe8 Mar 30 05:32:56 2018 C:\Windows\System32\KERNEL32.DLL
            7ff922e30000 5abdad60 Mar 30 05:22:08 2018 C:\Windows\System32\KERNELBASE.dll
            ...
            7ff91f4c0000 5ab31a04 Mar 22 03:50:44 2018 C:\Windows\SYSTEM32\wintypes.dll
            7ff920bb0000 5ab31a5c Mar 22 03:52:12 2018 C:\Windows\System32\twinapi.appcore.dll
    SubSystemData:     00007ff920cb0e30
    ProcessHeap:       000001efa1380000
    ProcessParameters: 000001efa13819b0
    CurrentDirectory:  'C:\Users\74wny0wl\'
    WindowTitle:  'C:\Users\74wny0wl\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\Notepad.lnk'
    ImageFile:    'C:\Windows\system32\notepad.exe'
    CommandLine:  '"C:\Windows\system32\notepad.exe" '
    DllPath:      '< Name not readable >'
    Environment:  000001efa1380dd0
        ALLUSERSPROFILE=C:\ProgramData
        APPDATA=C:\Users\74wny0wl\AppData\Roaming
        CommonProgramFiles=C:\Program Files\Common Files
        CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
        CommonProgramW6432=C:\Program Files\Common Files
        ...
        USERPROFILE=C:\Users\74wny0wl
        windir=C:\Windows

5. Manipulacja procesem

W poniższej części artykułu zostanie przedstawiony przykład świadomej manipulacji procesem

5.1 Modyfikacja sterty procesu

Jako przykład modyfikacji pamięci procesu, spróbujmy zmodyfikować zawartość pola tekstowego w oknie notatnika.

notepad secret content

Analiza PEB pozwoliła ujawnić adres sterty procesu notatnika:

ProcessHeap:       000001efa1380000

Przeszukajmy stertę w celu znalezienia adresu, pod którym znajduje się fragment szukanego tekstu “ANY SECRET CONTENT”. Należy pamiętać o tym, że wyszukiwany tekst jest zakodowany jako Unicode:

kd> s -u 000001efa1380000 L100000 "ANY SECRET CONTENT"
000001ef`a1385060  0041 004e 0059 0020 0053 0045 0043 0052  A.N.Y. .S.E.C.R.
kd> du 000001ef`a1385060
000001ef`a1385060  "ANY SECRET CONTENT"

Spróbujmy teraz zmodyfikować fragment pamięci procesu odpowiadający zawartości pola tekstowego okna notatnika:

kd> eu 000001ef`a1385060 "MALFORMED CONTENT"
kd> du 000001ef`a1385060
000001ef`a1385060  "MALFORMED CONTENTT"
kd> g

Wykonane zmiany w pamięci zostały natychmiast wprowadzone. Nie są natomiast natychmiast obserwowalne. W tym celu należy wymusić jeszcze odrysowanie okna notatnika. Jednym z rozwiązań jest wprowadzenie znaku (np. spacji) w polu edycyjnym notatnika lub zmiana rozmiaru okna. Od tej pory skutki modyfikacji pamięci będą widoczne dla użytkownika.

notepad secret content

5.2 Modyfikacja PEB

Spróbujmy zmodyfikować PEB procesu notepad.exe tak, aby w przypadku analizy tego procesu z wykorzystaniem popularnych narzędzi przypominał proces OneDrive.exe.

Należy zauważyć, że zrestartowałem system debugowanej maszyny i ponownie uruchomiłem notatnik, którego PID obecnie to 1728:

notepad-init-proc-exp-prop

Zostałem więc jednocześnie zmuszony do ponownego odczytania listy adresów istniejących procesów:

kd> !dml_proc
Address           PID  Image file name
ffffd58f`31e60400 4    System
ffffd58f`33295780 114  smss.exe
ffffd58f`33a8f080 170  csrss.exe
...
ffffd58f`34758080 6c0  notepad.exe
...
ffffd58f`35302780 1130 OneDrive.exe

Odczytajmy zatem dane procesu OneDrive.exe:

kd> .process /p ffffd58f`35302780
Implicit process is now ffffd58f`35302780
.cache forcedecodeuser done
kd> !peb
PEB at 0000000000a07000
...

Odczytajmy zawartość struktury PEB procesu OneDrive.exe:

kd> dt ntdll!_peb 0000000000a07000
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0 ''
   +0x003 BitField         : 0x4 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 IsPackagedProcess : 0y0
   +0x003 IsAppContainer   : 0y0
   +0x003 IsProtectedProcessLight : 0y0
   +0x003 IsLongPathAwareProcess : 0y0
   +0x004 Padding0         : [4]  ""
   +0x008 Mutant           : 0xffffffff`ffffffff Void
   +0x010 ImageBaseAddress : 0x00000000`01280000 Void
   +0x018 Ldr              : 0x00007ff9`bc4533a0 _PEB_LDR_DATA
   +0x020 ProcessParameters : 0x00000000`00e619b0 _RTL_USER_PROCESS_PARAMETERS
   ...
   +0x388 TppWorkerpListLock : 0
   +0x390 TppWorkerpList   : _LIST_ENTRY [ 0x00000000`00a07390 - 0x00000000`00a07390 ]
   +0x3a0 WaitOnAddressHashTable : [128] (null)

W celu prezentacji parametrów procesu OneDrive.exe należy odczytać zawartość instancji struktury _RTL_USER_PROCESS_PARAMETERS tego procesu:

kd> dt ntdll!_RTL_USER_PROCESS_PARAMETERS 0x00000000`00e619b0
   +0x000 MaximumLength    : 0x7d6
   +0x004 Length           : 0x7d6
   +0x008 Flags            : 0x6001
   +0x00c DebugFlags       : 0
   +0x010 ConsoleHandle    : 0xffffffff`fffffffe Void
   +0x018 ConsoleFlags     : 0
   +0x020 StandardInput    : (null)
   +0x028 StandardOutput   : (null)
   +0x030 StandardError    : (null)
   +0x038 CurrentDirectory : _CURDIR
   +0x050 DllPath          : _UNICODE_STRING ""
   +0x060 ImagePathName    : _UNICODE_STRING "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x070 CommandLine      : _UNICODE_STRING ""C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background"
   +0x080 Environment      : 0x00000000`00e60dd0 Void
   +0x088 StartingX        : 0
   +0x08c StartingY        : 0
   +0x090 CountX           : 0
   +0x094 CountY           : 0
   +0x098 CountCharsX      : 0
   +0x09c CountCharsY      : 0
   +0x0a0 FillAttribute    : 0
   +0x0a4 WindowFlags      : 1
   +0x0a8 ShowWindowFlags  : 1
   +0x0b0 WindowTitle      : _UNICODE_STRING "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x0c0 DesktopInfo      : _UNICODE_STRING "Winsta0\Default"
   +0x0d0 ShellInfo        : _UNICODE_STRING ""
   +0x0e0 RuntimeData      : _UNICODE_STRING ""
   +0x0f0 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
   +0x3f0 EnvironmentSize  : 0xbce
   +0x3f8 EnvironmentVersion : 3
   +0x400 PackageDependencyData : (null)
   +0x408 ProcessGroupId   : 0x1e8
   +0x40c LoaderThreads    : 0

Interesują nas ImagePathName, CommandLine oraz WindowTitle:

   +0x060 ImagePathName    : _UNICODE_STRING "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x070 CommandLine      : _UNICODE_STRING ""C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background"
   +0x0b0 WindowTitle      : _UNICODE_STRING "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"

Korzystając z okazji, przyjrzymy się strukturom _UNICODE_STRING:

kd> dt ntdll!_UNICODE_STRING 0x00000000`00e619b0+0x60
 "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x000 Length           : 0x7e
   +0x002 MaximumLength    : 0x80
   +0x008 Buffer           : 0x00000000`00e61fc8  "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
kd> dt ntdll!_UNICODE_STRING 0x00000000`00e619b0+0x70
 ""C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background"
   +0x000 Length           : 0x9a
   +0x002 MaximumLength    : 0x9c
   +0x008 Buffer           : 0x00000000`00e62048  ""C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background"
kd> dt ntdll!_UNICODE_STRING 0x00000000`00e619b0+0xb0
 "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x000 Length           : 0x7e
   +0x002 MaximumLength    : 0x80
   +0x008 Buffer           : 0x00000000`00e620e4  "C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"

Znamy już zapisane wartości ImagePathName, CommandLine oraz WindowTitle procesu OneDrive.exe. Wykonajmy zatem stosowne zmiany w strukturze procesu notepad.exe. W tym celu przełączmy się na kontekst tego procesu:

kd> .process /p ffffd58f`34758080
Implicit process is now ffffd58f`34758080
.cache forcedecodeuser done
kd> !peb
PEB at 0000008a19224000
...

Odczytajmy adres, pod którym zapisana jest instancja _RTL_USER_PROCESS_PARAMETERS:

kd> dt ntdll!_peb 0000008a19224000
...
    +0x020 ProcessParameters : 0x0000020b`0e5c19b0 _RTL_USER_PROCESS_PARAMETERS
...

Wypiszmy zawartość ProcessParameters procesu notepad.exe:

kd> dt ntdll!_RTL_USER_PROCESS_PARAMETERS 0x0000020b`0e5c19b0
   +0x000 MaximumLength    : 0x780
   +0x004 Length           : 0x780
   +0x008 Flags            : 0x6001
   +0x00c DebugFlags       : 0
   ...
   +0x060 ImagePathName    : _UNICODE_STRING "C:\Windows\system32\notepad.exe"
   +0x070 CommandLine      : _UNICODE_STRING ""C:\Windows\system32\notepad.exe" "
   ...
   +0x0b0 WindowTitle      : _UNICODE_STRING "C:\Users\74wny0wl\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\Notepad.lnk"
   ...
   +0x40c LoaderThreads    : 0

Ostatecznie przyjrzyjmy się w jaki sposób przechowywane są w pamięci interesujące nas parametry procesu:

kd> dt ntdll!_UNICODE_STRING 0x0000020b`0e5c19b0+0x60
 "C:\Windows\system32\notepad.exe"
   +0x000 Length           : 0x3e
   +0x002 MaximumLength    : 0x40
   +0x008 Buffer           : 0x0000020b`0e5c1fc8  "C:\Windows\system32\notepad.exe"
kd> dt ntdll!_UNICODE_STRING 0x0000020b`0e5c19b0+0x70
 ""C:\Windows\system32\notepad.exe" "
   +0x000 Length           : 0x44
   +0x002 MaximumLength    : 0x46
   +0x008 Buffer           : 0x0000020b`0e5c2008  ""C:\Windows\system32\notepad.exe" "
kd> dt ntdll!_UNICODE_STRING 0x0000020b`0e5c19b0+0xb0
 "C:\Users\74wny0wl\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\Notepad.lnk"
   +0x000 Length           : 0xbe
   +0x002 MaximumLength    : 0xc0
   +0x008 Buffer           : 0x0000020b`0e5c204e  "C:\Users\74wny0wl\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\Notepad.lnk"

Zauważmy, że odczytane ciągi znaków dla procesu OneDrive.exe są dłuższe od ich odpowiedników w procesie notepad.exe, przez co zajmują więcej miejsca w pamięci. Musimy zatem zastanowić się nad sposobem zapisania dłuższych ciągów znaków w pamięci procesu notepad.exe.

Zauważmy, że zajęta pamięć dla poszczególnych buforów dla ImagePathName, CommandLine oraz WindowTitle stanowi strukturę ciągłą, której rozmiar wynosi 0x146 bajtów.

memory layout before changes

Będziemy chcieli zapisać dwa ciągi:

C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe
"C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background

Możemy tutaj wykonać drobną optymalizację: ImagePathName oraz WindowTitle mogą korzystać z tego samego bufora. Policzmy ile potrzebujemy pamięci, żeby je zapisać. W tym celu uruchomiłem pythona w trybie interaktywnym:

$ python3 -q
>>> window_title=r'''C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe'''
>>> command_line=r'''"C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background'''
>>> hex((len(window_title)+len(command_line))*2)
'0x118'

Potrzebujemy 0x118 bajtów w przestrzeni adresowej procesu, więc zmieścimy się w obszarze o wielkości 0x146 bajtów, który został wykorzystywany do tej pory.

memory layout after changes

Zmodyfikujmy więc ImagePathName, CommandLine oraz WindowTitle procesu notepad.exe:

kd> ew 0x0000020b`0e5c19b0+0x60+0x0 0x7e
kd> ew 0x0000020b`0e5c19b0+0x60+0x2 0x80
kd> eu 0x0000020b`0e5c1fc8 "C:\\Users\\74wny0wl\\AppData\\Local\\Microsoft\\OneDrive\\OneDrive.exe"
kd> ew 0x0000020b`0e5c19b0+0x70+0x0 0x9a
kd> ew 0x0000020b`0e5c19b0+0x70+0x2 0x9c
kd> eu 0x0000020b`0e5c1fc8+0x80 "\"C:\\Users\\74wny0wl\\AppData\\Local\\Microsoft\\OneDrive\\OneDrive.exe\" \/background"
kd> ep 0x0000020b`0e5c19b0+0x70+0x8 0x0000020b`0e5c1fc8+0x80
kd> ew 0x0000020b`0e5c19b0+0xb0+0x0 0x7e
kd> ew 0x0000020b`0e5c19b0+0xb0+0x2 0x80
kd> ep 0x0000020b`0e5c19b0+0xb0+0x8 0x0000020b`0e5c1fc8

Podejrzyjmy PEB notepad.exe po wprowadzonych zmianach:

kd> !peb
PEB at 0000008a19224000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
    ImageBaseAddress:         00007ff7dc110000
    ...
    ProcessParameters: 0000020b0e5c19b0
    CurrentDirectory:  'C:\Users\74wny0wl\'
    WindowTitle:  'C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe'
    ImageFile:    'C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe'
    CommandLine:  '"C:\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background'
    ...

Przyjrzyjmy się jaki wpływ na system operacyjny wywarły wprowadzone zmiany.

Lista procesów odczytana przez debugger nie uległa zmianie:

kd> !dml_proc
Address           PID  Image file name
ffffd58f`31e60400 4    System
ffffd58f`33295780 114  smss.exe
...
ffffd58f`34758080 6c0  notepad.exe
...
ffffd58f`35302780 1130 OneDrive.exe
...

Podgląd procesu notatnika w Task Managerze:

notepad-basic-peb-taskmgr

Podgląd procesu notatnika w Process Explorerze:

notepad-basic-peb-procexp

Podgląd procesu notatnika w Process Hackerze:

notepad-basic-peb-prochacker

Nie ujawniono błędów w uruchomionej instancji programu notepad.exe.

5.3 Modyfikacja pól struktury procesu

W tej części artykułu zostanie podjęta kolejna próba modyfikacji procesu notepad.exe tak, aby w przypadku analizy tego procesu z wykorzystaniem popularnych narzędzi, przypominał proces OneDrive.exe.

Informacje dotyczące procesu są przechowywane przez jądro systemu Windows w strukturze EPROCESS. W celu prezentacji danych procesu notepad.exe, zawartych w instancji struktury EPROCESS, należy wykonać polecenie:

kd> dt ntdll!_eprocess ffffd58f`34758080
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   ...
   +0x468 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   ...

Jednym z pól tej struktury jest SeAuditProcessCreationInfo. Definicja _SE_AUDIT_PROCESS_CREATION_INFO wygląda jak poniżej:

typedef struct _SE_AUDIT_PROCESS_CREATION_INFO
{
     POBJECT_NAME_INFORMATION ImageFileName;
} SE_AUDIT_PROCESS_CREATION_INFO, *PSE_AUDIT_PROCESS_CREATION_INFO;

Przyjrzyjmy się zawartości SeAuditProcessCreationInfo w OneDrive.exe:

kd> dt ntdll!_SE_AUDIT_PROCESS_CREATION_INFO ffffd58f`35302780+0x468
   +0x000 ImageFileName    : 0xffffd58f`352cea40 _OBJECT_NAME_INFORMATION
kd> dt ntdll!_OBJECT_NAME_INFORMATION 0xffffd58f`352cea40
   +0x000 Name             : _UNICODE_STRING "\Device\HarddiskVolume2\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"

oraz w notepad.exe:

kd> kd> dt ntdll!_SE_AUDIT_PROCESS_CREATION_INFO ffffd58f`34758080+0x468
   +0x000 ImageFileName    : 0xffffd58f`350d2a50 _OBJECT_NAME_INFORMATION
kd> dt ntdll!_OBJECT_NAME_INFORMATION 0xffffd58f`350d2a50
   +0x000 Name             : _UNICODE_STRING "\Device\HarddiskVolume2\Windows\System32\notepad.exe"

Zatem SeAuditProcessCreationInfo to wskaźnik na _UNICODE_STRING. Wykorzystany adres znajduje się w przestrzeni jądra, więc możemy skorzystać z niego w polu SeAuditProcessCreationInfo z instancji struktury EPROCESS opisującej proces notepad.exe. Wykonajmy więc stosowną modyfikację tak, aby wskaźnik SeAuditProcessCreationInfo wskazywał na ten sam ImageFileName z procesu OneDrive.exe.

kd> ep ffffd58f`34758080+0x468 0xffffd58f`352cea40

Przyjrzyjmy się jaki wpływ na system operacyjny wywarły wprowadzone zmiany.

Lista procesów odczytana przez debugger nie uległa zmianie:

kd> !dml_proc
Address           PID  Image file name
ffffd58f`31e60400 4    System
ffffd58f`33295780 114  smss.exe
ffffd58f`33a8f080 170  csrss.exe
...
ffffd58f`34758080 6c0  notepad.exe
...
ffffd58f`35302780 1130 OneDrive.exe
...

ale dla polecenia !process mamy już zaskakujące zmiany:

kd> !process ffffd58f`34758080 7
PROCESS ffffd58f34758080
    SessionId: 1  Cid: 06c0    Peb: 8a19224000  ParentCid: 0054
    DirBase: 5d3e6000  ObjectTable: ffffa10dedf32540  HandleCount: <Data Not Accessible>
    Image: OneDrive.exe
    ...
    THREAD ffffd58f3479c540  Cid 06c0.0bc0  Teb: 0000008a19225000 Win32Thread: ffffd58f3500b8c0 WAIT: (WrUserRequest) UserMode Non-Alertable
            ffffd58f3528eb80  SynchronizationEvent
        Not impersonating
        DeviceMap                 ffffa10def921dd0
        Owning Process            ffffd58f34758080       Image:         OneDrive.exe
        ...

Podgląd procesu notatnika w Task Managerze:

notepad-seaudit-taskmgr

Podgląd procesu notatnika w Process Explorerze:

notepad-seaudit-procexp

Podgląd procesu notatnika w Process Hackerze:

notepad-seaudit-prochacker

Nie ujawniono błędów w uruchomionej instancji programu notepad.exe.

5.4 Kolejne modyfikacje pól struktury procesu

W tej części artykułu zostanie podjęta kolejna próba modyfikacji procesu notepad.exe tak, aby w przypadku analizy tego procesu z wykorzystaniem popularnych narzędzi przypominał proces OneDrive.exe.

Jednymi z pól struktury EPROCESS są ImageFilePointer oraz ImageFileName. Przyjrzyjmy się instancji EPROCESS opisującej proces OneDrive.exe:

kd> dt ntdll!_eprocess ffffd58f`35302780
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   ...
   +0x448 ImageFilePointer : 0xffffd58f`350f6bc0 _FILE_OBJECT
   +0x450 ImageFileName    : [15]  "OneDrive.exe"
   ...

W celu odczytania zawartości instancji _FILE_OBJECT należy wykonać polecenie dt jak poniżej:

kd> dt ntdll!_FILE_OBJECT 0xffffd58f`350f6bc0
   +0x000 Type             : 0n5
   +0x002 Size             : 0n216
   +0x008 DeviceObject     : 0xffffd58f`32fe2330 _DEVICE_OBJECT
   +0x010 Vpb              : 0xffffd58f`32fe3010 _VPB
   +0x018 FsContext        : 0xffffa10d`ee0c5150 Void
   +0x020 FsContext2       : 0xffffa10d`ed7130b0 Void
   +0x028 SectionObjectPointer : 0xffffd58f`351988a8 _SECTION_OBJECT_POINTERS
   +0x030 PrivateCacheMap  : (null)
   +0x038 FinalStatus      : 0n0
   +0x040 RelatedFileObject : (null)
   +0x048 LockOperation    : 0 ''
   +0x049 DeletePending    : 0 ''
   +0x04a ReadAccess       : 0x1 ''
   +0x04b WriteAccess      : 0 ''
   +0x04c DeleteAccess     : 0 ''
   +0x04d SharedRead       : 0x1 ''
   +0x04e SharedWrite      : 0 ''
   +0x04f SharedDelete     : 0x1 ''
   +0x050 Flags            : 0x44042
   +0x058 FileName         : _UNICODE_STRING "\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x068 CurrentByteOffset : _LARGE_INTEGER 0x0
   +0x070 Waiters          : 0
   +0x074 Busy             : 0
   +0x078 LastLock         : (null)
   +0x080 Lock             : _KEVENT
   +0x098 Event            : _KEVENT
   +0x0b0 CompletionContext : (null)
   +0x0b8 IrpListLock      : 0
   +0x0c0 IrpList          : _LIST_ENTRY [ 0xffffd58f`350f6c80 - 0xffffd58f`350f6c80 ]
   +0x0d0 FileObjectExtension : (null)

Podejrzyjmy instancję _FILE_OBJECT wskazywaną przez ImageFilePointer dla procesu notepad.exe:

kd> dt ntdll!_eprocess ffffd58f`34758080
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   ...
   +0x448 ImageFilePointer : 0xffffd58f`350caca0 _FILE_OBJECT
   +0x450 ImageFileName    : [15]  "notepad.exe"
   ...

kd> dt ntdll!_FILE_OBJECT 0xffffd58f`350caca0
   +0x000 Type             : 0n5
   +0x002 Size             : 0n216
   ...
   +0x058 FileName         : _UNICODE_STRING "\Windows\System32\notepad.exe"
   ...

Zanim brutalnie nadpiszemy ciągi znaków, sprawdźmy czy mamy wystarczająco dużo miejsca w pamięci:

kd> dt ntdll!_UNICODE_STRING 0xffffd58f`350f6bc0+0x58
 "\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   +0x000 Length           : 0x7a
   +0x002 MaximumLength    : 0xf8
   +0x008 Buffer           : 0xffffa10d`ee0cf0c0  "\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
kd> dt ntdll!_UNICODE_STRING 0xffffd58f`350caca0+0x58
 "\Windows\System32\notepad.exe"
   +0x000 Length           : 0x3a
   +0x002 MaximumLength    : 0x78
   +0x008 Buffer           : 0xffffa10d`ed71a230  "\Windows\System32\notepad.exe"

Rozmiar ciągu znaków, który chcemy zapisać to 0x7a bajtów. Do dyspozycji mamy bufor o rozmiarze 0x78 bajtów. Zatem brakuje nam kilka bajtów, które musimy sobie “pożyczyć” lub ponownie sprawić, aby adres bufora zawierającego ścieżkę do pliku OneDrive.exe był taki sam w obu strukturach:

kd> ew 0xffffd58f`350caca0+0x58+0x0 0x7a
kd> ew 0xffffd58f`350caca0+0x58+0x2 0xf8
kd> ep 0xffffd58f`350caca0+0x58+0x8 0xffffa10d`ee0cf0c0

Jako ostatni krok zmieńmy jeszcze wartość ImageFileName w strukturze EPROCESS opisującej proces notepad.exe:

kd> ea ffffd58f`34758080+0x450 "OneDrive.exe"

Skontrolujmy poprawność wprowadzonych zmian:

kd> dt ntdll!_eprocess ffffd58f`34758080
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   ...
   +0x448 ImageFilePointer : 0xffffd58f`350caca0 _FILE_OBJECT
   +0x450 ImageFileName    : [15]  "OneDrive.exe"
   ...

kd> dt ntdll!_FILE_OBJECT 0xffffd58f`350caca0
   +0x000 Type             : 0n5
   +0x002 Size             : 0n216
   +0x008 DeviceObject     : 0xffffd58f`32fe2330 _DEVICE_OBJECT
   ...
   +0x058 FileName         : _UNICODE_STRING "\Users\74wny0wl\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
   ...
   +0x0d0 FileObjectExtension : (null)

Przyjrzyjmy się jaki wpływ na system operacyjny wywarły wprowadzone zmiany.

Lista procesów odczytana przez rozszerzenie debuggera !dml_proc uległa zmianie i debugger listuje proces notatnika oraz oryginalny proces klienta chmury Microsoftu jako proces OneDrive.exe:

kd> !dml_proc
Address           PID  Image file name
ffffd58f`31e60400 4    System
ffffd58f`33295780 114  smss.exe
...
ffffd58f`34758080 6c0  OneDrive.exe
...
ffffd58f`35302780 1130 OneDrive.exe
...

Podgląd procesu notatnika w Task Managerze:

notepad-eprocess-taskmgr

Podgląd procesu notatnika w Process Explorerze:

notepad-eprocess-procexp

Podgląd procesu notatnika w Process Hackerze:

notepad-eprocess-prochacker

Nie ujawniono błędów w uruchomionej instancji programu notepad.exe.

5.5 Ostateczne upodobnienie procesu

W tej części artykułu zostanie podjęta ostateczna próba modyfikacji procesu notepad.exe tak, aby w przypadku analizy tego procesu z wykorzystaniem popularnych narzędzi przypominał proces OneDrive.exe.

Sprawmy, aby pole ImageFilePointer w instancji struktury EPROCESS procesu notepad.exe wskazywało na ten sam _FILE_OBJECT, na który wskazuje ImageFilePointer w instancji struktury EPROCESS procesu OneDrive.exe:

kd> ep ffffd58f`34758080+0x448 0xffffd58f`350f6bc0

Po zmianach struktura EPROCESS opisująca proces notepad.exe powinna wyglądać jak poniżej:

kd> dt ntdll!_eprocess ffffd58f`34758080
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   ...
   +0x448 ImageFilePointer : 0xffffd58f`350f6bc0 _FILE_OBJECT
   +0x450 ImageFileName    : [15]  "OneDrive.exe"
   ...

Przyjrzyjmy się jaki wpływ na system operacyjny wywarły wprowadzone zmiany.

Podgląd procesu notatnika w Task Managerze:

notepad-all-taskmgr

Podgląd procesu notatnika w Process Explorerze:

notepad-all-procexp

Podgląd procesu notatnika w Process Hackerze:

notepad-all-prochacker

Nie ujawniono błędów w uruchomionej instancji programu notepad.exe.

6. Podsumowanie

W artykule przedstawiono metody odczytu struktur danych definiujących proces w systemie operacyjnym Windows: EPROCESS oraz PEB. Zaprezentowano również użycie poleceń !dml_proc, !process, !peb, .process, dt, e* oraz d*.

Warto przy tej okazji podkreślić znaczenie powtarzanego często stwierdzenia: nie warto ufać wynikom pokazywanym przez tylko jedno narzędzie. Na różnych etapach wykonywanych kolejno modyfikacji struktur odpowiedzialnych za przechowywanie danych stanowiących definicję procesu w systemie, uzyskano różniące się pomiędzy sobą wyniki w popularnych narzędziach informujących o stanie procesów: Task Managerze, Process Explorerze oraz Process Hackerze. Ostatecznie doprowadzono do stanu systemu operacyjnego, w którym proces notatnika jest na pozór nierozróżnialny od procesu OneDrive.exe.

Potencjalne wykorzystanie opisanych metod przez malware, może prowadzić do wprowadzenia w błąd niektórych silników antywirusowych lub specjalistów DFIR. Poniżej zamieściłem tabelę przedstawiającą zmiany w przetestowanych narzędziach oraz skuteczność stosowania przedstawionych metod. Czytając tabelę, należy pamiętać o skumulowanym użyciu poszczególnych metod.

process-details-comparison-table-pl

W tym miejscu należy jednocześnie podkreślić, że modyfikacje nie wpłynęły na poprawność działania aplikacji lub systemu operacyjnego: system pracował z uruchomionym notatnikiem oraz OneDrive.exe przez kilka kolejnych godzin, notatnik był testowany pod względem dostępnych funkcjonalności i nie zauważyłem żadnych błędów w działaniu w aplikacji notatnika lub systemu operacyjnego.

Oczywiście po zabiciu procesu notatnika lub OneDrive.exe, pojawi się BSOD i zakończymy pracę. Dzieje się tak z powodu wykorzystania przez proces tych samych instancji struktur danych, które istnieją w przestrzeni jądra.

Warto też zauważyć, że mechanizmy istniejące w jądrze systemu operacyjnego Windows prawdopodobnie nie weryfikują integralności struktur stanowiących definicję procesu lub nie robią tego w skuteczny sposób.