Skip to content

Instantly share code, notes, and snippets.

@Auscitte
Created December 26, 2020 21:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Auscitte/ed807fd604d7b907ebd949628c6df725 to your computer and use it in GitHub Desktop.
Save Auscitte/ed807fd604d7b907ebd949628c6df725 to your computer and use it in GitHub Desktop.
Reverse-engineered basesrv::ServerDllInitialization()
/**
* @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 */
}
@Auscitte
Copy link
Author

Auscitte commented Jul 3, 2021

More info is available here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment