From 61e4d334b2bcd0c7a72dcf87ad5d9f46baeb3e4c Mon Sep 17 00:00:00 2001 From: pathtofile Date: Sat, 12 Dec 2020 18:29:21 +1100 Subject: [PATCH] initial with rebase --- .github/workflows/main.yml | 29 +++ .github/workflows/release.yml | 66 +++++++ .gitignore | 7 + README.md | 126 ++++++++++++ child_example/child_example.c | 31 +++ child_example/child_example.vcxproj | 189 ++++++++++++++++++ child_example/child_example.vcxproj.filters | 22 +++ child_example/child_example.vcxproj.user | 4 + elam_driver/elam_driver.c | 23 +++ elam_driver/elam_driver.rc | 16 ++ elam_driver/elam_driver.vcxproj | 183 ++++++++++++++++++ elam_driver/elam_driver.vcxproj.Filters | 31 +++ elam_driver/elam_driver.vcxproj.user | 16 ++ generate_cert.ps1 | 158 +++++++++++++++ ppl_runner.sln | 57 ++++++ ppl_runner/ppl_runner.c | 204 ++++++++++++++++++++ ppl_runner/ppl_runner.h | 14 ++ ppl_runner/ppl_runner.vcxproj | 198 +++++++++++++++++++ ppl_runner/ppl_runner.vcxproj.filters | 33 ++++ ppl_runner/ppl_runner.vcxproj.user | 23 +++ ppl_runner/ppl_runner_common.c | 18 ++ ppl_runner/ppl_runner_service.c | 178 +++++++++++++++++ sign_file.ps1 | 4 + 23 files changed, 1630 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 child_example/child_example.c create mode 100644 child_example/child_example.vcxproj create mode 100644 child_example/child_example.vcxproj.filters create mode 100644 child_example/child_example.vcxproj.user create mode 100644 elam_driver/elam_driver.c create mode 100644 elam_driver/elam_driver.rc create mode 100644 elam_driver/elam_driver.vcxproj create mode 100644 elam_driver/elam_driver.vcxproj.Filters create mode 100644 elam_driver/elam_driver.vcxproj.user create mode 100644 generate_cert.ps1 create mode 100644 ppl_runner.sln create mode 100644 ppl_runner/ppl_runner.c create mode 100644 ppl_runner/ppl_runner.h create mode 100644 ppl_runner/ppl_runner.vcxproj create mode 100644 ppl_runner/ppl_runner.vcxproj.filters create mode 100644 ppl_runner/ppl_runner.vcxproj.user create mode 100644 ppl_runner/ppl_runner_common.c create mode 100644 ppl_runner/ppl_runner_service.c create mode 100644 sign_file.ps1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..409043e --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Setup MsBuild + uses: microsoft/setup-msbuild@v1 + + - name: Generate Certificate + run: powershell.exe -f generate_cert.ps1 + + - name: Build Debug + run: msbuild.exe /nologo /m /t:Rebuild /p:Configuration=Debug ppl_runner.sln + + - name: Build Release + run: msbuild.exe /nologo /m /t:Rebuild /p:Configuration=Release ppl_runner.sln diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3b9366f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,66 @@ +name: Publish Release + +on: + push: + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + + +jobs: + build: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Setup MsBuild + uses: microsoft/setup-msbuild@v1 + + - name: Generate Certificate + run: powershell.exe -f generate_cert.ps1 + + - name: Build Release + run: msbuild.exe /nologo /m /t:Rebuild /p:Configuration=Release ppl_runner.sln + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + + - name: Upload Build - PPLRunner + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/x64/Release/ppl_runner.exe + asset_name: ppl_runner.exe + asset_content_type: application/octet-stream + + - name: Upload Build - ELAMDriver + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/x64/Release/elam_driver.sys + asset_name: elam_driver.sys + asset_content_type: application/octet-stream + + - name: Upload Cert + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./ppl_runner.pfx + asset_name: ppl_runner.pfx + asset_content_type: application/octet-stream diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59097bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.vs/ +.vscode/ +bin/ +*/Win32 +*/x64 +elam_driver/RC* +ppl_runner.pfx diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d2a9bd --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# PPLRunner + +This project is to enable running 'arbitrary`*`' process as an Anti Malware Protected-Process-Light (PPL), for research purposes. + +(`*` See the [Restrictions](#restrictions) section for more details) + +# Overview +System protected process is a security model in Windows designed to protect system and anti-virus processes from +tampering or introspection, even by Administrators/SYSTEM. + +Processes started as an Anti Malware 'Protected Process-Light' (PPL) are restricted in what they can do, can only load signed code, but cannot be debugged, inspected, or stopped by non-Protected Processes. Additionally, they can +get access to special data, such as the `Microsoft-Windows-Threat-Intelligence` ETW Provider. + +This project creates an Early-Launch Anti Malware (ELAM) driver and usermode service. The service will launch a configurable child process when it starts which will also be marked as PPL. + +The child binary must be signed with the same certificate as the service, along with some other [restrictions](#restrictions), but can otherwise be any binary and commandline arguments you chose. + +Honestly I'm not doing a good job of explaining what ELAM and PPL are, instead I recommend starting here: +- Chapter 3, Windows Internals Part 1 (#1 resource for anything Windows honestly) +- https://docs.microsoft.com/en-us/windows/win32/services/protecting-anti-malware-services- +- https://www.crowdstrike.com/blog/protected-processes-part-3-windows-pki-internals-signing-levels-scenarios-signers-root-keys/ +- https://googleprojectzero.blogspot.com/2018/10/injecting-code-into-windows-protected.html + + +# Setup +Make sure you have Windows SDKs installed. + +Open `generate_cert.ps1` and `sign_file.ps1`, and change the `$password` variable to something else (they must match each other). + +Run `generate_cert.ps1`. This will generate a `ppl_runner.pfx` with a new private and public certificate. +This will be used to sign all binaries used by PPLRunner. + +# Build +Build `ppl_runner.sln`. This will produce 3 binaries: +### elam_driver.sys +The ELAM Kernel Driver that has the certificate information in it. +The driver doesn't actually do anything, and won't actually be loaded, it is just used as a vessel for the +signing certificate. + +### ppl_runner.exe +The Service installer and binary. As a PPL service, when started it will launch a child process, +also as PPL, then stop and exit. + +### child_example.exe +An example executable that will be signed with the correct certificate by Visual Studio at build time. +PPLRunner can run almost any binary, this is just an example that will be automatically signed. + +# Install +**NOTE** Only install on a testing machine, not production/your home PC. + +1. Once built, copy `elam_driver.sys` and `ppl_runner.exe` to a folder on the target machine. + +2. Enable test signing by running this from an elevated prompt, then reboot: +```bash +bcdedit /set testsigning on +``` + +3. From an elevated command prompt, browse to the folder containing the copied executables and run: +```bash +ppl_runner.exe install +``` +This should install a service named `ppl_runner`. + +# Configure +To sign a binary to run, sign it with the `ppl_runner.pfx` cert, using either the `sign_file.ps1` script, +or just running `signtool.exe` yourself. If you don't have `signtool.exe`, it is in the Windows SDKs. + +Create the registry key `HKLM\SOFTWARE\PPL_RUNNER`. +Set the default/empty key to be a `REG_SZ`, containing the full path to the binary to execute, +and any commandline argument. e.g. from the commandline: +```cmd +REG.exe ADD HKLM\SOFTWARE\PPL_RUNNER /ve /t REG_SZ /d "C:\path\to\binay --argument 1" +``` + +# Run +To make the service launch the executable, just run from an elevated prompt: +```bash +net start ppl_runner +``` +As a PPL service, when started ppl_runner will read the registry key, launch the child process, +also as PPL, then stop and exit. A successful launch will still say `the service failed to run`, +but if you check the return code with `sc query ppl_runner`, it should be 0, i.e. ERROR_SUCCESS. + +# Cleanup/Removal +As the service is also Anti Malware PPL, it can only be stopped and deleted by a similarly high-level +process. However, we can use PPLRunner to remove itself, simply set the command in the registry key to be: +```powershell +C:\path\to\ppl_runner.exe remove +``` +And run the Service. i.e. run: +```powershell +REG.exe ADD HKLM\SOFTWARE\PPL_RUNNER /ve /t REG_SZ /d "C:\path\to\ppl_runner.exe remove" +net start ppl_runner +``` + +# Restrictions +- This project only works in `testsigning` mode. +- `ppl_runner.exe install` must be re-run after every reboot +- The child binary must be signed with the same certificate as the service +- Any DLLs the binary loads must also be signed + + +# Debugging +Run Sysinternal's DBGView and log `Win32 Global`, filtering on `*[PPL_RUNNER]*`. +This will show all logs from the service and installer. + + +# Example uses +TBD - Sealighter blog + +# Similar Projects +[James Forshaw](https://twitter.com/tiraniddo) created an awesome project to [inject code into existing PPL processes](https://googleprojectzero.blogspot.com/2018/10/injecting-code-into-windows-protected.html). + +# Futher Reading and Thanks +Following [Alex Ionescu](https://twitter.com/aionescu) is probably the best way to learn more about ELAM and PPL. +Possibly start with this: +https://www.crowdstrike.com/blog/protected-processes-part-3-windows-pki-internals-signing-levels-scenarios-signers-root-keys/ + + +Following [Matt Graeber](https://twitter.com/mattifestation) and [James Forshaw](https://twitter.com/tiraniddo) is another great way. + +Massive thanks to Matt for the +[powershell script](https://gist.github.com/mattifestation/660d7e17e43e8f32c38d820115274d2e +) to get the 'To-Be-Signed' hash from a certificate. + +James has [written](https://googleprojectzero.blogspot.com/2017/08/bypassing-virtualbox-process-hardening.html) a [lot](https://googleprojectzero.blogspot.com/2018/10/injecting-code-into-windows-protected.html) about PPL and its flaws. diff --git a/child_example/child_example.c b/child_example/child_example.c new file mode 100644 index 0000000..823c852 --- /dev/null +++ b/child_example/child_example.c @@ -0,0 +1,31 @@ +/* +This is an example Process that can be run by the ppl_runner service + +Visual Studio will build and sing the binary, so it can be run the the service as PPL +*/ + +#include +#include + +void log_msg(WCHAR* format, ...) +{ + WCHAR log_msg[1024]; + + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnwprintf_s(log_msg, 1024, 1024, format, arg_ptr); + va_end(arg_ptr); + OutputDebugString(log_msg); + wprintf(log_msg); +} + +DWORD main(int argc, char** argv) +{ + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + for (DWORD i = 0; i < 100; i++) { + log_msg(L"[PPL_RUNNER] Inside Child Runner: %02d\n", i); + Sleep(3000); + } + return 0; +} diff --git a/child_example/child_example.vcxproj b/child_example/child_example.vcxproj new file mode 100644 index 0000000..d04f880 --- /dev/null +++ b/child_example/child_example.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {3F506F07-052D-4792-9867-885D28201AFC} + Win32Proj + ppl_runner + 10.0 + + + + Application + true + v142 + Unicode + Static + + + Application + false + v142 + true + Unicode + Static + + + Application + true + v142 + Unicode + Static + + + Application + false + v142 + true + Unicode + Static + + + + + + + + + + + + + + + + + + + + + false + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + true + $(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + false + $(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + + + \ No newline at end of file diff --git a/child_example/child_example.vcxproj.filters b/child_example/child_example.vcxproj.filters new file mode 100644 index 0000000..028eb8a --- /dev/null +++ b/child_example/child_example.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/child_example/child_example.vcxproj.user b/child_example/child_example.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/child_example/child_example.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/elam_driver/elam_driver.c b/elam_driver/elam_driver.c new file mode 100644 index 0000000..5021a75 --- /dev/null +++ b/elam_driver/elam_driver.c @@ -0,0 +1,23 @@ +#include +#include +#include + +DRIVER_INITIALIZE DriverEntry; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#endif // ALLOC_PRAGMA + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath +) +{ + // We don't need our driver to do anything + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(RegistryPath); + DbgPrint("[PPL_RUNNER] DriverEntry\n"); + + return STATUS_SUCCESS; +} diff --git a/elam_driver/elam_driver.rc b/elam_driver/elam_driver.rc new file mode 100644 index 0000000..278179f --- /dev/null +++ b/elam_driver/elam_driver.rc @@ -0,0 +1,16 @@ +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "PPL_RUNNER Driver" +#define VER_INTERNALNAME_STR "elam_driver.sys" + +#include "common.ver" +MicrosoftElamCertificateInfo MSElamCertInfoID +{ + 1, + L"36C968D145A9DE42AB3B93AC1FDA96CE19D122593C72A8082C698E02AE7F888D\0", + 0x800C, + L"\0" +} diff --git a/elam_driver/elam_driver.vcxproj b/elam_driver/elam_driver.vcxproj new file mode 100644 index 0000000..bda4644 --- /dev/null +++ b/elam_driver/elam_driver.vcxproj @@ -0,0 +1,183 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD} + $(MSBuildProjectName) + 1 + Debug + Win32 + {015D536C-9F60-4228-AD44-42A90A1AD014} + elam_driver + $(LatestTargetPlatformVersion) + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + + + + + + + + + + + + + + $(Platform)\$(Configuration)\ + $(ProjectName) + + + $(Platform)\$(Configuration)\ + $(ProjectName) + + + $(Platform)\$(Configuration)\ + $(ProjectName) + + + $(Platform)\$(Configuration)\ + $(ProjectName) + + + + true + Level4 + + + + + + + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + true + Level4 + + + + + + + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + true + Level4 + + + + + + + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + true + Level4 + + + + + + + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elam_driver/elam_driver.vcxproj.Filters b/elam_driver/elam_driver.vcxproj.Filters new file mode 100644 index 0000000..55cd2e8 --- /dev/null +++ b/elam_driver/elam_driver.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {00E51A05-37B6-4ADF-8287-9A1E383D471E} + + + h;hpp;hxx;hm;inl;inc;xsd + {60767E85-74A5-45F0-8193-CE3A252910F0} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {84579BC5-14A3-4B25-870C-590B67752AFC} + + + inf;inv;inx;mof;mc; + {0ACF3248-3C68-463F-8976-4323BC0957D6} + + + + + Resource Files + + + + + Source Files + + + \ No newline at end of file diff --git a/elam_driver/elam_driver.vcxproj.user b/elam_driver/elam_driver.vcxproj.user new file mode 100644 index 0000000..fa2ff20 --- /dev/null +++ b/elam_driver/elam_driver.vcxproj.user @@ -0,0 +1,16 @@ + + + + WindowsLocalDebugger + Off + + + Off + + + Off + + + Off + + \ No newline at end of file diff --git a/generate_cert.ps1 b/generate_cert.ps1 new file mode 100644 index 0000000..dbbd2e6 --- /dev/null +++ b/generate_cert.ps1 @@ -0,0 +1,158 @@ +$password = "password" + +# This is very, very graciouslly taken from Matt G! +# https://gist.github.com/mattifestation/660d7e17e43e8f32c38d820115274d2e +filter Get-TBSHash { + [OutputType([String])] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [Security.Cryptography.X509Certificates.X509Certificate2] + $Certificate + ) + + Add-Type -TypeDefinition @' + using System; + using System.Runtime.InteropServices; + namespace Crypto { + public struct CRYPT_DATA_BLOB + { + public uint cbData; + public IntPtr pbData; + } + public struct CRYPT_OBJID_BLOB + { + public uint cbData; + public IntPtr pbData; + } + public struct CRYPT_ALGORITHM_IDENTIFIER + { + public string pszObjId; + public CRYPT_OBJID_BLOB Parameters; + } + public struct CRYPT_BIT_BLOB + { + public uint cbData; + public IntPtr pbData; + public uint cUnusedBits; + } + public struct CERT_SIGNED_CONTENT_INFO + { + public CRYPT_DATA_BLOB ToBeSigned; + public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + public CRYPT_BIT_BLOB Signature; + } + public class NativeMethods { + [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool CryptDecodeObject(uint dwCertEncodingType, IntPtr lpszStructType, [In] byte[] pbEncoded, uint cbEncoded, uint dwFlags, [Out] IntPtr pvStructInto, ref uint pcbStructInfo); + } + } +'@ + + $HashOIDs = @{ + '1.2.840.113549.1.1.4' = 'MD5' + '1.2.840.113549.1.1.5' = 'SHA1' + '1.3.14.3.2.29' = 'SHA1' + '1.2.840.113549.1.1.11' = 'SHA256' + '1.2.840.113549.1.1.12' = 'SHA384' + '1.2.840.113549.1.1.13' = 'SHA512' + } + + $CertBytes = $Certificate.RawData + + $X509_PKCS7_ENCODING = 65537 + $X509_CERT = 1 + $CRYPT_DECODE_TO_BE_SIGNED_FLAG = 2 + $ErrorMoreData = 234 + + $TBSData = [IntPtr]::Zero + [UInt32] $TBSDataSize = 0 + + $Success = [Crypto.NativeMethods]::CryptDecodeObject( + $X509_PKCS7_ENCODING, + [IntPtr] $X509_CERT, + $CertBytes, + $CertBytes.Length, + $CRYPT_DECODE_TO_BE_SIGNED_FLAG, + $TBSData, + [ref] $TBSDataSize + ); $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if((-not $Success) -and ($LastError -ne $ErrorMoreData)) + { + throw "[CryptDecodeObject] Error: $(([ComponentModel.Win32Exception] $LastError).Message)" + } + + $TBSData = [Runtime.InteropServices.Marshal]::AllocHGlobal($TBSDataSize) + + $Success = [Crypto.NativeMethods]::CryptDecodeObject( + $X509_PKCS7_ENCODING, + [IntPtr] $X509_CERT, + $CertBytes, + $CertBytes.Length, + $CRYPT_DECODE_TO_BE_SIGNED_FLAG, + $TBSData, + [ref] $TBSDataSize + ); $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if((-not $Success)) + { + throw "[CryptDecodeObject] Error: $(([ComponentModel.Win32Exception] $LastError).Message)" + } + + $SignedContentInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TBSData, [Type][Crypto.CERT_SIGNED_CONTENT_INFO]) + + $TBSBytes = New-Object Byte[]($SignedContentInfo.ToBeSigned.cbData) + [Runtime.InteropServices.Marshal]::Copy($SignedContentInfo.ToBeSigned.pbData, $TBSBytes, 0, $TBSBytes.Length) + + [Runtime.InteropServices.Marshal]::FreeHGlobal($TBSData) + + $HashAlgorithmStr = $HashOIDs[$SignedContentInfo.SignatureAlgorithm.pszObjId] + + if (-not $HashAlgorithmStr) { throw 'Hash algorithm is not supported or it could not be retrieved.' } + + $HashAlgorithm = [Security.Cryptography.HashAlgorithm]::Create($HashAlgorithmStr) + + $TBSHashBytes = $HashAlgorithm.ComputeHash($TBSBytes) + + ($TBSHashBytes | % { $_.ToString('X2') }) -join '' +} + +# Generate new Certificate +$certFolder = "Cert:\CurrentUser\My" +$cert = New-SelfSignedCertificate -certstorelocation $certFolder -HashAlgorithm SHA256 -Subject "CN=ppl_runner" -TextExtension @("2.5.29.37={text}1.3.6.1.4.1.311.61.4.1,1.3.6.1.5.5.7.3.3") +$certLocation = "$certFolder\"+$cert.Thumbprint +# Use the awesome 'Get-TBSHash' from above +$hash = Get-TBSHash $cert + +# Write Hash to update in resource +Write-Host "SHA256 Hash: $hash" + +# Export from store using the password +$passwordSecure = ConvertTo-SecureString -String $password -Force -AsPlainText +$outputFilename = "ppl_runner.pfx" +Export-PfxCertificate -cert $cert -FilePath $outputFilename -Password $passwordSecure + +# Delete Certificate from store +Remove-Item $certLocation +$cert = $null + +# Update Driver Resource with hash +Write-Output @" +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "ppl_runner Driver" +#define VER_INTERNALNAME_STR "elam_driver.sys" + +#include "common.ver" +MicrosoftElamCertificateInfo MSElamCertInfoID +{ + 1, + L"$hash\0", + 0x800C, + L"\0" +} +"@ | Set-Content -Path ".\elam_driver\elam_driver.rc" +Write-Host "Written Cert and key to $outputFilename" diff --git a/ppl_runner.sln b/ppl_runner.sln new file mode 100644 index 0000000..e164089 --- /dev/null +++ b/ppl_runner.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29806.167 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elam_driver", "elam_driver\elam_driver.vcxproj", "{D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ppl_runner", "ppl_runner\ppl_runner.vcxproj", "{9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}" + ProjectSection(ProjectDependencies) = postProject + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD} = {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "child_example", "child_example\child_example.vcxproj", "{3F506F07-052D-4792-9867-885D28201AFC}" + ProjectSection(ProjectDependencies) = postProject + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF} = {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Debug|Win32.ActiveCfg = Debug|Win32 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Debug|Win32.Build.0 = Debug|Win32 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Debug|x64.ActiveCfg = Debug|x64 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Debug|x64.Build.0 = Debug|x64 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Release|Win32.ActiveCfg = Release|Win32 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Release|Win32.Build.0 = Release|Win32 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Release|x64.ActiveCfg = Release|x64 + {D246CB84-E7A9-4CCF-A32C-EEB23910D7BD}.Release|x64.Build.0 = Release|x64 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Debug|Win32.Build.0 = Debug|Win32 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Debug|x64.ActiveCfg = Debug|x64 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Debug|x64.Build.0 = Debug|x64 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Release|Win32.ActiveCfg = Release|Win32 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Release|Win32.Build.0 = Release|Win32 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Release|x64.ActiveCfg = Release|x64 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF}.Release|x64.Build.0 = Release|x64 + {3F506F07-052D-4792-9867-885D28201AFC}.Debug|Win32.ActiveCfg = Debug|Win32 + {3F506F07-052D-4792-9867-885D28201AFC}.Debug|Win32.Build.0 = Debug|Win32 + {3F506F07-052D-4792-9867-885D28201AFC}.Debug|x64.ActiveCfg = Debug|x64 + {3F506F07-052D-4792-9867-885D28201AFC}.Debug|x64.Build.0 = Debug|x64 + {3F506F07-052D-4792-9867-885D28201AFC}.Release|Win32.ActiveCfg = Release|Win32 + {3F506F07-052D-4792-9867-885D28201AFC}.Release|Win32.Build.0 = Release|Win32 + {3F506F07-052D-4792-9867-885D28201AFC}.Release|x64.ActiveCfg = Release|x64 + {3F506F07-052D-4792-9867-885D28201AFC}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {048C4861-AD36-499B-8649-83666EA2E677} + EndGlobalSection +EndGlobal diff --git a/ppl_runner/ppl_runner.c b/ppl_runner/ppl_runner.c new file mode 100644 index 0000000..889ec8f --- /dev/null +++ b/ppl_runner/ppl_runner.c @@ -0,0 +1,204 @@ +/* +ppl_runner +*/ + +#include +#include +#include "ppl_runner.h" + + +DWORD install_elam_cert() +{ + DWORD retval = 0; + HANDLE fileHandle = NULL; + WCHAR driverName[] = DRIVER_NAME; + + log_message(L"[PPL_RUNNER] install_elam_cert: Opening driver file: %s\n", driverName); + fileHandle = CreateFile(driverName, + FILE_READ_DATA, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + if (fileHandle == INVALID_HANDLE_VALUE) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] install_elam_cert: CreateFile Error: %d\n", retval); + return retval; + } + + if (InstallELAMCertificateInfo(fileHandle) == FALSE) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] install_elam_cert: install_elam_certificateInfo Error: %d\n", retval); + return retval; + } + log_message(L"[PPL_RUNNER] install_elam_cert: Installed ELAM driver cert\n"); + + return retval; +} + +DWORD install_service() +{ + DWORD retval = 0; + SERVICE_LAUNCH_PROTECTED_INFO info; + SC_HANDLE hService; + SC_HANDLE hSCManager; + + DWORD SCManagerAccess = SC_MANAGER_ALL_ACCESS; + hSCManager = OpenSCManager(NULL, NULL, SCManagerAccess); + if (hSCManager == NULL) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] install_service: OpenSCManager Error: %d\n", retval); + return retval; + + } + + // Get full path to ourselves with 'service' argv + WCHAR serviceCMD[MAX_BUF_SIZE] = { 0 }; + GetModuleFileName(NULL, serviceCMD, MAX_BUF_SIZE); + DWORD serviceCMDLen = lstrlenW(serviceCMD); + wcscpy_s(serviceCMD + serviceCMDLen, MAX_BUF_SIZE - serviceCMDLen, L" service"); + + // Add PPL option + info.dwLaunchProtected = SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT; + hService = CreateService( + hSCManager, + SERVICE_NAME, + SERVICE_NAME, + SCManagerAccess, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + serviceCMD, + NULL, + NULL, + NULL, + NULL, /* ServiceAccount */ + NULL + ); + + if (hService == NULL) { + retval = GetLastError(); + if (retval == ERROR_SERVICE_EXISTS) { + log_message(L"[PPL_RUNNER] install_service: CreateService Error: Service '%s' Already Exists\n", SERVICE_NAME); + log_message(L"[PPL_RUNNER] install_service: Run 'net start %s' to start the service\n", SERVICE_NAME); + } + else { + log_message(L"[PPL_RUNNER] install_service: CreateService Error: %d\n", retval); + } + return retval; + } + + // Mark service as protected + if (ChangeServiceConfig2(hService, SERVICE_CONFIG_LAUNCH_PROTECTED, &info) == FALSE) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] install_service: ChangeServiceConfig2 Error: %d\n", retval); + return retval; + } + log_message(L"[PPL_RUNNER] install_service: install_service: Created Service: %s\n", serviceCMD); + log_message(L"[PPL_RUNNER] install_service: Run 'net start %s' to start the service\n", SERVICE_NAME); + return retval; +} + + +DWORD remove_service() { + DWORD retval = 0; + SC_HANDLE hSCManager; + SC_HANDLE hService; + SERVICE_STATUS_PROCESS ssp; + DWORD dwBytesNeeded; + log_message(L"[PPL_RUNNER] remove_service: Stopping and Deleting Service %s...\n", SERVICE_NAME); + + // Get Handle to Service Manager and Service + hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (hSCManager == NULL) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] remove_service: OpenSCManager Error: %d\n", retval); + return retval; + + } + hService = OpenService(hSCManager, SERVICE_NAME, SERVICE_ALL_ACCESS); + if (hService == NULL) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] remove_service: OpenService Error: %d\n", retval); + return retval; + } + + // Get status of service + if (!QueryServiceStatusEx( + hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] remove_service: QueryServiceStatusEx1 Error: %d\n", retval); + return retval; + } + + if (ssp.dwCurrentState != SERVICE_STOPPED) { + // Send a stop code to the service. + if (!ControlService(hService, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp)) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] remove_service: ControlService(Stop) Error: %d\n", retval); + return retval; + } + if (ssp.dwCurrentState != SERVICE_STOPPED) { + // Wait for service to die + Sleep(3000); + if (!QueryServiceStatusEx( + hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] remove_service: QueryServiceStatusEx2 Error: %d\n", retval); + return retval; + } + if (ssp.dwCurrentState != SERVICE_STOPPED) { + retval = ssp.dwCurrentState; + log_message(L"[PPL_RUNNER] remove_service: Waited but service stull not stopped: %d\n", retval); + return retval; + } + } + } + + // Service stopped, now remove it + if (!DeleteService(hService)) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] remove_service: DeleteService Error: %d\n", retval); + return retval; + } + + log_message(L"[PPL_RUNNER] remove_service: Deleted Service %s\n", SERVICE_NAME); + + return retval; +} + + +DWORD main(INT argc, CHAR** argv) +{ + DWORD retval = 0; + log_message(L"[PPL_RUNNER] main: Start\n"); + + if (argc != 2) { + log_message(L"[PPL_RUNNER] usage: ppl_runner.exe \n"); + retval = 1; + } + else if (strcmp(argv[1], "install") == 0) { + log_message(L"[PPL_RUNNER] setting up ELAM stuff...\n"); + retval = install_elam_cert(); + if (retval == 0) { + log_message(L"[PPL_RUNNER] Installing Service...\n"); + retval = install_service(); + } + } + else if (strcmp(argv[1], "service") == 0) { + log_message(L"[PPL_RUNNER] Starting as a service...\n"); + retval = service_entry(); + } + else if (strcmp(argv[1], "remove") == 0) { + log_message(L"[PPL_RUNNER] Removing Service...\n"); + retval = remove_service(); + } + else { + log_message(L"[PPL_RUNNER] invalid commandline option\n"); + retval = 1; + } + + return retval; +} diff --git a/ppl_runner/ppl_runner.h b/ppl_runner/ppl_runner.h new file mode 100644 index 0000000..5eb04be --- /dev/null +++ b/ppl_runner/ppl_runner.h @@ -0,0 +1,14 @@ +#pragma once + +// These are the globals params that can be changed +#define SERVICE_NAME L"ppl_runner" +#define DRIVER_NAME L"elam_driver.sys" +#define CMD_REGKEY L"SOFTWARE\\PPL_RUNNER" +#define MAX_BUF_SIZE 1024 + + +VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv); +VOID WINAPI service_ctrl_handler(DWORD); +DWORD service_entry(); + +VOID log_message(WCHAR* format, ...); diff --git a/ppl_runner/ppl_runner.vcxproj b/ppl_runner/ppl_runner.vcxproj new file mode 100644 index 0000000..7532cf2 --- /dev/null +++ b/ppl_runner/ppl_runner.vcxproj @@ -0,0 +1,198 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {9B632795-1BF2-44C7-8F23-BA24FB0DC0FF} + Win32Proj + elaminstaller + 10.0 + + + + Application + true + v142 + Unicode + Static + + + Application + false + v142 + true + Unicode + Static + + + Application + true + v142 + Unicode + Static + + + Application + false + v142 + true + Unicode + Static + + + + + + + + + + + + + + + + + + + + + true + $(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + false + $(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + Advapi32.lib + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + Advapi32.lib + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + Advapi32.lib + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + Advapi32.lib + + + powershell -ep bypass -f "$(SolutionDir)sign_file.ps1" "$(SolutionDir)ppl_runner.pfx" "$(TargetDir)$(TargetFileName)" + + + Sign + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ppl_runner/ppl_runner.vcxproj.filters b/ppl_runner/ppl_runner.vcxproj.filters new file mode 100644 index 0000000..efd9f9f --- /dev/null +++ b/ppl_runner/ppl_runner.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/ppl_runner/ppl_runner.vcxproj.user b/ppl_runner/ppl_runner.vcxproj.user new file mode 100644 index 0000000..859e8e8 --- /dev/null +++ b/ppl_runner/ppl_runner.vcxproj.user @@ -0,0 +1,23 @@ + + + + $(TargetDir) + WindowsLocalDebugger + install-service + + + $(TargetDir) + WindowsLocalDebugger + install-service + + + $(TargetDir) + WindowsLocalDebugger + install-service + + + $(TargetDir) + WindowsLocalDebugger + install-service + + \ No newline at end of file diff --git a/ppl_runner/ppl_runner_common.c b/ppl_runner/ppl_runner_common.c new file mode 100644 index 0000000..c77f0cc --- /dev/null +++ b/ppl_runner/ppl_runner_common.c @@ -0,0 +1,18 @@ +/* +Common Code and helper functions for ppl_runner +*/ +#include +#include +#include "ppl_runner.h" + +VOID log_message(WCHAR* format, ...) +{ + WCHAR message[MAX_BUF_SIZE]; + + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnwprintf_s(message, MAX_BUF_SIZE, MAX_BUF_SIZE, format, arg_ptr); + va_end(arg_ptr); + OutputDebugString(message); + wprintf(message); +} diff --git a/ppl_runner/ppl_runner_service.c b/ppl_runner/ppl_runner_service.c new file mode 100644 index 0000000..4d02f8f --- /dev/null +++ b/ppl_runner/ppl_runner_service.c @@ -0,0 +1,178 @@ +/* +ppl_runner Service +Code for the Service that gets run +*/ +#include +#include +#include "ppl_runner.h" + +SERVICE_STATUS g_ServiceStatus = { 0 }; +SERVICE_STATUS_HANDLE g_StatusHandle = NULL; + +DWORD start_child_process() +{ + DWORD retval = 0; + WCHAR childCMD[MAX_BUF_SIZE] = { 0 }; + DWORD dataSize = MAX_BUF_SIZE; + log_message(L"[PPL_RUNNER] start_child_process: Starting"); + + // Get Command to run from registry + log_message(L"[PPL_RUNNER] start_child_process: Looking for command in RegKey: HKLM\\%s\n", CMD_REGKEY); + + retval = RegGetValue(HKEY_LOCAL_MACHINE, CMD_REGKEY, NULL, RRF_RT_REG_SZ, NULL, &childCMD, &dataSize); + if (retval != ERROR_SUCCESS) { + log_message(L"[PPL_RUNNER] start_child_process: RegGetValue Error: %d\n", retval); + return retval; + } + + // Create Attribute List + STARTUPINFOEXW StartupInfoEx = { 0 }; + SIZE_T AttributeListSize = 0; + StartupInfoEx.StartupInfo.cb = sizeof(StartupInfoEx); + InitializeProcThreadAttributeList(NULL, 1, 0, &AttributeListSize); + if (AttributeListSize == 0) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] start_child_process: InitializeProcThreadAttributeList1 Error: %d\n", retval); + return retval; + } + StartupInfoEx.lpAttributeList = + (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, AttributeListSize); + if (InitializeProcThreadAttributeList(StartupInfoEx.lpAttributeList, 1, 0, &AttributeListSize) == FALSE) { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] start_child_process: InitializeProcThreadAttributeList2 Error: %d\n", retval); + return retval; + } + + // Set ProtectionLevel to be the same, i.e. PPL + DWORD ProtectionLevel = PROTECTION_LEVEL_SAME; + if (UpdateProcThreadAttribute(StartupInfoEx.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL, + &ProtectionLevel, + sizeof(ProtectionLevel), + NULL, + NULL) == FALSE) + { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] start_child_process: UpdateProcThreadAttribute Error: %d\n", retval); + return retval; + } + + // Start Process (hopefully) + PROCESS_INFORMATION ProcessInformation = { 0 }; + log_message(L"[PPL_RUNNER] start_child_process: Creating Process: '%s'\n", childCMD); + if (CreateProcess(NULL, + childCMD, + NULL, + NULL, + FALSE, + EXTENDED_STARTUPINFO_PRESENT | CREATE_PROTECTED_PROCESS, + NULL, + NULL, + (LPSTARTUPINFOW)&StartupInfoEx, + &ProcessInformation) == FALSE) + { + retval = GetLastError(); + if (retval == ERROR_INVALID_IMAGE_HASH) { + log_message(L"[PPL_RUNNER] start_child_process: CreateProcess Error: Invalid Certificate\n"); + } + else { + log_message(L"[PPL_RUNNER] start_child_process: CreateProcess Error: %d\n", retval); + } + return retval; + } + // Don't wait on process handle, we're setting our child free into the wild + // This is to prevent any possible deadlocks + + log_message(L"[PPL_RUNNER] start_child_process finished"); + return retval; +} + +VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv) +{ + DWORD retval = 0; + log_message(L"[PPL_RUNNER] ServiceMain: Starting\n"); + + // Register our service control handler with the SCM + g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, service_ctrl_handler); + if (g_StatusHandle == NULL) + { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] ServiceMain: Registerservice_ctrl_handler Error: %d\n", retval); + return; + } + ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus)); + g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + + // Start Child Process + retval = start_child_process(); + + // Tell the service controller we are stopped + // So we can be run again + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = retval; + g_ServiceStatus.dwServiceSpecificExitCode = retval; + g_ServiceStatus.dwCheckPoint = 0; + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] ServiceMain: SetServiceStatus(Stopped) Error: %d\n", retval); + return; + } + + log_message(L"[PPL_RUNNER] ServiceMain: Finished"); + return; +} + + +VOID WINAPI service_ctrl_handler(DWORD ctrlCode) +{ + // It should be very unlikley for this function to be called, + // as our service only lives long enough to launch the child process + DWORD retval = 0; + + log_message(L"[PPL_RUNNER] Starting service_ctrl_handler"); + switch (ctrlCode) + { + case SERVICE_CONTROL_STOP: + if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) + break; + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 4; + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] ServiceMain: SetServiceStatus(StopPending) Error: %d\n", retval); + return; + } + break; + + default: + break; + } +} + + +DWORD service_entry() +{ + // Basic boilerplate service setup + DWORD retval = 0; + SERVICE_TABLE_ENTRY serviceTable[] = + { + {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain}, + {NULL, NULL} + }; + + if (StartServiceCtrlDispatcher(serviceTable) == FALSE) + { + retval = GetLastError(); + log_message(L"[PPL_RUNNER] service_entry: StartServiceCtrlDispatcher error: %d\n", retval); + return retval; + } + + return retval; +} diff --git a/sign_file.ps1 b/sign_file.ps1 new file mode 100644 index 0000000..6c3692c --- /dev/null +++ b/sign_file.ps1 @@ -0,0 +1,4 @@ +# Change this: +$password = "password" + +signtool.exe sign /fd SHA256 /a /v /ph /f $args[0] /p $password $args[1]