From 0bad6df14d9fdbb1c23686d77003fcf53bcfcc44 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 30 May 2022 17:56:58 -0700 Subject: [PATCH 1/5] Simplify DllHijack mitigation and ensure urlmon is delay loaded First update the project to reduce the number of linked libraries and ensure the most likely non-OS loaded DLLS are delay loaded. Then simplify the DLL hijack mitigation to always dynamically link to SetDefaultDllDirectories in case Squirrel is used on and old Win7 that is missing the necessary KB. --- src/Setup/Setup.vcxproj | 8 ++++---- src/Setup/winmain.cpp | 16 ++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Setup/Setup.vcxproj b/src/Setup/Setup.vcxproj index 2cd5cfff..4a283df8 100644 --- a/src/Setup/Setup.vcxproj +++ b/src/Setup/Setup.vcxproj @@ -64,8 +64,8 @@ Windows true - kernel32.lib;user32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;urlmon.lib - user32.dll;advapi32.dll;shell32.dll;ole32.dll;oleaut32.dll;urlmon.dll;%(DelayLoadDLLs) + urlmon.lib + comctl32.dll;shell32.dll;shlwapi.dll;urlmon.dll;%(DelayLoadDLLs) compat.manifest @@ -91,8 +91,8 @@ true true AsInvoker - kernel32.lib;user32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;urlmon.lib - user32.dll;advapi32.dll;shell32.dll;ole32.dll;oleaut32.dll;urlmon.dll;%(DelayLoadDLLs) + urlmon.lib + comctl32.dll;shell32.dll;shlwapi.dll;urlmon.dll;%(DelayLoadDLLs) compat.manifest diff --git a/src/Setup/winmain.cpp b/src/Setup/winmain.cpp index 592ce3cb..de8a730e 100644 --- a/src/Setup/winmain.cpp +++ b/src/Setup/winmain.cpp @@ -23,22 +23,26 @@ void PreloadLibs() std::wstring version = (std::wstring(sys32Folder) + L"\\version.dll"); std::wstring logoncli = (std::wstring(sys32Folder) + L"\\logoncli.dll"); std::wstring sspicli = (std::wstring(sys32Folder) + L"\\sspicli.dll"); + std::wstring urlmon = (std::wstring(sys32Folder) + L"\\urlmon.dll"); LoadLibrary(version.c_str()); LoadLibrary(logoncli.c_str()); LoadLibrary(sspicli.c_str()); + LoadLibrary(urlmon.c_str()); } void MitigateDllHijacking() { // Set the default DLL lookup directory to System32 for ourselves and kernel32.dll - SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32); - HMODULE hKernel32 = LoadLibrary(L"kernel32.dll"); - ATLASSERT(hKernel32 != NULL); - - SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories"); - if (pfn) { (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); } + if (hKernel32) + { + SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories"); + if (pfn) + { + (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); + } + } PreloadLibs(); } From 6cf7bf03a0f61f7eff556bf0ddc19053f81ee8d1 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 30 May 2022 17:57:44 -0700 Subject: [PATCH 2/5] Minor code cleanup --- src/Setup/winmain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Setup/winmain.cpp b/src/Setup/winmain.cpp index de8a730e..1c1d9088 100644 --- a/src/Setup/winmain.cpp +++ b/src/Setup/winmain.cpp @@ -15,7 +15,7 @@ typedef BOOL(WINAPI *SetDefaultDllDirectoriesFunction)(DWORD DirectoryFlags); // Some libraries are still loaded from the current directories. // If we pre-load them with an absolute path then we are good. -void PreloadLibs() +static void PreloadLibs() { wchar_t sys32Folder[MAX_PATH]; GetSystemDirectory(sys32Folder, MAX_PATH); @@ -31,7 +31,7 @@ void PreloadLibs() LoadLibrary(urlmon.c_str()); } -void MitigateDllHijacking() +static void MitigateDllHijacking() { // Set the default DLL lookup directory to System32 for ourselves and kernel32.dll HMODULE hKernel32 = LoadLibrary(L"kernel32.dll"); @@ -52,7 +52,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { - MitigateDllHijacking(); + MitigateDllHijacking(); int exitCode = -1; CString cmdLine(lpCmdLine); From 01ea197d1fc541f6ed99ab6c6613f4e27890d00c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 30 May 2022 17:57:49 -0700 Subject: [PATCH 3/5] Ensure debug/release build outputs do not conflict in the obj folder --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 60124e53..17ade302 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -6,7 +6,7 @@ $(MSBuildProjectName) $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) - $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)obj\$(Configuration)\$(ProjectName)\ $(BaseOutputPath)$(Configuration)\ GitHub From dd8fe8ef9f2f8105c4d337f117039715a09ee646 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 30 May 2022 18:27:34 -0700 Subject: [PATCH 4/5] Update NuGet submodule to point into Squirrel org --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index a57c629f..836589c9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "vendor/nuget"] path = vendor/nuget - url = https://github.com/paulcbetts/NuGet + url = https://github.com/Squirrel/NuGet branch = fix-prerelease-comparison From 5ac0d8a871fb16b916ac57c200f508c208af68aa Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 30 May 2022 18:02:13 -0700 Subject: [PATCH 5/5] Move official build to GitHub Actions The "build_official.cmd" now creates all of the build artifacts and the "devbuild.cmd" is a quick way for developers to get a build from the command-line. With these two batch files in place, move the official build pipeline from Azure DevOps to GitHub Actions. --- .github/workflows/build.yml | 38 +++++++++++++++ azure-pipelines.yml | 57 ---------------------- devbuild.cmd | 26 ++++++++++ package-electron-winstaller.ps1 | 48 ------------------ src/build_official.cmd | 86 +++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 105 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 azure-pipelines.yml create mode 100644 devbuild.cmd delete mode 100644 package-electron-winstaller.ps1 create mode 100644 src/build_official.cmd diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..d295489b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,38 @@ +name: Build Squirrel + +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop + +env: + DOTNET_NOLOGO: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + NUGET_XMLDOC_MODE: skip + +jobs: + build: + name: Build + runs-on: windows-2019 + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: recursive + + - name: Build Squirrel + shell: cmd + run: ./src/build_official.cmd + + - name: Save build + uses: actions/upload-artifact@v2 + with: + name: artifacts + path: build/artifacts/ diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 2ed3c3e2..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,57 +0,0 @@ -pool: Hosted Windows 2019 with VS2019 - -trigger: -- master -- develop - -variables: - solution: 'Squirrel.sln' - buildPlatform: 'Any CPU' - buildConfiguration: 'Release' - -steps: -- checkout: self - submodules: true - -- task: NuGetToolInstaller@1 - inputs: - versionSpec: 4.9.4 - -- task: NuGetCommand@2 - displayName: Restore - inputs: - restoreSolution: '$(solution)' - -- task: VSBuild@1 - displayName: Build - inputs: - solution: '$(solution)' - platform: '$(buildPlatform)' - configuration: '$(buildConfiguration)' - -- task: VSTest@2 - displayName: Test - inputs: - testAssemblyVer2: | - **\$(buildConfiguration)\*test*.dll - !**\obj\** - platform: '$(buildPlatform)' - configuration: '$(buildConfiguration)' - -- task: NuGetCommand@2 - displayName: Pack - inputs: - command: pack - packagesToPack: src\Squirrel.nuspec - -- task: PublishBuildArtifacts@1 - displayName: Publish Artifacts - inputs: - pathtoPublish: '$(build.artifactStagingDirectory)' - -- task: PublishSymbols@2 - displayName: Publish Symbols - inputs: - searchPattern: 'build\$(buildConfiguration)\**\*.pdb' - publishSymbols: false - continueOnError: true diff --git a/devbuild.cmd b/devbuild.cmd new file mode 100644 index 00000000..5cda9040 --- /dev/null +++ b/devbuild.cmd @@ -0,0 +1,26 @@ +@echo off + +setlocal +pushd %~dp0 + +:parse_args +if /i "%1"=="release" set _C=/p:Configuration=Release +if /i "%1"=="init" set _INIT=1 +if /i "%1"=="initialize" set _INIT=1 +if /i "%1"=="inc" set _INCREMENTAL=1 +if /i "%1"=="incremental" set _INCREMENTAL=1 +if /i "%1"=="clean" set _INCREMENTAL= & set _CLEAN=1 +if not "%1"=="" shift & goto parse_args + +if not "%_INCREMENTAL"=="1" rd /s /q build packages 2> nul +if not "%_CLEAN%"=="" goto end + +if "%_INIT%"=="1" git submodule update --init --recursive + +nuget restore + +msbuild -Restore %_C% -m -nr:false -v:m + +:end +popd +endlocal diff --git a/package-electron-winstaller.ps1 b/package-electron-winstaller.ps1 deleted file mode 100644 index 73eabd7b..00000000 --- a/package-electron-winstaller.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -<# -Package script for electron-winstaller - -The NPM package electron-winstaller allows developers to -build Windows installers for Electron apps using Squirrel -(https://github.com/electron/windows-installer) - -This script copies the required files into a single folder -which can then be copied to the electron-winstaller/vendor folder -(either manually or in an automated way). -#> - -# Stop the script if an error occurs -$ErrorActionPreference = "Stop" -$In = ".\build\Release\" -$Out = ".\build\electron-winstaller\" -$Folders = @("./build", "./packages", "./test/bin", "./test/obj") - -# Ensure a clean state by removing build/package folders -foreach ($Folder in $Folders) { - if (Test-Path $Folder) { - Remove-Item -path $Folder -Recurse -Force - } -} - -# Build Squirrel -git submodule update --init --recursive -.\.NuGet\NuGet.exe restore -msbuild /p:Configuration=Release - -# Create the electron-winstaller folder -New-Item -Path $Out -ItemType "directory" | Out-Null - -# Copy over all files we need -Copy-Item "$In\net45\Update.exe" -Destination "$Out\Squirrel.exe" -Copy-Item "$In\net45\update.com" -Destination "$Out\Squirrel.com" -Copy-Item "$In\net45\Update.pdb" -Destination "$Out\Squirrel.pdb" -Copy-Item "$In\Win32\Setup.exe" -Destination $Out -Copy-Item "$In\Win32\Setup.pdb" -Destination $Out -Copy-Item "$In\net45\Update-Mono.exe" -Destination "$Out\Squirrel-Mono.exe" -Copy-Item "$In\net45\Update-Mono.pdb" -Destination "$Out\Squirrel-Mono.pdb" -Copy-Item "$In\Win32\StubExecutable.exe" -Destination $Out -Copy-Item "$In\net45\SyncReleases.exe" -Destination $Out -Copy-Item "$In\net45\SyncReleases.pdb" -Destination $Out -Copy-Item "$In\Win32\WriteZipToSetup.exe" -Destination $Out -Copy-Item "$In\Win32\WriteZipToSetup.pdb" -Destination $Out - -Write-Output "Successfully copied files for electron-winstaller to build/electron-winstaller." diff --git a/src/build_official.cmd b/src/build_official.cmd new file mode 100644 index 00000000..72b1ff19 --- /dev/null +++ b/src/build_official.cmd @@ -0,0 +1,86 @@ +@echo off + +setlocal +pushd %~dp0 + +:parse_args +@if not "%1"=="" shift & goto parse_args + +:: Init + +@if "%VCToolsVersion%"=="" call :StartDeveloperCommandPrompt || exit /b + + +:: Clean + +rd /s /q ..\build ..\packages ..\test\obj ..\test\bin 2> nul + + +:: Build + +nuget restore ..\Squirrel.sln || exit /b + +msbuild -Restore ..\Squirrel.sln -p:Configuration=Release -v:m -m -nr:false -bl:..\build\logs\build.binlog || exit /b + + +:: Pack .nupkg + +nuget pack Squirrel.nuspec -OutputDirectory ..\build\artifacts || exit /b + + +:: Layout electron-winstaller +:: +:: The NPM package electron-winstaller allows developers to +:: build Windows installers for Electron apps using Squirrel +:: (https://github.com/electron/windows-installer) +:: +:: The following copies the required files into a single folder +:: which can then be copied to the electron-winstaller/vendor folder +:: (either manually or in an automated way). + +md ..\build\artifacts\electron-winstaller\vendor + +copy ..\build\Release\net45\Update.exe ..\build\artifacts\electron-winstaller\vendor\Squirrel.exe || exit /b +copy ..\build\Release\net45\update.com ..\build\artifacts\electron-winstaller\vendor\Squirrel.com || exit /b +copy ..\build\Release\net45\Update.pdb ..\build\artifacts\electron-winstaller\vendor\Squirrel.pdb || exit /b +copy ..\build\Release\Win32\Setup.exe ..\build\artifacts\electron-winstaller\vendor || exit /b +copy ..\build\Release\Win32\Setup.pdb ..\build\artifacts\electron-winstaller\vendor || exit /b +copy ..\build\Release\net45\Update-Mono.exe ..\build\artifacts\electron-winstaller\vendor\Squirrel-Mono.exe || exit /b +copy ..\build\Release\net45\Update-Mono.pdb ..\build\artifacts\electron-winstaller\vendor\Squirrel-Mono.pdb || exit /b +copy ..\build\Release\Win32\StubExecutable.exe ..\build\artifacts\electron-winstaller\vendor || exit /b +copy ..\build\Release\net45\SyncReleases.exe ..\build\artifacts\electron-winstaller\vendor || exit /b +copy ..\build\Release\net45\SyncReleases.pdb ..\build\artifacts\electron-winstaller\vendor || exit /b +copy ..\build\Release\Win32\WriteZipToSetup.exe ..\build\artifacts\electron-winstaller\vendor || exit /b +copy ..\build\Release\Win32\WriteZipToSetup.pdb ..\build\artifacts\electron-winstaller\vendor || exit /b + + +goto LExit + +:StartDeveloperCommandPrompt +if not "%SquirrelSkipVsDevCmd%"=="" ( + echo Skipping initializing developer command prompt + exit /b +) + +echo Initializing developer command prompt + +if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" ( + "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" + exit /b 2 +) + +for /f "usebackq delims=" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [16.0^,18.0^) -property installationPath`) do ( + if exist "%%i\Common7\Tools\vsdevcmd.bat" ( + call "%%i\Common7\Tools\vsdevcmd.bat" -no_logo + exit /b + ) + echo developer command prompt not found in %%i +) + +echo No versions of developer command prompt found +exit /b 2 + +:LExit + +popd +endlocal