Created
December 26, 2020 21:33
-
-
Save Auscitte/ed807fd604d7b907ebd949628c6df725 to your computer and use it in GitHub Desktop.
Reverse-engineered basesrv::ServerDllInitialization()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file ServerDllInitialization.cpp | |
* @brief Some partially reverse-engineered functions and structures from basesrv.dll (build 1804). | |
* | |
* The file contains ServerDllInitialization(), BaseSrvInitializeIniFileMappings(), and CreateBaseAcls(), | |
* reverse-engineered to the extent and with accuracy necessary to figure out the reason why the OS | |
* installed on authors' laptop failed to boot. | |
* | |
* @author Ry Auscitte | |
*/ | |
/** | |
* @name CSR_SERVER_DLL | |
* | |
* This defitnition comes from http://www.geoffchappell.com/studies/windows/win32/csrsrv/api/srvloadr/server_dll.htm | |
*/ | |
typedef struct _CSR_SERVER_DLL { | |
ANSI_STRING ModuleName; //0x0 | |
HMODULE ModuleHandle; //0x10 | |
DWORD ServerDllIndex; //0x18 | |
DWORD ServerDllConnectInfoLength; //0x1C | |
DWORD ApiNumberBase; //0x20 | |
DWORD MaxApiNumber; //0x24 | |
PCSR_API_ROUTINE *ApiDispatchTable; //0x28 | |
BOOLEAN *ApiServerValidTable; //0x30 | |
QWORD Reserved1; //0x38 | |
DWORD PerProcessDataLength; //0x40 | |
DWORD Reserved2; //0x44 | |
LONG (*ConnectRoutine) (CSR_PROCESS*, PVOID, ULONG *); //0x48 | |
VOID (*DisconnectRoutine) (CSR_PROCESS *); //0x50 | |
VOID (*HardErrorRoutine) (CSR_THREAD*, HARDERROR_MSG *); //0x58 | |
PVOID SharedStaticServerData; //0x60 | |
LONG (*AddProcessRoutine) (CSR_PROCESS*, CSR_PROCESS *); //0x68 | |
ULONG (*ShutdownProcessRoutine) (CSR_PROCESS *, ULONG, UCHAR); //0x70 | |
} CSR_SERVER_DLL, *PCSR_SERVER_DLL; | |
/** | |
* @name BASE_STATIC_SERVER_DATA | |
* | |
* This definition is a "joint effort" of ReactOS team (see https://doxygen.reactos.org/d3/d5a/base_8h_source.html) and the author. | |
*/ | |
typedef struct _BASE_STATIC_SERVER_DATA | |
{ | |
UNICODE_STRING WindowsDirectory; //0x000 | |
UNICODE_STRING WindowsSystemDirectory; //0x010 | |
UNICODE_STRING NamedObjectDirectory; //0x020 | |
char padding1[6]; //0x030 (WindowsMajorVersion, WindowsMinorVersion, and BuildNumber will fit right in) | |
int16_t CSDNumber; //0x036 | |
int16_t RCNumber; //0x038 | |
WCHAR CSDVersion[128]; //0x03a | |
char padding2[6]; //0x13a | |
SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay; //0x140 (sizeof(BASE_STATIC_SERVER_DATA) = 0x30; https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/timeofday.htm) | |
PVOID IniFileMapping; //0x170 | |
NLS_USER_INFO NlsUserInfo; //0x178 | |
char padding3[0x958 - 0x178 - sizeof(NLS_USER_INFO)]; //do not know the NLS_USER_INFO layout in Win10 | |
unsigned char DefaultSeparateVDM; //0x958 | |
unsigned char IsWowTaskReady; //0x959 | |
UNICODE_STRING WindowsSys32x86Directory; //0x960 | |
unsigned char fTermsrvAppInstallMode; //0x970 | |
char paddng5[447]; //0x971 | |
int32_t TermsrvClientTimeZoneId; //0xb30 | |
unsigned char LUIDDeviceMapsEnabled; //0xb34 | |
char padding6[3]; //0xb35 | |
int32_t TermsrvClientTimeZoneChangeNum; //0xb38 | |
char padding7[4]; //0xb3c | |
UNICODE_STRING AppContainerNamedObjectsDirectory; //0xb40 | |
struct BASE_STATIC_SERVER_DATA* pSelf; //0xb50 | |
UNICODE_STRING UserObjectsDirectory; //0xb58 | |
} BASE_STATIC_SERVER_DATA, *PBASE_STATIC_SERVER_DATA; | |
/////////////////////////////////////////////////// | |
// Global variables with public symbols in .pdb | |
/////////////////////////////////////////////////// | |
HANDLE g_BaseSrvHeap; | |
HANDLE g_BaseSrvSharedHeap; | |
UNICODE_STRING g_BaseSrvCSDString; | |
SHORT g_BaseSrvCSDNumber[2]; | |
DWORD g_InteractiveUserNameSpaceSeparation; | |
HANDLE g_BaseSrvNamedObjectDirectory; | |
HANDLE g_BaseSrvUserObjectDirectory; | |
SYSTEM_BASIC_INFORMATION g_SysInfo; | |
RTL_QUERY_REGISTRY_TABLE g_BaseServerRegistryConfigurationTable = | |
{ | |
NULL, | |
RTL_QUERY_REGISTRY_DIRECT, | |
L"CSDVersion", | |
&g_BaseSrvCSDString, //0x180010960 | |
REG_NONE, | |
NULL, | |
0 | |
}; | |
RTL_QUERY_REGISTRY_TABLE g_BaseServerRegistryConfigurationTable1 = | |
{ | |
NULL, | |
RTL_QUERY_REGISTRY_DIRECT, | |
L"CSDVersion", | |
&g_BaseSrvCSDNumber, //0x180010970 | |
REG_NONE, | |
NULL, | |
0 | |
}; | |
RTL_QUERY_REGISTRY_TABLE g_BnoRegistryConfigurationTable = | |
{ | |
NULL, | |
RTL_QUERY_REGISTRY_TYPECHECK | RTL_QUERY_REGISTRY_DIRECT, | |
L"InteractiveUserSeparation", | |
&g_InteractiveUserNameSpaceSeparation, | |
0x4000000, | |
NULL, | |
0 | |
}; | |
///////////////////////////////////////////////////// | |
// Unnamed global variables (not in the symbol file) | |
///////////////////////////////////////////////////// | |
UNICODE_STRING g_uWOWRegistryKeyName = { 0x6c, 0x6e, L"Registry\\Machine\\System\\CurrentControlSet\\Control\\WOW" }; | |
UNICODE_STRING g_uWOWRegistryValueName = { 0x24, 0x26, L"DefaultSeparateVDM" }; | |
UNICODE_STRING g_uGlobal = { 0xc, 0xe, L"Global" }; | |
UNICODE_STRING g_uAppContainerNamedObjects = { 0x30, 0x32, L"AppContainerNamedObjects" }; | |
UNICODE_STRING g_uBaseNamedObjectsNZSLink = { 0x22, 0x24, L"\\BaseNamedObjects" }; | |
UNICODE_STRING g_uLocal = { 0xa, 0xc, "Local" }; | |
/////////////////////////////////////////////////// | |
// Macro | |
/////////////////////////////////////////////////// | |
#define HALT_NO_MEM_IF_FALSE(op) if (!op) {\ | |
RtlDeleteCriticalSection(&g_BaseSrvDosDeviceCritSec);\ | |
return STATUS_NO_MEMORY;\ | |
} | |
#define HALT_NO_MEM_IF_NULL(op) if ((op) == NULL) {\ | |
RtlDeleteCriticalSection(&g_BaseSrvDosDeviceCritSec);\ | |
return STATUS_NO_MEMORY;\ | |
} | |
#define HALT_IF_FAIL(op) ret = op;\ | |
if (!NT_SUCCESS(ret)) {\ | |
RtlDeleteCriticalSection(&g_BaseSrvDosDeviceCritSec);\ | |
return ret;\ | |
} | |
#define COPY_UNICODE_STRING_EXACT(to, from) to = from;\ | |
to.MaximumLength = from.Length + sizeof(WORD);\ | |
HALT_NO_MEM_IF_NULL(pDst = RtlAllocateHeap(g_BaseSrvSharedHeap, g_BaseSrvSharedTag, from.Length + sizeof(WORD)))\ | |
memcpy(pDst, to.Buffer, to.MaximumLength);\ | |
to.Buffer = pDst; | |
#define COPY_UNICODE_STRING(to, from) to = from;\ | |
HALT_NO_MEM_IF_NULL(pDst = RtlAllocateHeap(g_BaseSrvSharedHeap, g_BaseSrvSharedTag, from.MaximumLength))\ | |
memcpy(pDst, to.Buffer, from.MaximumLength);\ | |
to.Buffer = pDst; | |
#define WINDOWS_DIR_MAX_LEN 0x190 | |
#define NAMED_OBJECTS_DIR_MAX_LEN 0x100 | |
#define NAMED_PIPE_BUFFER_MAX_LEN 0x112 | |
/** | |
* Reverse-engineered ServerDllInitialization() | |
*/ | |
NTSTATUS ServerDllInitialization(struct CSR_SERVER_DLL* pInput) | |
{ | |
DWORD pdwAccessMasks[] = { 4, 0x100002, 8, 0x100004, 0 }; | |
//https://www.vergiliusproject.com/kernels/x64/Windows%2010%20|%202016/1809%20Redstone%205%20(October%20Update)/_PEB | |
g_SessionId = ((struct _PEB*)(__readgsqword(0x60)))->SessionId; | |
g_ServiceSessionId = RtlGetCurrentServiceSessionId(); | |
//OBJ_PERMANENT: If this flag is specified, the object is not deleted when all open handles are closed. | |
DWORD dwAttributes = (OBJ_OPENIF | OBJ_CASE_INSENSITIVE) | (g_SessionId == g_ServiceSessionId ? OBJ_PERMANENT : 0); | |
g_BaseSrvHeap = ((struct _PEB*)(__readgsqword(0x60)))->ProcessHeap; | |
g_BaseSrvTag = RtlCreateTagHeap(g_BaseSrvHeap, 0, L"BASESRV!", L"TMP"); | |
g_BaseSrvSharedHeap = pInput->SharedStaticServerData; | |
g_BaseSrvSharedTag = RtlCreateTagHeap(pInput->SharedStaticServerData, 0, L"BASESRV!", L"INIT"); | |
pInput->ApiNumberBase = 0; | |
pInput->ApiDispatchTable = g_BaseServerApiDispatchTable; | |
pInput->ApiServerValidTable = g_BaseServerApiServerValidTable; | |
pInput->ConnectRoutine = g_BaseClientConnectRoutine; | |
pInput->DisconnectRoutine = g_BaseClientDisconnectRoutine; | |
pInput->MaxApiNumber = 0x1D; | |
pInput->PerProcessDataLength = 8; | |
NTSTATUS ret = RtlInitializeCriticalSection(&g_BaseSrvDosDeviceCritSec); | |
if (!NT_SUCCESS(ret)) | |
return ret; | |
WORD czWindowsDirectory[WINDOWS_DIR_MAX_LEN]; | |
UNICODE_STRING uSysRoot = { 0, WINDOWS_DIR_MAX_LEN * sizeof(WORD), czWindowsDirectory }; //[rbp+20h] | |
RtlExpandEnvironmentStrings_U(NULL, g_UnexpandedSystemRootString, &uSysRoot, 0); //g_UnexpandedSystemRootString == L"%SystemRoot%" | |
HALT_NO_MEM_IF_FALSE(uSysRoot.Length != WINDOWS_DIR_MAX_LEN * sizeof(WORD)) | |
if (uSysRoot.Length & 0xFFFE == WINDOWS_DIR_MAX_LEN * sizeof(WORD)) { | |
_report_rangecheckfailure (0x0000000180004618) | |
asm{ int 3} | |
} | |
czWindowsDirectory[uSysRoot.Length] = L'\x0'; //uSysRoot.Length does not count the terminating NULL (and it is not always present) | |
HALT_NO_MEM_IF_FALSE(RtlCreateUnicodeString(&g_BaseSrvWindowsDirectory, czWindowsDirectory)) | |
wcscat_s(czWindowsDirectory, WINDOWS_DIR_MAX_LEN, L"\\system32"); | |
HALT_NO_MEM_IF_FALSE(RtlCreateUnicodeString(&g_BaseSrvWindowsSystemDirectory, czWindowsDirectory)) | |
WORD szBaseNamedObjects[NAMED_OBJECTS_DIR_MAX_LEN]; | |
if (g_SessionId != g_ServiceSessionId) | |
swprintf_s(szBaseNamedObjects, NAMED_OBJECTS_DIR_MAX_LEN, L"%ws\\%ld\\BaseNamedObjects", L"Sessions", g_SessionId); | |
else | |
wcscpy_s(szBaseNamedObjects, NAMED_OBJECTS_DIR_MAX_LEN, L"\\BaseNamedObjects"); | |
WORD szAppContainerNamedObjects[NAMED_OBJECTS_DIR_MAX_LEN]; //[rbp+7A0h] | |
swprintf_s(szAppContainerNamedObjects, NAMED_OBJECTS_DIR_MAX_LEN, L"%ws\\%ld\\AppContainerNamedObjects", L"Sessions", g_SessionId); | |
WORD szBaseUserObjects[NAMED_OBJECTS_DIR_MAX_LEN]; //[rbp+5A0h] | |
RtlStringCchPrintfW(szBaseUserObjects, NAMED_OBJECTS_DIR_MAX_LEN, L"%ws\\%ld\\BaseNamedObjects", L"Sessions", g_SessionId); | |
struct UNICODE_STRING uBaseUserObjects; //[rbp-30h] | |
RtlInitUnicodeString(&uBaseUserObjects, szBaseUserObjects); | |
struct UNICODE_STRING uTmpBuffer; //[rbp-68h] | |
RtlInitUnicodeString(&uTmpBuffer, szBaseNamedObjects); | |
struct UNICODE_STRING uAppContainerNamedObjects; //[rbp-20h] | |
RtlInitUnicodeString(&uAppContainerNamedObjects, szAppContainerNamedObjects); | |
HALT_NO_MEM_IF_NULL(g_BaseSrvpStaticServerData = RtlAllocateHeap(g_BaseSrvSharedHeap, g_BaseSrvSharedTag, 0x0B68)) | |
pInput->SharedStaticServerData = g_BaseSrvpStaticServerData; | |
struct BASE_STATIC_SERVER_DATA* pS = (struct BASE_STATIC_SERVER_DATA*)(g_BaseSrvpStaticServerData); | |
pS->pSelf = g_BaseSrvpStaticServerData; //xb50 | |
pS->TermsrvClientTimeZoneId = TIME_ZONE_ID_INVALID; //0xb30 | |
pS->TermsrvClientTimeZoneChangeNum = 0; //0x38 | |
HALT_IF_FAIL(NtQuerySystemInformation(SystemTimeOfDayInformation, &pS->TimeOfDay, sizeof(SYSTEM_TIMEOFDAY_INFORMATION), NULL)) | |
COPY_UNICODE_STRING(pS->WindowsDirectory, g_BaseSrvWindowsDirectory) | |
COPY_UNICODE_STRING(pS->WindowsSystemDirectory, g_BaseSrvWindowsSystemDirectory) | |
*(DWORD*)(&pS->WindowsSys32x86Directory.Length) = 0; | |
pS->WindowsSys32x86Directory.Buffer = NULL; | |
COPY_UNICODE_STRING_EXACT(pS->NamedObjectDirectory, uTmpBuffer) | |
COPY_UNICODE_STRING_EXACT(pS->AppContainerNamedObjectsDirectory, uAppContainerNamedObjects) | |
COPY_UNICODE_STRING_EXACT(pS->UserObjectsDirectory, uBaseUserObjects) | |
pS->fTermsrvAppInstallMode = FALSE; | |
WCHAR szCSDVersion[200]; //[rbp+2d8h], 200 == 0xC8 | |
g_BaseSrvCSDString.MaxLength = 200; | |
g_BaseSrvCSDString.Length = 0; | |
g_BaseSrvCSDString.Buffer = szCSDVersion; | |
ret = RtlQueryRegistryValuesEx(RTL_REGISTRY_WINDOWS_NT, L"\x0", &g_BaseServerRegistryConfigurationTable1, NULL, NULL); | |
if (NT_SUCCESS(ret)) { | |
pS->CSDNumber = g_BaseSrvCSDNumber[0]; | |
pS->RCNumber = g_BaseSrvCSDNumber[1]; | |
} | |
else | |
*(int32_t*)(pS->CSDNumber) = 0L; | |
ret = RtlQueryRegistryValuesEx(RTL_REGISTRY_WINDOWS_NT, L"\x0", &g_BaseServerRegistryConfigurationTable, NULL, NULL); | |
if (NT_SUCCESS(ret)) | |
wcsncpy_s(pS->CSDVersion, 128, g_BaseSrvCSDString.Buffer, g_BaseSrvCSDString.Length / sizeof(WCHAR)); | |
else | |
pS->CSDVersion[0] = L'\x0'; | |
HALT_IF_FAIL(RtlInitUnicodeStringEx(&g_BaseSrvCSDString, NULL)) | |
HALT_IF_FAIL(NtQuerySystemInformation(SystemBasicInformation, &g_SysInfo, sizeof(SYSTEM_BASIC_INFORMATION), NULL)) | |
HALT_IF_FAIL(BaseSrvInitializeIniFileMappings()) | |
pS->DefaultSeparateVDM = FALSE; | |
HANDLE hKey; //[rbp-10h] | |
struct OBJECT_ATTRIBUTES oa; //[rsp+60h] | |
InitializeObjectAttributes(&oa, &g_uWOWRegistryKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
ret = NtOpenKey(&hKey, READ_CONTROL | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa); | |
if (NT_SUCCESS(ret)) { | |
ULONG len; //[rbp+30h] | |
ret = NtQueryValueKey(hKey, &g_uWOWRegistryValueName, KeyValuePartialInformation, czWindowsDirectory, WINDOWS_DIR_MAX_LEN * sizeof(DWORD), &len); | |
if (NT_SUCCESS(ret)) { | |
struct _KEY_VALUE_PARTIAL_INFORMATION* pKi = (struct _KEY_VALUE_PARTIAL_INFORMATION*)(czWindowsDirectory); | |
switch (pKi->Type) { | |
case REG_DWORD: | |
if (*(DWORD*)(pKi->Data) != 0) | |
pS->DefaultSeparateVDM = TRUE; | |
break; | |
case REG_SZ: | |
if (wcsicmp((WCHAR*)(pKi->Data), L"yes") == 0 || wcsicmp((WCHAR*)(pKi->Data), L"1") == 0) | |
pS->DefaultSeparateVDM = TRUE; | |
break; | |
default: | |
} | |
} | |
NtClose(hKey); | |
} | |
pS->IsWowTaskReady = FALSE; | |
RtlQueryRegistryValuesEx(RTL_REGISTRY_CONTROL, L"Session Manager\\NamespaceSeparation", &g_BnoRegistryConfigurationTable, NULL, NULL); | |
//did not check the return value | |
struct SECURITY_DESCRIPTOR* pBNOSd = (struct SECURITY_DESCRIPTOR*)(RtlAllocateHeap(g_BaseSrvHeap, g_BaseSrvTag, 0x400)); //x400 ??? | |
HALT_NO_MEM_IF_NULL(pBNOSd) | |
HALT_IF_FAIL(RtlCreateSecurityDescriptor(pBNOSd, SECURITY_DESCRIPTOR_REVISION)) | |
struct SECURITY_DESCRIPTOR* pBLowBoxOSd = (struct SECURITY_DESCRIPTOR*)(RtlAllocateHeap(g_BaseSrvHeap, g_BaseSrvTag, | |
sizeof(SECURITY_DESCRIPTOR))); | |
HALT_NO_MEM_IF_NULL(pBLowBoxOSd) | |
HALT_IF_FAIL(RtlCreateSecurityDescriptor(pBLowBoxOSd, SECURITY_DESCRIPTOR_REVISION)) | |
struct SECURITY_DESCRIPTOR* pBUserOSd; //[rbp+8] | |
struct ACL* pBUserODAcl = NULL; //[rbp-38h] | |
if (g_InteractiveUserNameSpaceSeparation) { | |
pBUserOSd = (struct SECURITY_DESCRIPTOR*)(RtlAllocateHeap(g_BaseSrvSharedHeap, g_BaseSrvSharedTag, sizeof(SECURITY_DESCRIPTOR))); | |
HALT_NO_MEM_IF_NULL(pBUserOSd) | |
HALT_IF_FAIL(RtlCreateSecurityDescriptor(pBUserOSd, SECURITY_DESCRIPTOR_REVISION)) | |
} | |
struct ACL* pBNODAcl; //[rbp-40h] | |
struct ACL* pRestrictedDAcl; //[rbp] | |
struct ACL* pBLowBoxODAcl; //[rbp-8h] | |
struct ACL* pBNOSAcl; //[rbp-48h] | |
HALT_IF_FAIL(CreateBaseAcls(&pBNODAcl, &pRestrictedDAcl, &pBLowBoxODAcl, &pBNOSAcl, g_InteractiveUserNameSpaceSeparation ? &pBUserODAcl : NULL)) | |
HALT_IF_FAIL(RtlSetDaclSecurityDescriptor(pBNOSd, TRUE, pBNODAcl, FALSE)) | |
HALT_IF_FAIL(RtlSetSaclSecurityDescriptor(pBNOSd, TRUE, pBNOSAcl, FALSE)) | |
HALT_IF_FAIL(RtlSetDaclSecurityDescriptor(pBLowBoxOSd, TRUE, pBLowBoxODAcl, FALSE)) | |
if (g_InteractiveUserNameSpaceSeparation) | |
HALT_IF_FAIL(RtlSetDaclSecurityDescriptor(pBUserOSd, TRUE, pBUserODAcl, FALSE)) | |
//Creating \BaseNamedObjects and \Sessions\sid\BaseNamedObjects directories | |
InitializeObjectAttributes(&oa, &uTmpBuffer, dwAttributes, NULL, pBNOSd); | |
HALT_IF_FAIL(NtCreateDirectoryObject(&g_BaseSrvNamedObjectDirectory, DIRECTORY_ALL_ACCESS | STANDARD_RIGHTS_REQUIRED, &oa)) | |
//Creating Sessions\sid\AppContainerNamedObjects directories | |
InitializeObjectAttributes(&oa, &uAppContainerNamedObjects, dwAttributes, NULL, pBLowBoxOSd); | |
HALT_IF_FAIL(NtCreateDirectoryObject(&g_BaseSrvLowBoxObjectDirectory, DIRECTORY_ALL_ACCESS | STANDARD_RIGHTS_REQUIRED, &oa)) | |
if (g_SessionId == g_ServiceSessionId) { | |
//I got the value of ObjectSessionInformation from: https://processhacker.sourceforge.io/doc/ntobapi_8h.html#a95bdc934501aaea6ec12ae1b4cd31f8a | |
HALT_IF_FAIL(NtSetInformationObject(g_BaseSrvNamedObjectDirectory, ObjectSessionInformation, NULL, 0)) | |
if (g_SessionId != 0) { | |
WCHAR szBuffer[NAMED_OBJECTS_DIR_MAX_LEN]; //[rbp+9A0h] | |
swprintf_s(szBuffer, NAMED_OBJECTS_DIR_MAX_LEN, L"%ws\\%ld\\BaseNamedObjects", L"\\Sessions", g_SessionId); | |
struct UNICODE_STRING uBaseNamedObjectsNZS; //[rbp+48h] | |
RtlInitUnicodeString(&uBaseNamedObjectsNZS, szBuffer); | |
HANDLE hBNOLink; //[rbp-70] | |
InitializeObjectAttributes(&oa, &uBaseNamedObjectsNZS, dwAttributes, NULL, pBNOSd); | |
ret = NtCreateSymbolicLinkObject(&hBNOLink, DIRECTORY_QUERY | STANDARD_RIGHTS_REQUIRED, &oa, &g_uBaseNamedObjectsNZSLink); | |
if (!NT_SUCCESS(ret)) | |
return ret; //No, the critical section is not released here | |
NtClose(hBNOLink); | |
} | |
} | |
if (g_InteractiveUserNameSpaceSeparation && g_SessionId == 0) { | |
InitializeObjectAttributes(&oa, &uBaseUserObjects, dwAttributes, NULL, pBUserOSd); | |
HALT_IF_FAIL(NtCreateDirectoryObject(&g_BaseSrvUserObjectDirectory, DIRECTORY_ALL_ACCESS | STANDARD_RIGHTS_REQUIRED, &oa)) | |
} | |
//ProcessLUIDDeviceMapsEnabled is defined here: https://processhacker.sourceforge.io/doc/ntpsapi_8h_source.html | |
DWORD bLUIDDeviceMapsEna; | |
ret = NtQueryInformationProcess(INVALID_HANDLE, ProcessLUIDDeviceMapsEnabled, &bLUIDDeviceMapsEna, 4, NULL); | |
pS->LUIDDeviceMapsEnabled = NT_SUCCESS(ret) ? bLUIDDeviceMapsEna : 0; | |
if (pS->LUIDDeviceMapsEnabled) | |
HALT_IF_FAIL(RtlInitializeCriticalSectionAndSpinCount(&g_BaseSrvDDDBSMCritSec, 0x80000000)) | |
//"Sessions\sid\BaseNamedObjects\Global" → "\BaseNamedObjects" (logon session) | |
//"\BaseNamedObjects\Global" → "\BaseNamedObjects" (for service) | |
HANDLE hBNOLink; //[rbp-70h] | |
InitializeObjectAttributes(&oa, &g_uGlobal, dwAttributes, g_BaseSrvNamedObjectDirectory, pBNOSd); | |
HALT_IF_FAIL(NtCreateSymbolicLinkObject(&hBNOLink, DIRECTORY_QUERY | STANDARD_RIGHTS_REQUIRED, &oa, &g_uBaseNamedObjectsNZSLink)) | |
if (g_SessionId == g_ServiceSessionId) | |
NtClose(hBNOLink); | |
//"Sessions\sid\BaseNamedObjects\Local" → "Sessions\sid\BaseNamedObjects" | |
//"\BaseNamedObjects\Local" → "\BaseNamedObjects" (service) | |
struct UNICODE_STRING uBNOs; // [rbp+38h] | |
RtlInitUnicodeString(&uBNOs, szBaseNamedObjects); | |
InitializeObjectAttributes(&oa, &g_uLocal, dwAttributes, g_BaseSrvNamedObjectDirectory, pBNOSd); | |
HALT_IF_FAIL(NtCreateSymbolicLinkObject(&hBNOLink, DIRECTORY_QUERY | STANDARD_RIGHTS_REQUIRED, &oa, &uBNOs)) | |
if (g_SessionId == g_ServiceSessionId) | |
NtClose(hBNOLink); | |
//"\BaseNamedObjects\AppContainerNamedObjects" → "Sessions\sid\AppContainerNamedObjects" (service) | |
//"Sessions\sid\BaseNamedObjects\AppContainerNamedObjects" → "Sessions\sid\AppContainerNamedObjects" | |
InitializeObjectAttributes(&oa, &g_uAppContainerNamedObjects, dwAttributes, g_BaseSrvNamedObjectDirectory, pBNOSd); | |
HALT_IF_FAIL(NtCreateSymbolicLinkObject(&hBNOLink, DIRECTORY_QUERY | STANDARD_RIGHTS_REQUIRED, &oa, &uAppContainerNamedObjects)) | |
if (g_SessionId == g_ServiceSessionId) | |
NtClose(hBNOLink); | |
//"\BaseNamedObjects\Session” → “\Sessions\BNOLINKS” (service) | |
//"Sessions\sid\BaseNamedObjects\Session” → “\Sessions\BNOLINKS” | |
RtlInitUnicodeString(&uTmpBuffer, L"Session"); | |
UNICODE_STRING uSessionBNOLinks; //[rbp+38h] | |
RtlInitUnicodeString(&uSessionBNOLinks, L"\\Sessions\\BNOLINKS"); | |
InitializeObjectAttributes(&oa, &uTmpBuffer, dwAttributes, g_BaseSrvNamedObjectDirectory, pBNOSd); | |
HALT_IF_FAIL(NtCreateSymbolicLinkObject(&hBNOLink, DIRECTORY_QUERY | STANDARD_RIGHTS_REQUIRED, &oa, &uSessionBNOLinks)) | |
if (g_SessionId == g_ServiceSessionId) | |
NtClose(hBNOLink); | |
RtlInitUnicodeString(&uTmpBuffer, L"Restricted"); | |
HALT_IF_FAIL(RtlSetDaclSecurityDescriptor(pBNOSd, TRUE, pRestrictedDAcl, FALSE)) | |
InitializeObjectAttributes(&oa, &uTmpBuffer, dwAttributes, g_BaseSrvNamedObjectDirectory, pBNOSd); | |
HALT_IF_FAIL(NtCreateDirectoryObject(&g_BaseSrvRestrictedObjectDirectory, DIRECTORY_ALL_ACCESS | STANDARD_RIGHTS_REQUIRED, &oa)) | |
//each of ACCESS_DENIED_ACE, SYSTEM_ALARM_ACE, SYSTEM_AUDIT_ACE has an access mask | |
//immediately following the header (similar to ACCESS_ALLOWED_ACE) | |
struct ACCESS_ALLOWED_ACE* pAce; //[rbp-50h] | |
int idx = 0; | |
ret = RtlGetAce(pBLowBoxOSd->Dacl, idx, &pAce); | |
struct ACL* pNamedPipeACL = NT_SUCCESS(ret) ? pBNOSAcl : NULL; | |
while (NT_SUCCESS(ret)) | |
{ | |
DWORD* pdw = pdwAccessMasks; | |
do { | |
ACCESS_MASK mask = pAce->Mask; | |
pAce->Mask &= 0xFFFF0000; | |
mask &= *pdw; | |
mask &= 0x0000FFFF; | |
if (mask == *pdw) | |
pAce->Mask |= *(pdw + 1); | |
pdw += 2; //sizeof(DWORD) == 4 | |
} | |
while (*pdw != 0); | |
ret = RtlGetAce(pBLowBoxOSd->Dacl, ++idx, &pAce); | |
} | |
//\Device\NamedPipe\Sessions\sid\AppContainerNamedObjects | |
WCHAR szNamedPipe[NAMED_PIPE_BUFFER_MAX_LEN]; //[rbp+BA0h] | |
struct UNICODE_STRING uNamedPipe = { 0, NAMED_PIPE_BUFFER_MAX_LEN * sizeof(WCHAR), szNamedPipe }; //[rbp+10h] | |
RtlAppendUnicodeToString(&uNamedPipe, L"\\Device\\NamedPipe"); | |
AppendUnicodeStringToString(&uNamedPipe, &uAppContainerNamedObjects); | |
struct IO_STATUS_BLOCK iosb; //[rbp+58h] | |
InitializeObjectAttributes(&oa, &uNamedPipe, FILE_ATTRIBUTE_DEVICE, NULL, pBLowBoxOSd); | |
HALT_IF_FAIL(NtCreateFile(&g_BaseSrvLowBoxPipePrefix, FILE_ALL_ACCESS, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, | |
FILE_CREATE, FILE_NON_DIRECTORY_FILE, NULL, 0)) | |
RtlFreeHeap(g_BaseSrvHeap, pBNODAcl); | |
RtlFreeHeap(g_BaseSrvHeap, pRestrictedDAcl); | |
RtlFreeHeap(g_BaseSrvHeap, pBLowBoxODAcl); | |
RtlFreeHeap(g_BaseSrvHeap, pNamedPipeACL); | |
RtlFeeHeap(g_BaseSrvHeap, pBNOSd); | |
RtlFreeHeap(g_BaseSrvHeap, pBLowBoxOSd); | |
if (pBUserODAcl != NULL) | |
RtlFreeHeap(g_BaseSrvHeap, pBUserODAcl); | |
RtlFreeHeap(g_BaseSrvHeap, pBUserOSd); | |
RtlInitializeCriticalSection(&g_BaseSrvVDMCriticalSection); | |
RtlInitializeCriticalSection(&g_BaseSrvVDMNTVDMCplCriticalSection); | |
ret = RtlInitializeCriticalSection(&g_NlsCacheCriticalSection); | |
if (NT_SUCCESS(ret)) | |
g_pNlsRegUserInfo = pS->NlsUserInfo; | |
return STATUS_SUCCESS; | |
} | |
typedef struct _INIFILE_MAPPING { | |
struct S* pHead; //0x0 | |
struct S* pSTop; //0x8 | |
struct S* pSWinIni; //0x10 | |
BYTE Reserved[8];//0x18 | |
} INIFILE_MAPPING; | |
typedef struct _S { | |
struct S* pNext; //0x0 | |
struct UNICODE_STRING uPayload; //0x8 | |
struct DWORD dwFlag; //0x18 | |
} S; | |
NTSTATUS BaseSrvSaveFileNameMapping(UNICODE_STRING* pParam1, struct S** pps); | |
INIFILE_MAPPING* g_BaseSrvIniFileMapping; | |
/** | |
* Partially Reverse-engineered BaseSrvInitializeIniFileMappings() | |
*/ | |
NTSTATUS BaseSrvInitializeIniFileMappings() | |
{ | |
struct UNICODE_STRING uIniFile; //[rbp-38h] | |
RtlInitUnicodeString(&uIniFile, L"win.ini"); | |
struct UNICODE_STRING uBuffer; //[rsp+68h] | |
RtlInitUnicodeString(&Buffer, NULL); | |
g_BaseSrvIniFileMapping = RtlAllocateHeap(g_BaseSrvSharedHeap, g_BaseSrvSharedTag | HEAP_ZERO_MEMORY | 0x40000, 0x20); | |
if (g_BaseSrvIniFileMapping == NULL) | |
return STATUS_NO_MEMORY; | |
struct BASE_STATIC_SERVER_DATA* pServerData = (BASE_STATIC_SERVER_DATA*)(g_BaseSrvpStaticServerData); | |
pServerData->IniFileMapping = g_BaseSrvIniFileMapping; | |
UNICODE_STRING uIniKeyName; //[rbp-58h] | |
RtlInitUnicodeString(&uIniKeyName, L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping"); | |
HANDLE hKey; //[rsp+38h] | |
struct OBJECT_ATTRIBUTES oa; | |
InitializeObjectAttributes(&oa, &uIniKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
NTSTATUS ret = NtOpenKey(&hKey, GENERIC_READ, &oa); | |
if (!NT_SUCCESS(ret)) | |
return ret; | |
struct UNICODE_STRING uEmptyString; //[rbp-48h] | |
RtlInitUnicodeString(&uEmptyString, NULL); | |
struct KEY_VALUE_PARTIAL_INFORMATION info; //[rbp-20h] | |
ULONG len; //[rsp+30h] | |
struct S* pS48; //[rsp+48h] | |
ret = NtQueryValueKey(hKey, &uEmptyString, KeyValuePartialInformation, &info, 0x400, &len); //0x400 is at [rsp+20h] | |
if (NT_SUCCESS(ret)) { | |
ret = BaseSrvSaveFileNameMapping(&uBuffer, &g_BaseSrvIniFileMapping->pSTop); | |
if (!NT_SUCCESS(ret)) | |
return ret; //no call to NtClose() | |
QWORD qw60; | |
ret = BaseSrvSaveAppNameMapping(g_BaseSrvIniFileMapping->pSTop, &uBuffer, &qw60); | |
if (!NT_SUCCESS(ret)) | |
return ret; //no call to NtClose() | |
ret = BaseSrvSaveVarNameMapping(g_BaseSrvIniFileMapping->pSTop, qw60, &uBuffer, &info->Data, &pS48); | |
if (NT_SUCCESS(ret)) | |
pS48->dwFlag |= 0x30000000; | |
if (ret == STATUS_NO_MEMORY) | |
return ret; | |
} //if the call to NtQueryValueKey() is unsuccessful we carry on as if nothing happened | |
struct S* ppS = (S*)(g_BaseSrvIniFileMapping); //reinterpret_cast<S*>(g_BaseSrvIniFileMapping); | |
ppS->Next = NULL; //g_BaseSrvIniFileMapping->pHead = NULL; | |
int i = 0; | |
ret = NtEnumerateKey(hKey, i++, KeyValueBasicInformation, &info, 0x400, &len); | |
while (NT_SUCCESS(ret)) { | |
struct UNICODE_STRING uKN; //[rsp+50h] | |
uKN.Buffer = (WCHAR*)(&info->Data[4]); //Apparently, data contains a flat UNICODE_STRING | |
uKN.Length = *(WORD*)(&info->Data[0]); | |
uKN.MaximumLength = *(WORD*)(&info->Data[0]); | |
HANDLE hInnerKey; //[rsp+40h] | |
InitializeObjectAttributes(&oa, &uKN, OBJ_CASE_INSENSITIVE, hKey, NULL); | |
ret = NtOpenKey(&hInnerKey, GENERIC_READ, &oa); | |
if (NT_SUCCESS(ret)) { | |
ret = BaseSrvSaveFileNameMapping(&uKN, &pS48); | |
if (NT_SUCCESS(ret)) { | |
ret = BaseSrvSaveIniFileMapping(pS48, hInnerKey); | |
if (NT_SUCCESS(ret)) { | |
if (RtlEqualUnicodeString(&pS48->uPayload, &uIniFile, TRUE)) | |
g_BaseSrvIniFileMapping->pSWinIni = pS48; | |
} | |
else | |
BaseSrvFreeFileMapping(pS48); | |
} | |
NtClose(hInnerKey); | |
} | |
if (!NT_SUCCESS(ret)) | |
break; | |
ppS->pNext = pS48; | |
ppS = pS48; | |
ret = NtEnumerateKey(hKey, i++, KeyValueBasicInformation, &info, 0x400, &len); | |
} | |
if (ret == STATUS_NO_MORE_ENTRIES) | |
ret = 0; | |
NtClose(hKey); | |
return ret; | |
} | |
/** | |
* Partially Reverse-engineered CreateBaseAcls() | |
*/ | |
// rcx rdx r8 r9 stack | |
NTSTATUS CreateBaseAcls(struct ACL** pAcl1, struct ACL** pAcl2, struct ACL** pAcl3, struct ACL** pAcl4, struct ACL** pAcl5) | |
{ | |
struct UNICODE_STRING uName; //[rbp - 0x48] | |
RtlInitUnicodeString(&uName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"); | |
HANDLE hKey; //[rbp - 0x58] | |
InitializeObjectAttributes(&oa, &uName, OBJ_CASE_INSENSITIVE, NULL, NULL) | |
NTSTATUS ret = NtOpenKey(&hKey, KEY_READ, &oa); | |
if (NT_SUCCESS(ret)) { | |
RtlInitUnicodeString(&uName, L"ProtectionMode"); | |
struct KEY_VALUE_PARTIAL_INFORMATION info; //[rbp + 0x20] | |
ULONG len; //[rbp - 0x50] | |
ret = NtQueryValueKey(hKey, &uName, KeyValuePartialInformation, &info, 0x40, &len); | |
if (NT_SUCCESS(ret) && info.Type == REG_DWORD && *(QWORD*)(info.data) != 0) | |
g_ProtectionMode = *(QWORD*)(info.data); | |
NtClose(hKey); | |
} | |
DWORD dw80m; //[rbp - 0x80] | |
if ((_PEB*)(gs:0x60)->SessionId == g_ServiceSessionId) | |
dw80m = NULL; | |
else { | |
ret = NtQuerySystemInformation(SystemObjectSecurityMode, &dw80m, 4, NULL); | |
if (!NT_SUCCESS(ret)) | |
dw80m = NULL; | |
} | |
/* Continue with Access Control Lists - related stuff */ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More info is available here.