// ============================================================================= // PatchLicense.cs — one-command license-gate bypass for dnaFusion Flex API // ============================================================================= // Patches OpenOptions.dnaFusion.Flex.ServiceManager.LoadLicense() in // OpenOptions.dnaFusion.Flex.Common.dll (NOT strong-name signed) so it installs // a hand-built DNAFusion license with the features the gate checks: // // Flex = true (bool gate: IsFeatureLicensed("Flex")) // FlexMobile = 1000000 (int gate: device count) // WebUsers = 1000000 (int gate: web operator count) // // Plus a Holder (HaspKey="" / SoftKey=) so the FlexInterop.BroadcastEvent // named-pipe path (which reads license.Holder.HaspKey/SoftKey) doesn't NRE. // // The Features list is written straight into License's private // 'k__BackingField' field (located by type at patch time), so it works // regardless of how the obfuscated constructor initializes it. // // Build (needs Mono.Cecil): // dotnet new console -o patcher && cd patcher // dotnet add package Mono.Cecil // copy PatchLicense.cs over Program.cs // dotnet run -- [softkey] // // The script backs the original up to .orig, then writes the patched // assembly in place. Restart the Flex service afterwards. // ============================================================================= using System; using System.IO; using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; internal static class Program { // The SoftKey the desktop DNAFusion client must present to BroadcastEvent. // If you don't use the named-pipe interop, the value is irrelevant. private const string DefaultSoftKey = "FLEXSTUB"; private const string FlexValue = "true"; private const string FlexMobileValue = "1000000"; private const string WebUsersValue = "1000000"; private static int Main(string[] args) { string path = args.Length > 0 ? args[0] : "OpenOptions.dnaFusion.Flex.Common.dll"; string softKey = args.Length > 1 ? args[1] : DefaultSoftKey; if (!File.Exists(path)) { Console.Error.WriteLine($"File not found: {path}"); return 2; } var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); var asm = AssemblyDefinition.ReadAssembly(path, new ReaderParameters { AssemblyResolver = resolver, ReadingMode = ReadingMode.Deferred }); var mod = asm.Modules[0]; var sm = mod.GetType("OpenOptions.dnaFusion.Flex.ServiceManager"); if (sm == null) { Console.Error.WriteLine("ServiceManager type not found."); return 1; } var m = sm.Methods.FirstOrDefault(x => x.Name == "LoadLicense" && x.Parameters.Count == 0); if (m == null) { Console.Error.WriteLine("LoadLicense() (0-param) not found."); return 1; } // ---- resolve the (referenced) SoftwareProtection types ------------- var spRef = mod.AssemblyReferences.First(r => r.Name == "OpenOptions.SoftwareProtection"); TypeReference TR(string name) => new TypeReference("OpenOptions.SoftwareProtection", name, mod, spRef); var licDef = TR("License").Resolve(); var featDef = TR("Feature").Resolve(); var dnaDef = TR("DNAFusion").Resolve(); var holderDef = TR("Holder").Resolve(); var getFeatures = licDef.Methods.First(x => x.Name == "get_Features"); var listDef = getFeatures.ReturnType.Resolve(); // List var featField = licDef.Fields.First(f => f.FieldType.FullName == getFeatures.ReturnType.FullName); MethodReference dnaCtor = dnaDef.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); MethodReference featCtor = featDef.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); MethodReference featSetName = featDef.Methods.First(x => x.Name == "set_Name"); MethodReference featSetValue = featDef.Methods.First(x => x.Name == "set_Value"); MethodReference listCtor = listDef.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); MethodReference listAdd = listDef.Methods.First(x => x.Name == "Add"); MethodReference holderCtor = holderDef.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); MethodReference holderSetH = holderDef.Methods.First(x => x.Name == "set_HaspKey"); MethodReference holderSetS = holderDef.Methods.First(x => x.Name == "set_SoftKey"); MethodReference setHolder = licDef.Methods.First(x => x.Name == "set_Holder"); MethodReference setLicense = sm.Properties.First(p => p.Name == "License").SetMethod; // import cross-assembly references into the target module MethodReference ImpM(MethodReference r) => mod.ImportReference(r); dnaCtor = ImpM(dnaCtor); featCtor = ImpM(featCtor); featSetName = ImpM(featSetName); featSetValue = ImpM(featSetValue); listCtor = ImpM(listCtor); listAdd = ImpM(listAdd); holderCtor = ImpM(holderCtor); holderSetH = ImpM(holderSetH); holderSetS = ImpM(holderSetS); setHolder = ImpM(setHolder); FieldReference featFieldRef = mod.ImportReference(featField); // ---- rebuild body -------------------------------------------------- var body = m.Body; body.Instructions.Clear(); body.Variables.Clear(); var il = body.GetILProcessor(); var lLic = AddLocal(body, dnaCtor.DeclaringType); // DNAFusion var lFeats = AddLocal(body, listAdd.DeclaringType); // List // var lic = new DNAFusion(); E(il, OpCodes.Newobj, dnaCtor); E(il, OpCodes.Stloc, lLic); // var feats = new List(); E(il, OpCodes.Newobj, listCtor); E(il, OpCodes.Stloc, lFeats); // feats.Add(new Feature { Name=..., Value=... }); x3 EmitFeatureAdd(il, lFeats, featCtor, featSetName, featSetValue, listAdd, "Flex", FlexValue); EmitFeatureAdd(il, lFeats, featCtor, featSetName, featSetValue, listAdd, "FlexMobile", FlexMobileValue); EmitFeatureAdd(il, lFeats, featCtor, featSetName, featSetValue, listAdd, "WebUsers", WebUsersValue); // lic.k__BackingField = feats; E(il, OpCodes.Ldloc, lLic); E(il, OpCodes.Ldloc, lFeats); E(il, OpCodes.Stfld, featFieldRef); // lic.Holder = new Holder { HaspKey = "", SoftKey = softKey }; E(il, OpCodes.Ldloc, lLic); E(il, OpCodes.Newobj, holderCtor); E(il, OpCodes.Dup); E(il, OpCodes.Ldstr, ""); E(il, OpCodes.Callvirt, holderSetH); E(il, OpCodes.Dup); E(il, OpCodes.Ldstr, softKey); E(il, OpCodes.Callvirt, holderSetS); E(il, OpCodes.Callvirt, setHolder); // this.License = lic; E(il, OpCodes.Ldarg_0); E(il, OpCodes.Ldloc, lLic); E(il, OpCodes.Callvirt, setLicense); E(il, OpCodes.Ret); // ---- write + verify ----------------------------------------------- // Preload embedded-resource bytes so the deferred writer doesn't re-read // them from the (soon-closed) original stream (avoids BadImageFormatException). foreach (var r in mod.Resources.OfType()) r.GetResourceData(); var backup = path + ".orig"; if (!File.Exists(backup)) File.Copy(path, backup); var tmp = path + ".new"; asm.Write(tmp, new WriterParameters { }); // write to a new file (same-file write truncates the read stream) File.Delete(path); File.Move(tmp, path); var check = AssemblyDefinition.ReadAssembly(path); var m2 = check.MainModule.GetType("OpenOptions.dnaFusion.Flex.ServiceManager") .Methods.First(x => x.Name == "LoadLicense" && x.Parameters.Count == 0); Console.WriteLine($"OK patched {Path.GetFileName(path)}"); Console.WriteLine($" backup -> {backup}"); Console.WriteLine($" LoadLicense() -> {m2.Body.Instructions.Count} IL instructions, {m2.Body.Variables.Count} locals"); Console.WriteLine($" SoftKey -> {softKey}"); Console.WriteLine(" Restart the Flex service to apply."); return 0; } // Append an instruction and return it. // 0.11.6 hides the Instruction ctor; use the static Instruction.Create factory. private static Instruction E(ILProcessor il, OpCode op, object operand = null) { Instruction i = operand switch { null => Instruction.Create(op), MethodReference mr => Instruction.Create(op, mr), FieldReference fr => Instruction.Create(op, fr), string s => Instruction.Create(op, s), int n => Instruction.Create(op, n), VariableDefinition v => Instruction.Create(op, v), TypeReference t => Instruction.Create(op, t), Instruction ins => Instruction.Create(op, ins), _ => throw new InvalidOperationException($"Unsupported IL operand type: {operand.GetType()}") }; il.Append(i); return i; } private static VariableDefinition AddLocal(MethodBody body, TypeReference type) { var v = new VariableDefinition(type); body.Variables.Add(v); return v; } private static void EmitFeatureAdd(ILProcessor il, VariableDefinition feats, MethodReference cctor, MethodReference setName, MethodReference setValue, MethodReference add, string name, string value) { E(il, OpCodes.Ldloc, feats); E(il, OpCodes.Newobj, cctor); E(il, OpCodes.Dup); E(il, OpCodes.Ldstr, name); E(il, OpCodes.Callvirt, setName); E(il, OpCodes.Dup); E(il, OpCodes.Ldstr, value); E(il, OpCodes.Callvirt, setValue); E(il, OpCodes.Callvirt, add); E(il, OpCodes.Pop); } }