mirror of https://github.com/stascorp/rdpwrap
Add src-csharp/ solution structure for the Delphi-to-C# migration: Solution & projects: - RDPWrap.sln (VS 2022, x86/x64 configurations) - Directory.Build.props (net8.0-windows, shared TFM/platform settings) - RDPWrap.Common.csproj - shared class library - RDPWInst.csproj - console installer (stub) - RDPConf.csproj - WinForms config UI (stub) - RDPCheck.csproj - WinForms RDP tester (stub) RDPWrap.Common helpers (all translated from Delphi source): - NativeMethods.cs - all P/Invoke: kernel32, advapi32, winsta.dll - ArchHelper.cs - arch detection + WOW64 redirection - RegistryHelper.cs - HKLM typed read/write with WOW64 view support - ServiceHelper.cs - SCM wrappers (start-type, start, state, enum) - FileVersionHelper.cs - file version reading via BCL FileVersionInfo - ProcessHelper.cs - ExecWait (hidden) + KillProcess - HttpHelper.cs - HttpClient replacing WinInet (sync + async) - ResourceHelper.cs - embedded manifest resource extract/read - IniHelper.cs - INI section presence check + support level - SecurityHelper.cs - SID/ACL (GrantSidFullAccess) + token privileges Also adds TODO.md tracking the full 44-item migration plan.pull/4062/head
parent
e57e252d5c
commit
ae426da271
@ -0,0 +1,15 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Platforms>x86;x64</Platforms>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<PlatformTarget>$(Platform)</PlatformTarget>
|
||||||
|
<!-- Ensure single-file publish works -->
|
||||||
|
<SelfContained>false</SelfContained>
|
||||||
|
<!-- Reproducible builds -->
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<RootNamespace>RDPCheck</RootNamespace>
|
||||||
|
<AssemblyName>RDPCheck</AssemblyName>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\RDPWrap.Common\RDPWrap.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- COM interop for AxMsRdpClient2 — generated via:
|
||||||
|
tlbimp %SystemRoot%\System32\mstscax.dll /out:MSTSCLib.dll
|
||||||
|
Then reference it here:
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="MSTSCLib">
|
||||||
|
<HintPath>Interop\MSTSCLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="AxInterop.MSTSCLib">
|
||||||
|
<HintPath>Interop\AxInterop.MSTSCLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<RootNamespace>RDPConf</RootNamespace>
|
||||||
|
<AssemblyName>RDPConf</AssemblyName>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\RDPWrap.Common\RDPWrap.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>RDPWInst</RootNamespace>
|
||||||
|
<AssemblyName>RDPWInst</AssemblyName>
|
||||||
|
<!-- Embed binary payloads -->
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\RDPWrap.Common\RDPWrap.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Embedded payload resources (DLLs, rdpwrap.ini) go here once binaries are available -->
|
||||||
|
<!-- Example:
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Resources\rdpw32.dll" />
|
||||||
|
<EmbeddedResource Include="Resources\rdpw64.dll" />
|
||||||
|
<EmbeddedResource Include="Resources\rdpwrap.ini" />
|
||||||
|
</ItemGroup>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Architecture detection and WOW64 file-system redirection control.
|
||||||
|
/// Mirrors the Arch / DisableWowRedirection / RevertWowRedirection logic
|
||||||
|
/// from RDPWInst.dpr and RDPConf MainUnit.pas.
|
||||||
|
/// </summary>
|
||||||
|
public static class ArchHelper
|
||||||
|
{
|
||||||
|
private static readonly Lazy<byte> _arch = new(DetectArch);
|
||||||
|
|
||||||
|
/// <summary>Raw architecture byte: 32 or 64. 0 = unsupported.</summary>
|
||||||
|
public static byte Arch => _arch.Value;
|
||||||
|
|
||||||
|
/// <summary><c>true</c> when running on a 64-bit Windows installation.</summary>
|
||||||
|
public static bool Is64Bit => Arch == 64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> when the processor architecture is supported
|
||||||
|
/// (x86 or x64). Itanium and unknown architectures return <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsSupported => Arch != 0;
|
||||||
|
|
||||||
|
private static byte DetectArch()
|
||||||
|
{
|
||||||
|
NativeMethods.GetNativeSystemInfo(out var si);
|
||||||
|
return si.wProcessorArchitecture switch
|
||||||
|
{
|
||||||
|
NativeMethods.PROCESSOR_ARCHITECTURE_INTEL => 32,
|
||||||
|
NativeMethods.PROCESSOR_ARCHITECTURE_AMD64 => 64,
|
||||||
|
_ => 0 // Itanium or unknown — unsupported
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── WOW64 filesystem redirection ─────────────────────────────────────────
|
||||||
|
|
||||||
|
private static IntPtr _wow64OldValue = IntPtr.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables WOW64 filesystem redirection so that 32-bit processes can
|
||||||
|
/// reach the real <c>%SystemRoot%\System32</c>. Call only on 64-bit hosts.
|
||||||
|
/// Returns <c>true</c> on success.
|
||||||
|
/// </summary>
|
||||||
|
public static bool DisableWow64Redirection()
|
||||||
|
{
|
||||||
|
if (!Is64Bit) return false;
|
||||||
|
return NativeMethods.Wow64DisableWow64FsRedirection(out _wow64OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reverts the WOW64 filesystem redirection state saved by the last call
|
||||||
|
/// to <see cref="DisableWow64Redirection"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static bool RevertWow64Redirection()
|
||||||
|
{
|
||||||
|
if (!Is64Bit) return false;
|
||||||
|
return NativeMethods.Wow64RevertWow64FsRedirection(_wow64OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Environment path expansion ────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expands environment strings in <paramref name="path"/>, replacing
|
||||||
|
/// <c>%ProgramFiles%</c> with <c>%ProgramW6432%</c> on 64-bit hosts
|
||||||
|
/// to avoid redirection to the x86 Program Files folder.
|
||||||
|
/// </summary>
|
||||||
|
public static string ExpandPath(string path)
|
||||||
|
{
|
||||||
|
if (Is64Bit)
|
||||||
|
path = path.Replace("%ProgramFiles%", "%ProgramW6432%",
|
||||||
|
StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var buf = new System.Text.StringBuilder(1024);
|
||||||
|
NativeMethods.ExpandEnvironmentStrings(path, buf, (uint)buf.Capacity);
|
||||||
|
return buf.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// File-version reading helper. Mirrors the GetFileVersion function used in
|
||||||
|
/// RDPWInst.dpr and RDPConf MainUnit.pas.
|
||||||
|
/// </summary>
|
||||||
|
public static class FileVersionHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Strongly-typed representation of a Windows file version.
|
||||||
|
/// </summary>
|
||||||
|
public record FileVersionInfo(
|
||||||
|
ushort Major,
|
||||||
|
ushort Minor,
|
||||||
|
ushort Release,
|
||||||
|
ushort Build,
|
||||||
|
bool IsDebug,
|
||||||
|
bool IsPrerelease,
|
||||||
|
bool IsPrivate,
|
||||||
|
bool IsSpecial)
|
||||||
|
{
|
||||||
|
/// <summary>e.g. "10.0.26100.3476"</summary>
|
||||||
|
public override string ToString() => $"{Major}.{Minor}.{Release}.{Build}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the file version of <paramref name="filePath"/>, or <c>null</c>
|
||||||
|
/// if the file does not exist or has no version resource.
|
||||||
|
/// Uses the BCL <see cref="System.Diagnostics.FileVersionInfo"/> which does
|
||||||
|
/// not require loading the DLL as executable — safe for locked DLLs.
|
||||||
|
/// </summary>
|
||||||
|
public static FileVersionInfo? GetVersion(string filePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath)) return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(filePath);
|
||||||
|
return new FileVersionInfo(
|
||||||
|
(ushort)(fvi.FileMajorPart),
|
||||||
|
(ushort)(fvi.FileMinorPart),
|
||||||
|
(ushort)(fvi.FileBuildPart),
|
||||||
|
(ushort)(fvi.FilePrivatePart),
|
||||||
|
fvi.IsDebug,
|
||||||
|
fvi.IsPreRelease,
|
||||||
|
fvi.IsPrivateBuild,
|
||||||
|
fvi.IsSpecialBuild);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convenience overload: resolves the path via
|
||||||
|
/// <see cref="ArchHelper.ExpandPath"/> before reading.
|
||||||
|
/// </summary>
|
||||||
|
public static FileVersionInfo? GetVersionExpanded(string pathWithEnvVars)
|
||||||
|
=> GetVersion(ArchHelper.ExpandPath(pathWithEnvVars));
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTTP download helpers that replace the WinInet-based GitINIFile /
|
||||||
|
/// DownloadFileToDisk procedures from RDPWInst.dpr.
|
||||||
|
/// Uses <see cref="HttpClient"/> with a shared static instance.
|
||||||
|
/// </summary>
|
||||||
|
public static class HttpHelper
|
||||||
|
{
|
||||||
|
// Single shared instance — HttpClient is designed to be reused.
|
||||||
|
private static readonly HttpClient _client = new(new HttpClientHandler
|
||||||
|
{
|
||||||
|
AllowAutoRedirect = true,
|
||||||
|
MaxAutomaticRedirections = 5,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(60),
|
||||||
|
DefaultRequestHeaders = { { "User-Agent", "RDP-Wrapper-Updater/1.0" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads the text content at <paramref name="url"/> and returns it as
|
||||||
|
/// a string. Returns <c>null</c> on any failure.
|
||||||
|
/// Mirrors the Delphi GitINIFile function.
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<string?> DownloadStringAsync(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _client.GetStringAsync(url).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"[-] HTTP download failed ({url}): {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads the binary content at <paramref name="url"/> and saves it to
|
||||||
|
/// <paramref name="destPath"/>. Returns <c>true</c> when the file exists
|
||||||
|
/// and is non-empty after download.
|
||||||
|
/// Mirrors the Delphi DownloadFileToDisk function.
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<bool> DownloadFileAsync(string url, string destPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var response = await _client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
|
await using var file = File.Create(destPath);
|
||||||
|
await stream.CopyToAsync(file).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return new FileInfo(destPath).Length > 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"[-] HTTP file download failed ({url}): {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronous wrapper for <see cref="DownloadStringAsync"/> — suitable
|
||||||
|
/// for the installer's purely-sequential flow.
|
||||||
|
/// </summary>
|
||||||
|
public static string? DownloadString(string url)
|
||||||
|
=> DownloadStringAsync(url).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronous wrapper for <see cref="DownloadFileAsync"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static bool DownloadFile(string url, string destPath)
|
||||||
|
=> DownloadFileAsync(url, destPath).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lightweight INI-file helpers used to check whether a specific version
|
||||||
|
/// section exists in rdpwrap.ini. Mirrors the INIHasSection function and
|
||||||
|
/// CheckSupport version-lookup logic from RDPWInst.dpr and RDPConf MainUnit.pas.
|
||||||
|
/// </summary>
|
||||||
|
public static class IniHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> when the INI file at <paramref name="iniPath"/>
|
||||||
|
/// contains the section header <c>[<paramref name="section"/>]</c>.
|
||||||
|
/// Mirrors the Delphi INIHasSection function.
|
||||||
|
/// </summary>
|
||||||
|
public static bool HasSection(string iniPath, string section)
|
||||||
|
{
|
||||||
|
if (!File.Exists(iniPath)) return false;
|
||||||
|
var needle = $"[{section}]";
|
||||||
|
foreach (var line in File.ReadLines(iniPath))
|
||||||
|
{
|
||||||
|
if (line.Contains(needle, StringComparison.Ordinal))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the full text of <paramref name="iniPath"/> and returns it,
|
||||||
|
/// or an empty string if the file does not exist.
|
||||||
|
/// </summary>
|
||||||
|
public static string LoadText(string iniPath)
|
||||||
|
=> File.Exists(iniPath) ? File.ReadAllText(iniPath) : string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks the support level of a given termsrv.dll version against the
|
||||||
|
/// INI content string <paramref name="iniContent"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// 0 = not supported, 1 = partially supported (Vista/7 legacy),
|
||||||
|
/// 2 = fully supported (entry found in ini).
|
||||||
|
/// </returns>
|
||||||
|
public static int CheckSupportLevel(string iniContent,
|
||||||
|
FileVersionHelper.FileVersionInfo fv)
|
||||||
|
{
|
||||||
|
int level = 0;
|
||||||
|
|
||||||
|
// Vista (6.0) and Windows 7 (6.1) are "partially" supported without
|
||||||
|
// a specific INI entry — mirrors the Delphi CheckSupport logic.
|
||||||
|
if ((fv.Major == 6 && fv.Minor == 0) ||
|
||||||
|
(fv.Major == 6 && fv.Minor == 1))
|
||||||
|
level = 1;
|
||||||
|
|
||||||
|
var verTxt = fv.ToString(); // "major.minor.release.build"
|
||||||
|
if (iniContent.Contains($"[{verTxt}]", StringComparison.Ordinal))
|
||||||
|
level = 2;
|
||||||
|
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,467 @@
|
|||||||
|
// Copyright 2024 sjackson0109
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All P/Invoke declarations used across RDPWInst, RDPConf and RDPCheck.
|
||||||
|
/// Mirrors the unhooked Win32 imports from the original Delphi sources.
|
||||||
|
/// </summary>
|
||||||
|
internal static class NativeMethods
|
||||||
|
{
|
||||||
|
// ─── DLL names ────────────────────────────────────────────────────────────
|
||||||
|
internal const string Kernel32 = "kernel32.dll";
|
||||||
|
internal const string Advapi32 = "advapi32.dll";
|
||||||
|
internal const string WinSta = "winsta.dll";
|
||||||
|
|
||||||
|
// ─── Constants ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Architecture
|
||||||
|
internal const ushort PROCESSOR_ARCHITECTURE_INTEL = 0;
|
||||||
|
internal const ushort PROCESSOR_ARCHITECTURE_IA64 = 6;
|
||||||
|
internal const ushort PROCESSOR_ARCHITECTURE_AMD64 = 9;
|
||||||
|
|
||||||
|
// Registry
|
||||||
|
internal const uint KEY_WOW64_64KEY = 0x0100;
|
||||||
|
internal const uint KEY_WOW64_32KEY = 0x0200;
|
||||||
|
internal const uint KEY_READ = 0x20019;
|
||||||
|
internal const uint KEY_WRITE = 0x20006;
|
||||||
|
internal const uint KEY_QUERY_VALUE = 0x0001;
|
||||||
|
internal const uint KEY_SET_VALUE = 0x0002;
|
||||||
|
|
||||||
|
// Service control manager
|
||||||
|
internal const uint SC_MANAGER_CONNECT = 0x0001;
|
||||||
|
internal const uint SC_MANAGER_CREATE_SERVICE = 0x0002;
|
||||||
|
internal const uint SC_MANAGER_ENUMERATE_SERVICE = 0x0004;
|
||||||
|
internal const uint SC_MANAGER_ALL_ACCESS = 0xF003F;
|
||||||
|
|
||||||
|
internal const uint SERVICE_QUERY_CONFIG = 0x0001;
|
||||||
|
internal const uint SERVICE_CHANGE_CONFIG = 0x0002;
|
||||||
|
internal const uint SERVICE_QUERY_STATUS = 0x0004;
|
||||||
|
internal const uint SERVICE_START = 0x0010;
|
||||||
|
internal const uint SERVICE_STOP = 0x0020;
|
||||||
|
internal const uint SERVICE_ALL_ACCESS = 0xF01FF;
|
||||||
|
|
||||||
|
internal const uint SERVICE_WIN32 = 0x30;
|
||||||
|
internal const uint SERVICE_STATE_ALL = 0x03;
|
||||||
|
internal const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
|
||||||
|
internal const uint SERVICE_AUTO_START = 0x02;
|
||||||
|
internal const uint SERVICE_DEMAND_START = 0x03;
|
||||||
|
internal const uint SERVICE_DISABLED = 0x04;
|
||||||
|
|
||||||
|
internal const uint SERVICE_STOPPED = 0x00000001;
|
||||||
|
internal const uint SERVICE_START_PENDING = 0x00000002;
|
||||||
|
internal const uint SERVICE_STOP_PENDING = 0x00000003;
|
||||||
|
internal const uint SERVICE_RUNNING = 0x00000004;
|
||||||
|
internal const uint SERVICE_CONTINUE_PENDING = 0x00000005;
|
||||||
|
internal const uint SERVICE_PAUSE_PENDING = 0x00000006;
|
||||||
|
internal const uint SERVICE_PAUSED = 0x00000007;
|
||||||
|
|
||||||
|
internal const uint SC_ENUM_PROCESS_INFO = 0;
|
||||||
|
internal const uint SC_STATUS_PROCESS_INFO = 0;
|
||||||
|
internal const uint ERROR_MORE_DATA = 234;
|
||||||
|
internal const uint ERROR_SERVICE_DOES_NOT_EXIST = 1060;
|
||||||
|
internal const uint ERROR_SERVICE_NOT_ACTIVE = 1062;
|
||||||
|
|
||||||
|
// Process/Thread
|
||||||
|
internal const uint PROCESS_TERMINATE = 0x0001;
|
||||||
|
internal const uint THREAD_SUSPEND_RESUME = 0x0002;
|
||||||
|
internal const uint TH32CS_SNAPTHREAD = 0x00000004;
|
||||||
|
|
||||||
|
// Token privileges
|
||||||
|
internal const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
|
||||||
|
internal const uint TOKEN_QUERY = 0x0008;
|
||||||
|
internal const uint SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||||
|
|
||||||
|
// Privilege names
|
||||||
|
internal const string SE_DEBUG_NAME = "SeDebugPrivilege";
|
||||||
|
internal const string SE_RESTORE_NAME = "SeRestorePrivilege";
|
||||||
|
internal const string SE_BACKUP_NAME = "SeBackupPrivilege";
|
||||||
|
|
||||||
|
// Security
|
||||||
|
internal const uint DACL_SECURITY_INFORMATION = 0x00000004;
|
||||||
|
internal const uint SE_FILE_OBJECT = 1;
|
||||||
|
internal const uint GRANT_ACCESS = 1;
|
||||||
|
internal const uint SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3;
|
||||||
|
internal const uint NO_MULTIPLE_TRUSTEE = 0;
|
||||||
|
internal const uint TRUSTEE_IS_SID = 0;
|
||||||
|
internal const uint TRUSTEE_IS_WELL_KNOWN_GROUP = 5;
|
||||||
|
internal const uint GENERIC_ALL = 0x10000000;
|
||||||
|
|
||||||
|
// LoadLibraryEx flags
|
||||||
|
internal const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
|
||||||
|
|
||||||
|
// Version resource type
|
||||||
|
internal const uint RT_VERSION = 16;
|
||||||
|
|
||||||
|
// CreateProcess - STARTUPINFO flags
|
||||||
|
internal const uint STARTF_USESHOWWINDOW = 0x00000001;
|
||||||
|
internal const ushort SW_HIDE = 0;
|
||||||
|
|
||||||
|
// GetModuleHandleEx
|
||||||
|
internal const uint GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004;
|
||||||
|
internal const uint GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 0x00000002;
|
||||||
|
|
||||||
|
// Error codes
|
||||||
|
internal const uint ERROR_SUCCESS = 0;
|
||||||
|
internal const uint ERROR_ACCESS_DENIED = 5;
|
||||||
|
internal const uint ERROR_NOT_SUPPORTED = 50;
|
||||||
|
internal const uint ERROR_SERVICE_ALREADY_RUNNING = 1056;
|
||||||
|
|
||||||
|
// ─── Structures ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct SYSTEM_INFO
|
||||||
|
{
|
||||||
|
internal ushort wProcessorArchitecture;
|
||||||
|
internal ushort wReserved;
|
||||||
|
internal uint dwPageSize;
|
||||||
|
internal IntPtr lpMinimumApplicationAddress;
|
||||||
|
internal IntPtr lpMaximumApplicationAddress;
|
||||||
|
internal UIntPtr dwActiveProcessorMask;
|
||||||
|
internal uint dwNumberOfProcessors;
|
||||||
|
internal uint dwProcessorType;
|
||||||
|
internal uint dwAllocationGranularity;
|
||||||
|
internal ushort wProcessorLevel;
|
||||||
|
internal ushort wProcessorRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct STARTUPINFO
|
||||||
|
{
|
||||||
|
internal uint cb;
|
||||||
|
internal string? lpReserved;
|
||||||
|
internal string? lpDesktop;
|
||||||
|
internal string? lpTitle;
|
||||||
|
internal uint dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars;
|
||||||
|
internal uint dwFillAttribute;
|
||||||
|
internal uint dwFlags;
|
||||||
|
internal ushort wShowWindow;
|
||||||
|
internal ushort cbReserved2;
|
||||||
|
internal IntPtr lpReserved2;
|
||||||
|
internal IntPtr hStdInput, hStdOutput, hStdError;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct PROCESS_INFORMATION
|
||||||
|
{
|
||||||
|
internal IntPtr hProcess;
|
||||||
|
internal IntPtr hThread;
|
||||||
|
internal uint dwProcessId;
|
||||||
|
internal uint dwThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct THREADENTRY32
|
||||||
|
{
|
||||||
|
internal uint dwSize;
|
||||||
|
internal uint cntUsage;
|
||||||
|
internal uint th32ThreadID;
|
||||||
|
internal uint th32OwnerProcessID;
|
||||||
|
internal int tpBasePri;
|
||||||
|
internal int tpDeltaPri;
|
||||||
|
internal uint dwFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct SERVICE_STATUS_PROCESS
|
||||||
|
{
|
||||||
|
internal uint dwServiceType;
|
||||||
|
internal uint dwCurrentState;
|
||||||
|
internal uint dwControlsAccepted;
|
||||||
|
internal uint dwWin32ExitCode;
|
||||||
|
internal uint dwServiceSpecificExitCode;
|
||||||
|
internal uint dwCheckPoint;
|
||||||
|
internal uint dwWaitHint;
|
||||||
|
internal uint dwProcessId;
|
||||||
|
internal uint dwServiceFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct ENUM_SERVICE_STATUS_PROCESS
|
||||||
|
{
|
||||||
|
internal string lpServiceName;
|
||||||
|
internal string lpDisplayName;
|
||||||
|
internal SERVICE_STATUS_PROCESS ServiceStatusProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct QUERY_SERVICE_CONFIG
|
||||||
|
{
|
||||||
|
internal uint dwServiceType;
|
||||||
|
internal uint dwStartType;
|
||||||
|
internal uint dwErrorControl;
|
||||||
|
internal string lpBinaryPathName;
|
||||||
|
internal string lpLoadOrderGroup;
|
||||||
|
internal uint dwTagId;
|
||||||
|
internal string lpDependencies;
|
||||||
|
internal string lpServiceStartName;
|
||||||
|
internal string lpDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct LUID
|
||||||
|
{
|
||||||
|
internal uint LowPart;
|
||||||
|
internal int HighPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct LUID_AND_ATTRIBUTES
|
||||||
|
{
|
||||||
|
internal LUID Luid;
|
||||||
|
internal uint Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct TOKEN_PRIVILEGES
|
||||||
|
{
|
||||||
|
internal uint PrivilegeCount;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
|
||||||
|
internal LUID_AND_ATTRIBUTES[] Privileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct EXPLICIT_ACCESS
|
||||||
|
{
|
||||||
|
internal uint grfAccessPermissions;
|
||||||
|
internal uint grfAccessMode;
|
||||||
|
internal uint grfInheritance;
|
||||||
|
internal TRUSTEE Trustee;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct TRUSTEE
|
||||||
|
{
|
||||||
|
internal IntPtr pMultipleTrustee;
|
||||||
|
internal uint MultipleTrusteeOperation;
|
||||||
|
internal uint TrusteeForm;
|
||||||
|
internal uint TrusteeType;
|
||||||
|
internal IntPtr ptstrName; // SID pointer or string pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct WTS_SESSION_INFO
|
||||||
|
{
|
||||||
|
internal uint SessionId;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)]
|
||||||
|
internal string Name;
|
||||||
|
internal uint State;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── kernel32.dll ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern void GetNativeSystemInfo(out SYSTEM_INFO lpSystemInfo);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool Wow64DisableWow64FsRedirection(out IntPtr oldValue);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool Wow64RevertWow64FsRedirection(IntPtr oldValue);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool FreeLibrary(IntPtr hModule);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
internal static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr LockResource(IntPtr hResData);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool CreateProcess(
|
||||||
|
string? lpApplicationName,
|
||||||
|
string lpCommandLine,
|
||||||
|
IntPtr lpProcessAttributes,
|
||||||
|
IntPtr lpThreadAttributes,
|
||||||
|
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
|
||||||
|
uint dwCreationFlags,
|
||||||
|
IntPtr lpEnvironment,
|
||||||
|
string? lpCurrentDirectory,
|
||||||
|
ref STARTUPINFO lpStartupInfo,
|
||||||
|
out PROCESS_INFORMATION lpProcessInformation);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr OpenProcess(uint dwDesiredAccess,
|
||||||
|
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern uint GetCurrentProcessId();
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern uint GetCurrentThreadId();
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool Thread32First(IntPtr hSnapshot, ref THREADENTRY32 lpte);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool Thread32Next(IntPtr hSnapshot, ref THREADENTRY32 lpte);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr OpenThread(uint dwDesiredAccess,
|
||||||
|
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern uint SuspendThread(IntPtr hThread);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
internal static extern uint ResumeThread(IntPtr hThread);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
internal static extern uint GetModuleFileName(IntPtr hModule,
|
||||||
|
System.Text.StringBuilder lpFilename, uint nSize);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool GetModuleHandleEx(uint dwFlags, IntPtr lpModuleName,
|
||||||
|
out IntPtr phModule);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
internal static extern uint ExpandEnvironmentStrings(string lpSrc,
|
||||||
|
System.Text.StringBuilder lpDst, uint nSize);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool DeleteFile(string lpFileName);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool RemoveDirectory(string lpPathName);
|
||||||
|
|
||||||
|
[DllImport(Kernel32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool OpenProcessToken(IntPtr processHandle,
|
||||||
|
uint desiredAccess, out IntPtr tokenHandle);
|
||||||
|
|
||||||
|
// ─── advapi32.dll ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr OpenSCManager(string? lpMachineName,
|
||||||
|
string? lpDatabaseName, uint dwDesiredAccess);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
internal static extern IntPtr OpenService(IntPtr hSCManager,
|
||||||
|
string lpServiceName, uint dwDesiredAccess);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool QueryServiceConfig(IntPtr hService,
|
||||||
|
IntPtr lpServiceConfig, uint cbBufSize, out uint pcbBytesNeeded);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool QueryServiceStatusEx(IntPtr hService,
|
||||||
|
uint InfoLevel, IntPtr lpBuffer, uint cbBufSize, out uint pcbBytesNeeded);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool ChangeServiceConfig(IntPtr hService,
|
||||||
|
uint dwServiceType, uint dwStartType, uint dwErrorControl,
|
||||||
|
string? lpBinaryPathName, string? lpLoadOrderGroup, IntPtr lpdwTagId,
|
||||||
|
string? lpDependencies, string? lpServiceStartName, string? lpPassword,
|
||||||
|
string? lpDisplayName);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool StartService(IntPtr hService,
|
||||||
|
uint dwNumServiceArgs, string[]? lpServiceArgVectors);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool EnumServicesStatusEx(
|
||||||
|
IntPtr hSCManager, uint InfoLevel, uint dwServiceType, uint dwServiceState,
|
||||||
|
IntPtr lpServices, uint cbBufSize,
|
||||||
|
out uint pcbBytesNeeded, out uint lpServicesReturned,
|
||||||
|
ref uint lpResumeHandle, string? pszGroupName);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool LookupPrivilegeValue(string? lpSystemName,
|
||||||
|
string lpName, out LUID lpLuid);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool AdjustTokenPrivileges(IntPtr tokenHandle,
|
||||||
|
[MarshalAs(UnmanagedType.Bool)] bool disableAllPrivileges,
|
||||||
|
ref TOKEN_PRIVILEGES newState, uint bufferLength,
|
||||||
|
IntPtr previousState, IntPtr returnLength);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool ConvertStringSidToSid(string stringSid, out IntPtr sid);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
internal static extern uint SetEntriesInAcl(uint cCountOfExplicitEntries,
|
||||||
|
ref EXPLICIT_ACCESS pListOfExplicitEntries, IntPtr oldAcl, out IntPtr newAcl);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
internal static extern uint SetNamedSecurityInfo(string pObjectName,
|
||||||
|
uint ObjectType, uint SecurityInfo,
|
||||||
|
IntPtr psidOwner, IntPtr psidGroup, IntPtr pDacl, IntPtr pSacl);
|
||||||
|
|
||||||
|
[DllImport(Advapi32, SetLastError = true)]
|
||||||
|
internal static extern IntPtr LocalFree(IntPtr hMem);
|
||||||
|
|
||||||
|
// ─── winsta.dll ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates WTS sessions on the local server.
|
||||||
|
/// Pass <c>IntPtr.Zero</c> as hServer for the local machine.
|
||||||
|
/// </summary>
|
||||||
|
[DllImport(WinSta, EntryPoint = "WinStationEnumerateW",
|
||||||
|
CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool WinStationEnumerate(IntPtr hServer,
|
||||||
|
out IntPtr ppSessionInfo, out uint pCount);
|
||||||
|
|
||||||
|
[DllImport(WinSta, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool WinStationFreeMemory(IntPtr p);
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process creation and termination helpers. Mirrors ExecWait and KillProcess
|
||||||
|
/// from RDPWInst.dpr (console variant) and RDPConf MainUnit.pas (GUI variant).
|
||||||
|
/// </summary>
|
||||||
|
public static class ProcessHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a process from <paramref name="commandLine"/>, waits for it to
|
||||||
|
/// exit, then returns <c>true</c>. The process window is hidden.
|
||||||
|
/// Mirrors the Delphi ExecWait procedure.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ExecWait(string commandLine, bool hideWindow = true)
|
||||||
|
{
|
||||||
|
var si = new NativeMethods.STARTUPINFO
|
||||||
|
{
|
||||||
|
cb = (uint)Marshal.SizeOf<NativeMethods.STARTUPINFO>(),
|
||||||
|
dwFlags = hideWindow ? NativeMethods.STARTF_USESHOWWINDOW : 0u,
|
||||||
|
wShowWindow = hideWindow ? NativeMethods.SW_HIDE : (ushort)1
|
||||||
|
};
|
||||||
|
|
||||||
|
// CommandLine must be mutable — pass a copy
|
||||||
|
string cmdCopy = new(commandLine);
|
||||||
|
|
||||||
|
if (!NativeMethods.CreateProcess(null, cmdCopy, IntPtr.Zero, IntPtr.Zero,
|
||||||
|
true, 0, IntPtr.Zero, null, ref si, out var pi))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] CreateProcess error (code {Marshal.GetLastWin32Error()}).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeMethods.WaitForSingleObject(pi.hProcess, 0xFFFFFFFF);
|
||||||
|
NativeMethods.CloseHandle(pi.hThread);
|
||||||
|
NativeMethods.CloseHandle(pi.hProcess);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Terminates the process with <paramref name="pid"/>. Mirrors the Delphi
|
||||||
|
/// KillProcess procedure.
|
||||||
|
/// </summary>
|
||||||
|
public static void KillProcess(uint pid)
|
||||||
|
{
|
||||||
|
var hProc = NativeMethods.OpenProcess(NativeMethods.PROCESS_TERMINATE, false, pid);
|
||||||
|
if (hProc == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] OpenProcess error (code {Marshal.GetLastWin32Error()}).");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NativeMethods.TerminateProcess(hProc, 0))
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] TerminateProcess error (code {Marshal.GetLastWin32Error()}).");
|
||||||
|
|
||||||
|
NativeMethods.CloseHandle(hProc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>RDPWrap.Common</RootNamespace>
|
||||||
|
<AssemblyName>RDPWrap.Common</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thin wrappers around <see cref="Microsoft.Win32.Registry"/> that mirror the
|
||||||
|
/// Delphi TRegistry usage in RDPWInst and RDPConf, with optional WOW64 flag
|
||||||
|
/// support (KEY_WOW64_64KEY) for 64-bit registry views from 32-bit processes.
|
||||||
|
/// </summary>
|
||||||
|
public static class RegistryHelper
|
||||||
|
{
|
||||||
|
// On 64-bit Windows we always open the 64-bit view to match the Delphi code
|
||||||
|
// that passes KEY_WOW64_64KEY when Arch = 64.
|
||||||
|
private static RegistryView ViewForArch() =>
|
||||||
|
ArchHelper.Is64Bit ? RegistryView.Registry64 : RegistryView.Default;
|
||||||
|
|
||||||
|
// ── Convenience open helpers ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Opens a read-only key under HKLM, respecting the host architecture.</summary>
|
||||||
|
public static RegistryKey? OpenHklmRead(string subKey)
|
||||||
|
{
|
||||||
|
using var hive = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, ViewForArch());
|
||||||
|
return hive.OpenSubKey(subKey, writable: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Opens a writable key under HKLM (creates if absent).</summary>
|
||||||
|
public static RegistryKey OpenHklmWrite(string subKey)
|
||||||
|
{
|
||||||
|
using var hive = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, ViewForArch());
|
||||||
|
return hive.CreateSubKey(subKey, writable: true)
|
||||||
|
?? throw new InvalidOperationException($"Cannot open/create HKLM\\{subKey}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Typed read helpers ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a string value from HKLM. Returns <c>null</c> if the key or
|
||||||
|
/// value does not exist.
|
||||||
|
/// </summary>
|
||||||
|
public static string? ReadString(string subKey, string valueName)
|
||||||
|
{
|
||||||
|
using var key = OpenHklmRead(subKey);
|
||||||
|
return key?.GetValue(valueName) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a DWORD value from HKLM. Returns <paramref name="defaultValue"/>
|
||||||
|
/// if the key or value does not exist.
|
||||||
|
/// </summary>
|
||||||
|
public static int ReadInt(string subKey, string valueName, int defaultValue = 0)
|
||||||
|
{
|
||||||
|
using var key = OpenHklmRead(subKey);
|
||||||
|
if (key is null) return defaultValue;
|
||||||
|
var raw = key.GetValue(valueName);
|
||||||
|
return raw is int i ? i : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a DWORD as a bool (non-zero = true). Returns <paramref name="defaultValue"/>
|
||||||
|
/// if absent.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ReadBool(string subKey, string valueName, bool defaultValue = false)
|
||||||
|
{
|
||||||
|
using var key = OpenHklmRead(subKey);
|
||||||
|
if (key is null) return defaultValue;
|
||||||
|
var raw = key.GetValue(valueName);
|
||||||
|
return raw is int i ? i != 0 : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Typed write helpers ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Writes a REG_SZ string value.</summary>
|
||||||
|
public static void WriteString(string subKey, string valueName, string value)
|
||||||
|
{
|
||||||
|
using var key = OpenHklmWrite(subKey);
|
||||||
|
key.SetValue(valueName, value, RegistryValueKind.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a REG_EXPAND_SZ string value (mirrors Delphi WriteExpandString).</summary>
|
||||||
|
public static void WriteExpandString(string subKey, string valueName, string value)
|
||||||
|
{
|
||||||
|
using var key = OpenHklmWrite(subKey);
|
||||||
|
key.SetValue(valueName, value, RegistryValueKind.ExpandString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a DWORD integer value.</summary>
|
||||||
|
public static void WriteInt(string subKey, string valueName, int value)
|
||||||
|
{
|
||||||
|
using var key = OpenHklmWrite(subKey);
|
||||||
|
key.SetValue(valueName, value, RegistryValueKind.DWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a boolean as a DWORD (1/0).</summary>
|
||||||
|
public static void WriteBool(string subKey, string valueName, bool value)
|
||||||
|
=> WriteInt(subKey, valueName, value ? 1 : 0);
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helpers for reading and extracting managed embedded resources.
|
||||||
|
/// Mirrors the Delphi ExtractRes / ExtractResText procedures from RDPWInst.dpr
|
||||||
|
/// and RDPConf MainUnit.pas, translated to the .NET manifest-resource model.
|
||||||
|
/// </summary>
|
||||||
|
public static class ResourceHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the contents of the embedded resource named
|
||||||
|
/// <paramref name="resourceName"/> from <paramref name="assembly"/>
|
||||||
|
/// and returns it as a UTF-8 string. Returns <c>null</c> if not found.
|
||||||
|
/// </summary>
|
||||||
|
public static string? ReadText(string resourceName,
|
||||||
|
Assembly? assembly = null)
|
||||||
|
{
|
||||||
|
assembly ??= Assembly.GetCallingAssembly();
|
||||||
|
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||||
|
if (stream is null) return null;
|
||||||
|
using var reader = new StreamReader(stream, System.Text.Encoding.UTF8);
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the contents of the embedded resource named
|
||||||
|
/// <paramref name="resourceName"/> and returns the raw bytes.
|
||||||
|
/// Returns <c>null</c> if not found.
|
||||||
|
/// </summary>
|
||||||
|
public static byte[]? ReadBytes(string resourceName,
|
||||||
|
Assembly? assembly = null)
|
||||||
|
{
|
||||||
|
assembly ??= Assembly.GetCallingAssembly();
|
||||||
|
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||||
|
if (stream is null) return null;
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
stream.CopyTo(ms);
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the embedded resource <paramref name="resourceName"/> to disk
|
||||||
|
/// at <paramref name="destPath"/>, creating parent directories as needed.
|
||||||
|
/// Returns <c>true</c> on success. Mirrors the Delphi ExtractRes procedure.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ExtractToDisk(string resourceName, string destPath,
|
||||||
|
Assembly? assembly = null)
|
||||||
|
{
|
||||||
|
assembly ??= Assembly.GetCallingAssembly();
|
||||||
|
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||||
|
if (stream is null)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"[-] Resource not found: {resourceName}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = Path.GetDirectoryName(destPath);
|
||||||
|
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var file = File.Create(destPath);
|
||||||
|
stream.CopyTo(file);
|
||||||
|
Console.WriteLine($"[+] Extracted {resourceName} -> {destPath}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] Failed to extract resource {resourceName} to {destPath}: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lists all manifest resource names in <paramref name="assembly"/>
|
||||||
|
/// — useful for debugging resource name mismatches.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<string> ListResources(Assembly? assembly = null)
|
||||||
|
{
|
||||||
|
assembly ??= Assembly.GetCallingAssembly();
|
||||||
|
return assembly.GetManifestResourceNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Security helpers: granting SID-based DACL entries (GrantSidFullAccess) and
|
||||||
|
/// adjusting process token privileges (AddPrivilege). Mirrors the Delphi
|
||||||
|
/// implementations in RDPWInst.dpr.
|
||||||
|
/// </summary>
|
||||||
|
public static class SecurityHelper
|
||||||
|
{
|
||||||
|
// ── DACL: grant a well-known SID full access to a file/folder ─────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grants GENERIC_ALL access to the well-known SID string
|
||||||
|
/// <paramref name="stringSid"/> on the file/directory at
|
||||||
|
/// <paramref name="path"/>. Mirrors the Delphi GrantSidFullAccess
|
||||||
|
/// procedure (used for "S-1-5-18" = Local System, "S-1-5-6" = Service).
|
||||||
|
/// </summary>
|
||||||
|
public static void GrantSidFullAccess(string path, string stringSid)
|
||||||
|
{
|
||||||
|
if (!NativeMethods.ConvertStringSidToSid(stringSid, out IntPtr pSid))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] ConvertStringSidToSid error (code {Marshal.GetLastWin32Error()}) " +
|
||||||
|
$"for SID {stringSid}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ea = new NativeMethods.EXPLICIT_ACCESS
|
||||||
|
{
|
||||||
|
grfAccessPermissions = NativeMethods.GENERIC_ALL,
|
||||||
|
grfAccessMode = NativeMethods.GRANT_ACCESS,
|
||||||
|
grfInheritance = NativeMethods.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
|
||||||
|
Trustee = new NativeMethods.TRUSTEE
|
||||||
|
{
|
||||||
|
pMultipleTrustee = IntPtr.Zero,
|
||||||
|
MultipleTrusteeOperation = NativeMethods.NO_MULTIPLE_TRUSTEE,
|
||||||
|
TrusteeForm = NativeMethods.TRUSTEE_IS_SID,
|
||||||
|
TrusteeType = NativeMethods.TRUSTEE_IS_WELL_KNOWN_GROUP,
|
||||||
|
ptstrName = pSid
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint result = NativeMethods.SetEntriesInAcl(1, ref ea, IntPtr.Zero, out IntPtr pNewAcl);
|
||||||
|
if (result == NativeMethods.ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
uint secResult = NativeMethods.SetNamedSecurityInfo(
|
||||||
|
path,
|
||||||
|
NativeMethods.SE_FILE_OBJECT,
|
||||||
|
NativeMethods.DACL_SECURITY_INFORMATION,
|
||||||
|
IntPtr.Zero, IntPtr.Zero, pNewAcl, IntPtr.Zero);
|
||||||
|
|
||||||
|
if (secResult != NativeMethods.ERROR_SUCCESS)
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] SetNamedSecurityInfo error (code {secResult}).");
|
||||||
|
|
||||||
|
NativeMethods.LocalFree(pNewAcl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"[-] SetEntriesInAcl error (code {result}).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NativeMethods.LocalFree(pSid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Token privileges ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables the named privilege (e.g. <c>SeDebugPrivilege</c>) for the
|
||||||
|
/// current process token. Mirrors the Delphi AddPrivilege function.
|
||||||
|
/// Returns <c>true</c> on success.
|
||||||
|
/// </summary>
|
||||||
|
public static bool AddPrivilege(string privilegeName)
|
||||||
|
{
|
||||||
|
if (!NativeMethods.OpenProcessToken(
|
||||||
|
System.Diagnostics.Process.GetCurrentProcess().Handle,
|
||||||
|
NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY,
|
||||||
|
out IntPtr hToken))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] OpenProcessToken error (code {Marshal.GetLastWin32Error()}).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!NativeMethods.LookupPrivilegeValue(null, privilegeName, out var luid))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] LookupPrivilegeValue error (code {Marshal.GetLastWin32Error()}).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tp = new NativeMethods.TOKEN_PRIVILEGES
|
||||||
|
{
|
||||||
|
PrivilegeCount = 1,
|
||||||
|
Privileges = new[]
|
||||||
|
{
|
||||||
|
new NativeMethods.LUID_AND_ATTRIBUTES
|
||||||
|
{
|
||||||
|
Luid = luid,
|
||||||
|
Attributes = NativeMethods.SE_PRIVILEGE_ENABLED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!NativeMethods.AdjustTokenPrivileges(
|
||||||
|
hToken, false, ref tp,
|
||||||
|
(uint)Marshal.SizeOf(tp),
|
||||||
|
IntPtr.Zero, IntPtr.Zero))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[-] AdjustTokenPrivileges error (code {Marshal.GetLastWin32Error()}).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NativeMethods.CloseHandle(hToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── RDP-Tcp listener ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> when there is an active "RDP-Tcp" WTS listener on
|
||||||
|
/// the local machine — i.e. <c>WinStationEnumerateW</c> returns a session
|
||||||
|
/// named "RDP-Tcp". Mirrors the Delphi IsListenerWorking function.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsRdpListenerWorking()
|
||||||
|
{
|
||||||
|
if (!NativeMethods.WinStationEnumerate(IntPtr.Zero,
|
||||||
|
out IntPtr ppInfo, out uint count))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int ptrSize = IntPtr.Size;
|
||||||
|
// WTS_SESSION_INFO is: DWORD SessionId + 34 WCHARs (Name) + DWORD State
|
||||||
|
// = 4 + 68 + 4 = 76 bytes, but aligned to 4-byte boundary → 76 bytes.
|
||||||
|
int entrySize = 4 + 34 * 2 + 4; // = 76
|
||||||
|
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
IntPtr entry = ppInfo + (int)(i * (uint)entrySize);
|
||||||
|
// Name starts at offset 4
|
||||||
|
string name = Marshal.PtrToStringUni(entry + 4, 34).TrimEnd('\0');
|
||||||
|
if (name == "RDP-Tcp") return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NativeMethods.WinStationFreeMemory(ppInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.11.35303.130
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDPWrap.Common", "RDPWrap.Common\RDPWrap.Common.csproj", "{A1B2C3D4-0001-4000-8000-000000000001}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDPWInst", "RDPWInst\RDPWInst.csproj", "{A1B2C3D4-0001-4000-8000-000000000002}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDPConf", "RDPConf\RDPConf.csproj", "{A1B2C3D4-0001-4000-8000-000000000003}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDPCheck", "RDPCheck\RDPCheck.csproj", "{A1B2C3D4-0001-4000-8000-000000000004}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000001}.Release|x86.Build.0 = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000002}.Release|x86.Build.0 = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000003}.Release|x86.Build.0 = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{A1B2C3D4-0001-4000-8000-000000000004}.Release|x86.Build.0 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
Loading…
Reference in new issue