1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
// Copyright Epic Games, Inc. All Rights Reserved.
#include <zencore/windows.h>
#if ZEN_PLATFORM_WINDOWS
# include <Aclapi.h>
# include <sddl.h>
# include <stdio.h>
# include <userenv.h>
# pragma comment(lib, "userenv.lib")
# pragma comment(lib, "Advapi32.lib")
// Helper: add (or merge) an ACE for a SID on a directory
DWORD
GrantFolderAccessToSid(LPCWSTR path, PSID sid, DWORD accessMask)
{
PACL oldDacl = nullptr, newDacl = nullptr;
PSECURITY_DESCRIPTOR sd = nullptr;
DWORD err = GetNamedSecurityInfoW(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &oldDacl, nullptr, &sd);
if (err != ERROR_SUCCESS)
return err;
EXPLICIT_ACCESSW ea = {};
ea.grfAccessMode = GRANT_ACCESS;
ea.grfAccessPermissions = accessMask;
ea.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; // fine for AppContainer SIDs
ea.Trustee.ptstrName = (LPWSTR)sid;
err = SetEntriesInAclW(1, &ea, oldDacl, &newDacl);
if (err == ERROR_SUCCESS)
{
err = SetNamedSecurityInfoW((LPWSTR)path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, newDacl, nullptr);
}
if (newDacl)
LocalFree(newDacl);
if (sd)
LocalFree(sd);
return err;
}
int
zwmain()
{
// 1) Choose a stable AppContainer name for your app/sandbox
LPCWSTR containerName = L"com.epicgames.zenmaster.sandbox";
LPCWSTR displayName = L"zenmaster sandbox";
LPCWSTR description = L"Sandbox for running restricted zenserver processes";
// 1a) Create the profile once (ok if it already exists)
// Requires elevation the first time you create it. If it exists, creation will fail with ERROR_ALREADY_EXISTS—which is fine.
HRESULT hr = CreateAppContainerProfile(containerName, displayName, description, nullptr, 0, nullptr);
if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
{
hr = S_OK;
}
if (FAILED(hr))
{
wprintf(L"CreateAppContainerProfile failed: 0x%08X\n", hr);
return 1;
}
// 1b) Get the AppContainer SID
PSID appContainerSid = nullptr;
hr = DeriveAppContainerSidFromAppContainerName(containerName, &appContainerSid);
if (FAILED(hr))
{
wprintf(L"DeriveAppContainerSidFromAppContainerName failed: 0x%08X\n", hr);
return 1;
}
// 2) Grant access to specific folders (repeat for each allowed path)
// Example: allow read/write/list/execute on D:\Allowed\WorkDir
LPCWSTR allowedFolder = L"D:\\Allowed\\WorkDir";
CreateDirectoryW(allowedFolder, nullptr); // ensure it exists (optional)
DWORD fsAccess = FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | READ_CONTROL | SYNCHRONIZE;
DWORD err = GrantFolderAccessToSid(allowedFolder, appContainerSid, fsAccess);
if (err != ERROR_SUCCESS)
{
wprintf(L"GrantFolderAccessToSid failed: %lu\n", err);
FreeSid(appContainerSid);
return 1;
}
// 3) Prepare SECURITY_CAPABILITIES with the AppContainer SID (no broad capabilities)
SID_AND_ATTRIBUTES capSidAndAttrs = {}; // none for now
SECURITY_CAPABILITIES secCaps = {};
secCaps.AppContainerSid = appContainerSid;
secCaps.Capabilities = nullptr; // you can add capability SIDs here if needed
secCaps.CapabilityCount = 0;
secCaps.Reserved = 0;
// 4) Build attribute list with PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES
SIZE_T attrListSize = 0;
InitializeProcThreadAttributeList(nullptr, 1, 0, &attrListSize);
LPPROC_THREAD_ATTRIBUTE_LIST attrList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attrListSize);
if (!attrList)
{
wprintf(L"HeapAlloc failed\n");
FreeSid(appContainerSid);
return 1;
}
if (!InitializeProcThreadAttributeList(attrList, 1, 0, &attrListSize))
{
wprintf(L"InitializeProcThreadAttributeList failed: %lu\n", GetLastError());
HeapFree(GetProcessHeap(), 0, attrList);
FreeSid(appContainerSid);
return 1;
}
if (!UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES, &secCaps, sizeof(secCaps), nullptr, nullptr))
{
wprintf(L"UpdateProcThreadAttribute failed: %lu\n", GetLastError());
DeleteProcThreadAttributeList(attrList);
HeapFree(GetProcessHeap(), 0, attrList);
FreeSid(appContainerSid);
return 1;
}
// 5) Launch the target EXE in the AppContainer
STARTUPINFOEXW si = {};
si.StartupInfo.cb = sizeof(si);
si.lpAttributeList = attrList;
PROCESS_INFORMATION pi = {};
wchar_t cmdline[] = L"C:\\Path\\To\\YourChild.exe"; // child process
BOOL ok = CreateProcessW(nullptr,
cmdline,
nullptr,
nullptr,
FALSE,
EXTENDED_STARTUPINFO_PRESENT,
nullptr, // environment (null = inherit)
allowedFolder, // set working dir to an allowed folder (optional but handy)
(LPSTARTUPINFOW)&si,
&pi);
if (!ok)
{
wprintf(L"CreateProcessW failed: %lu\n", GetLastError());
}
else
{
wprintf(L"Launched pid=%lu in AppContainer.\n", pi.dwProcessId);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
// Cleanup
DeleteProcThreadAttributeList(attrList);
HeapFree(GetProcessHeap(), 0, attrList);
FreeSid(appContainerSid);
return ok ? 0 : 1;
}
#endif
|