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.:
- unikalny identyfikator,
- zdefiniowany kontekst bezpieczeństwa,
- wykorzystywane identyfikatory zasobów systemowych,
- znane zmienne środowiskowe,
- przypisany priorytet,
- przydzieloną i dokładnie określoną wirtualną przestrzeń adresową,
- przydzielone strony pamięci fizycznej,
- kod wykonywalny,
- co najmniej jeden wątek, który może tworzyć inne wątki i procesy.
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.
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.
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:
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.
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.
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:
Podgląd procesu notatnika w Process Explorerze:
Podgląd procesu notatnika w Process Hackerze:
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:
Podgląd procesu notatnika w Process Explorerze:
Podgląd procesu notatnika w Process Hackerze:
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:
Podgląd procesu notatnika w Process Explorerze:
Podgląd procesu notatnika w Process Hackerze:
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:
Podgląd procesu notatnika w Process Explorerze:
Podgląd procesu notatnika w Process Hackerze:
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.
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.