// ============================================================================= // 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. // // Build (needs Mono.Cecil): // dotnet new console -o patcher && cd patcher // dotnet add package Mono.Cecil // copy PatchLicense.cs into the project (replace 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.Collections.Generic; 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 asm = AssemblyDefinition.ReadAssembly(path, new ReaderParameters { 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 types ------------------------------------------------- var tDNAFusion = mod.ImportType("OpenOptions.SoftwareProtection.DNAFusion, OpenOptions.SoftwareProtection"); var tFeature = mod.ImportType("OpenOptions.SoftwareProtection.Feature, OpenOptions.SoftwareProtection"); var tLicense = mod.ImportType("OpenOptions.SoftwareProtection.License, OpenOptions.SoftwareProtection"); var tHolder = mod.ImportType("OpenOptions.SoftwareProtection.Holder, OpenOptions.SoftwareProtection"); var tListOpen = mod.ImportType("System.Collections.Generic.List`1, mscorlib"); var tListF = new GenericInstanceType(tListOpen) { ArgumentTypes = { tFeature } }; // member refs var cctorDNAFusion = tDNAFusion.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); var cctorFeature = tFeature.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); var cctorHolder = tHolder.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); var cctorListF = tListF.Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 0); var setFeatureName = tFeature.Methods.First(x => x.Name == "set_Name"); var setFeatureValue = tFeature.Methods.First(x => x.Name == "set_Value"); var listAdd = tListF.Methods.First(x => x.Name == "Add"); var listCount = tListF.Methods.First(x => x.Name == "get_Count"); var listItem = tListF.Methods.First(x => x.Name == "get_Item"); var setHolder = tLicense.Methods.First(x => x.Name == "set_Holder"); var setLicense = sm.Properties.First(p => p.Name == "License").SetMethod; var setHaspKey = tHolder.Methods.First(x => x.Name == "set_HaspKey"); var setSoftKey = tHolder.Methods.First(x => x.Name == "set_SoftKey"); var typeGetFromHandle = mod.ImportType("System.Type").Methods.First(x => x.Name == "GetTypeFromHandle"); var typeGetFields = mod.ImportType("System.Type").Methods.First(x => x.Name == "GetFields" && x.Parameters.Count == 1); var fldInfoType = mod.ImportType("System.Reflection.FieldInfo"); var fldInfoGetFieldType = fldInfoType.Methods.First(x => x.Name == "get_FieldType"); var fldInfoSetValue = fldInfoType.Methods.First(x => x.Name == "SetValue" && x.Parameters.Count == 2); // ---- rebuild body -------------------------------------------------- var body = m.Body; body.Instructions.Clear(); body.LocalVariables.Clear(); var il = body.GetILProcessor(); var lLic = body.InitLocal(tDNAFusion); // 0 var lFeats = body.InitLocal(tListF); // 1 var lFld = body.InitLocal(fldInfoType); // 2 var lArr = body.InitLocal(new ArrayType(fldInfoType)); // 3 var lI = body.InitLocal(mod.ImportType("System.Int32")); // 4 var lF = body.InitLocal(fldInfoType); // 5 var lJ = body.InitLocal(mod.ImportType("System.Int32")); // 6 // var lic = new DNAFusion(); il.Emit(OpCodes.Newobj, cctorDNAFusion); il.Emit(OpCodes.Stloc, lLic); // var feats = new List(); il.Emit(OpCodes.Newobj, cctorListF); il.Emit(OpCodes.Stloc, lFeats); // feats.Add(new Feature { Name="Flex", Value="true" }); (+2 more) EmitFeatureAdd(il, lFeats, cctorFeature, setFeatureName, setFeatureValue, listAdd, "Flex", FlexValue); EmitFeatureAdd(il, lFeats, cctorFeature, setFeatureName, setFeatureValue, listAdd, "FlexMobile", FlexMobileValue); EmitFeatureAdd(il, lFeats, cctorFeature, setFeatureName, setFeatureValue, listAdd, "WebUsers", WebUsersValue); // FieldInfo[] arr = typeof(License).GetFields(NonPublic|Instance); il.Emit(OpCodes.Ldtoken, tLicense); il.Emit(OpCodes.Call, typeGetFromHandle); il.Emit(OpCodes.Ldc_I4, 0x3); // BindingFlags.NonPublic | Instance il.Emit(OpCodes.Callvirt, typeGetFields); il.Emit(OpCodes.Stloc, lArr); // int i = 0; FieldInfo fld = null; il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc, lI); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Stloc, lFld); // for (i = 0; i < arr.Length; i++) if (arr[i].FieldType == typeof(List)) { fld = arr[i]; break; } il.Emit(OpCodes.Ldloc, lI); il.Emit(OpCodes.Ldloc, lArr); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); var brBlt = il.Emit(OpCodes.Blt_S, null); var brExit1 = il.Emit(OpCodes.Br_S, null); var bodyStart = il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldloc, lArr); il.Emit(OpCodes.Ldloc, lI); il.Emit(OpCodes.Ldelem_Ref); il.Emit(OpCodes.Stloc, lF); il.Emit(OpCodes.Ldloc, lF); il.Emit(OpCodes.Callvirt, fldInfoGetFieldType); il.Emit(OpCodes.Ldtoken, tListF); il.Emit(OpCodes.Call, typeGetFromHandle); var brMatch = il.Emit(OpCodes.Beq_S, null); il.Emit(OpCodes.Ldloc, lI); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc, lI); var brLoop = il.Emit(OpCodes.Br_S, bodyStart); var matchLbl = il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldloc, lF); il.Emit(OpCodes.Stloc, lFld); var brAfter = il.Emit(OpCodes.Br_S, null); var afterLbl = il.Emit(OpCodes.Nop); // fix up branches brBlt.Operand = bodyStart; brExit1.Operand = afterLbl; brMatch.Operand = matchLbl; brLoop.Operand = bodyStart; brAfter.Operand = afterLbl; // if (fld != null) fld.SetValue(lic, feats); else for (j...) lic.Features.Add(feats[j]); il.Emit(OpCodes.Ldloc, lFld); var brSet = il.Emit(OpCodes.Brtrue_S, null); // else: for (int j = 0; j < feats.Count; j++) lic.Features.Add(feats[j]); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc, lJ); var jCond = il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldloc, lJ); il.Emit(OpCodes.Ldloc, lFeats); il.Emit(OpCodes.Callvirt, listCount); var jBlt = il.Emit(OpCodes.Blt_S, null); var jExit = il.Emit(OpCodes.Br_S, null); var jBody = il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldloc, lLic); il.Emit(OpCodes.Ldloc, lFeats); il.Emit(OpCodes.Ldloc, lJ); il.Emit(OpCodes.Callvirt, listItem); il.Emit(OpCodes.Callvirt, listAdd); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldloc, lJ); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc, lJ); var jBr = il.Emit(OpCodes.Br_S, jCond); var setLbl = il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldloc, lFld); il.Emit(OpCodes.Ldloc, lLic); il.Emit(OpCodes.Ldloc, lFeats); il.Emit(OpCodes.Callvirt, fldInfoSetValue); il.Emit(OpCodes.Pop); jBlt.Operand = jBody; jExit.Operand = setLbl; jBr.Operand = jCond; brSet.Operand = setLbl; // lic.Holder = new Holder { HaspKey = "", SoftKey = softKey }; il.Emit(OpCodes.Ldloc, lLic); il.Emit(OpCodes.Newobj, cctorHolder); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldstr, ""); il.Emit(OpCodes.Callvirt, setHaspKey); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldstr, softKey); il.Emit(OpCodes.Callvirt, setSoftKey); il.Emit(OpCodes.Callvirt, setHolder); // this.License = lic; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldloc, lLic); il.Emit(OpCodes.Callvirt, setLicense); il.Emit(OpCodes.Ret); // ---- write + verify ----------------------------------------------- var backup = path + ".orig"; if (!File.Exists(backup)) File.Copy(path, backup); asm.Write(path, new WriterParameters { }); // re-read to confirm it parses and the method is now small 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 (was ~large)"); Console.WriteLine($" SoftKey -> {softKey}"); Console.WriteLine(" Restart the Flex service to apply."); return 0; } private static void EmitFeatureAdd(ILProcessor il, LocalVariable feats, MethodReference cctor, MethodReference setName, MethodReference setValue, MethodReference add, string name, string value) { il.Emit(OpCodes.Ldloc, feats); il.Emit(OpCodes.Newobj, cctor); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldstr, name); il.Emit(OpCodes.Callvirt, setName); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldstr, value); il.Emit(OpCodes.Callvirt, setValue); il.Emit(OpCodes.Callvirt, add); il.Emit(OpCodes.Pop); } }