mirror of
https://github.com/jamesstringer90/appsandbox.git
synced 2026-10-19 01:31:28 -05:00
Add ARM64 (Windows on ARM) support
Build, EV-sign, Microsoft attestation-sign, and package AppSandbox for Windows on ARM with feature parity to x64 except the vTPM and guest-state isolation (neither is supported by ARM Windows HCS). Target architecture is resolved at compile time (ASB_IS_ARM64 in disk_util.h); the supported pairing is x64 build/host/guest and ARM64 build/host/guest, so one flag drives every architecture decision. x64 behavior is unchanged throughout. Runtime: - hcs_vm.c: omit SecuritySettings (vTPM + GuestStateOnly isolation) on ARM; Secure Boot is retained. - disk_util.c: emit the arch token in every autounattend/unattend processorArchitecture attribute; on ARM, bypass the Win11 Setup hardware checks via the HKLM\SYSTEM\Setup\LabConfig keys across all VM-creation paths; select the arch-correct OpenSSH Server MSI. - d3dlayers.c / agent.c: resolve, download, and stage the arm64 D3D mapping layers for GPU-PV; stage the Vulkan ICD manifest under the arch-neutral name dzn_icd.json. Build and release pipeline: - AppSandbox.sln: add the missing AppSandboxPackage Release|ARM64 Build.0 so the VS GUI actually runs sign+package for the ARM64 config. - AppSandboxPackage.vcxproj: run make-release.ps1 for Release on x64 or ARM64, passing -Platform $(Platform). - make-release.ps1 / sign-drivers.ps1: -Platform ARM64 selects bin\Release-ARM64, the ARM64 attestation profile, and the AppSandbox-<ver>-win-arm64.zip name. Sanitize LIB before Add-Type so the .NET Framework C# compiler does not fail (CS1668) on the non-existent atlmfc\lib\ARM64 path the VS ARM64 build adds to LIB. - Directory.Build.props: disable ApiValidator only when cross-building ARM64 on a non-ARM64 host (the x86 ApiExtractor cannot read ARM64 PEs); Microsoft re-runs full API validation on every attestation submission. - Package.vcxproj: stage the vendored ARM64 devcon.exe into the ARM64 drivers output, mirroring the x64 devcon copy. - vendor/devcon/arm64/devcon.exe: add the vendored ARM64 devcon.exe, un-ignored in .gitignore mirroring the existing vendor/webview2/arm64 precedent.
This commit is contained in:
1 parent
3f4294dab1
commit
717c5b5148
16 files changed
+297
-92
No files matched your search
@@ -419,6 +419,12 @@ FodyWeavers.xsd
|
||||
!/vendor/webview2/arm64
|
||||
!/vendor/webview2/arm64/**
|
||||
|
||||
# Vendored ARM64 devcon.exe — same explicit un-ignore (the arm64/ rule above
|
||||
# otherwise catches it). The x64 devcon at vendor/devcon/devcon.exe is tracked
|
||||
# directly; the ARM64 build stages this copy into bin\Release-ARM64\drivers\.
|
||||
!/vendor/devcon/arm64
|
||||
!/vendor/devcon/arm64/**
|
||||
|
||||
# Local-only planning and notes — kept on disk for context, not pushed.
|
||||
/docs/
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ Global
|
||||
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|x64.ActiveCfg = Release|x64
|
||||
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|x64.Build.0 = Release|x64
|
||||
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|ARM64.Build.0 = Release|ARM64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -33,4 +33,19 @@
|
||||
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' != 'true'">
|
||||
<SkipPackageVerification>true</SkipPackageVerification>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
Same class of problem as the InfVerif skip above, for ARM64 driver builds.
|
||||
ARM64 drivers must be Universal, so the WDK runs ApiValidator on them — but it
|
||||
defaults to the x86 ApiExtractor (aitstatic.exe), which cannot read an ARM64 PE
|
||||
and fails with exit code 193 (ERROR_BAD_EXE_FORMAT) when cross-building ARM64 on
|
||||
a non-ARM64 host (e.g. the x64 dev/CI box). Disable it ONLY for that cross-build
|
||||
case: native ARM64 hosts and every x64 build still run it, and Microsoft re-runs
|
||||
full API validation on every attestation submission, so nothing ships unchecked.
|
||||
(Checks both PROCESSOR_ARCHITECTURE and ...ITEW6432 so an emulated x64 MSBuild on
|
||||
an ARM64 host is correctly treated as "host can validate".)
|
||||
-->
|
||||
<PropertyGroup Condition="'$(Platform)' == 'ARM64' And '$(PROCESSOR_ARCHITECTURE)' != 'ARM64' And '$(PROCESSOR_ARCHITEW6432)' != 'ARM64'">
|
||||
<ApiValidator_Enable>false</ApiValidator_Enable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+38
-22
@@ -7,8 +7,8 @@
|
||||
* 2. Windows Update FE3 client protocol over WinHTTP (HTTPS):
|
||||
* GetCookie -> SyncUpdates(filtered by WuCategoryId)
|
||||
* -> GetExtendedUpdateInfo2 -> time-limited CDN url.
|
||||
* The x64 package is located by identifier in the response, so version
|
||||
* changes / extra updates do not break resolution.
|
||||
* The package for the build architecture is located by identifier in the
|
||||
* response, so version changes / extra updates do not break resolution.
|
||||
* 3. Download the .appx (a zip) from the Microsoft CDN.
|
||||
* 4. Extract with the in-box tar.exe (libarchive handles zip natively).
|
||||
* 5. Copy the mapping-layer files into <exe_dir>\d3dlayers\ (cached).
|
||||
@@ -36,17 +36,33 @@
|
||||
#define DL_FE3_PATH_SECURED L"/ClientWebService/client.asmx/secured"
|
||||
#define DL_SOAP_HEADERS L"Content-Type: application/soap+xml; charset=utf-8\r\n"
|
||||
#define DL_CACHE_SUBDIR L"d3dlayers"
|
||||
#define DL_APPX_NAME L"D3DMappingLayers_x64.appx"
|
||||
/* The D3D Mapping Layers Store package ships a per-architecture .appx; pick the
|
||||
one matching the guest (= build) architecture. The host downloads it, so on
|
||||
the ARM64 build we fetch and stage the arm64 layers for the arm64 guest. */
|
||||
#if defined(_M_ARM64)
|
||||
# define DL_ARCH L"arm64"
|
||||
# define DL_ARCH_A "arm64"
|
||||
#else
|
||||
# define DL_ARCH L"x64"
|
||||
# define DL_ARCH_A "x64"
|
||||
#endif
|
||||
#define DL_APPX_NAME L"D3DMappingLayers_" DL_ARCH L".appx"
|
||||
/* Substring identifying the package file in the SyncUpdates response — the
|
||||
InstallerSpecificIdentifier contains e.g. "..._x64_..." / "..._arm64_...". */
|
||||
#define DL_ISI_ARCH "_" DL_ARCH_A "_"
|
||||
#define DL_MAX_ROUNDS 5 /* FE3 faults intermittently; retry the chain */
|
||||
|
||||
/* Files to extract: dest leaf name <- source path inside the .appx. */
|
||||
/* Files to extract: dest leaf name <- source path inside the .appx. The arch
|
||||
subfolder inside the package (x64\ / arm64\) is selected by DL_ARCH; the
|
||||
Vulkan ICD manifest is staged under the arch-neutral name dzn_icd.json so the
|
||||
guest agent references one fixed filename regardless of architecture. */
|
||||
static const wchar_t *const DL_DST[] = {
|
||||
L"OpenGLOn12.dll", L"dxil.dll", L"OpenCLOn12.dll",
|
||||
L"clon12compiler.dll", L"vulkan_dzn.dll", L"dzn_icd.x64.json"
|
||||
L"clon12compiler.dll", L"vulkan_dzn.dll", L"dzn_icd.json"
|
||||
};
|
||||
static const wchar_t *const DL_SRC[] = {
|
||||
L"x64\\OpenGLOn12.dll", L"x64\\dxil.dll", L"x64\\OpenCLOn12.dll",
|
||||
L"x64\\clon12compiler.dll", L"x64\\vulkan_dzn.dll", L"dzn_icd.x64.json"
|
||||
DL_ARCH L"\\OpenGLOn12.dll", DL_ARCH L"\\dxil.dll", DL_ARCH L"\\OpenCLOn12.dll",
|
||||
DL_ARCH L"\\clon12compiler.dll", DL_ARCH L"\\vulkan_dzn.dll", L"dzn_icd." DL_ARCH L".json"
|
||||
};
|
||||
#define DL_NFILES ((int)(sizeof(DL_DST)/sizeof(DL_DST[0])))
|
||||
|
||||
@@ -318,10 +334,10 @@ done:
|
||||
|
||||
/* ---- response parsing ---- */
|
||||
|
||||
/* Find the x64 D3DMappingLayers .appx in a (decoded) SyncUpdates response and
|
||||
* resolve its UpdateIdentity by joining the file's enclosing <Update> <ID> to
|
||||
* the matching NewUpdates <UpdateInfo> <UpdateIdentity>. */
|
||||
static BOOL parse_x64_leaf(const char *buf, char *uid, size_t uid_cch,
|
||||
/* Find the D3DMappingLayers .appx matching the build architecture in a
|
||||
* (decoded) SyncUpdates response and resolve its UpdateIdentity by joining the
|
||||
* file's enclosing <Update> <ID> to the matching NewUpdates <UpdateIdentity>. */
|
||||
static BOOL parse_leaf(const char *buf, char *uid, size_t uid_cch,
|
||||
char *rev, size_t rev_cch, char *digest, size_t dig_cch)
|
||||
{
|
||||
const char *p = buf;
|
||||
@@ -340,11 +356,11 @@ static BOOL parse_x64_leaf(const char *buf, char *uid, size_t uid_cch,
|
||||
!attr_in(tag, "InstallerSpecificIdentifier", isi, sizeof(isi))) {
|
||||
p = gt + 1; continue;
|
||||
}
|
||||
if (!strstr(isi, "D3DMappingLayers") || !strstr(isi, "_x64_") ||
|
||||
if (!strstr(isi, "D3DMappingLayers") || !strstr(isi, DL_ISI_ARCH) ||
|
||||
!strstr(fn, ".appx")) {
|
||||
p = gt + 1; continue;
|
||||
}
|
||||
/* matched the x64 package file */
|
||||
/* matched the package file for this architecture */
|
||||
attr_in(tag, "Digest", digest, dig_cch);
|
||||
|
||||
/* nearest <ID>N</ID> before this <File> = the ExtendedUpdateInfo id */
|
||||
@@ -404,7 +420,7 @@ static BOOL parse_file_url(const char *buf, const char *digest,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Resolve the x64 package CDN url from Microsoft. Returns 0 on success. */
|
||||
/* Resolve the (build-architecture) package CDN url from Microsoft. Returns 0 on success. */
|
||||
static int resolve_url(const char *wucatid, wchar_t *url_out, size_t url_cch)
|
||||
{
|
||||
int round, rc = -1;
|
||||
@@ -438,8 +454,8 @@ static int resolve_url(const char *wucatid, wchar_t *url_out, size_t url_cch)
|
||||
if (t1) { sync_req = str_replace_all(t1, "%CATID%", wucatid); free(t1); }
|
||||
if (sync_req) { sync_resp = fe3_post(DL_FE3_PATH, sync_req); free(sync_req); }
|
||||
if (sync_resp) html_unescape(sync_resp);
|
||||
if (!sync_resp || !parse_x64_leaf(sync_resp, uid, sizeof(uid), rev, sizeof(rev),
|
||||
digest, sizeof(digest))) {
|
||||
if (!sync_resp || !parse_leaf(sync_resp, uid, sizeof(uid), rev, sizeof(rev),
|
||||
digest, sizeof(digest))) {
|
||||
free(sync_resp); Sleep(1500); continue;
|
||||
}
|
||||
free(sync_resp);
|
||||
@@ -462,7 +478,7 @@ static int resolve_url(const char *wucatid, wchar_t *url_out, size_t url_cch)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The shipped dzn_icd.x64.json gives library_path as "x64\vulkan_dzn.dll",
|
||||
/* The shipped dzn_icd.<arch>.json gives library_path as "<arch>\vulkan_dzn.dll",
|
||||
* relative to the manifest. The layers are staged into one flat directory, so
|
||||
* point library_path at the DLL's fully-qualified location in the guest.
|
||||
*
|
||||
@@ -479,7 +495,7 @@ static void rewrite_dzn_json(const wchar_t *dir)
|
||||
char *buf, *fixed;
|
||||
size_t rd;
|
||||
|
||||
swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.x64.json", dir);
|
||||
swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.json", dir);
|
||||
if (_wfopen_s(&f, path, L"rb") != 0 || !f) return;
|
||||
fseek(f, 0, SEEK_END); sz = ftell(f); fseek(f, 0, SEEK_SET);
|
||||
if (sz <= 0 || sz > 65536) { fclose(f); return; }
|
||||
@@ -489,10 +505,10 @@ static void rewrite_dzn_json(const wchar_t *dir)
|
||||
fclose(f);
|
||||
buf[rd] = 0;
|
||||
|
||||
/* On disk the JSON value is the 18 bytes x64\\vulkan_dzn.dll (two
|
||||
* backslash characters, JSON-escaped). Match that exactly and replace it
|
||||
* with the absolute guest path (each backslash JSON-escaped). */
|
||||
fixed = str_replace_all(buf, "x64\\\\vulkan_dzn.dll",
|
||||
/* On disk the JSON value is <arch>\\vulkan_dzn.dll (two backslash
|
||||
* characters, JSON-escaped). Match that exactly and replace it with the
|
||||
* absolute guest path (each backslash JSON-escaped). */
|
||||
fixed = str_replace_all(buf, DL_ARCH_A "\\\\vulkan_dzn.dll",
|
||||
"C:\\\\Windows\\\\AppSandbox\\\\d3dlayers\\\\vulkan_dzn.dll");
|
||||
free(buf);
|
||||
if (!fixed) return;
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
* OpenCLOn12.dll OpenCL -> D3D12
|
||||
* clon12compiler.dll OpenCL compiler backend
|
||||
* vulkan_dzn.dll Vulkan (Mesa Dozen) -> D3D12
|
||||
* dzn_icd.x64.json Vulkan ICD manifest (points at vulkan_dzn.dll)
|
||||
* dzn_icd.json Vulkan ICD manifest (points at vulkan_dzn.dll); staged under
|
||||
* this arch-neutral name from the package's dzn_icd.<arch>.json
|
||||
*/
|
||||
|
||||
#ifndef D3DLAYERS_H
|
||||
|
||||
+121
-32
@@ -15,8 +15,27 @@
|
||||
|
||||
#include <urlmon.h>
|
||||
|
||||
#define SSH_MSI_URL L"https://github.com/PowerShell/Win32-OpenSSH/releases/download/10.0.0.0p2-Preview/OpenSSH-Win64-v10.0.0.0.msi"
|
||||
#define SSH_MSI_NAME L"OpenSSH-Win64-v10.0.0.0.msi"
|
||||
/* Architecture token written into every autounattend/unattend component's
|
||||
processorArchitecture attribute. Matches the guest, which is the build arch
|
||||
(see ASB_IS_ARM64 in disk_util.h). */
|
||||
#if ASB_IS_ARM64
|
||||
# define ASB_UA_ARCH L"arm64"
|
||||
#else
|
||||
# define ASB_UA_ARCH L"amd64"
|
||||
#endif
|
||||
|
||||
/* OpenSSH Server MSI, staged into the guest and installed by SetupComplete.cmd.
|
||||
The Win32-OpenSSH release ships a per-architecture MSI; pick the one matching
|
||||
the guest (= build) architecture so it installs on an ARM64 guest. */
|
||||
#if ASB_IS_ARM64
|
||||
# define SSH_MSI_URL L"https://github.com/PowerShell/Win32-OpenSSH/releases/download/10.0.0.0p2-Preview/OpenSSH-ARM64-v10.0.0.0.msi"
|
||||
# define SSH_MSI_NAME L"OpenSSH-ARM64-v10.0.0.0.msi"
|
||||
# define SSH_MSI_NAME_A "OpenSSH-ARM64-v10.0.0.0.msi" /* narrow, for the cmd scripts */
|
||||
#else
|
||||
# define SSH_MSI_URL L"https://github.com/PowerShell/Win32-OpenSSH/releases/download/10.0.0.0p2-Preview/OpenSSH-Win64-v10.0.0.0.msi"
|
||||
# define SSH_MSI_NAME L"OpenSSH-Win64-v10.0.0.0.msi"
|
||||
# define SSH_MSI_NAME_A "OpenSSH-Win64-v10.0.0.0.msi" /* narrow, for the cmd scripts */
|
||||
#endif
|
||||
|
||||
static const GUID VHDX_VENDOR_MS = {
|
||||
0xec984aec, 0xa0f9, 0x47e9,
|
||||
@@ -212,6 +231,32 @@ static const wchar_t *lang_to_input_locale(const wchar_t *lang)
|
||||
return L"0409:00000409"; /* fallback */
|
||||
}
|
||||
|
||||
#if ASB_IS_ARM64
|
||||
/* Emit RunSynchronousCommand entries (inside an already-open <RunSynchronous>)
|
||||
that disable the Windows 11 setup hardware requirement checks by writing the
|
||||
HKLM\SYSTEM\Setup\LabConfig bypass keys. ARM Windows HCS exposes no vTPM, so
|
||||
without these Windows Setup's appraiser would refuse to install. `order` is
|
||||
the next <Order> value to use; returns the order after the last entry.
|
||||
ARM-only: on x64 the guest gets a real vTPM from HCS and needs no bypass. */
|
||||
static int write_tpm_bypass_commands(FILE *f, int order)
|
||||
{
|
||||
static const wchar_t *const keys[] = {
|
||||
L"BypassTPMCheck", L"BypassSecureBootCheck", L"BypassRAMCheck",
|
||||
L"BypassStorageCheck", L"BypassCPUCheck"
|
||||
};
|
||||
int i;
|
||||
for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); i++) {
|
||||
fwprintf(f,
|
||||
L" <RunSynchronousCommand wcm:action=\"add\">\n"
|
||||
L" <Order>%d</Order>\n"
|
||||
L" <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v %s /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n",
|
||||
order++, keys[i]);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate autounattend.xml with credentials and VM name substituted.
|
||||
If is_template, adds SecureStartup-FilterDriver to disable BitLocker
|
||||
(required for sysprep). */
|
||||
@@ -238,7 +283,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
L"\n"
|
||||
L" <settings pass=\"windowsPE\">\n"
|
||||
L" <component name=\"Microsoft-Windows-International-Core-WinPE\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <SetupUILanguage><UILanguage>%s</UILanguage></SetupUILanguage>\n"
|
||||
@@ -248,7 +293,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
L" <UserLocale>%s</UserLocale>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <UserData>\n"
|
||||
@@ -274,19 +319,32 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
L" <ModifyPartition wcm:action=\"add\"><Order>2</Order><PartitionID>2</PartitionID></ModifyPartition>\n"
|
||||
L" <ModifyPartition wcm:action=\"add\"><Order>3</Order><PartitionID>3</PartitionID><Format>NTFS</Format><Label>Windows</Label><Letter>C</Letter></ModifyPartition>\n"
|
||||
L" </ModifyPartitions>\n"
|
||||
L" </Disk></DiskConfiguration>\n"
|
||||
L" </Disk></DiskConfiguration>\n",
|
||||
lang, lang_to_input_locale(lang), lang, lang, lang);
|
||||
|
||||
/* ARM only: disable the Windows 11 Setup hardware checks via the
|
||||
HKLM\SYSTEM\Setup\LabConfig keys, from a windowsPE RunSynchronous in the
|
||||
Setup component (runs before the appraiser). ARM Windows HCS has no vTPM,
|
||||
so Setup would otherwise refuse to install. */
|
||||
#if ASB_IS_ARM64
|
||||
fwprintf(f, L" <RunSynchronous>\n");
|
||||
write_tpm_bypass_commands(f, 1);
|
||||
fwprintf(f, L" </RunSynchronous>\n");
|
||||
#endif
|
||||
|
||||
fwprintf(f,
|
||||
L" </component>\n"
|
||||
L" </settings>\n"
|
||||
L"\n"
|
||||
L" <settings pass=\"specialize\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <ComputerName>%s</ComputerName>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <RunSynchronous>\n"
|
||||
@@ -294,7 +352,6 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
L" <Order>1</Order>\n"
|
||||
L" <Path>reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE /v BypassNRO /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n",
|
||||
lang, lang_to_input_locale(lang), lang, lang, lang,
|
||||
comp_name);
|
||||
|
||||
{
|
||||
@@ -336,7 +393,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
if (is_template) {
|
||||
fwprintf(f,
|
||||
L" <component name=\"Microsoft-Windows-SecureStartup-FilterDriver\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <PreventDeviceEncryption>true</PreventDeviceEncryption>\n"
|
||||
@@ -353,7 +410,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
fwprintf(f,
|
||||
L" <settings pass=\"oobeSystem\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <Reseal>\n"
|
||||
@@ -364,7 +421,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
L"\n"
|
||||
L" <settings pass=\"auditUser\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <RunSynchronous>\n"
|
||||
@@ -382,7 +439,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
fwprintf(f,
|
||||
L" <settings pass=\"oobeSystem\">\n"
|
||||
L" <component name=\"Microsoft-Windows-International-Core\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <InputLocale>%s</InputLocale>\n"
|
||||
@@ -391,7 +448,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
|
||||
L" <UserLocale>%s</UserLocale>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <OOBE>\n"
|
||||
@@ -730,9 +787,9 @@ HRESULT iso_create_resources(const wchar_t *iso_path,
|
||||
if (ssh_enabled) {
|
||||
fputs(
|
||||
"REM Install OpenSSH Server\r\n"
|
||||
"if exist \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" (\r\n"
|
||||
"if exist \"%ISODRV%\\" SSH_MSI_NAME_A "\" (\r\n"
|
||||
" echo [SSH] Installing OpenSSH Server... >> \"%LOG%\"\r\n"
|
||||
" msiexec /i \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
|
||||
" msiexec /i \"%ISODRV%\\" SSH_MSI_NAME_A "\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
|
||||
" echo [SSH] msiexec exit code: %errorlevel% >> \"%LOG%\"\r\n"
|
||||
" sc config sshd start= auto >> \"%LOG%\" 2>&1\r\n"
|
||||
" net start sshd >> \"%LOG%\" 2>&1\r\n"
|
||||
@@ -791,13 +848,13 @@ static BOOL generate_unattend_instance(const wchar_t *output_path,
|
||||
L"\n"
|
||||
L" <settings pass=\"specialize\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <ComputerName>%s</ComputerName>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <RunSynchronous>\n"
|
||||
@@ -821,13 +878,37 @@ static BOOL generate_unattend_instance(const wchar_t *output_path,
|
||||
L" <Order>5</Order>\n"
|
||||
L" <Path>cmd /c for %%d in (D E F G H I J) do @if exist %%d:\\SetupComplete.cmd copy /Y %%d:\\SetupComplete.cmd C:\\Windows\\Setup\\Scripts\\</Path>\n"
|
||||
L" </RunSynchronousCommand>\n"
|
||||
#if ASB_IS_ARM64
|
||||
/* ARM only: Windows 11 LabConfig hardware-check bypass keys (no vTPM on
|
||||
ARM). Orders 6-10 continue this component's RunSynchronous. */
|
||||
L" <RunSynchronousCommand wcm:action=\"add\">\n"
|
||||
L" <Order>6</Order>\n"
|
||||
L" <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n"
|
||||
L" <RunSynchronousCommand wcm:action=\"add\">\n"
|
||||
L" <Order>7</Order>\n"
|
||||
L" <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n"
|
||||
L" <RunSynchronousCommand wcm:action=\"add\">\n"
|
||||
L" <Order>8</Order>\n"
|
||||
L" <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n"
|
||||
L" <RunSynchronousCommand wcm:action=\"add\">\n"
|
||||
L" <Order>9</Order>\n"
|
||||
L" <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassStorageCheck /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n"
|
||||
L" <RunSynchronousCommand wcm:action=\"add\">\n"
|
||||
L" <Order>10</Order>\n"
|
||||
L" <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassCPUCheck /t REG_DWORD /d 1 /f</Path>\n"
|
||||
L" </RunSynchronousCommand>\n"
|
||||
#endif
|
||||
L" </RunSynchronous>\n"
|
||||
L" </component>\n"
|
||||
L" </settings>\n"
|
||||
L"\n"
|
||||
L" <settings pass=\"oobeSystem\">\n"
|
||||
L" <component name=\"Microsoft-Windows-International-Core\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <InputLocale>%s</InputLocale>\n"
|
||||
@@ -836,7 +917,7 @@ static BOOL generate_unattend_instance(const wchar_t *output_path,
|
||||
L" <UserLocale>%s</UserLocale>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <OOBE>\n"
|
||||
@@ -1204,9 +1285,9 @@ static void stage_agent_and_setup(const wchar_t *staging, const wchar_t *res_dir
|
||||
if (ssh_enabled) {
|
||||
fputs(
|
||||
"REM Install OpenSSH Server\r\n"
|
||||
"if exist \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" (\r\n"
|
||||
"if exist \"%ISODRV%\\" SSH_MSI_NAME_A "\" (\r\n"
|
||||
" echo [SSH] Installing OpenSSH Server... >> \"%LOG%\"\r\n"
|
||||
" msiexec /i \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
|
||||
" msiexec /i \"%ISODRV%\\" SSH_MSI_NAME_A "\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
|
||||
" echo [SSH] msiexec exit code: %errorlevel% >> \"%LOG%\"\r\n"
|
||||
" sc config sshd start= auto >> \"%LOG%\" 2>&1\r\n"
|
||||
" net start sshd >> \"%LOG%\" 2>&1\r\n"
|
||||
@@ -1329,13 +1410,13 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
|
||||
L"\n"
|
||||
L" <settings pass=\"specialize\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <ComputerName>%s</ComputerName>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <RunSynchronous>\n"
|
||||
@@ -1364,6 +1445,10 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
|
||||
L" <Path>bcdedit /set testsigning on</Path>\n"
|
||||
L" </RunSynchronousCommand>\n", order++);
|
||||
}
|
||||
#if ASB_IS_ARM64
|
||||
/* ARM only: Windows 11 LabConfig hardware-check bypass keys (no vTPM on ARM). */
|
||||
write_tpm_bypass_commands(f, order);
|
||||
#endif
|
||||
}
|
||||
|
||||
fwprintf(f,
|
||||
@@ -1376,7 +1461,7 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
|
||||
fwprintf(f,
|
||||
L" <settings pass=\"oobeSystem\">\n"
|
||||
L" <component name=\"Microsoft-Windows-International-Core\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <InputLocale>%s</InputLocale>\n"
|
||||
@@ -1385,7 +1470,7 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
|
||||
L" <UserLocale>%s</UserLocale>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <OOBE>\n"
|
||||
@@ -1455,13 +1540,13 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
|
||||
L"\n"
|
||||
L" <settings pass=\"specialize\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Shell-Setup\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <ComputerName>%s</ComputerName>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <RunSynchronous>\n"
|
||||
@@ -1490,13 +1575,17 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
|
||||
L" <Path>bcdedit /set testsigning on</Path>\n"
|
||||
L" </RunSynchronousCommand>\n", order++);
|
||||
}
|
||||
#if ASB_IS_ARM64
|
||||
/* ARM only: Windows 11 LabConfig hardware-check bypass keys (no vTPM on ARM). */
|
||||
write_tpm_bypass_commands(f, order);
|
||||
#endif
|
||||
}
|
||||
|
||||
fwprintf(f,
|
||||
L" </RunSynchronous>\n"
|
||||
L" </component>\n"
|
||||
L" <component name=\"Microsoft-Windows-SecureStartup-FilterDriver\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <PreventDeviceEncryption>true</PreventDeviceEncryption>\n"
|
||||
@@ -1509,7 +1598,7 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
|
||||
fwprintf(f,
|
||||
L" <settings pass=\"oobeSystem\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <Reseal>\n"
|
||||
@@ -1520,7 +1609,7 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
|
||||
L"\n"
|
||||
L" <settings pass=\"auditUser\">\n"
|
||||
L" <component name=\"Microsoft-Windows-Deployment\"\n"
|
||||
L" processorArchitecture=\"amd64\"\n"
|
||||
L" processorArchitecture=\"" ASB_UA_ARCH L"\"\n"
|
||||
L" publicKeyToken=\"31bf3856ad364e35\"\n"
|
||||
L" language=\"neutral\" versionScope=\"nonSxS\">\n"
|
||||
L" <RunSynchronous>\n"
|
||||
@@ -1604,9 +1693,9 @@ BOOL generate_vhdx_setupcomplete(const wchar_t *output_path, BOOL ssh_enabled)
|
||||
fputs(
|
||||
"REM Install OpenSSH Server\r\n"
|
||||
"set SSHDIR=%SystemRoot%\\AppSandbox\r\n"
|
||||
"if exist \"%SSHDIR%\\OpenSSH-Win64-v10.0.0.0.msi\" (\r\n"
|
||||
"if exist \"%SSHDIR%\\" SSH_MSI_NAME_A "\" (\r\n"
|
||||
" echo [SSH] Installing OpenSSH Server... >> \"%LOG%\"\r\n"
|
||||
" msiexec /i \"%SSHDIR%\\OpenSSH-Win64-v10.0.0.0.msi\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
|
||||
" msiexec /i \"%SSHDIR%\\" SSH_MSI_NAME_A "\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
|
||||
" echo [SSH] msiexec exit code: %errorlevel% >> \"%LOG%\"\r\n"
|
||||
" sc config sshd start= auto >> \"%LOG%\" 2>&1\r\n"
|
||||
" net start sshd >> \"%LOG%\" 2>&1\r\n"
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
/* Target architecture, resolved at compile time. The supported pairing is
|
||||
x64 build -> x64 host -> x64 guest, and ARM64 build -> ARM host -> ARM guest
|
||||
(no cross-arch installs), so build arch == host arch == guest arch and a
|
||||
single compile-time flag drives every architecture decision: the HCS
|
||||
SecuritySettings (ARM Windows HCS has no vTPM / guest-state isolation), the
|
||||
autounattend processorArchitecture token + Win11 TPM-check bypass, the
|
||||
OpenSSH MSI variant, and the GPU-PV D3D mapping-layer package. */
|
||||
#if defined(_M_ARM64)
|
||||
# define ASB_IS_ARM64 1
|
||||
#else
|
||||
# define ASB_IS_ARM64 0
|
||||
#endif
|
||||
|
||||
/* Create a new dynamically-expanding VHDX file.
|
||||
size_gb: maximum virtual size in gigabytes. */
|
||||
HRESULT vhdx_create(const wchar_t *path, ULONGLONG size_gb);
|
||||
|
||||
@@ -61,7 +61,7 @@ BOOL gpu_append_lxsslib_share(GpuDriverShareList *list);
|
||||
|
||||
/* Append a synthetic Plan9 share (name "AppSandbox.GlLayers") exposing the
|
||||
host directory that holds the D3D mapping layers (OpenGLOn12/dxil/OpenCLOn12/
|
||||
clon12compiler/vulkan_dzn + dzn_icd.x64.json) and the OpenGL ICD shim. The
|
||||
clon12compiler/vulkan_dzn + dzn_icd.json) and the OpenGL ICD shim. The
|
||||
Windows guest agent copies this share AFTER the GPU driver shares and then
|
||||
provisions it (stages dxil to System32, sets AppInit_DLLs + the Khronos
|
||||
OpenCL/Vulkan ICD registry keys). guest_path is C:\Windows\AppSandbox\d3dlayers.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "hcs_vm.h"
|
||||
#include "vm_agent.h"
|
||||
#include "ui.h"
|
||||
#include "disk_util.h" /* ASB_IS_ARM64 */
|
||||
#include <stdio.h>
|
||||
#include <sddl.h>
|
||||
#include <aclapi.h>
|
||||
@@ -1041,9 +1042,14 @@ BOOL hcs_build_vm_json(const VmConfig *config, const wchar_t *endpoint_guid,
|
||||
name_esc);
|
||||
}
|
||||
|
||||
/* SecuritySettings with TPM — enabled for all Windows VMs.
|
||||
TPM does not block test-signed drivers; only Secure Boot does. */
|
||||
/* SecuritySettings (virtual TPM + GuestStateOnly isolation) — x64 only.
|
||||
The vTPM is required by the Windows 11 guest on x64. ARM Windows HCS
|
||||
supports neither a vTPM nor guest-state isolation, so the whole block is
|
||||
emitted only on x64. The Windows 11 Setup TPM/SecureBoot requirement
|
||||
check is bypassed via the autounattend LabConfig keys (see
|
||||
generate_autounattend in disk_util.c). */
|
||||
security_section[0] = L'\0';
|
||||
#if !ASB_IS_ARM64
|
||||
if (is_windows) {
|
||||
wcscpy_s(security_section, 512,
|
||||
L",\"SecuritySettings\":{"
|
||||
@@ -1051,6 +1057,7 @@ BOOL hcs_build_vm_json(const VmConfig *config, const wchar_t *endpoint_guid,
|
||||
L"\"Isolation\":{\"IsolationType\":\"GuestStateOnly\"}"
|
||||
L"}");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Plan9 shares — GPU driver directories for agent p9copy.
|
||||
Skip for template VMs (no GPU assigned during template creation). */
|
||||
|
||||
+1
-1
@@ -454,7 +454,7 @@ static void gl_provision(const wchar_t *dir)
|
||||
The manifest's library_path is the absolute guest DLL path (set host-side
|
||||
in d3dlayers.c) so the loader can LoadLibraryEx it under
|
||||
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, which requires a fully-qualified path. */
|
||||
swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.x64.json", dir);
|
||||
swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.json", dir);
|
||||
if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) {
|
||||
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Khronos\\Vulkan\\Drivers",
|
||||
0, NULL, 0, KEY_SET_VALUE, NULL, &key, NULL) == ERROR_SUCCESS) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
AppSandboxPackage — a Utility project that runs tools\sign\make-release.ps1 -NoBuild
|
||||
AFTER the solution builds, but ONLY for Release|x64.
|
||||
-Platform $(Platform) AFTER the solution builds, for Release|x64 or Release|ARM64.
|
||||
|
||||
In AppSandbox.sln this project is set to depend on every other project, so Visual
|
||||
Studio builds it LAST and SKIPS it entirely if any project failed to build — i.e.
|
||||
@@ -54,9 +54,9 @@
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
<Target Name="AppSandboxSignAndPackage" AfterTargets="Build"
|
||||
Condition="'$(Configuration)|$(Platform)'=='Release|x64' And '$(ASB_SKIP_PACKAGE_PROJECT)' != '1'">
|
||||
<Message Importance="high" Text="==== AppSandbox: make-release.ps1 -NoBuild (sign + package) ====" />
|
||||
<Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$(SolutionDir)tools\sign\make-release.ps1" -NoBuild" />
|
||||
Condition="'$(Configuration)'=='Release' And ('$(Platform)'=='x64' Or '$(Platform)'=='ARM64') And '$(ASB_SKIP_PACKAGE_PROJECT)' != '1'">
|
||||
<Message Importance="high" Text="==== AppSandbox: make-release.ps1 -NoBuild -Platform $(Platform) (sign + package) ====" />
|
||||
<Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$(SolutionDir)tools\sign\make-release.ps1" -NoBuild -Platform $(Platform)" />
|
||||
</Target>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
+40
-18
@@ -36,6 +36,8 @@ via attestation through Partner Center.
|
||||
associated** to it (Partner Center adds the cert under the hardware account).
|
||||
3. **Azure AD (Entra) app** for the Hardware submission API (below).
|
||||
4. Windows SDK + WDK installed (`signtool.exe`, `infverif.exe`, `makecab.exe`).
|
||||
5. For **ARM64** releases: the **ARM64 MSVC + WDK build tools** (cross-building from an x64
|
||||
host is fully supported — no ARM device is needed to build, sign, or submit).
|
||||
|
||||
### 2a. Create the Entra app (for the submission API)
|
||||
|
||||
@@ -77,11 +79,11 @@ tools\sign\test-hardware-api.ps1
|
||||
| File | Committed? | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `partner-center.local.json` | **no** (git-ignored) | Entra API creds (tenant/client/secret) |
|
||||
| `submission-config.json` | yes | Product names, OS signature targets, driver→cab mapping, timestamp URL, optional `evCertThumbprint` |
|
||||
| `submission-config.json` | yes | Product names, OS signature targets, driver→cab mapping, timestamp URL, optional `evCertThumbprint` (x64 values; `sign-drivers.ps1 -Platform ARM64` derives the ARM64 equivalents at sign time) |
|
||||
| `test-hardware-api.ps1` | yes | Validate creds + Hardware roles |
|
||||
| `sign-drivers.ps1` | yes | EV-sign drivers (binary + catalog + cab) → attestation submit (both in parallel) → download MS-signed |
|
||||
| `make-release.ps1` | yes | Build → (if YubiKey) sign all binaries + driver-attest + ZIP; else skip |
|
||||
| `AppSandboxPackage.vcxproj` | yes | Utility project; runs `make-release.ps1 -NoBuild` after a successful `Release|x64` VS build |
|
||||
| `AppSandboxPackage.vcxproj` | yes | Utility project; runs `make-release.ps1 -NoBuild -Platform $(Platform)` after a successful `Release|x64` **or** `Release|ARM64` VS build |
|
||||
| `SIGNING.md` | yes | This document |
|
||||
|
||||
Versioning lives at the **repo root**: `Directory.Build.props` (the version numbers + the
|
||||
@@ -97,18 +99,20 @@ that is inherent to EV signing — so the goal is "keep it out of source," not s
|
||||
## 4. Running it
|
||||
|
||||
### From Visual Studio (the usual path)
|
||||
**Build Solution** in **Release|x64**. The `AppSandboxPackage` project depends on every
|
||||
other project, so Visual Studio builds it **last** and — via its `AfterTargets="Build"`
|
||||
step — runs `make-release.ps1 -NoBuild`, **but only if every project built successfully**
|
||||
(a failed build skips it, so no signing/packaging on a broken build). With no EV YubiKey
|
||||
it no-ops (build only); with the token in it signs + attestation-signs + zips. Debug
|
||||
builds and Release|ARM64 never trigger it.
|
||||
**Build Solution** in **Release|x64** or **Release|ARM64**. The `AppSandboxPackage` project
|
||||
depends on every other project, so Visual Studio builds it **last** and — via its
|
||||
`AfterTargets="Build"` step — runs `make-release.ps1 -NoBuild -Platform $(Platform)`, **but
|
||||
only if every project built successfully** (a failed build skips it, so no signing/packaging
|
||||
on a broken build). With no EV YubiKey it no-ops (build only); with the token in it signs +
|
||||
attestation-signs + zips (ARM64 outputs to `bin\Release-ARM64`, with its ARM64 attestation
|
||||
profile and `-win-arm64.zip`). Debug builds never trigger it.
|
||||
|
||||
### Normal release (the one command)
|
||||
```powershell
|
||||
tools\sign\make-release.ps1
|
||||
tools\sign\make-release.ps1 # x64
|
||||
tools\sign\make-release.ps1 -Platform ARM64 # Windows on ARM
|
||||
```
|
||||
- Builds `Release|x64`.
|
||||
- Builds `Release|x64` (or `Release|ARM64` with `-Platform ARM64`; output under `bin\Release-ARM64\`).
|
||||
- **If the EV YubiKey is in:** EV-signs all binaries — app `.exe`/`.dll` **and** driver
|
||||
`.sys`/`.dll` — in one call (**PIN 1**); then `sign-drivers.ps1` regenerates + EV-signs the
|
||||
catalogs (**PIN 2**) and EV-signs the cabs (**PIN 3**), submits both drivers to Microsoft,
|
||||
@@ -164,16 +168,34 @@ API flow (`https://manage.devcenter.microsoft.com/v2.0/my/hardware`): token →
|
||||
URL) → PUT the signed CAB → `POST …/commit` → poll `workflowStatus.state` until `completed` →
|
||||
download `downloads.items[type=="signedPackage"]`. Both drivers run **in parallel**.
|
||||
|
||||
**One product per version.** A product accepts exactly **one** submission (a second returns
|
||||
`"Initial submission already exists"`), so each release is its own product — the version is
|
||||
baked into the product name (e.g. `App Sandbox Virtual Display Driver 0.1.0`) so the dashboard
|
||||
reads as a version history. Products **cannot be deleted** via the API (only renamed, via
|
||||
**One product per version per architecture.** A product accepts exactly **one** submission (a
|
||||
second returns `"Initial submission already exists"`), so each release is its own product — the
|
||||
version **and architecture** are baked into the product name (e.g. `App Sandbox Virtual Display
|
||||
Driver x64 0.1.0` / `… ARM64 0.1.0`) so the dashboard reads as a per-arch version history.
|
||||
Products **cannot be deleted** via the API (only renamed, via
|
||||
`PATCH /products/{id}`), so retire old ones by renaming, not removal.
|
||||
|
||||
**OS targets** are set in `submission-config.json` → `requestedSignatures`. Current
|
||||
codes are listed in Microsoft's *Get product data* doc; Windows 11 x64:
|
||||
`WINDOWS_v100_X64_CO_FULL` (21H2), `_NI_FULL` (22H2), `_GE_FULL` (24H2),
|
||||
`_25H2_FULL` (25H2).
|
||||
**OS targets** are set in `submission-config.json` → `requestedSignatures`. Current codes
|
||||
are listed in Microsoft's *Get product data* doc; Windows 11 x64: `WINDOWS_v100_X64_CO_FULL`
|
||||
(21H2), `_NI_FULL` (22H2), `_GE_FULL` (24H2), `_25H2_FULL` (25H2). For an ARM64 build,
|
||||
`sign-drivers.ps1 -Platform ARM64` rewrites these to the `ARM64` codes (and uses `inf2cat
|
||||
/os:10_CO_ARM64`) at sign time, so the committed config stays x64.
|
||||
|
||||
**x64 and ARM64 are submitted separately.** `sign-drivers.ps1 -Platform <arch>` (driven by
|
||||
`make-release.ps1 -Platform <arch>`) builds the cab from that arch's driver output,
|
||||
regenerates the catalog with `inf2cat /os:10_CO_<ARCH>`, and creates its **own** product.
|
||||
The **architecture is in the product/submission name** (`… x64 <ver>` / `… ARM64 <ver>`)
|
||||
so the two are easy to tell apart in the Partner Center dashboard. (A single combined
|
||||
multi-arch submission is technically possible but would need both arches built before
|
||||
signing, which breaks the per-platform "build one config → auto sign+zip" automation —
|
||||
hence separate per-arch submissions.)
|
||||
|
||||
**ARM64 ApiValidator** (the Universal-DDI check the WDK forces on ARM64 drivers) is **disabled
|
||||
for ARM64 cross-builds from a non-ARM64 host** (`ApiValidator_Enable=false` in
|
||||
`Directory.Build.props`, gated on `Platform==ARM64` + host≠ARM64). The WDK defaults it to the x86
|
||||
ApiExtractor, which can't read an ARM64 PE and fails with exit 193; native ARM64 hosts and all x64
|
||||
builds still run it, and Microsoft re-runs API validation on submission. Same rationale as the
|
||||
InfVerif skip below.
|
||||
|
||||
**INF verification** (`infverif /h`, required by Partner Center since Apr 2025) runs at
|
||||
**build time inside Visual Studio**. Command-line / CI builds skip the build-time task
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
make-release.ps1 - Build AppSandbox, then conditionally sign + package.
|
||||
|
||||
Flow:
|
||||
1. Build the solution (Release|x64) unless -NoBuild.
|
||||
1. Build the solution (Release|$Platform) unless -NoBuild.
|
||||
2. Check whether the App Sandbox LLC EV YubiKey is plugged in.
|
||||
- NOT present -> SKIP signing, driver attestation, and ZIP.
|
||||
The (test-signed) dev build is left as-is. Done.
|
||||
@@ -14,15 +14,21 @@
|
||||
So nothing is signed or zipped on an ordinary dev machine without the token.
|
||||
|
||||
USAGE:
|
||||
.\make-release.ps1 # build + (if YubiKey) sign + attest + zip
|
||||
.\make-release.ps1 -NoBuild # package the current bin\Release
|
||||
.\make-release.ps1 # x64: build + (if YubiKey) sign + attest + zip
|
||||
.\make-release.ps1 -Platform ARM64 # Windows on ARM (bin\Release-ARM64 -> *-win-arm64.zip)
|
||||
.\make-release.ps1 -NoBuild # package the current bin\Release[-ARM64]
|
||||
.\make-release.ps1 -SkipDrivers # sign app binaries + zip; leave drivers as-is
|
||||
.\make-release.ps1 -ForceDriverSign # re-submit drivers for attestation even if cached
|
||||
|
||||
-Platform selects the bin\ output dir (ARM64 -> bin\Release-ARM64), the driver
|
||||
attestation OS/signature codes (sign-drivers.ps1 -Platform rewrites the x64 config
|
||||
at sign time), and the zip name. The AppSandboxPackage project passes
|
||||
-Platform $(Platform) after a Release build.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Configuration = 'Release',
|
||||
[string]$Platform = 'x64',
|
||||
[ValidateSet('x64','ARM64')][string]$Platform = 'x64',
|
||||
[string]$OS = 'win', # OS token in the zip name; not passed to MSBuild
|
||||
[string]$Version = '', # empty => read from Directory.Build.props (single source)
|
||||
[switch]$NoBuild,
|
||||
@@ -40,7 +46,10 @@ $env:ASB_SKIP_PACKAGE_PROJECT = '1'
|
||||
|
||||
$repo = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
||||
$sln = Join-Path $repo 'AppSandbox.sln'
|
||||
$bin = Join-Path $repo ("bin\{0}" -f $Configuration) # x64 -> bin\Release
|
||||
# Output dir matches the .vcxproj OutDir convention: x64 -> bin\Release,
|
||||
# ARM64 -> bin\Release-ARM64.
|
||||
$binLeaf = if ($Platform -eq 'ARM64') { "$Configuration-ARM64" } else { $Configuration }
|
||||
$bin = Join-Path $repo ("bin\{0}" -f $binLeaf)
|
||||
|
||||
$cfgPath = Join-Path $PSScriptRoot 'submission-config.json'
|
||||
$cfg = if (Test-Path $cfgPath) { Get-Content $cfgPath -Raw | ConvertFrom-Json } else { $null }
|
||||
@@ -84,6 +93,11 @@ function Get-AsbVersion([string]$repoRoot) {
|
||||
# returns an error (never UI) when the specific card bearing it is absent.
|
||||
# This is what tells our token apart from some unrelated card the user inserted.
|
||||
if (-not ([System.Management.Automation.PSTypeName]'AsbScReader').Type) {
|
||||
# Add-Type shells out to the .NET Framework C# compiler, which reads the LIB environment
|
||||
# variable and fails the compile (CS1668, warning-as-error) on any entry that does not exist.
|
||||
# The VS ARM64 build pushes a non-existent ...\atlmfc\lib\ARM64 onto LIB (the ATL/MFC ARM64
|
||||
# libs aren't installed); csc needs nothing from LIB here, so drop missing entries first.
|
||||
if ($env:LIB) { $env:LIB = (($env:LIB -split ';') | Where-Object { $_ -and (Test-Path $_) }) -join ';' }
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -290,8 +304,8 @@ if (-not $SkipDrivers) {
|
||||
if ($reusable) {
|
||||
Write-Host "Reusing existing Microsoft-signed drivers (v$($asb.Full)) from drivers-signed - no resubmit."
|
||||
} else {
|
||||
Write-Host "Driver attestation signing (sign-drivers.ps1) - Microsoft round-trip..."
|
||||
& (Join-Path $PSScriptRoot 'sign-drivers.ps1')
|
||||
Write-Host "Driver attestation signing (sign-drivers.ps1 -Platform $Platform) - Microsoft round-trip..."
|
||||
& (Join-Path $PSScriptRoot 'sign-drivers.ps1') -Platform $Platform
|
||||
if ($LASTEXITCODE -ne 0) { throw "Driver attestation failed." }
|
||||
}
|
||||
# Deposit the MS-signed drivers into bin\Release\drivers (deposited .sys/.dll match the MS
|
||||
@@ -360,7 +374,7 @@ $files | ForEach-Object { Write-Host (" " + $_.FullName.Substring($stage.Length
|
||||
$mb = [math]::Round((($files | Measure-Object Length -Sum).Sum) / 1MB, 1)
|
||||
Write-Host ("--- {0} files, {1} MB ---`n" -f $files.Count, $mb)
|
||||
|
||||
$zip = Join-Path $bin ("AppSandbox-{0}-{1}-{2}.zip" -f $Version, $OS, $Platform)
|
||||
$zip = Join-Path $bin ("AppSandbox-{0}-{1}-{2}.zip" -f $Version, $OS, $Platform.ToLower())
|
||||
if (Test-Path $zip) { Remove-Item $zip -Force }
|
||||
# Flat layout: the items sit at the zip root (file.zip\AppSandbox.exe, file.zip\drivers\...),
|
||||
# with the drivers\ / resources\ / web\ subfolders preserved - no extra parent folder.
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
param(
|
||||
[string]$ConfigPath = (Join-Path $PSScriptRoot 'submission-config.json'),
|
||||
[string]$CredPath = (Join-Path $PSScriptRoot 'partner-center.local.json'),
|
||||
[ValidateSet('x64','ARM64')][string]$Platform = 'x64', # build target arch
|
||||
[switch]$NoCab,
|
||||
[switch]$NoSign,
|
||||
[switch]$SkipSubmit,
|
||||
@@ -87,6 +88,11 @@ function Invoke-EvSign([string[]]$files, [string]$what) {
|
||||
# returns an error (never UI) when the specific card bearing it is absent.
|
||||
# This is what tells our token apart from some unrelated card the user inserted.
|
||||
if (-not ([System.Management.Automation.PSTypeName]'AsbScReader').Type) {
|
||||
# Add-Type shells out to the .NET Framework C# compiler, which reads the LIB environment
|
||||
# variable and fails the compile (CS1668, warning-as-error) on any entry that does not exist.
|
||||
# The VS ARM64 build pushes a non-existent ...\atlmfc\lib\ARM64 onto LIB (the ATL/MFC ARM64
|
||||
# libs aren't installed); csc needs nothing from LIB here, so drop missing entries first.
|
||||
if ($env:LIB) { $env:LIB = (($env:LIB -split ';') | Where-Object { $_ -and (Test-Path $_) }) -join ';' }
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -401,6 +407,18 @@ function New-DriverCab($d, [string]$workDir) {
|
||||
|
||||
if (-not (Test-Path $ConfigPath)) { throw "Config not found: $ConfigPath" }
|
||||
$cfg = Get-Content $ConfigPath -Raw | ConvertFrom-Json
|
||||
|
||||
# The config holds the x64 defaults. For an ARM64 build target, rewrite the
|
||||
# arch-specific values (output dirs, inf2cat OS, requested signatures); on x64
|
||||
# the config is used as-is.
|
||||
if ($Platform -eq 'ARM64') {
|
||||
$cfg.workDir = $cfg.workDir -replace 'bin\\Release(?=\\)', 'bin\Release-ARM64'
|
||||
$cfg.outDir = $cfg.outDir -replace 'bin\\Release(?=\\)', 'bin\Release-ARM64'
|
||||
$cfg.symbolDir = $cfg.symbolDir -replace '\\x64\\', '\ARM64\'
|
||||
$cfg.inf2catOs = $cfg.inf2catOs -replace 'X64', 'ARM64'
|
||||
$cfg.requestedSignatures = @($cfg.requestedSignatures | ForEach-Object { $_ -replace 'X64','ARM64' })
|
||||
foreach ($d in $cfg.drivers) { $d.sourceDir = $d.sourceDir -replace 'bin\\Release(?=\\)', 'bin\Release-ARM64' }
|
||||
}
|
||||
$asb = Get-AsbVersion # version from Directory.Build.props (single source)
|
||||
$workDir = Resolve-RelPath $cfg.workDir
|
||||
$script:OutDir = Resolve-RelPath $cfg.outDir
|
||||
@@ -483,7 +501,8 @@ $ver = $asb.Short
|
||||
$pending = @()
|
||||
foreach ($j in $jobs) {
|
||||
$d = $j.Driver
|
||||
$productDisplayName = "$($d.productName) $ver"
|
||||
$archTag = if ($Platform -eq 'ARM64') { ' ARM64' } else { '' }
|
||||
$productDisplayName = "$($d.productName)$archTag $ver"
|
||||
Write-Host "`n=== $productDisplayName ==="
|
||||
|
||||
$product = Invoke-Api POST "$ApiBase/products" @{
|
||||
|
||||
@@ -113,7 +113,8 @@
|
||||
copy /Y "$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\AppSandboxVDD.dll" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\"
|
||||
copy /Y "$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\AppSandboxVDD.inf" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\"
|
||||
copy /Y "$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\appsandboxvdd.cat" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\AppSandboxVDD.cat"
|
||||
powershell -NoProfile -Command "$sig = Get-AuthenticodeSignature '$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\appsandboxvdd.cat'; if ($sig.SignerCertificate) { [IO.File]::WriteAllBytes('$(SolutionDir)bin\$(Configuration)-ARM64\drivers\AppSandboxVDD.cer', $sig.SignerCertificate.RawData) }"</Command>
|
||||
powershell -NoProfile -Command "$sig = Get-AuthenticodeSignature '$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\appsandboxvdd.cat'; if ($sig.SignerCertificate) { [IO.File]::WriteAllBytes('$(SolutionDir)bin\$(Configuration)-ARM64\drivers\AppSandboxVDD.cer', $sig.SignerCertificate.RawData) }"
|
||||
copy /Y "$(SolutionDir)vendor\devcon\arm64\devcon.exe" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\"</Command>
|
||||
<Message>Copying VDD driver files to bin\$(Configuration)-ARM64\drivers\...</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -134,7 +135,8 @@ copy /Y "$(SolutionDir)vendor\devcon\devcon.exe" "$(SolutionDir)bin\$(Configurat
|
||||
copy /Y "$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\AppSandboxVDD.dll" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\"
|
||||
copy /Y "$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\AppSandboxVDD.inf" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\"
|
||||
copy /Y "$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\appsandboxvdd.cat" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\AppSandboxVDD.cat"
|
||||
powershell -NoProfile -Command "$sig = Get-AuthenticodeSignature '$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\appsandboxvdd.cat'; if ($sig.SignerCertificate) { [IO.File]::WriteAllBytes('$(SolutionDir)bin\$(Configuration)-ARM64\drivers\AppSandboxVDD.cer', $sig.SignerCertificate.RawData) }"</Command>
|
||||
powershell -NoProfile -Command "$sig = Get-AuthenticodeSignature '$(SolutionDir)ARM64\$(Configuration)\AppSandboxVDD\appsandboxvdd.cat'; if ($sig.SignerCertificate) { [IO.File]::WriteAllBytes('$(SolutionDir)bin\$(Configuration)-ARM64\drivers\AppSandboxVDD.cer', $sig.SignerCertificate.RawData) }"
|
||||
copy /Y "$(SolutionDir)vendor\devcon\arm64\devcon.exe" "$(SolutionDir)bin\$(Configuration)-ARM64\drivers\"</Command>
|
||||
<Message>Copying VDD driver files to bin\$(Configuration)-ARM64\drivers\...</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Reference in new issue
Block a user