mirror of
https://github.com/openziti/desktop-edge-win.git
synced 2026-10-15 09:01:27 +00:00
Merge branch 'main' into dns-requests-delay-fix
This commit is contained in:
@@ -22,7 +22,9 @@ jobs:
|
||||
JFROG_ACCESS_TOKEN: ${{ secrets.JFROG_ACCESS_TOKEN }}
|
||||
gh_ci_key: ${{ secrets.GH_CI_KEY }}
|
||||
ZITI_DEBUG: "yes_please"
|
||||
|
||||
OPENZITI_P12_PASS: ${{ secrets.OPENZITI_P12_PASS }}
|
||||
OPENZITI_P12: ${{ secrets.OPENZITI_P12 }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: microsoft/setup-msbuild@v1
|
||||
@@ -39,7 +41,9 @@ jobs:
|
||||
run: |
|
||||
echo "${env:SIGNING_CERT}" > WinSign.b64
|
||||
echo "${env:ADV_INST_KEY}" > ADV_INST_KEY.key
|
||||
echo "${env:OPENZITI_P12}" > OPENZITI_P12.b64
|
||||
C:\Windows\System32\certutil.exe -decode WinSign.b64 Installer/WinSign.p12
|
||||
C:\Windows\System32\certutil.exe -decode OPENZITI_P12.b64 Installer/openziti.p12
|
||||
- name: Trying to run choco install.
|
||||
run: Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
- name: "Install Choco needs: advanced-installer wixtoolset zip ninja"
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ service/libuv.dll
|
||||
Installer/build/
|
||||
Installer/ziti-tunnel-service.zip
|
||||
ziti-tunnel-service.zip
|
||||
WinSign.p12
|
||||
WinSign.*
|
||||
*.p12
|
||||
*.pfx
|
||||
*.pem
|
||||
|
||||
@@ -1439,7 +1439,8 @@ namespace ZitiDesktopEdge {
|
||||
public bool CanExecute(object parameter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma warning disable CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used
|
||||
public event EventHandler CanExecuteChanged;
|
||||
#pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,6 @@ using System.Windows;
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.9.6.0")]
|
||||
[assembly: AssemblyVersion("1.9.6.0")]
|
||||
[assembly: AssemblyFileVersion("1.9.6.0")]
|
||||
// [assembly: AssemblyVersion("1.9.8.0")]
|
||||
[assembly: AssemblyVersion("1.9.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.9.8.0")]
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
<ROW Property="CTRLS" Value="2"/>
|
||||
<ROW Property="Manufacturer" Value="NetFoundry, Inc."/>
|
||||
<ROW Property="MsiLogging" MultiBuildValue="AnyCPU_Release:vp"/>
|
||||
<ROW Property="ProductCode" Value="1033:{831C7D48-901A-4B7D-A2F5-60BFDA7A6859} " Type="16"/>
|
||||
<ROW Property="ProductCode" Value="1033:{AE5F8F1C-44E1-4BA6-8F72-BA58D2641742} " Type="16"/>
|
||||
<ROW Property="ProductLanguage" Value="1033"/>
|
||||
<ROW Property="ProductName" Value="Ziti Desktop Edge"/>
|
||||
<ROW Property="ProductVersion" Value="1.9.6" Type="32"/>
|
||||
<ROW Property="ProductVersion" Value="1.9.8" Type="32"/>
|
||||
<ROW Property="REBOOT" MultiBuildValue="AnyCPU_Release:Suppress"/>
|
||||
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
|
||||
<ROW Property="UpgradeCode" Value="{CB7E173A-663D-4A23-BB22-D1E8D885C2F3}"/>
|
||||
|
||||
+7
-1
@@ -70,5 +70,11 @@ if($gituser -eq "ziti-ci") {
|
||||
} else {
|
||||
echo "detected user [${gituser}] which is not ziti-ci - skipping installer commit"
|
||||
}
|
||||
(Get-FileHash "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256"
|
||||
|
||||
$exeAbsPath="${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe"
|
||||
|
||||
echo "adding additional signature to executable with openziti.org signing certificate"
|
||||
signtool sign /f "${scriptPath}\openziti.p12" /p "${env:OPENZITI_P12_PASS}" /tr http://ts.ssl.com /fd sha512 /td sha512 /as "${exeAbsPath}"
|
||||
|
||||
(Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256"
|
||||
echo "========================== build.ps1 competed =========================="
|
||||
|
||||
@@ -165,10 +165,10 @@ namespace ZitiDesktopEdge.ServiceClient {
|
||||
} else {
|
||||
//ClientDisconnected(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (Exception) {
|
||||
try {
|
||||
ReconnectFailureEvent("reconnect failure");
|
||||
} catch (Exception ex){
|
||||
} catch (Exception){
|
||||
// don't care - just catch it and continue... it's a timeout...
|
||||
}
|
||||
var now = DateTime.Now;
|
||||
|
||||
@@ -6,7 +6,10 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D66B1C6A-ADFE-437E-B037-C4C849A6CA97}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
Installer\build.ps1 = Installer\build.ps1
|
||||
.github\workflows\installer.build.yml = .github\workflows\installer.build.yml
|
||||
README.md = README.md
|
||||
Installer\reg.bat = Installer\reg.bat
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZitiDesktopEdge", "DesktopEdge\ZitiDesktopEdge.csproj", "{FA4291EA-A97E-4854-959F-91EE7B330B37}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<!--
|
||||
@@ -10,32 +10,32 @@
|
||||
|
||||
This operation is should probably remain high such as "1:0:0" (every hour) or "1:0:0:0" (every day)
|
||||
-->
|
||||
<add key="UpdateTimer" value="0:10:0"/>
|
||||
<add key="UpdateTimer" value="0:10:0" />
|
||||
<!--default to look for updates once every ten minutes-->
|
||||
<add key="Version" value="release"/>
|
||||
<add key="ClientSettingsProvider.ServiceUri" value=""/>
|
||||
<add key="UseBetaReleases" value="false"/>
|
||||
<add key="Version" value="release" />
|
||||
<add key="ClientSettingsProvider.ServiceUri" value="" />
|
||||
<add key="UseBetaReleases" value="false" />
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
<system.web>
|
||||
<membership defaultProvider="ClientAuthenticationMembershipProvider">
|
||||
<providers>
|
||||
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri=""/>
|
||||
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
|
||||
</providers>
|
||||
</membership>
|
||||
<roleManager defaultProvider="ClientRoleProvider" enabled="true">
|
||||
<providers>
|
||||
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400"/>
|
||||
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
|
||||
</providers>
|
||||
</roleManager>
|
||||
</system.web>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
@@ -30,6 +30,6 @@ using System.Runtime.InteropServices;
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.9.6.0")]
|
||||
[assembly: AssemblyVersion("1.9.6.0")]
|
||||
[assembly: AssemblyFileVersion("1.9.6.0")]
|
||||
// [assembly: AssemblyVersion("1.9.8.0")]
|
||||
[assembly: AssemblyVersion("1.9.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.9.8.0")]
|
||||
|
||||
@@ -19,6 +19,7 @@ using NLog;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
using DnsClient;
|
||||
using ZitiUpdateService.Checkers.PeFile;
|
||||
|
||||
namespace ZitiUpdateService {
|
||||
public partial class UpdateService : ServiceBase {
|
||||
@@ -33,12 +34,9 @@ namespace ZitiUpdateService {
|
||||
|
||||
public bool useGithubCheck { get; private set; }
|
||||
|
||||
private static string[] expected_subject = new string[] { @"CN=""NetFoundry, Inc."", O=""NetFoundry, Inc."", L=Herndon, S=Virginia, C=US", @"CN=NetFoundry Inc., O=NetFoundry Inc., L=Herndon, S=Virginia, C=US" };
|
||||
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private Timer _updateTimer = new Timer();
|
||||
private Timer openzitiRemovalTimer = new Timer();
|
||||
private bool inUpdateCheck = false;
|
||||
|
||||
private string exeLocation = null;
|
||||
@@ -54,7 +52,7 @@ namespace ZitiUpdateService {
|
||||
IPCServer svr = new IPCServer();
|
||||
Task ipcServer = null;
|
||||
Task eventServer = null;
|
||||
checkers.UpdateCheck check = null;
|
||||
Checkers.UpdateCheck check = null;
|
||||
|
||||
private Timer dnsProbeTimer = new Timer();
|
||||
private IPAddress dnsIpAddress = null;
|
||||
@@ -574,9 +572,9 @@ namespace ZitiUpdateService {
|
||||
updateUrl = "https://api.github.com/repos/openziti/desktop-edge-win-beta/releases/latest";
|
||||
}
|
||||
if (useGithubCheck) {
|
||||
check = new checkers.GithubCheck(updateUrl);
|
||||
check = new Checkers.GithubCheck(updateUrl);
|
||||
} else {
|
||||
check = new checkers.FilesystemCheck(1);
|
||||
check = new Checkers.FilesystemCheck(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,10 +603,10 @@ namespace ZitiUpdateService {
|
||||
Logger.Warn("Still in update check. This is abnormal. Please report if you see this warning");
|
||||
return;
|
||||
}
|
||||
inUpdateCheck = true; //simple semaphone
|
||||
inUpdateCheck = true; //simple semaphore
|
||||
try {
|
||||
Logger.Debug("checking for update");
|
||||
|
||||
|
||||
int avail = check.IsUpdateAvailable(assemblyVersion);
|
||||
if (avail >= 0) {
|
||||
Logger.Debug("update check complete. no update available");
|
||||
@@ -626,8 +624,8 @@ namespace ZitiUpdateService {
|
||||
string fileDestination = Path.Combine(updateFolder, filename);
|
||||
|
||||
if (check.AlreadyDownloaded(updateFolder, filename)) {
|
||||
Logger.Info("package has already been downloaded to {0} - moving to install phase", Path.Combine(updateFolder, filename));
|
||||
} else {
|
||||
Logger.Info("package has already been downloaded to {0} - moving to install phase", fileDestination);
|
||||
} else {
|
||||
Logger.Info("copying update package begins");
|
||||
check.CopyUpdatePackage(updateFolder, filename);
|
||||
Logger.Info("copying update package complete");
|
||||
@@ -640,23 +638,8 @@ namespace ZitiUpdateService {
|
||||
return;
|
||||
}
|
||||
Logger.Debug("downloaded file hash was correct. update can continue.");
|
||||
|
||||
// check digital signature
|
||||
var signer = X509Certificate.CreateFromSignedFile(fileDestination);
|
||||
/* keep these commented out lines - just in case we need all the certs from the file use this
|
||||
var coll = new X509Certificate2Collection();
|
||||
coll.Import(filePath);
|
||||
*/
|
||||
|
||||
var subject = signer.Subject;
|
||||
if (!expected_subject.Contains(subject)) {
|
||||
Logger.Error("the file downloaded uses a subject that is unknown! the installation will not proceed. [subject:{0}]", subject);
|
||||
inUpdateCheck = false;
|
||||
return;
|
||||
|
||||
} else {
|
||||
Logger.Info("the file downloaded uses a known subject. installation and can proceed. [subject:{0}]", subject);
|
||||
}
|
||||
|
||||
new SignedFileValidator(fileDestination).Verify();
|
||||
|
||||
StopZiti();
|
||||
StopUI().Wait();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<TargetFrameworkProfile />
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
@@ -28,7 +29,6 @@
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
@@ -98,9 +98,26 @@
|
||||
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.Security.Cryptography.Pkcs, Version=5.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Pkcs.5.0.1\lib\net461\System.Security.Cryptography.Pkcs.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@@ -113,6 +130,10 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="checkers\FilesystemCheck.cs" />
|
||||
<Compile Include="checkers\GithubCheck.cs" />
|
||||
<Compile Include="checkers\PeFile\SignedFileStructs.cs" />
|
||||
<Compile Include="checkers\PeFile\SignedFileValidator.cs" />
|
||||
<Compile Include="checkers\PeFile\StructHelper.cs" />
|
||||
<Compile Include="checkers\PeFile\Win32Crypto.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="ProjectInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
@@ -150,6 +171,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="ziti-green.ico" />
|
||||
<EmbeddedResource Include="checkers\PeFile\openziti.rootCA.rsa.pem.txt" />
|
||||
<Content Include="ziti-white.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -170,5 +192,6 @@
|
||||
<Name>ZitiDesktopEdge.Client</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using NLog;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ZitiUpdateService.checkers {
|
||||
namespace ZitiUpdateService.Checkers {
|
||||
internal class FilesystemCheck : UpdateCheck {
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
string dest = null;
|
||||
|
||||
@@ -7,8 +7,9 @@ using NLog;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using ZitiDesktopEdge.Utility;
|
||||
using ZitiUpdateService.Checkers.PeFile;
|
||||
|
||||
namespace ZitiUpdateService.checkers {
|
||||
namespace ZitiUpdateService.Checkers {
|
||||
|
||||
internal class GithubCheck : UpdateCheck {
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
@@ -97,7 +98,7 @@ namespace ZitiUpdateService.checkers {
|
||||
File.Delete(sha256dest);
|
||||
return computed.ToLower().Trim() == hash.ToLower().Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public Version GetNextVersion() {
|
||||
return nextVersion;
|
||||
|
||||
@@ -0,0 +1,539 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ZitiUpdateService.Checkers.PeFile {
|
||||
|
||||
//defined by http://www.pinvoke.net/default.aspx/Structures/IMAGE_FILE_HEADER.html
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct IMAGE_FILE_HEADER {
|
||||
public UInt16 Machine;
|
||||
public UInt16 NumberOfSections;
|
||||
public UInt32 TimeDateStamp;
|
||||
public UInt32 PointerToSymbolTable;
|
||||
public UInt32 NumberOfSymbols;
|
||||
public UInt16 SizeOfOptionalHeader;
|
||||
public UInt16 Characteristics;
|
||||
}
|
||||
|
||||
//see http://www.pinvoke.net/default.aspx/Structures/IMAGE_OPTIONAL_HEADER32.html
|
||||
//defined by http://www.pinvoke.net/default.aspx/Structures/IMAGE_OPTIONAL_HEADER32.html
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct IMAGE_DATA_DIRECTORY {
|
||||
public UInt32 VirtualAddress;
|
||||
public UInt32 Size;
|
||||
}
|
||||
|
||||
public enum MachineType : ushort {
|
||||
Native = 0,
|
||||
I386 = 0x014c,
|
||||
Itanium = 0x0200,
|
||||
x64 = 0x8664
|
||||
}
|
||||
|
||||
public sealed class MagicType {
|
||||
public const Int16 IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
|
||||
public const Int16 IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
|
||||
}
|
||||
|
||||
public enum SubSystemType : ushort {
|
||||
IMAGE_SUBSYSTEM_UNKNOWN = 0,
|
||||
IMAGE_SUBSYSTEM_NATIVE = 1,
|
||||
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2,
|
||||
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3,
|
||||
IMAGE_SUBSYSTEM_POSIX_CUI = 7,
|
||||
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9,
|
||||
IMAGE_SUBSYSTEM_EFI_APPLICATION = 10,
|
||||
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11,
|
||||
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12,
|
||||
IMAGE_SUBSYSTEM_EFI_ROM = 13,
|
||||
IMAGE_SUBSYSTEM_XBOX = 14
|
||||
|
||||
}
|
||||
|
||||
public enum DllCharacteristicsType : ushort {
|
||||
RES_0 = 0x0001,
|
||||
RES_1 = 0x0002,
|
||||
RES_2 = 0x0004,
|
||||
RES_3 = 0x0008,
|
||||
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040,
|
||||
IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
|
||||
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100,
|
||||
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200,
|
||||
IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400,
|
||||
IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800,
|
||||
RES_4 = 0x1000,
|
||||
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000,
|
||||
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct IMAGE_OPTIONAL_HEADER32 {
|
||||
[FieldOffset(0)] public ushort Magic;
|
||||
|
||||
[FieldOffset(2)] public byte MajorLinkerVersion;
|
||||
|
||||
[FieldOffset(3)] public byte MinorLinkerVersion;
|
||||
|
||||
[FieldOffset(4)] public uint SizeOfCode;
|
||||
|
||||
[FieldOffset(8)] public uint SizeOfInitializedData;
|
||||
|
||||
[FieldOffset(12)] public uint SizeOfUninitializedData;
|
||||
|
||||
[FieldOffset(16)] public uint AddressOfEntryPoint;
|
||||
|
||||
[FieldOffset(20)] public uint BaseOfCode;
|
||||
|
||||
// PE32 contains this additional field
|
||||
[FieldOffset(24)] public uint BaseOfData;
|
||||
|
||||
[FieldOffset(28)] public uint ImageBase;
|
||||
|
||||
[FieldOffset(32)] public uint SectionAlignment;
|
||||
|
||||
[FieldOffset(36)] public uint FileAlignment;
|
||||
|
||||
[FieldOffset(40)] public ushort MajorOperatingSystemVersion;
|
||||
|
||||
[FieldOffset(42)] public ushort MinorOperatingSystemVersion;
|
||||
|
||||
[FieldOffset(44)] public ushort MajorImageVersion;
|
||||
|
||||
[FieldOffset(46)] public ushort MinorImageVersion;
|
||||
|
||||
[FieldOffset(48)] public ushort MajorSubsystemVersion;
|
||||
|
||||
[FieldOffset(50)] public ushort MinorSubsystemVersion;
|
||||
|
||||
[FieldOffset(52)] public uint Win32VersionValue;
|
||||
|
||||
[FieldOffset(56)] public uint SizeOfImage;
|
||||
|
||||
[FieldOffset(60)] public uint SizeOfHeaders;
|
||||
|
||||
[FieldOffset(64)] public uint CheckSum;
|
||||
|
||||
[FieldOffset(68)] public SubSystemType Subsystem;
|
||||
|
||||
[FieldOffset(70)] public DllCharacteristicsType DllCharacteristics;
|
||||
|
||||
[FieldOffset(72)] public uint SizeOfStackReserve;
|
||||
|
||||
[FieldOffset(76)] public uint SizeOfStackCommit;
|
||||
|
||||
[FieldOffset(80)] public uint SizeOfHeapReserve;
|
||||
|
||||
[FieldOffset(84)] public uint SizeOfHeapCommit;
|
||||
|
||||
[FieldOffset(88)] public uint LoaderFlags;
|
||||
|
||||
[FieldOffset(92)] public uint NumberOfRvaAndSizes;
|
||||
|
||||
[FieldOffset(96)] public IMAGE_DATA_DIRECTORY ExportTable;
|
||||
|
||||
[FieldOffset(104)] public IMAGE_DATA_DIRECTORY ImportTable;
|
||||
|
||||
[FieldOffset(112)] public IMAGE_DATA_DIRECTORY ResourceTable;
|
||||
|
||||
[FieldOffset(120)] public IMAGE_DATA_DIRECTORY ExceptionTable;
|
||||
|
||||
[FieldOffset(128)] public IMAGE_DATA_DIRECTORY CertificateTable;
|
||||
|
||||
[FieldOffset(136)] public IMAGE_DATA_DIRECTORY BaseRelocationTable;
|
||||
|
||||
[FieldOffset(144)] public IMAGE_DATA_DIRECTORY Debug;
|
||||
|
||||
[FieldOffset(152)] public IMAGE_DATA_DIRECTORY Architecture;
|
||||
|
||||
[FieldOffset(160)] public IMAGE_DATA_DIRECTORY GlobalPtr;
|
||||
|
||||
[FieldOffset(168)] public IMAGE_DATA_DIRECTORY TLSTable;
|
||||
|
||||
[FieldOffset(176)] public IMAGE_DATA_DIRECTORY LoadConfigTable;
|
||||
|
||||
[FieldOffset(184)] public IMAGE_DATA_DIRECTORY BoundImport;
|
||||
|
||||
[FieldOffset(192)] public IMAGE_DATA_DIRECTORY IAT;
|
||||
|
||||
[FieldOffset(200)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
|
||||
|
||||
[FieldOffset(208)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
|
||||
|
||||
[FieldOffset(216)] public IMAGE_DATA_DIRECTORY Reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct IMAGE_OPTIONAL_HEADER64 {
|
||||
[FieldOffset(0)] public ushort Magic;
|
||||
|
||||
[FieldOffset(2)] public byte MajorLinkerVersion;
|
||||
|
||||
[FieldOffset(3)] public byte MinorLinkerVersion;
|
||||
|
||||
[FieldOffset(4)] public uint SizeOfCode;
|
||||
|
||||
[FieldOffset(8)] public uint SizeOfInitializedData;
|
||||
|
||||
[FieldOffset(12)] public uint SizeOfUninitializedData;
|
||||
|
||||
[FieldOffset(16)] public uint AddressOfEntryPoint;
|
||||
|
||||
[FieldOffset(20)] public uint BaseOfCode;
|
||||
|
||||
[FieldOffset(24)] public ulong ImageBase;
|
||||
|
||||
[FieldOffset(32)] public uint SectionAlignment;
|
||||
|
||||
[FieldOffset(36)] public uint FileAlignment;
|
||||
|
||||
[FieldOffset(40)] public ushort MajorOperatingSystemVersion;
|
||||
|
||||
[FieldOffset(42)] public ushort MinorOperatingSystemVersion;
|
||||
|
||||
[FieldOffset(44)] public ushort MajorImageVersion;
|
||||
|
||||
[FieldOffset(46)] public ushort MinorImageVersion;
|
||||
|
||||
[FieldOffset(48)] public ushort MajorSubsystemVersion;
|
||||
|
||||
[FieldOffset(50)] public ushort MinorSubsystemVersion;
|
||||
|
||||
[FieldOffset(52)] public uint Win32VersionValue;
|
||||
|
||||
[FieldOffset(56)] public uint SizeOfImage;
|
||||
|
||||
[FieldOffset(60)] public uint SizeOfHeaders;
|
||||
|
||||
[FieldOffset(64)] public uint CheckSum;
|
||||
|
||||
[FieldOffset(68)] public SubSystemType Subsystem;
|
||||
|
||||
[FieldOffset(70)] public DllCharacteristicsType DllCharacteristics;
|
||||
|
||||
[FieldOffset(72)] public ulong SizeOfStackReserve;
|
||||
|
||||
[FieldOffset(80)] public ulong SizeOfStackCommit;
|
||||
|
||||
[FieldOffset(88)] public ulong SizeOfHeapReserve;
|
||||
|
||||
[FieldOffset(96)] public ulong SizeOfHeapCommit;
|
||||
|
||||
[FieldOffset(104)] public uint LoaderFlags;
|
||||
|
||||
[FieldOffset(108)] public uint NumberOfRvaAndSizes;
|
||||
|
||||
[FieldOffset(112)] public IMAGE_DATA_DIRECTORY ExportTable;
|
||||
|
||||
[FieldOffset(120)] public IMAGE_DATA_DIRECTORY ImportTable;
|
||||
|
||||
[FieldOffset(128)] public IMAGE_DATA_DIRECTORY ResourceTable;
|
||||
|
||||
[FieldOffset(136)] public IMAGE_DATA_DIRECTORY ExceptionTable;
|
||||
|
||||
[FieldOffset(144)] public IMAGE_DATA_DIRECTORY CertificateTable;
|
||||
|
||||
[FieldOffset(152)] public IMAGE_DATA_DIRECTORY BaseRelocationTable;
|
||||
|
||||
[FieldOffset(160)] public IMAGE_DATA_DIRECTORY Debug;
|
||||
|
||||
[FieldOffset(168)] public IMAGE_DATA_DIRECTORY Architecture;
|
||||
|
||||
[FieldOffset(176)] public IMAGE_DATA_DIRECTORY GlobalPtr;
|
||||
|
||||
[FieldOffset(184)] public IMAGE_DATA_DIRECTORY TLSTable;
|
||||
|
||||
[FieldOffset(192)] public IMAGE_DATA_DIRECTORY LoadConfigTable;
|
||||
|
||||
[FieldOffset(200)] public IMAGE_DATA_DIRECTORY BoundImport;
|
||||
|
||||
[FieldOffset(208)] public IMAGE_DATA_DIRECTORY IAT;
|
||||
|
||||
[FieldOffset(216)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
|
||||
|
||||
[FieldOffset(224)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
|
||||
|
||||
[FieldOffset(232)] public IMAGE_DATA_DIRECTORY Reserved;
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct IMAGE_SECTION_HEADER {
|
||||
//FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html
|
||||
[FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public char[] Name;
|
||||
|
||||
[FieldOffset(8)] public UInt32 VirtualSize;
|
||||
|
||||
[FieldOffset(12)] public UInt32 VirtualAddress;
|
||||
|
||||
[FieldOffset(16)] public UInt32 SizeOfRawData;
|
||||
|
||||
[FieldOffset(20)] public UInt32 PointerToRawData;
|
||||
|
||||
[FieldOffset(24)] public UInt32 PointerToRelocations;
|
||||
|
||||
[FieldOffset(28)] public UInt32 PointerToLinenumbers;
|
||||
|
||||
[FieldOffset(32)] public UInt16 NumberOfRelocations;
|
||||
|
||||
[FieldOffset(34)] public UInt16 NumberOfLinenumbers;
|
||||
|
||||
[FieldOffset(36)] public DataSectionFlags Characteristics;
|
||||
|
||||
public string Section {
|
||||
get { return new string(Name); }
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DataSectionFlags : uint {
|
||||
//FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
TypeReg = 0x00000000,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
TypeDsect = 0x00000001,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
TypeNoLoad = 0x00000002,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
TypeGroup = 0x00000004,
|
||||
|
||||
/// <summary>
|
||||
/// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files.
|
||||
/// </summary>
|
||||
TypeNoPadded = 0x00000008,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
TypeCopy = 0x00000010,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains executable code.
|
||||
/// </summary>
|
||||
ContentCode = 0x00000020,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains initialized data.
|
||||
/// </summary>
|
||||
ContentInitializedData = 0x00000040,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains uninitialized data.
|
||||
/// </summary>
|
||||
ContentUninitializedData = 0x00000080,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
LinkOther = 0x00000100,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
|
||||
/// </summary>
|
||||
LinkInfo = 0x00000200,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
TypeOver = 0x00000400,
|
||||
|
||||
/// <summary>
|
||||
/// The section will not become part of the image. This is valid only for object files.
|
||||
/// </summary>
|
||||
LinkRemove = 0x00000800,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files.
|
||||
/// </summary>
|
||||
LinkComDat = 0x00001000,
|
||||
|
||||
/// <summary>
|
||||
/// Reset speculative exceptions handling bits in the TLB entries for this section.
|
||||
/// </summary>
|
||||
NoDeferSpecExceptions = 0x00004000,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains data referenced through the global pointer (GP).
|
||||
/// </summary>
|
||||
RelativeGP = 0x00008000,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
MemPurgeable = 0x00020000,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
Memory16Bit = 0x00020000,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
MemoryLocked = 0x00040000,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future use.
|
||||
/// </summary>
|
||||
MemoryPreload = 0x00080000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 1-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align1Bytes = 0x00100000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 2-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align2Bytes = 0x00200000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 4-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align4Bytes = 0x00300000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on an 8-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align8Bytes = 0x00400000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 16-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align16Bytes = 0x00500000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 32-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align32Bytes = 0x00600000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 64-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align64Bytes = 0x00700000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 128-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align128Bytes = 0x00800000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 256-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align256Bytes = 0x00900000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 512-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align512Bytes = 0x00A00000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 1024-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align1024Bytes = 0x00B00000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 2048-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align2048Bytes = 0x00C00000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on a 4096-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align4096Bytes = 0x00D00000,
|
||||
|
||||
/// <summary>
|
||||
/// Align data on an 8192-byte boundary. Valid only for object files.
|
||||
/// </summary>
|
||||
Align8192Bytes = 0x00E00000,
|
||||
|
||||
/// <summary>
|
||||
/// The section contains extended relocations.
|
||||
/// </summary>
|
||||
LinkExtendedRelocationOverflow = 0x01000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section can be discarded as needed.
|
||||
/// </summary>
|
||||
MemoryDiscardable = 0x02000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section cannot be cached.
|
||||
/// </summary>
|
||||
MemoryNotCached = 0x04000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section is not pageable.
|
||||
/// </summary>
|
||||
MemoryNotPaged = 0x08000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section can be shared in memory.
|
||||
/// </summary>
|
||||
MemoryShared = 0x10000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section can be executed as code.
|
||||
/// </summary>
|
||||
MemoryExecute = 0x20000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section can be read.
|
||||
/// </summary>
|
||||
MemoryRead = 0x40000000,
|
||||
|
||||
/// <summary>
|
||||
/// The section can be written to.
|
||||
/// </summary>
|
||||
MemoryWrite = 0x80000000
|
||||
}
|
||||
|
||||
|
||||
public class ImageOptionalHeaderWrapper {
|
||||
public IMAGE_OPTIONAL_HEADER32 h32;
|
||||
public IMAGE_OPTIONAL_HEADER64 h64;
|
||||
public bool IsPe;
|
||||
|
||||
|
||||
public uint CertificateTableVirtualAddress {
|
||||
get {
|
||||
if (IsPe) {
|
||||
return h32.CertificateTable.VirtualAddress;
|
||||
} else {
|
||||
return h64.CertificateTable.VirtualAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint CertificateTableSize {
|
||||
get {
|
||||
if (IsPe) {
|
||||
return h32.CertificateTable.Size;
|
||||
} else {
|
||||
return h64.CertificateTable.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint SizeOfHeaders {
|
||||
get {
|
||||
if (IsPe) {
|
||||
return h32.SizeOfHeaders;
|
||||
} else {
|
||||
return h64.SizeOfHeaders;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.Pkcs;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using VerifyingFiles.PInvoke;
|
||||
|
||||
namespace ZitiUpdateService.Checkers.PeFile {
|
||||
public class SignedFileValidator {
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public const int LengthOfChecksum = 4; //the checksum is 4 bytes
|
||||
public const int LengthCertificateTable = 8; //the Certificate Table is 8 bytes
|
||||
|
||||
public HashPositions ImportantHashPositions { get; private set; }
|
||||
public string FilePath { get; private set; }
|
||||
public PeType Type { get; private set; }
|
||||
public IMAGE_FILE_HEADER CoffHeader { get; private set; }
|
||||
public IMAGE_OPTIONAL_HEADER32 StandardFieldsPe32 { get; private set; }
|
||||
public IMAGE_OPTIONAL_HEADER64 StandardFieldsPe32Plus { get; private set; }
|
||||
|
||||
private readonly X509Certificate2 expectedRootCa = null;
|
||||
private readonly X509Store empty = new X509Store();
|
||||
private X509Store SystemCAs = new X509Store();
|
||||
private const string oldKnownThumbprint = "39636E9F5E80308DE370C914CE8112876ECF4E0C";
|
||||
|
||||
public SignedFileValidator(string pathToFile) {
|
||||
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
|
||||
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
|
||||
SystemCAs.Open(OpenFlags.ReadWrite);
|
||||
SystemCAs.AddRange(store.Certificates);
|
||||
|
||||
expectedRootCa = certFromResource("ZitiUpdateService.checkers.PeFile.openziti.rootCA.rsa.pem.txt");
|
||||
FilePath = pathToFile;
|
||||
ImportantHashPositions = new HashPositions();
|
||||
|
||||
parse();
|
||||
if (ImportantHashPositions.reorderNeeded) {
|
||||
ImportantHashPositions.SectionTableHeaders.Sort((x, y) => x.PointerToRawData.CompareTo(y.PointerToRawData));
|
||||
}
|
||||
}
|
||||
|
||||
private X509Certificate2 certFromResource(string resourceName) {
|
||||
var assembly = this.GetType().Assembly;
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
using (BinaryReader reader = new BinaryReader(stream)) {
|
||||
byte[] bytes = new byte[stream.Length];
|
||||
stream.Read(bytes, 0, bytes.Length);
|
||||
return new X509Certificate2(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private void parse() {
|
||||
using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
|
||||
using (BinaryReader reader = new BinaryReader(stream)) {
|
||||
|
||||
//seek to 0x3c to get right to the PE header location...
|
||||
stream.Seek(0x3c, SeekOrigin.Begin);
|
||||
uint startCoffHeader = BitConverter.ToUInt32(reader.ReadBytes(4), 0);
|
||||
ImportantHashPositions.SetStartCoffHeader(startCoffHeader);
|
||||
stream.Seek(startCoffHeader, SeekOrigin.Begin);
|
||||
|
||||
//parse COFF Header (28 or 24 bytes): https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
|
||||
byte[] coffHeaderBytes = reader.ReadBytes(24);
|
||||
CoffHeader = StructHelper.FromBytes<IMAGE_FILE_HEADER>(coffHeaderBytes);
|
||||
|
||||
byte[] magicNumberBytes = reader.ReadBytes(2);
|
||||
int magicNumber = BitConverter.ToInt16(magicNumberBytes, 0);
|
||||
Type = magicNumber == 0x10b ? PeType.Pe32 : PeType.Pe32Plus;
|
||||
stream.Seek(-2, SeekOrigin.Current); //skip back to before the magic number so the struct below is created properly
|
||||
|
||||
//parse Optional Header Standard and Windows-Specific fields (66 or 88 bytes)
|
||||
//see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only
|
||||
if (Type == PeType.Pe32) {
|
||||
byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER32)));
|
||||
StandardFieldsPe32 = StructHelper.FromBytes<IMAGE_OPTIONAL_HEADER32>(headerBytes);
|
||||
} else {
|
||||
byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER64)));
|
||||
StandardFieldsPe32Plus = StructHelper.FromBytes<IMAGE_OPTIONAL_HEADER64>(headerBytes);
|
||||
}
|
||||
|
||||
ImageOptionalHeaderWrapper wrapper = new ImageOptionalHeaderWrapper() {
|
||||
h32 = StandardFieldsPe32,
|
||||
h64 = StandardFieldsPe32Plus,
|
||||
IsPe = Type == PeType.Pe32
|
||||
};
|
||||
ImportantHashPositions.SetCertificateDetails(Type, wrapper.SizeOfHeaders, wrapper.CertificateTableVirtualAddress, wrapper.CertificateTableSize);
|
||||
|
||||
for (int i = 0; i < CoffHeader.NumberOfSections; i++) {
|
||||
byte[] nextSection = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER))); //should be 40 bytes...
|
||||
IMAGE_SECTION_HEADER sectionTableHeader = StructHelper.FromBytes<IMAGE_SECTION_HEADER>(nextSection);
|
||||
ImportantHashPositions.AddSectionTableHeader(sectionTableHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addCertToList(SignerInfo si, List<X509Certificate2> list) {
|
||||
|
||||
X509Certificate2 cert = si.Certificate;
|
||||
if (list.Find(x => x.Thumbprint == cert.Thumbprint) == null) {
|
||||
list.Add(cert);
|
||||
} else {
|
||||
Logger.Debug("Certificate with Thumbprint {0} already in list. skipping.", cert.Thumbprint);
|
||||
}
|
||||
}
|
||||
|
||||
public List<X509Certificate2> ExtractVerifiedSignatureCertificates() {
|
||||
List<X509Certificate2> list = new List<X509Certificate2>();
|
||||
using (FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
|
||||
using (BinaryReader reader = new BinaryReader(stream)) {
|
||||
//verify all the embedded signatures
|
||||
stream.Seek(this.ImportantHashPositions.CertificateTableStart, SeekOrigin.Begin);
|
||||
int readCertLen = reader.ReadInt32();
|
||||
int readRevision = reader.ReadInt16();
|
||||
int readCertType = reader.ReadInt16();
|
||||
byte[] pkcs7 = reader.ReadBytes(readCertLen);
|
||||
|
||||
SignedCms cms = new SignedCms();
|
||||
cms.Decode(pkcs7);
|
||||
cms.CheckHash();
|
||||
|
||||
//shout out to Scott MG: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/655f5c27-b049-4275-a8b3-cc1c0be2b4f2/retrieve-certificate-info-for-dualsigned-sha1sha256
|
||||
//for indicating that the unsigned attributes contain the other SignerInfos
|
||||
foreach (var cmsSi in cms.SignerInfos) {
|
||||
cmsSi.CheckSignature(true);
|
||||
if (VerifyTrust(SystemCAs, expectedRootCa, cmsSi.Certificate).Result) {
|
||||
VerifyFileHash(cms, cmsSi);
|
||||
addCertToList(cmsSi, list);
|
||||
}
|
||||
if (cmsSi.UnsignedAttributes.Count > 0) {
|
||||
foreach (var unsignedAttr in cmsSi.UnsignedAttributes) {
|
||||
foreach (AsnEncodedData asn in unsignedAttr.Values) {
|
||||
SignedCms innerCms = new SignedCms();
|
||||
innerCms.Decode(asn.RawData);
|
||||
innerCms.CheckHash();
|
||||
if (innerCms.SignerInfos.Count > 0) {
|
||||
SignerInfo innerSignerInfo = innerCms.SignerInfos[0];
|
||||
try {
|
||||
innerSignerInfo.CheckSignature(false);
|
||||
}
|
||||
catch (CryptographicException) {
|
||||
if (innerSignerInfo.Certificate.Thumbprint == oldKnownThumbprint) {
|
||||
//special handling for the known 'old' NetFoundry signing certificate, now expired...
|
||||
//TODO: remove this code after 2021
|
||||
//just allow this one error
|
||||
Logger.Warn("Ignoring timestamp validity issue for the existing code signing certificate. Subject: {0}, Thumbprint: {1}", innerSignerInfo.Certificate.Subject, innerSignerInfo.Certificate.Thumbprint);
|
||||
} else {
|
||||
if (IsCertificateOpenZitiVerifies(innerSignerInfo.Certificate)) {
|
||||
Logger.Debug("Signature could not be verified. This is expected for the certificates issued by openziti/netfoundry.");
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (VerifyTrust(SystemCAs, expectedRootCa, innerSignerInfo.Certificate).Result) {
|
||||
VerifyFileHash(innerCms, innerSignerInfo);
|
||||
addCertToList(innerSignerInfo, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public bool IsCertificateOpenZitiVerifies(X509Certificate2 cert) {
|
||||
bool yesOrNo = cert.Subject.ToLower().Contains("netfoundry") || cert.Subject.ToLower().Contains("openziti");
|
||||
if (yesOrNo) {
|
||||
Logger.Debug("Certificate does need verification: {0}", cert.Subject);
|
||||
} else {
|
||||
Logger.Debug("Certificate does not need verification: {0}", cert.Subject);
|
||||
}
|
||||
return yesOrNo;
|
||||
}
|
||||
|
||||
public void Verify() {
|
||||
Logger.Debug("extracting certificates from file: {0}", FilePath);
|
||||
List<X509Certificate2> list = ExtractVerifiedSignatureCertificates();
|
||||
Logger.Info("Certificates extracted: {0}", list.Count);
|
||||
foreach (X509Certificate2 cert in list) {
|
||||
Logger.Info("Checking certificate: [{0}] {1}", cert.Thumbprint, cert);
|
||||
if (IsCertificateOpenZitiVerifies(cert)) {
|
||||
//verify this certificate was issued from the known CA
|
||||
try {
|
||||
Logger.Info("Verifying trust of certificate: [{0}] {1}", cert, cert.Thumbprint);
|
||||
VerifyTrust(empty, expectedRootCa, cert, true).Wait();
|
||||
Logger.Info("Certificate [{0}] {1} was verified signed by [{2}] {3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject);
|
||||
return; //yes!
|
||||
}
|
||||
catch {
|
||||
//empty on purpose. keep checking...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new CryptographicException("Executable not signed by an appropriate certificate");
|
||||
}
|
||||
|
||||
public void VerifyFileHash(SignedCms cms, SignerInfo signerInfo) {
|
||||
if (!IsCertificateOpenZitiVerifies(signerInfo.Certificate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//calculate the hash using the first signed info
|
||||
var hashAlg = IncrementalHash.CreateHash(new HashAlgorithmName(signerInfo.DigestAlgorithm.FriendlyName.ToUpper()));
|
||||
byte[] calculatedHash = CalculatePeHashStreaming(hashAlg);
|
||||
string hash = BitConverter.ToString(calculatedHash).Replace("-", "");
|
||||
|
||||
string content = BitConverter.ToString(cms.ContentInfo.Content).Replace("-", "");
|
||||
if (!content.Contains(hash)) {
|
||||
throw new CryptographicException("The expected hash and the actual hash did not match!");
|
||||
}
|
||||
}
|
||||
|
||||
public async static Task<bool> VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) {
|
||||
return await VerifyTrust(cas, trustedRootCertificateAuthority, certificate, false);
|
||||
}
|
||||
|
||||
public async static Task<bool> VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate, bool verifyTrustedRoot) {
|
||||
X509Chain chain = new X509Chain();
|
||||
chain.ChainPolicy.ExtraStore.Add(trustedRootCertificateAuthority);
|
||||
chain.ChainPolicy.ExtraStore.AddRange(cas.Certificates);
|
||||
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
|
||||
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
|
||||
chain.Build(new X509Certificate2(certificate));
|
||||
List<Exception> exceptions = new List<Exception>();
|
||||
foreach (X509ChainStatus status in chain.ChainStatus) {
|
||||
if (status.Status == X509ChainStatusFlags.NoError || status.Status == X509ChainStatusFlags.UntrustedRoot) {
|
||||
//X509ChainStatusFlags.UntrustedRoot simply means it was not found in the computer/user's trust store.... that's fine...
|
||||
} else {
|
||||
if (status.Status == X509ChainStatusFlags.NotTimeValid && certificate.Thumbprint == oldKnownThumbprint) {
|
||||
//X509ChainStatusFlags.NotTimeValid means the certificate has expired - we're allowing this for now (summer 2021) to allow older clients to update
|
||||
Logger.Warn("Executable is signed using the old signing certificate. Allowing this certificate to report as expired");
|
||||
} else {
|
||||
exceptions.Add(new CryptographicException("Could not verify trust: " + status.StatusInformation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//final check - make sure the last certificate matches the expected thumbprint
|
||||
if (verifyTrustedRoot && chain.ChainElements[chain.ChainElements.Count - 1].Certificate.Thumbprint != trustedRootCertificateAuthority.Thumbprint) {
|
||||
exceptions.Add(new Exception("Could not verify trust. The expected thumbprint was not found!"));
|
||||
}
|
||||
|
||||
foreach (var e in certificate.Extensions) {
|
||||
if (e.Oid.Value == "2.5.29.31") { //2.5.29.31 == CRL Distribution Points
|
||||
try {
|
||||
CrlDistributionPointParser t = new CrlDistributionPointParser(e.RawData);
|
||||
|
||||
if (t != null) {
|
||||
foreach (string url in t.urls) {
|
||||
try {
|
||||
//good - things are going the way we want... keep going
|
||||
//fetch the CRL from the url provided and parse it...
|
||||
byte[] crlBytes = await new HttpClient().GetByteArrayAsync(url);
|
||||
|
||||
if (crlBytes != null && crlBytes.Length > 0) {
|
||||
//fetch the crl
|
||||
Win32Crypto.CrlInfo info = Win32Crypto.FromBlob(crlBytes);
|
||||
if (info.RevokedSerialNumbers.Contains(certificate.SerialNumber)) {
|
||||
exceptions.Add(new CryptographicException("Serial number " + certificate.SerialNumber + " has been revoked."));
|
||||
}
|
||||
} else {
|
||||
exceptions.Add(new CryptographicException("Could not retrieve revocation list from " + url + "- cannot verify trust"));
|
||||
}
|
||||
}
|
||||
catch (Exception innerException) {
|
||||
exceptions.Add(new CryptographicException("crl at " + url + " could not be used", innerException));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Count > 0) {
|
||||
throw new AggregateException(exceptions);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] CalculatePeHashStreaming(IncrementalHash alg) {
|
||||
using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
|
||||
using (BinaryReader reader = new BinaryReader(stream)) {
|
||||
stream.Seek(ImportantHashPositions.Chunk1.Start, SeekOrigin.Begin);
|
||||
alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk1.Length));
|
||||
|
||||
stream.Seek(ImportantHashPositions.Chunk2.Start, SeekOrigin.Begin);
|
||||
alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk2.Length));
|
||||
|
||||
stream.Seek(ImportantHashPositions.Chunk3.Start, SeekOrigin.Begin);
|
||||
alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk3.Length));
|
||||
|
||||
int blockSize = 8192;
|
||||
int read = 0;
|
||||
byte[] buf = new byte[blockSize];
|
||||
|
||||
foreach (IMAGE_SECTION_HEADER h in ImportantHashPositions.SectionTableHeaders) {
|
||||
stream.Seek(h.PointerToRawData, SeekOrigin.Begin);
|
||||
if (h.SizeOfRawData <= blockSize) {
|
||||
//just read the full block as one
|
||||
alg.AppendData(reader.ReadBytes((int)h.SizeOfRawData));
|
||||
} else {
|
||||
long pos = stream.Position;
|
||||
long end = h.PointerToRawData + h.SizeOfRawData - blockSize;
|
||||
//process the data into blocks
|
||||
while (pos < end) {
|
||||
read = stream.Read(buf, 0, blockSize);
|
||||
alg.AppendData(buf, 0, read);
|
||||
pos += blockSize;
|
||||
}
|
||||
|
||||
int remaining = (int)(end - pos + blockSize);
|
||||
if (remaining > 0) {
|
||||
read = stream.Read(buf, 0, remaining);
|
||||
alg.AppendData(buf, 0, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
14. Create a value called FILE_SIZE, which is not part of the signature. Set this value to the
|
||||
image’s file size, acquired from the underlying file system. If FILE_SIZE is greater than
|
||||
SUM_OF_BYTES_HASHED, the file contains extra data that must be added to the hash. This data
|
||||
begins at the SUM_OF_BYTES_HASHED file offset, and its length is:
|
||||
(File Size) – ((Size of AttributeCertificateTable) + SUM_OF_BYTES_HASHED)
|
||||
Note: The size of Attribute Certificate Table is specified in the second ULONG value in the
|
||||
Certificate Table entry (32 bit: offset 132, 64 bit: offset 148) in Optional Header Data
|
||||
Directories.
|
||||
*/
|
||||
long FILE_SIZE = new FileInfo(FilePath).Length;
|
||||
|
||||
long leftoverBytesToHash = FILE_SIZE - ImportantHashPositions.SumOfBytesHashed - ImportantHashPositions.CertificateTableSize;
|
||||
|
||||
if (leftoverBytesToHash > 0) {
|
||||
//HashBlock(alg, reader, SUM_OF_BYTES_HASHED + certTableSize, FILE_SIZE);
|
||||
stream.Seek(ImportantHashPositions.SumOfBytesHashed, SeekOrigin.Begin);
|
||||
byte[] leftoverBytes = reader.ReadBytes((int)leftoverBytesToHash);
|
||||
alg.AppendData(leftoverBytes);
|
||||
} else {
|
||||
//no more bytes to hash...
|
||||
}
|
||||
//alg.TransformFinalBlock(new byte[0], 0, 0);
|
||||
|
||||
byte[] result = alg.GetHashAndReset();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class HashPositions {
|
||||
internal HashPositions() {
|
||||
Chunk1 = new Range();
|
||||
Chunk2 = new Range();
|
||||
Chunk3 = new Range();
|
||||
SectionTableHeaders = new List<IMAGE_SECTION_HEADER>();
|
||||
}
|
||||
/*
|
||||
* These functions are all based from the document linked to from the bottom of:
|
||||
* https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only
|
||||
*
|
||||
* Link to the document:
|
||||
* https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx
|
||||
*
|
||||
* Relevant section copied from that document:
|
||||
* To calculate the hash value:
|
||||
1. Load the image header into memory.
|
||||
2. Initialize a hash algorithm context.
|
||||
3. Hash the image header from its base to immediately before the start of the
|
||||
checksum address, as specified in Optional Header Windows-Specific Fields.
|
||||
4. Skip over the checksum, which is a 4-byte field.
|
||||
5. Hash everything from the end of the checksum field to immediately before the start of
|
||||
the Certificate Table entry, as specified in Optional Header Data Directories.
|
||||
6. Get the Attribute Certificate Table address and size from the Certificate Table entry.
|
||||
For details, see section 5.7 of the PE/COFF specification.
|
||||
7. Exclude the Certificate Table entry from the calculation and hash everything from the
|
||||
end of the Certificate Table entry to the end of image header, including Section Table
|
||||
(headers).The Certificate Table entry is 8 bytes long, as specified in Optional Header
|
||||
Data Directories.
|
||||
8. Create a counter called SUM_OF_BYTES_HASHED, which is not part of the signature. Set
|
||||
this counter to the SizeOfHeaders field, as specified in Optional Header Windows-Specific
|
||||
Field.
|
||||
9. Build a temporary table of pointers to all of the section headers in the image. The
|
||||
NumberOfSections field of COFF File Header indicates how big the table should be. Do not
|
||||
include any section headers in the table whose SizeOfRawData field is zero.
|
||||
10. Using the PointerToRawData field (offset 20) in the referenced SectionHeader structure as
|
||||
a key, arrange the table's elements in ascending order. In other words, sort the section
|
||||
headers in ascending order according to the disk-file offset of the sections.
|
||||
11. Walk through the sorted table, load the corresponding section into memory, and hash the entire
|
||||
section. Use the SizeOfRawData field in the SectionHeader structure to determine the amount
|
||||
of data to hash.
|
||||
12. Add the section’s SizeOfRawData value to SUM_OF_BYTES_HASHED.
|
||||
13. Repeat steps 11 and 12 for all of the sections in the sorted table.
|
||||
14. Create a value called FILE_SIZE, which is not part of the signature. Set this value to the
|
||||
image’s file size, acquired from the underlying file system. If FILE_SIZE is greater than
|
||||
SUM_OF_BYTES_HASHED, the file contains extra data that must be added to the hash. This data
|
||||
begins at the SUM_OF_BYTES_HASHED file offset, and its length is:
|
||||
(File Size) – ((Size of AttributeCertificateTable) + SUM_OF_BYTES_HASHED)
|
||||
Note: The size of Attribute Certificate Table is specified in the second ULONG value in the
|
||||
Certificate Table entry (32 bit: offset 132, 64 bit: offset 148) in Optional Header Data
|
||||
Directories.
|
||||
15. Finalize the hash algorithm context.
|
||||
Note: This procedure uses offset values from the PE/COFF specification, version 8.1 .
|
||||
For authoritative offset values, refer to the most recent version of the PE/COFF specification.
|
||||
*/
|
||||
|
||||
public List<IMAGE_SECTION_HEADER> SectionTableHeaders { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chunk1 Start will set the position in the file which the hashing of the file should begin from
|
||||
/// This is always the start of the file and thus is hardcoded to 0 and takes no parameters
|
||||
///
|
||||
/// Chunk1 End is defined to be: "immediately before the start of the checksum address"
|
||||
///
|
||||
/// Relevant step:
|
||||
/// 3. Hash the image header from its base to immediately before the start of the
|
||||
/// checksum address, as specified in Optional Header Windows-Specific Fields.
|
||||
/// </summary>
|
||||
public Range Chunk1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chunk2 Start is defined to be: "end of the checksum field"
|
||||
/// Chunk2 End is defined to be: "immediately before the start of the Certificate Table entry"
|
||||
///
|
||||
/// Relevant step:
|
||||
/// 5. Hash everything from the end of the checksum field to immediately before the start of
|
||||
/// the Certificate Table entry, as specified in Optional Header Data Directories.
|
||||
/// </summary>
|
||||
public Range Chunk2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chunk3 Start is defined to be: "the end of the Certificate Table entry"
|
||||
/// Chunk3 End is defined to be: "the end of image header"
|
||||
/// When testing/developing this solution - this was found to be the position specified by
|
||||
/// the SizeOfHeaders field
|
||||
///
|
||||
/// Relevant step:
|
||||
/// 7. Exclude the Certificate Table entry from the calculation and hash everything from the
|
||||
/// end of the Certificate Table entry to the end of image header, including Section Table
|
||||
/// (headers).The Certificate Table entry is 8 bytes long, as specified in Optional Header
|
||||
/// Data Directories.
|
||||
/// </summary>
|
||||
public Range Chunk3 { get; private set; }
|
||||
|
||||
public uint SizeOfHeaders { get; private set; }
|
||||
public uint SumOfBytesHashed { get; private set; }
|
||||
public long CertificateTableStart { get; private set; }
|
||||
public long CertificateTableSize { get; private set; }
|
||||
|
||||
internal bool reorderNeeded = false;
|
||||
private uint lastAddress = 0;
|
||||
public void AddSectionTableHeader(IMAGE_SECTION_HEADER sectionTableHeader) {
|
||||
|
||||
if (sectionTableHeader.SizeOfRawData > 0) {
|
||||
SectionTableHeaders.Add(sectionTableHeader);
|
||||
}
|
||||
|
||||
if (lastAddress < sectionTableHeader.PointerToRawData) {
|
||||
//good - it's in order...
|
||||
} else {
|
||||
reorderNeeded = true;
|
||||
}
|
||||
lastAddress = sectionTableHeader.PointerToRawData;
|
||||
SumOfBytesHashed += sectionTableHeader.SizeOfRawData;
|
||||
}
|
||||
|
||||
public void SetStartCoffHeader(uint peHeaderStart) {
|
||||
Chunk1.Start = 0;
|
||||
uint checksumStart = peHeaderStart + 88; //88 is the size of the COFF Header (24) + Standard Headers + Windows-Specific Fields up to checksum (offset 64)
|
||||
Chunk1.End = checksumStart;
|
||||
Chunk2.Start = checksumStart + LengthOfChecksum;
|
||||
|
||||
//and the four tables before the Certificate Table
|
||||
//(ExportTable, ImportTable, ResourceTable, ExceptionTable)
|
||||
uint certHeaderTableOffset = checksumStart + 64;
|
||||
|
||||
Chunk2.End = certHeaderTableOffset;
|
||||
Chunk3.Start = certHeaderTableOffset + LengthCertificateTable;
|
||||
}
|
||||
|
||||
public void SetCertificateDetails(PeType type, uint sizeOfHeaders, uint certificateTableStart,
|
||||
uint certificateTableSize) {
|
||||
if (type == PeType.Pe32Plus) {
|
||||
uint winSpecificExtraBytes = 16; //need to account for the extra bytes in the Windows-Specific Fields (each field has 4 extra bytes)
|
||||
Chunk2.End += winSpecificExtraBytes;
|
||||
Chunk3.Start += winSpecificExtraBytes;
|
||||
}
|
||||
|
||||
Chunk3.End = sizeOfHeaders;
|
||||
SizeOfHeaders = sizeOfHeaders;
|
||||
SumOfBytesHashed = sizeOfHeaders;
|
||||
CertificateTableStart = certificateTableStart;
|
||||
CertificateTableSize = certificateTableSize;
|
||||
}
|
||||
}
|
||||
|
||||
public class Range {
|
||||
public long Start { get; internal set; }
|
||||
public long End { get; internal set; }
|
||||
|
||||
public int Length => (int)(End - Start);
|
||||
}
|
||||
}
|
||||
|
||||
public enum PeType {
|
||||
Pe32,
|
||||
Pe32Plus
|
||||
}
|
||||
|
||||
internal class CrlDistributionPointParser {
|
||||
internal int offset;
|
||||
internal int length;
|
||||
internal List<CrlDistributionPointParser> tags = new List<CrlDistributionPointParser>();
|
||||
private CrlDistributionPointParser parent = null;
|
||||
private AsnTagType type = AsnTagType.NONE;
|
||||
internal List<string> urls = new List<string>();
|
||||
private int consumedBytes = 0;
|
||||
|
||||
internal CrlDistributionPointParser(byte[] asn1Data) : this(new MemoryStream(asn1Data)) {
|
||||
|
||||
}
|
||||
|
||||
internal CrlDistributionPointParser(MemoryStream asn1Data) : this(null, asn1Data/*new BinaryReader(asn1Data)*/, 0) {
|
||||
|
||||
}
|
||||
|
||||
internal CrlDistributionPointParser(CrlDistributionPointParser parent, MemoryStream memoryStream, /*BinaryReader asn1Reader,*/ int offset) {
|
||||
BinaryReader asn1Reader = new BinaryReader(memoryStream);
|
||||
this.offset = offset;
|
||||
type = readTag(asn1Reader);
|
||||
length = readTagLen(asn1Reader);
|
||||
this.parent = parent;
|
||||
int headerBytes = consumedBytes;
|
||||
while (this.consumedBytes < length + headerBytes) {
|
||||
switch (type) {
|
||||
case AsnTagType.SEQUENCE:
|
||||
case AsnTagType.ARRAY_ELEMENT:
|
||||
var t = new CrlDistributionPointParser(this, memoryStream, (int) memoryStream.Position);
|
||||
consumedBytes += t.consumedBytes;
|
||||
tags.Add(t);
|
||||
this.urls.AddRange(t.urls);
|
||||
break;
|
||||
case AsnTagType.STRING:
|
||||
consumedBytes += length;
|
||||
urls.Add(System.Text.Encoding.UTF8.GetString(asn1Reader.ReadBytes(length)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal AsnTagType readTag(BinaryReader asn1Data) {
|
||||
byte tagByte = asn1Data.ReadByte();
|
||||
if (tagByte != 0x30 && tagByte != 0xA0 && tagByte != 0x86) {
|
||||
throw new CryptographicException("Could not parse CRL Distribution Points. First byte not asn SEQUENCE|ARRAY|STRING marker");
|
||||
}
|
||||
|
||||
consumedBytes++;
|
||||
if (tagByte == 0x30) return AsnTagType.SEQUENCE;
|
||||
if (tagByte == 0xA0) return AsnTagType.ARRAY_ELEMENT;
|
||||
if (tagByte == 0x86) return AsnTagType.STRING;
|
||||
return AsnTagType.NONE;
|
||||
}
|
||||
|
||||
internal int readTagLen(BinaryReader asn1Data) {
|
||||
int tagLen = asn1Data.ReadByte();
|
||||
consumedBytes++;
|
||||
if (tagLen > 128) {
|
||||
//means there's more bytes to read that control the overall length of the next section
|
||||
int moreBytes = tagLen - 128;
|
||||
byte[] lenBytes = new byte[4];
|
||||
byte[] d = asn1Data.ReadBytes(moreBytes);
|
||||
|
||||
int pos = 0;
|
||||
for (int i = d.Length - 1; i >= 0; i--) {
|
||||
lenBytes[pos++] = d[i];
|
||||
}
|
||||
|
||||
tagLen = BitConverter.ToInt32(lenBytes, 0);
|
||||
consumedBytes += moreBytes;
|
||||
}
|
||||
|
||||
return tagLen;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum AsnTagType {
|
||||
SEQUENCE = 0x30,
|
||||
ARRAY_ELEMENT = 0xA0,
|
||||
STRING = 0x86,
|
||||
NONE = 0,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ZitiUpdateService.Checkers.PeFile {
|
||||
public static class StructHelper {
|
||||
|
||||
public static T FromBytes<T>(byte[] bytes) {
|
||||
// Pin the managed memory while, copy it out the data, then unpin it
|
||||
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||
handle.Free();
|
||||
|
||||
return theStructure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace VerifyingFiles.PInvoke {
|
||||
public static class Win32Crypto {
|
||||
//based on https://docs.microsoft.com/en-us/archive/blogs/alejacma/how-to-get-information-from-a-crl-net
|
||||
|
||||
#region APIs
|
||||
[DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern Boolean CryptQueryObject(
|
||||
Int32 dwObjectType,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] String pvObject,
|
||||
Int32 dwExpectedContentTypeFlags,
|
||||
Int32 dwExpectedFormatTypeFlags,
|
||||
Int32 dwFlags,
|
||||
IntPtr pdwMsgAndCertEncodingType,
|
||||
IntPtr pdwContentType,
|
||||
IntPtr pdwFormatType,
|
||||
IntPtr phCertStore,
|
||||
IntPtr phMsg,
|
||||
ref IntPtr ppvContext
|
||||
);
|
||||
[DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern Boolean CryptQueryObject2(
|
||||
Int32 dwObjectType,
|
||||
IntPtr pvObject,
|
||||
Int32 dwExpectedContentTypeFlags,
|
||||
Int32 dwExpectedFormatTypeFlags,
|
||||
Int32 dwFlags,
|
||||
IntPtr pdwMsgAndCertEncodingType,
|
||||
IntPtr pdwContentType,
|
||||
IntPtr pdwFormatType,
|
||||
ref IntPtr phCertStore,
|
||||
IntPtr phMsg,
|
||||
ref IntPtr ppvContext
|
||||
);
|
||||
|
||||
[DllImport("CRYPT32.DLL", EntryPoint = "CertFreeCRLContext", SetLastError = true)]
|
||||
public static extern Boolean CertFreeCRLContext(
|
||||
IntPtr pCrlContext
|
||||
);
|
||||
|
||||
[DllImport("CRYPT32.DLL", EntryPoint = "CertNameToStr", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern Int32 CertNameToStr(
|
||||
Int32 dwCertEncodingType,
|
||||
ref CRYPTOAPI_BLOB pName,
|
||||
Int32 dwStrType,
|
||||
StringBuilder psz,
|
||||
Int32 csz
|
||||
);
|
||||
|
||||
[DllImport("CRYPT32.DLL", EntryPoint = "CertFindExtension", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr CertFindExtension(
|
||||
[MarshalAs(UnmanagedType.LPStr)] String pszObjId,
|
||||
Int32 cExtensions,
|
||||
IntPtr rgExtensions
|
||||
);
|
||||
|
||||
[DllImport("CRYPT32.DLL", EntryPoint = "CryptFormatObject", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern Boolean CryptFormatObject(
|
||||
Int32 dwCertEncodingType,
|
||||
Int32 dwFormatType,
|
||||
Int32 dwFormatStrType,
|
||||
IntPtr pFormatStruct,
|
||||
[MarshalAs(UnmanagedType.LPStr)] String lpszStructType,
|
||||
IntPtr pbEncoded,
|
||||
Int32 cbEncoded,
|
||||
StringBuilder pbFormat,
|
||||
ref Int32 pcbFormat
|
||||
);
|
||||
|
||||
#endregion APIs
|
||||
|
||||
#region Structs
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CRL_CONTEXT {
|
||||
public Int32 dwCertEncodingType;
|
||||
public IntPtr pbCrlEncoded;
|
||||
public Int32 cbCrlEncoded;
|
||||
public IntPtr pCrlInfo;
|
||||
public IntPtr hCertStore;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CRL_INFO {
|
||||
public Int32 dwVersion;
|
||||
public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
|
||||
public CRYPTOAPI_BLOB Issuer;
|
||||
public FILETIME ThisUpdate;
|
||||
public FILETIME NextUpdate;
|
||||
public Int32 cCRLEntry;
|
||||
public IntPtr rgCRLEntry;
|
||||
public Int32 cExtension;
|
||||
public IntPtr rgExtension;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CRYPT_ALGORITHM_IDENTIFIER {
|
||||
[MarshalAs(UnmanagedType.LPStr)] public String pszObjId;
|
||||
public CRYPTOAPI_BLOB Parameters;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CRYPTOAPI_BLOB {
|
||||
public Int32 cbData;
|
||||
public IntPtr pbData;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct FILETIME {
|
||||
public Int32 dwLowDateTime;
|
||||
public Int32 dwHighDateTime;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CRL_ENTRY {
|
||||
public CRYPTOAPI_BLOB SerialNumber;
|
||||
public FILETIME RevocationDate;
|
||||
public Int32 cExtension;
|
||||
public IntPtr rgExtension;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CERT_EXTENSION {
|
||||
[MarshalAs(UnmanagedType.LPStr)] public String pszObjId;
|
||||
public Boolean fCritical;
|
||||
public CRYPTOAPI_BLOB Value;
|
||||
}
|
||||
|
||||
#endregion Structs
|
||||
|
||||
#region Consts
|
||||
|
||||
public const Int32 CERT_QUERY_OBJECT_BLOB = 0x00000002;
|
||||
public const Int32 CERT_QUERY_CONTENT_CRL = 3;
|
||||
public const Int32 CERT_QUERY_CONTENT_FLAG_CRL = 1 << CERT_QUERY_CONTENT_CRL;
|
||||
public const Int32 CERT_QUERY_FORMAT_BINARY = 1;
|
||||
public const Int32 CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
|
||||
public const Int32 CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
|
||||
public const Int32 CERT_QUERY_FORMAT_FLAG_BINARY = 1 << CERT_QUERY_FORMAT_BINARY;
|
||||
public const Int32 CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = 1 << CERT_QUERY_FORMAT_BASE64_ENCODED;
|
||||
public const Int32 CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = 1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED;
|
||||
public const Int32 CERT_QUERY_FORMAT_FLAG_ALL = CERT_QUERY_FORMAT_FLAG_BINARY | CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED | CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;
|
||||
|
||||
public const Int32 X509_ASN_ENCODING = 0x00000001;
|
||||
public const Int32 PKCS_7_ASN_ENCODING = 0x00010000;
|
||||
|
||||
public const Int32 X509_NAME = 7;
|
||||
|
||||
public const Int32 CERT_SIMPLE_NAME_STR = 1;
|
||||
public const Int32 CERT_OID_NAME_STR = 2;
|
||||
public const Int32 CERT_X500_NAME_STR = 3;
|
||||
|
||||
public const String szOID_CRL_REASON_CODE = "2.5.29.21";
|
||||
|
||||
#endregion
|
||||
|
||||
private static List<string> GetRevokedSerialNumbers(CRL_INFO stCrlInfo) {
|
||||
List<string> rtn = new List<string>();
|
||||
var rgCrlEntry = stCrlInfo.rgCRLEntry;
|
||||
|
||||
for (var i = 0; i < stCrlInfo.cCRLEntry; i++) {
|
||||
var serial = string.Empty;
|
||||
var stCrlEntry = (CRL_ENTRY)Marshal.PtrToStructure(rgCrlEntry, typeof(CRL_ENTRY));
|
||||
|
||||
IntPtr pByte = stCrlEntry.SerialNumber.pbData;
|
||||
for (var j = 0; j < stCrlEntry.SerialNumber.cbData; j++) {
|
||||
Byte bByte = Marshal.ReadByte(pByte);
|
||||
serial = bByte.ToString("X").PadLeft(2, '0') + serial;
|
||||
pByte = pByte + Marshal.SizeOf(typeof(byte));
|
||||
|
||||
}
|
||||
rtn.Add(serial);
|
||||
rgCrlEntry = rgCrlEntry + Marshal.SizeOf(typeof(CRL_ENTRY));
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
public static DateTime FileTimeToDateTime(FILETIME fileTime) {
|
||||
DateTime dateTime;
|
||||
IntPtr int64Ptr = Marshal.AllocHGlobal(sizeof(Int64));
|
||||
try {
|
||||
Marshal.StructureToPtr(fileTime, int64Ptr, true);
|
||||
dateTime = DateTime.FromFileTime(Marshal.ReadInt64(int64Ptr));
|
||||
} finally {
|
||||
Marshal.FreeHGlobal(int64Ptr);
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
public static CrlInfo FromBlob(byte[] CrlDERBytes) {
|
||||
|
||||
var phCertStore = IntPtr.Zero;
|
||||
var pvContext = IntPtr.Zero;
|
||||
var hCrlData = new GCHandle();
|
||||
var hCryptBlob = new GCHandle();
|
||||
try {
|
||||
hCrlData = GCHandle.Alloc(CrlDERBytes, GCHandleType.Pinned);
|
||||
CRYPTOAPI_BLOB stCryptBlob;
|
||||
stCryptBlob.cbData = CrlDERBytes.Length;
|
||||
stCryptBlob.pbData = hCrlData.AddrOfPinnedObject();
|
||||
hCryptBlob = GCHandle.Alloc(stCryptBlob, GCHandleType.Pinned);
|
||||
|
||||
if (!CryptQueryObject2(
|
||||
CERT_QUERY_OBJECT_BLOB,
|
||||
hCryptBlob.AddrOfPinnedObject(),
|
||||
CERT_QUERY_CONTENT_FLAG_CRL,
|
||||
CERT_QUERY_FORMAT_FLAG_BINARY,
|
||||
0,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
ref phCertStore,
|
||||
IntPtr.Zero,
|
||||
ref pvContext
|
||||
)) {
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "CRL is Corrupted.");
|
||||
}
|
||||
var stCrlContext = (CRL_CONTEXT)Marshal.PtrToStructure(pvContext, typeof(CRL_CONTEXT));
|
||||
var stCrlInfo = (CRL_INFO)Marshal.PtrToStructure(stCrlContext.pCrlInfo, typeof(CRL_INFO));
|
||||
|
||||
CrlInfo info = new CrlInfo();
|
||||
info.validTo = FileTimeToDateTime(stCrlInfo.NextUpdate);
|
||||
info.validFrom = FileTimeToDateTime(stCrlInfo.ThisUpdate);
|
||||
info.RevokedSerialNumbers = GetRevokedSerialNumbers(stCrlInfo);
|
||||
|
||||
return info;
|
||||
} finally {
|
||||
if (hCrlData.IsAllocated) hCrlData.Free();
|
||||
if (hCryptBlob.IsAllocated) hCryptBlob.Free();
|
||||
if (!pvContext.Equals(IntPtr.Zero)) {
|
||||
CertFreeCRLContext(pvContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct CrlInfo {
|
||||
public DateTime validTo;
|
||||
public DateTime validFrom;
|
||||
public List<string> RevokedSerialNumbers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFozCCA4ugAwIBAgIUdhfTn8Eu4pU39MIPpoerTLqbLRkwDQYJKoZIhvcNAQEN
|
||||
BQAwYTEYMBYGA1UEAwwPUm9vdCBTaWduaW5nIENBMRkwFwYDVQQKDBBvcGVueml0
|
||||
aS5vcmcgSW5jMRAwDgYDVQQLDAdhZHYtZGV2MQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||
CAwCTkMwHhcNMjEwNjAyMTcyNDMzWhcNNDEwNjE3MTcyNDMzWjBhMRgwFgYDVQQD
|
||||
DA9Sb290IFNpZ25pbmcgQ0ExGTAXBgNVBAoMEG9wZW56aXRpLm9yZyBJbmMxEDAO
|
||||
BgNVBAsMB2Fkdi1kZXYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzCCAiIwDQYJ
|
||||
KoZIhvcNAQEBBQADggIPADCCAgoCggIBALcLp/jh0zYqDQw6Gxzoxnib08QLwBo7
|
||||
AHLdJSOxdFy3K0a7xSEZjkhJgWQWw/c/HArw5PiHS4jdsh7o8mSJGLqBj+s/Gj4M
|
||||
tM4k3A5jJHcgAbDKPAaAZAXQ2aEDp8eA65S1mRqNnYHbmTm3Q3t3U7dqI1lISzjz
|
||||
k5jBQnXS3Qtlj2SyMXRYPpPJIQlXgZXdvLmL7wG1FpmCFBSEEdOVMIhEr5hJ/5rc
|
||||
HpTRSueLPpdJUSv3B3uuPdo5rI/pJJ6gAJUtmMw/G8CAlMwXlUbSLt5IvNriyjqT
|
||||
pisTjkQR0Vs8pvAN0CKfOqwoPWDwuJkump+LRTTQ0PLaNCnlU96mpoX9D7yhQAGP
|
||||
C5h0N0jPV2MpnEFNVPaGE43Wj1zejVP1S5Ep3i/Xu/aSUGmIK5MQ95z/J927LBKe
|
||||
AOyQDdQC0WkCBpCzpe0G9c+M0u7lYUHWXExWB22nQT2rM3aG5ZdOJRu42PXFNUr4
|
||||
bgkZq56PiCCgwDENOlln6/h8wD2a2o5n638BCwW3Qq4w8u2e7dKIAjNroQ053iyw
|
||||
ZmSIwImf9N+0631d8LDoVLllry+geP1bSlI1UCYcVjJWGSDwEmK9h+kwUsMX+yl7
|
||||
TAzgrnAkBS+C62sDvGHRjhp82IDklqsBQV7SsRG5ZqHAipbIa0NfCURsqmQ6iyNR
|
||||
a3/9oiE9ARdHAgMBAAGjUzBRMB0GA1UdDgQWBBS0v5OYBy16UEw7k7nMLg7Y3Fs7
|
||||
uDAfBgNVHSMEGDAWgBS0v5OYBy16UEw7k7nMLg7Y3Fs7uDAPBgNVHRMBAf8EBTAD
|
||||
AQH/MA0GCSqGSIb3DQEBDQUAA4ICAQBPtyCaKyk+WhXI1Gu83h6ePKuevwwnBygr
|
||||
xOEJM7b0kAxtcgK46B9wPgvLuUw1znmDzAuAqU52k00q1xLGZ+N5JNhlzzNWW0n2
|
||||
8Kj0gFLmu3tO7XXjQfQPKiZp/iiwOG9XPVVDTF2HVqkOsvdGYD2XH+wI4v/4m3wa
|
||||
xTvliEz9NCei7/NOhuTbJvbvcSmqAvs3+gH55bzBvvr+Hx/krx8HMcBikx92S5nF
|
||||
NKBkHIUQVqGoCHxGY/uM0xou5797faVEzLkaDle+oisMiy061pYI0bb9CRdHAU0r
|
||||
UoemoY3UOKlyK57wdXKRizDxmEyND2zxPYrktZu0SybjZmaFOCHLLp1WrcTlcp3C
|
||||
7Lsuj9Vzui2mjJuQzSTEyd1R04AT3xwb9slN+iHuN1dAGexVsENahW2q9ZAjEtbu
|
||||
E6B+gOrSJ7WaL8R7AAz/E3tfU2bYm99JoGCPjI2/kP7udw7EqvoKYfJO1gbRo45z
|
||||
37xJmDrxb2SJFEtn5mqPO63HjGXGiC6xI52Olmv4Csk3WOseO3j9/SwazDAMqZSh
|
||||
dY0BN0DADO8kiohyF+Cq7nPR/tX17DpQiWFgJ4xoYHRFYCyb6QZ5jOzMhrZJa7cM
|
||||
v7BIbBcUe1vcKJ+gD6dXMT77KakdkRSfuiuEAeh6GmjPC130ni1stn+chw0pymax
|
||||
ZSpzkClEkQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -3,7 +3,7 @@ using System.IO;
|
||||
|
||||
using NLog;
|
||||
|
||||
namespace ZitiUpdateService.checkers {
|
||||
namespace ZitiUpdateService.Checkers {
|
||||
|
||||
abstract class UpdateCheck {
|
||||
public abstract int IsUpdateAvailable(Version current);
|
||||
|
||||
@@ -5,4 +5,9 @@
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
|
||||
<package id="NLog" version="4.7.8" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||
</packages>
|
||||
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.Pkcs" version="5.0.1" targetFramework="net48" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
|
||||
</packages>
|
||||
@@ -1,3 +1,17 @@
|
||||
# Release 1.9.7
|
||||
|
||||
## What's New
|
||||
* none
|
||||
|
||||
## Other changes:
|
||||
* none
|
||||
|
||||
## Bugs fixed:
|
||||
* none
|
||||
|
||||
## Dependency Updates
|
||||
* tsdk is updated to v0.15.7, c-sdk 0.23.3
|
||||
|
||||
# Release 1.9.6
|
||||
|
||||
## What's New
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ REM See the License for the specific language governing permissions and
|
||||
REM limitations under the License.
|
||||
REM
|
||||
SET REPO_URL=https://github.com/openziti/ziti-tunnel-sdk-c.git
|
||||
SET ZITI_TUNNEL_REPO_BRANCH=v0.15.6
|
||||
SET ZITI_TUNNEL_REPO_BRANCH=v0.15.7
|
||||
REM override the c sdk used in the build - leave blank for the same as specified in the tunneler sdk
|
||||
SET ZITI_SDK_C_BRANCH=
|
||||
REM the number of TCP connections the tunneler sdk can have at any one time
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
Version = "1.9.6"
|
||||
Revision = "ed9a214d5c33"
|
||||
Version = "1.9.8"
|
||||
Revision = "a763f2acac36"
|
||||
Branch = "main"
|
||||
BuildUser = ""
|
||||
BuildDate = "2021-05-31 14:36:25"
|
||||
BuildDate = "2021-06-07 18:26:10"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user