diff --git a/DeveloperLabs/SandboxIPCSample/win32/.gitignore b/DeveloperLabs/SandboxIPCSample/win32/.gitignore new file mode 100644 index 0000000..ffde08e --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/.gitignore @@ -0,0 +1,57 @@ +# Visual Studio +.vs/ +bin/ +obj/ +*.user +*.suo +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Build results +*.exe +*.dll +*.pdb +*.ilk +*.obj +*.iobj +*.pch +*.ipdb +*.idb +*.lib +*.exp + +# Package files +*.appx +*.appxbundle +*.msix +*.msixbundle +Package/ + +# Test certificates +*.pfx +*.cer diff --git a/DeveloperLabs/SandboxIPCSample/win32/IPCDemo.sln b/DeveloperLabs/SandboxIPCSample/win32/IPCDemo.sln new file mode 100644 index 0000000..bbff78b --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/IPCDemo.sln @@ -0,0 +1,100 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo_host", "demo_host.vcxproj", "{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo_child", "demo_child.vcxproj", "{B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|Any CPU.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|Any CPU.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM64.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|ARM64.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x64.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x64.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x86.ActiveCfg = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Debug|x86.Build.0 = Debug|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|Any CPU.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|Any CPU.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM64.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|ARM64.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x64.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x64.Build.0 = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x86.ActiveCfg = Release|x64 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}.Release|x86.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|Any CPU.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|Any CPU.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM64.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|ARM64.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x64.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x64.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x86.ActiveCfg = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Debug|x86.Build.0 = Debug|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|Any CPU.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|Any CPU.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM64.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|ARM64.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x64.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x64.Build.0 = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x86.ActiveCfg = Release|x64 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E}.Release|x86.Build.0 = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM.ActiveCfg = Debug|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM.Build.0 = Debug|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM.Deploy.0 = Debug|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM64.Build.0 = Debug|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x64.ActiveCfg = Debug|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x64.Build.0 = Debug|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x64.Deploy.0 = Debug|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x86.ActiveCfg = Debug|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x86.Build.0 = Debug|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Debug|x86.Deploy.0 = Debug|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|Any CPU.Build.0 = Release|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|Any CPU.Deploy.0 = Release|Any CPU + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM.ActiveCfg = Release|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM.Build.0 = Release|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM.Deploy.0 = Release|ARM + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM64.ActiveCfg = Release|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM64.Build.0 = Release|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|ARM64.Deploy.0 = Release|ARM64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x64.ActiveCfg = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x64.Build.0 = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x64.Deploy.0 = Release|x64 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x86.ActiveCfg = Release|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x86.Build.0 = Release|x86 + {C59347EC-47B1-43FB-BAAB-63E4944FADEF}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {12345678-1234-1234-1234-123456789012} + EndGlobalSection +EndGlobal diff --git a/DeveloperLabs/SandboxIPCSample/win32/README.md b/DeveloperLabs/SandboxIPCSample/win32/README.md new file mode 100644 index 0000000..2663666 --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/README.md @@ -0,0 +1,127 @@ +# IPC Demo - Parent-Child Process Communication + +This project demonstrates interprocess communication (IPC) between a full trust parent process and a sandboxed (AppContainer) child process using named pipes. + +## Architecture + +- **demo_parent.exe**: Full trust console application that: + - Spawns the child process using the app execution alias (`demo_child_ac.exe`) + - Creates a named pipe with proper security attributes to allow AppContainer access + - Sends messages to the child and receives echoed responses + - Manages the child process lifecycle + +- **demo_child.exe**: Sandboxed (AppContainer) console application that: + - Runs in a restricted security context + - Connects to the named pipe created by the parent + - Receives messages and echoes them back + - Exits gracefully when receiving the EXIT command + +## Key Features + +### Security Attributes for Named Pipe + +The parent process creates the named pipe with a security descriptor that allows AppContainer processes to access it: + +```cpp +// SDDL: Grants Generic All to World Domain and Generic Read/Write to ALL APPLICATION PACKAGES +LPCWSTR sddl = L"D:(A;;GA;;;WD)(A;;GRGW;;;AC)"; +``` + +This is critical for allowing the sandboxed child process to communicate with the full trust parent. + +### App Execution Alias + +The manifest uses the `uap5:AppExecutionAlias` feature to create aliases: +- `demo_parent.exe` - Full trust parent +- `demo_child_ac.exe` - Sandboxed child (alias for `demo_child.exe`) + +The parent spawns the child using `demo_child_ac.exe`, which automatically runs it in the AppContainer sandbox. + +## Building + +1. Open `IPCDemo.sln` in Visual Studio 2022 or later +2. Build the solution (both Debug and Release configurations supported) +3. Executables will be output to `bin\Debug\` or `bin\Release\` + +## Creating the APPX Package + +To deploy this as a packaged application: + +1. Create a folder structure: + ``` + Package\ + ├── demo_parent.exe + ├── demo_child.exe + ├── AppxManifest.xml + └── Images\ + ├── StoreLogo.png + └── AppList.png + ``` + +2. Copy the built executables to the Package folder + +3. Create placeholder images (or use real ones): + - StoreLogo.png (150x150) + - AppList.png (44x44) + +4. Use `MakeAppx.exe` to create the package: + ```powershell + MakeAppx.exe pack /d Package /p IPCDemo.appx + ``` + +5. Sign the package (for testing, use a test certificate): + ```powershell + SignTool.exe sign /fd SHA256 /a /f TestCert.pfx IPCDemo.appx + ``` + +## Running + +### From Visual Studio (Debug) + +Simply run the `demo_parent` project. It will automatically launch the child process. + +### From Command Line (Packaged) + +1. Install the package: `Add-AppxPackage .\IPCDemo.appx` +2. Run: `demo_parent.exe` + +## Communication Flow + +1. Parent starts and creates security descriptor +2. Parent launches child using `demo_child_ac.exe` alias +3. Parent creates named pipe with AppContainer-accessible security +4. Parent waits for child to connect +5. Child waits for pipe availability and connects +6. Parent sends "Hello from parent!" → Child echoes back +7. Parent sends "This is message number 2" → Child echoes back +8. Parent sends "EXIT" command +9. Child closes pipe and exits +10. Parent waits for child exit and closes + +## Important Notes + +- The child process runs in an AppContainer, which has restricted access to system resources +- The named pipe security descriptor must explicitly grant access to AppContainer processes (SID: S-1-15-2-1) +- The app execution alias mechanism ensures the child runs in the correct security context +- Both processes are console applications for easy debugging and demonstration + +## Requirements + +- Windows 10 version 1809 (17763) or later +- Visual Studio 2022 with C++ development tools +- Windows SDK 10.0 or later + +## Troubleshooting + +If the child fails to connect: +- Ensure the security descriptor on the pipe includes AppContainer access (`AC` in SDDL) +- Verify the child is being launched with the `_ac.exe` alias +- Check that the package is properly installed and the manifest is correct +- Look for error codes in the console output + +## References + +- [AppContainer Isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation) +- [Named Pipes](https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes) +- [Security Descriptor String Format (SDDL)](https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format) +- [App Execution Alias](https://docs.microsoft.com/en-us/windows/uwp/launch-resume/execute-in-app-context) diff --git a/DeveloperLabs/SandboxIPCSample/win32/demo_child.cpp b/DeveloperLabs/SandboxIPCSample/win32/demo_child.cpp new file mode 100644 index 0000000..c5bcb04 --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/demo_child.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include + +#define PIPE_NAME L"\\\\.\\pipe\\IPCDemoPipe" +#define BUFFER_SIZE 512 + +// Function to get and display the current process integrity level and AppContainer status +void DisplayProcessSecurityInfo() +{ + HANDLE hToken = nullptr; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + printf("[Child] Failed to open process token. Error: %lu\n", GetLastError()); + return; + } + + // Get integrity level + DWORD dwLengthNeeded = 0; + GetTokenInformation(hToken, TokenIntegrityLevel, nullptr, 0, &dwLengthNeeded); + + TOKEN_MANDATORY_LABEL* pTIL = (TOKEN_MANDATORY_LABEL*)LocalAlloc(0, dwLengthNeeded); + if (pTIL) + { + if (GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, dwLengthNeeded, &dwLengthNeeded)) + { + DWORD dwIntegrityLevel = *GetSidSubAuthority(pTIL->Label.Sid, + (DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid) - 1)); + + const char* integrityLevelStr = "Unknown"; + if (dwIntegrityLevel < SECURITY_MANDATORY_LOW_RID) + integrityLevelStr = "Untrusted"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_MEDIUM_RID) + integrityLevelStr = "Low"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "Medium"; + else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "High/System"; + + printf("[Child] *** Process Integrity Level: %s (0x%X) ***\n", integrityLevelStr, dwIntegrityLevel); + + // Convert SID to string for display + LPWSTR szIntegritySid = nullptr; + if (ConvertSidToStringSidW(pTIL->Label.Sid, &szIntegritySid)) + { + printf("[Child] *** Integrity SID: %ls ***\n", szIntegritySid); + LocalFree(szIntegritySid); + } + } + LocalFree(pTIL); + } + + // Check if running in AppContainer + DWORD dwIsAppContainer = 0; + dwLengthNeeded = sizeof(DWORD); + if (GetTokenInformation(hToken, TokenIsAppContainer, &dwIsAppContainer, sizeof(DWORD), &dwLengthNeeded)) + { + printf("[Child] *** Running in AppContainer: %s ***\n", dwIsAppContainer ? "YES" : "NO"); + } + + // Get AppContainer SID if applicable + if (dwIsAppContainer) + { + dwLengthNeeded = 0; + GetTokenInformation(hToken, TokenAppContainerSid, nullptr, 0, &dwLengthNeeded); + + TOKEN_APPCONTAINER_INFORMATION* pAppContainerInfo = (TOKEN_APPCONTAINER_INFORMATION*)LocalAlloc(0, dwLengthNeeded); + if (pAppContainerInfo) + { + if (GetTokenInformation(hToken, TokenAppContainerSid, pAppContainerInfo, dwLengthNeeded, &dwLengthNeeded)) + { + LPWSTR szAppContainerSid = nullptr; + if (ConvertSidToStringSidW(pAppContainerInfo->TokenAppContainer, &szAppContainerSid)) + { + printf("[Child] *** AppContainer SID: %ls ***\n", szAppContainerSid); + LocalFree(szAppContainerSid); + } + } + LocalFree(pAppContainerInfo); + } + } + + CloseHandle(hToken); +} + +bool SendMessage(HANDLE hPipe, const char* message) +{ + DWORD bytesWritten; + DWORD messageLen = (DWORD)strlen(message) + 1; + + printf("[Child] Sending: %s\n", message); + + if (!WriteFile(hPipe, message, messageLen, &bytesWritten, nullptr)) + { + printf("[Child] Failed to write to pipe. Error: %lu\n", GetLastError()); + return false; + } + + return true; +} + +bool ReceiveMessage(HANDLE hPipe, char* buffer, DWORD bufferSize) +{ + DWORD bytesRead; + + if (!ReadFile(hPipe, buffer, bufferSize, &bytesRead, nullptr)) + { + printf("[Child] Failed to read from pipe. Error: %lu\n", GetLastError()); + return false; + } + + printf("[Child] Received: %s\n", buffer); + return true; +} + +int main() +{ + printf("=== IPC Demo - Child Process (Sandboxed) ===\n"); + printf("[Child] Starting...\n"); + + // Display security information + printf("\n[Child] === Security Context Information ===\n"); + DisplayProcessSecurityInfo(); + printf("[Child] ==========================================\n\n"); + + // Wait for pipe to be available + printf("[Child] Waiting for pipe to become available...\n"); + + int retries = 0; + while (!WaitNamedPipeW(PIPE_NAME, 5000)) + { + printf("[Child] Pipe not available yet, retrying... (attempt %d)\n", ++retries); + if (retries > 10) + { + printf("[Child] Timeout waiting for pipe\n"); + return 1; + } + } + + printf("[Child] Pipe is available, connecting...\n"); + + // Open the named pipe + HANDLE hPipe = ::CreateFileW( + PIPE_NAME, + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + 0, + nullptr); + + if (hPipe == INVALID_HANDLE_VALUE) + { + printf("[Child] Failed to open pipe. Error: %lu\n", GetLastError()); + return 1; + } + + printf("[Child] Connected to pipe\n"); + + // Set pipe to message mode + DWORD mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(hPipe, &mode, nullptr, nullptr)) + { + printf("[Child] Failed to set pipe mode. Error: %lu\n", GetLastError()); + CloseHandle(hPipe); + return 1; + } + + char buffer[BUFFER_SIZE]; + + // Message loop + while (true) + { + // Receive message from parent + if (!ReceiveMessage(hPipe, buffer, BUFFER_SIZE)) + { + break; + } + + // Check for exit command + if (strcmp(buffer, "EXIT") == 0) + { + printf("[Child] Received exit command\n"); + break; + } + + // Echo the message back + char response[BUFFER_SIZE]; + snprintf(response, BUFFER_SIZE, "Echo: %s", buffer); + + if (!SendMessage(hPipe, response)) + { + break; + } + } + + // Close pipe + printf("[Child] Closing pipe\n"); + CloseHandle(hPipe); + + printf("[Child] Child process exiting\n"); + + // Wait for user input before closing the console + printf("\n[Child] *** Press Enter to close this window... ***\n"); + getchar(); + + return 0; +} diff --git a/DeveloperLabs/SandboxIPCSample/win32/demo_child.vcxproj b/DeveloperLabs/SandboxIPCSample/win32/demo_child.vcxproj new file mode 100644 index 0000000..9eeb7c5 --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/demo_child.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {B2C3D4E5-F6A7-4B5C-9D0E-1F2A3B4C5D6E} + Win32Proj + DemoChild + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_child + + + false + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_child + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + kernel32.lib;user32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + kernel32.lib;user32.lib;%(AdditionalDependencies) + + + + + + + + + diff --git a/DeveloperLabs/SandboxIPCSample/win32/demo_host.cpp b/DeveloperLabs/SandboxIPCSample/win32/demo_host.cpp new file mode 100644 index 0000000..21b2099 --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/demo_host.cpp @@ -0,0 +1,429 @@ +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "userenv.lib") + +#define PIPE_NAME L"\\\\.\\pipe\\IPCDemoPipe" +#define BUFFER_SIZE 512 +#define APPCONTAINER_NAME L"SandboxIpcDemoApp" + +// Function to get and display the current process integrity level +void DisplayProcessSecurityInfo() +{ + HANDLE hToken = nullptr; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + printf("[Host] Failed to open process token. Error: %lu\n", GetLastError()); + return; + } + + // Get integrity level + DWORD dwLengthNeeded = 0; + GetTokenInformation(hToken, TokenIntegrityLevel, nullptr, 0, &dwLengthNeeded); + + TOKEN_MANDATORY_LABEL* pTIL = (TOKEN_MANDATORY_LABEL*)LocalAlloc(0, dwLengthNeeded); + if (pTIL) + { + if (GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, dwLengthNeeded, &dwLengthNeeded)) + { + DWORD dwIntegrityLevel = *GetSidSubAuthority(pTIL->Label.Sid, + (DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid) - 1)); + + const char* integrityLevelStr = "Unknown"; + if (dwIntegrityLevel < SECURITY_MANDATORY_LOW_RID) + integrityLevelStr = "Untrusted"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_MEDIUM_RID) + integrityLevelStr = "Low"; + else if (dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "Medium"; + else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) + integrityLevelStr = "High/System"; + + printf("[Parent] *** Process Integrity Level: %s (0x%X) ***\n", integrityLevelStr, dwIntegrityLevel); + + // Convert SID to string for display + LPWSTR szIntegritySid = nullptr; + if (ConvertSidToStringSidW(pTIL->Label.Sid, &szIntegritySid)) + { + printf("[Parent] *** Integrity SID: %ls ***\n", szIntegritySid); + LocalFree(szIntegritySid); + } + } + LocalFree(pTIL); + } + + // Check if running in AppContainer + DWORD dwIsAppContainer = 0; + dwLengthNeeded = sizeof(DWORD); + if (GetTokenInformation(hToken, TokenIsAppContainer, &dwIsAppContainer, sizeof(DWORD), &dwLengthNeeded)) + { + printf("[Parent] *** Running in AppContainer: %s ***\n", dwIsAppContainer ? "YES" : "NO"); + } + + CloseHandle(hToken); +} + + +bool GetSecurityAttributes(SECURITY_ATTRIBUTES& sa, + SECURITY_DESCRIPTOR& sd, + const PSID& app_container_sid) { + // Create a new security descriptor + if (0 == ::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + auto gle = ::GetLastError(); + printf("InitializeSecurityDescriptor failed. Error: %lu\n", gle); + return false; + } + + LPCSTR everyone_sid_string = "S-1-1-0"; // Also known as world sid. + PSID everyone_sid; + + if (!ConvertStringSidToSidA(everyone_sid_string, &everyone_sid)) { + printf("Failed to convert String to Sid"); + return false; + } + + // Create a new ACL + PACL acl = nullptr; + constexpr unsigned int kMaxExplicitAccessEntries = 2; + EXPLICIT_ACCESSW ea_list[kMaxExplicitAccessEntries] = {}; + for (int i = 0; i < kMaxExplicitAccessEntries; i++) { + ea_list[i].grfAccessPermissions = FILE_ALL_ACCESS; + ea_list[i].grfAccessMode = GRANT_ACCESS; + ea_list[i].grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ea_list[i].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea_list[i].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea_list[i].Trustee.pMultipleTrustee = nullptr; + } + + ea_list[0].Trustee.ptstrName = static_cast(everyone_sid); + // here we explicitly give access to the AppContainer SID + ea_list[1].Trustee.ptstrName = static_cast(app_container_sid); + + auto result = ::SetEntriesInAclW(kMaxExplicitAccessEntries, ea_list, nullptr, &acl); + if (result != ERROR_SUCCESS) { + printf("SetEntriesInAclW failed. Error: %lu\n", result); + return false; + } + + // Set the DACL in the security descriptor + if (!::SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE)) { + printf("SetSecurityDescriptorDacl failed. Error: %lu\n", GetLastError()); + return false; + } + sa.lpSecurityDescriptor = &sd; + return true; +} + +bool SendMessage(HANDLE hPipe, const char* message); +bool ReceiveMessage(HANDLE hPipe, char* buffer, DWORD bufferSize); + +int main() +{ + printf("=== IPC Demo - Host Process ===\n"); + printf("[Host] Starting...\n"); + + // Display security information + printf("\n[Host] === Security Context Information ===\n"); + DisplayProcessSecurityInfo(); + printf("[Host] ==========================================\n\n"); + + + // Create or get AppContainer profile for the child process + PSID pAppContainerSid = nullptr; + HRESULT hr = CreateAppContainerProfile( + APPCONTAINER_NAME, + L"IPC Demo Child AppContainer", + L"Sandboxed child process for IPC demonstration", + nullptr, // No capabilities for now + 0, + &pAppContainerSid); + + if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) + { + printf("[Parent] Failed to create AppContainer profile. HRESULT: 0x%08X\n", hr); + return 1; + } + + // If AppContainerProfile already exists, get the SID + if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) + { + hr = DeriveAppContainerSidFromAppContainerName(APPCONTAINER_NAME, &pAppContainerSid); + if (FAILED(hr)) + { + printf("[Parent] Failed to get AppContainer SID. HRESULT: 0x%08X\n", hr); + return 1; + } + } + + + // Use the the AppContainer SID to create a security attributes for the Named Pipe + SECURITY_DESCRIPTOR sd = { 0 }; + SECURITY_ATTRIBUTES sa = { 0 }; + sa.bInheritHandle = FALSE; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + + + if (!GetSecurityAttributes(sa, sd, pAppContainerSid)) { + printf("[Host] Failed to create security attributes\n"); + return 1; + } + + // Create named pipe BEFORE launching child process + printf("[Host] Creating named pipe: %ls\n", PIPE_NAME); + + HANDLE hPipe = ::CreateNamedPipeW( + PIPE_NAME, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 1, + BUFFER_SIZE, + BUFFER_SIZE, + 0, + &sa); + + if (hPipe == INVALID_HANDLE_VALUE) + { + printf("[Parent] Failed to create named pipe. Error: %lu\n", GetLastError()); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + printf("[Parent] Named pipe created successfully\n"); + + // Set up security capabilities for the AppContainer + SECURITY_CAPABILITIES securityCapabilities = { 0 }; + securityCapabilities.AppContainerSid = pAppContainerSid; + securityCapabilities.Capabilities = nullptr; + securityCapabilities.CapabilityCount = 0; + securityCapabilities.Reserved = 0; + + // Set up process attribute list + SIZE_T attributeListSize = 0; + InitializeProcThreadAttributeList(nullptr, 1, 0, &attributeListSize); + + LPPROC_THREAD_ATTRIBUTE_LIST pAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( + GetProcessHeap(), 0, attributeListSize); + + if (!pAttributeList) + { + printf("[Parent] Failed to allocate attribute list\n"); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + if (!InitializeProcThreadAttributeList(pAttributeList, 1, 0, &attributeListSize)) + { + printf("[Parent] Failed to initialize attribute list. Error: %lu\n", GetLastError()); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + if (!UpdateProcThreadAttribute( + pAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES, + &securityCapabilities, + sizeof(securityCapabilities), + nullptr, + nullptr)) + { + printf("[Parent] Failed to update proc thread attribute. Error: %lu\n", GetLastError()); + DeleteProcThreadAttributeList(pAttributeList); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + // NOW launch the child process + STARTUPINFOW si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; + si.cb = sizeof(si); + + // Use STARTUPINFOEX to pass the attribute list + STARTUPINFOEXW siex = { 0 }; + siex.StartupInfo.cb = sizeof(STARTUPINFOEXW); + siex.lpAttributeList = pAttributeList; + + // Get the current module path to find the child exe + wchar_t modulePath[MAX_PATH]; + GetModuleFileNameW(nullptr, modulePath, MAX_PATH); + + // Remove the exe name to get just the directory + wchar_t* lastSlash = wcsrchr(modulePath, L'\\'); + if (lastSlash) + { + *(lastSlash + 1) = L'\0'; + } + + // Construct full path to child executable + wchar_t cmdLine[MAX_PATH]; + swprintf_s(cmdLine, MAX_PATH, L"\"%schild.exe\"", modulePath); + + printf("[Parent] Launching child process: %ls\n", cmdLine); + + if (!CreateProcessW( + nullptr, + cmdLine, + nullptr, + nullptr, + FALSE, + EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, // Use extended startup info + nullptr, + nullptr, + &siex.StartupInfo, // Use the extended startup info + &pi)) + { + printf("[Parent] Failed to create child process. Error: %lu\n", GetLastError()); + DeleteProcThreadAttributeList(pAttributeList); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + CloseHandle(hPipe); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + printf("[Parent] Child process created. PID: %lu\n", pi.dwProcessId); + + // Clean up attribute list and AppContainer SID + DeleteProcThreadAttributeList(pAttributeList); + HeapFree(GetProcessHeap(), 0, pAttributeList); + FreeSid(pAppContainerSid); + // Clean up security descriptor - no longer needed + LocalFree(sa.lpSecurityDescriptor); + + // Wait for child to connect to the pipe + printf("[Parent] Waiting for child to connect...\n"); + + if (!ConnectNamedPipe(hPipe, nullptr)) + { + DWORD error = GetLastError(); + if (error != ERROR_PIPE_CONNECTED) + { + printf("[Parent] Failed to connect to pipe. Error: %lu\n", error); + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + } + + printf("[Parent] Child connected to pipe\n"); + + char buffer[BUFFER_SIZE]; + + // Send first message + if (!SendMessage(hPipe, "Hello from parent!")) + { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Receive response + if (!ReceiveMessage(hPipe, buffer, BUFFER_SIZE)) + { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Send second message + if (!SendMessage(hPipe, "This is message number 2")) + { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Receive response + if (!ReceiveMessage(hPipe, buffer, BUFFER_SIZE)) + { + CloseHandle(hPipe); + TerminateProcess(pi.hProcess, 1); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 1; + } + + // Send exit message + printf("[Parent] Sending exit command\n"); + SendMessage(hPipe, "EXIT"); + + // Close pipe + printf("[Parent] Closing pipe\n"); + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + + // Wait for child to exit (wait indefinitely for user to close child console) + printf("[Parent] Waiting for child process to exit...\n"); + printf("[Parent] (Child console is waiting for user input - press Enter in child window)\n"); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exitCode; + GetExitCodeProcess(pi.hProcess, &exitCode); + printf("[Parent] Child process exited with code: %lu\n", exitCode); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + printf("[Parent] Parent process exiting\n"); + + // Wait for user input before closing the parent console + printf("\n[Parent] *** Press Enter to close this window... ***\n"); + getchar(); + + return 0; +} + + + + +bool SendMessage(HANDLE hPipe, const char* message) +{ + DWORD bytesWritten; + DWORD messageLen = (DWORD)strlen(message) + 1; + + printf("[Host] Sending: %s\n", message); + + if (!WriteFile(hPipe, message, messageLen, &bytesWritten, nullptr)) + { + printf("[Host] Failed to write to pipe. Error: %lu\n", GetLastError()); + return false; + } + + return true; +} + +bool ReceiveMessage(HANDLE hPipe, char* buffer, DWORD bufferSize) +{ + DWORD bytesRead; + + if (!ReadFile(hPipe, buffer, bufferSize, &bytesRead, nullptr)) + { + printf("[Parent] Failed to read from pipe. Error: %lu\n", GetLastError()); + return false; + } + + printf("[Parent] Received: %s\n", buffer); + return true; +} diff --git a/DeveloperLabs/SandboxIPCSample/win32/demo_host.vcxproj b/DeveloperLabs/SandboxIPCSample/win32/demo_host.vcxproj new file mode 100644 index 0000000..2f792f4 --- /dev/null +++ b/DeveloperLabs/SandboxIPCSample/win32/demo_host.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D} + Win32Proj + DemoHost + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_host + + + false + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + demo_host + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + kernel32.lib;user32.lib;advapi32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + kernel32.lib;user32.lib;advapi32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file