Files
lserp_cs_6.0/插件库/Lskj.AgentBridge/BridgeClientProcessIdentityVerifier.cs
T
2026-08-14 14:28:28 +08:00

179 lines
6.7 KiB
C#

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace Lskj.AgentBridge
{
/// <summary>
/// Binds a named-pipe UAT request to the actual Windows client process.
/// The customer-signed authorization pins the complete executable SHA-256;
/// therefore an Authenticode mutation, certificate transplant or unsigned
/// replacement necessarily changes the approved hash. The embedded signer
/// thumbprint is checked as a second independent identity dimension.
/// </summary>
public sealed class WindowsBridgeClientProcessIdentityVerifier :
IBridgeClientProcessIdentityVerifier
{
private const uint TokenQuery = 0x0008;
private const int TokenElevationClass = 20;
public bool Verify(
int processId,
WorkflowUatExecutableIdentity expectedIdentity)
{
if (Environment.OSVersion.Platform != PlatformID.Win32NT
|| processId <= 0
|| expectedIdentity == null
|| string.IsNullOrWhiteSpace(expectedIdentity.FileName)
|| !Lskj.CommandKernel.CommandInputFingerprint.IsValid(
expectedIdentity.Sha256)
|| WindowsTrustedPeopleSignatureVerifier.NormalizeThumbprint(
expectedIdentity.SignerThumbprint) == null)
return false;
try
{
using (Process process = Process.GetProcessById(processId))
{
if (process.HasExited || process.MainModule == null)
return false;
string path = Path.GetFullPath(process.MainModule.FileName);
if (!string.Equals(
Path.GetFileName(path),
expectedIdentity.FileName,
StringComparison.OrdinalIgnoreCase))
return false;
FileInfo file = new FileInfo(path);
if (!file.Exists || file.Length <= 0 || file.Length > 128L * 1024L * 1024L
|| (file.Attributes & (FileAttributes.Directory
| FileAttributes.Device
| FileAttributes.ReparsePoint)) != 0
|| !NoReparseDirectoryChain(file.Directory))
return false;
string actualHash;
using (FileStream stream = new FileStream(
file.FullName,
FileMode.Open,
FileAccess.Read,
FileShare.Read))
using (SHA256 sha = SHA256.Create())
actualHash = Hex(sha.ComputeHash(stream));
if (!WorkflowUatAuthorizationVerifier.FixedEquals(
actualHash,
expectedIdentity.Sha256))
return false;
string actualSigner = SignerThumbprint(file.FullName);
if (!string.Equals(
actualSigner,
WindowsTrustedPeopleSignatureVerifier.NormalizeThumbprint(
expectedIdentity.SignerThumbprint),
StringComparison.Ordinal))
return false;
return !expectedIdentity.RequiresElevation
|| IsElevated(process.Handle);
}
}
catch
{
return false;
}
}
private static bool NoReparseDirectoryChain(DirectoryInfo directory)
{
try
{
DirectoryInfo current = directory;
while (current != null)
{
if (!current.Exists
|| (current.Attributes & FileAttributes.ReparsePoint) != 0)
return false;
current = current.Parent;
}
return true;
}
catch { return false; }
}
private static string SignerThumbprint(string path)
{
X509Certificate certificate = null;
X509Certificate2 certificate2 = null;
try
{
certificate = X509Certificate.CreateFromSignedFile(path);
certificate2 = new X509Certificate2(certificate);
return WindowsTrustedPeopleSignatureVerifier.NormalizeThumbprint(
certificate2.Thumbprint);
}
catch { return null; }
finally
{
if (certificate2 != null) certificate2.Reset();
}
}
private static bool IsElevated(IntPtr processHandle)
{
IntPtr token = IntPtr.Zero;
try
{
if (processHandle == IntPtr.Zero
|| !OpenProcessToken(processHandle, TokenQuery, out token)
|| token == IntPtr.Zero)
return false;
TokenElevation elevation;
int returned;
int size = Marshal.SizeOf(typeof(TokenElevation));
return GetTokenInformation(
token,
TokenElevationClass,
out elevation,
size,
out returned)
&& returned == size
&& elevation.TokenIsElevated != 0;
}
catch { return false; }
finally
{
if (token != IntPtr.Zero) CloseHandle(token);
}
}
private static string Hex(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty)
.ToLowerInvariant();
}
[StructLayout(LayoutKind.Sequential)]
private struct TokenElevation
{
public int TokenIsElevated;
}
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
IntPtr processHandle,
uint desiredAccess,
out IntPtr tokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool GetTokenInformation(
IntPtr tokenHandle,
int tokenInformationClass,
out TokenElevation tokenInformation,
int tokenInformationLength,
out int returnLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr handle);
}
}