From 51f0521b6e587ab525a2a240d00f7ea4e6baa4e1 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Sat, 25 Apr 2026 13:39:51 +0200 Subject: [PATCH] feat: reduce AV false positives in runtime stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows Defender flags obfuscated output as malware due to heuristic pattern matching against the combined runtime stub patterns. Changes: - Remove GCHandle.Alloc(Pinned) from Compressor runtime stubs — pinning is unnecessary since the byte[] is locally referenced and won't be GC'd - Remove System.Runtime.InteropServices dependency from runtime stubs - Add instruction count limit (150) to HardeningPhase inlining to prevent aggregating all suspicious patterns into a single .cctor method body All protection features remain fully functional. The GCHandle removal changes the memory management approach without affecting the decryption logic. The hardening limit keeps large protection initializers as separate method calls instead of inlining them, which spreads heuristic patterns across multiple methods. --- Confuser.Protections/HardeningPhase.cs | 5 ++++- Confuser.Runtime/Compressor.Compat.cs | 17 +++++------------ Confuser.Runtime/Compressor.cs | 17 +++++------------ 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Confuser.Protections/HardeningPhase.cs b/Confuser.Protections/HardeningPhase.cs index 88de86bff..17dabdfe4 100644 --- a/Confuser.Protections/HardeningPhase.cs +++ b/Confuser.Protections/HardeningPhase.cs @@ -46,7 +46,10 @@ private static void HardenMethod(ConfuserContext context, ModuleDef module) { if (!marker.IsMarked(targetMethod) || !(marker.GetHelperParent(targetMethod) is Protection protection)) continue; // Resource protection needs to rewrite the method during the write phase. Not compatible! - if (protection.FullId.Equals(ResourceProtection._FullId)) continue; + if (protection.FullId.Equals(ResourceProtection._FullId)) continue; + + // Skip large methods to avoid aggregating suspicious patterns in .cctor + if (targetMethod.HasBody && targetMethod.Body.Instructions.Count > 150) continue; cctor.Body.MergeCall(instructions[i]); targetMethod.DeclaringType.Methods.Remove(targetMethod); diff --git a/Confuser.Runtime/Compressor.Compat.cs b/Confuser.Runtime/Compressor.Compat.cs index 74afa433e..3dbe11874 100644 --- a/Confuser.Runtime/Compressor.Compat.cs +++ b/Confuser.Runtime/Compressor.Compat.cs @@ -1,14 +1,13 @@ -using System; +using System; using System.IO; using System.Reflection; -using System.Runtime.InteropServices; using System.Text; namespace Confuser.Runtime { internal static class CompressorCompat { static byte[] key; - static GCHandle Decrypt(uint[] data, uint seed) { + static byte[] Decrypt(uint[] data, uint seed) { var w = new uint[0x10]; var k = new uint[0x10]; ulong s = seed; @@ -35,14 +34,13 @@ static GCHandle Decrypt(uint[] data, uint seed) { byte[] j = Lzma.Decompress(b); Array.Clear(b, 0, b.Length); - GCHandle g = GCHandle.Alloc(j, GCHandleType.Pinned); var z = (uint)(s % 0x8a5cb7); for (int i = 0; i < j.Length; i++) { j[i] ^= (byte)s; if ((i & 0xff) == 0) s = (s * s) % 0x8a5cb7; } - return g; + return j; } [STAThread] @@ -50,11 +48,9 @@ static int Main(string[] args) { var l = (uint)Mutation.KeyI0; uint[] q = Mutation.Placeholder(new uint[Mutation.KeyI0]); - GCHandle h = Decrypt(q, (uint)Mutation.KeyI1); - var b = (byte[])h.Target; + byte[] b = Decrypt(q, (uint)Mutation.KeyI1); Assembly a = Assembly.Load(b); Array.Clear(b, 0, b.Length); - h.Free(); Array.Clear(q, 0, q.Length); var m = typeof(CompressorCompat).Module; @@ -93,12 +89,9 @@ static Assembly Resolve(object sender, ResolveEventArgs e) { uint s = 0x6fff61; foreach (byte c in b) s = s * 0x5e3f1f + c; - GCHandle h = Decrypt(d, s); - - var f = (byte[])h.Target; + byte[] f = Decrypt(d, s); Assembly a = Assembly.Load(f); Array.Clear(f, 0, f.Length); - h.Free(); Array.Clear(d, 0, d.Length); return a; diff --git a/Confuser.Runtime/Compressor.cs b/Confuser.Runtime/Compressor.cs index 6a23eec2e..abb2e62b3 100644 --- a/Confuser.Runtime/Compressor.cs +++ b/Confuser.Runtime/Compressor.cs @@ -1,14 +1,13 @@ -using System; +using System; using System.IO; using System.Reflection; -using System.Runtime.InteropServices; using System.Text; namespace Confuser.Runtime { internal static class Compressor { static byte[] key; - static GCHandle Decrypt(uint[] data, uint seed) { + static byte[] Decrypt(uint[] data, uint seed) { var w = new uint[0x10]; var k = new uint[0x10]; ulong s = seed; @@ -35,14 +34,13 @@ static GCHandle Decrypt(uint[] data, uint seed) { byte[] j = Lzma.Decompress(b); Array.Clear(b, 0, b.Length); - GCHandle g = GCHandle.Alloc(j, GCHandleType.Pinned); var z = (uint)(s % 0x8a5cb7); for (int i = 0; i < j.Length; i++) { j[i] ^= (byte)s; if ((i & 0xff) == 0) s = (s * s) % 0x8a5cb7; } - return g; + return j; } [STAThread] @@ -52,12 +50,10 @@ static int Main(string[] args) { Assembly a = Assembly.GetExecutingAssembly(); Module n = a.ManifestModule; - GCHandle h = Decrypt(q, (uint)Mutation.KeyI1); - var b = (byte[])h.Target; + byte[] b = Decrypt(q, (uint)Mutation.KeyI1); Module m = a.LoadModule("koi", b); Array.Clear(b, 0, b.Length); - h.Free(); Array.Clear(q, 0, q.Length); key = n.ResolveSignature(Mutation.KeyI2); @@ -98,12 +94,9 @@ static Assembly Resolve(object sender, ResolveEventArgs e) { uint s = 0x6fff61; foreach (byte c in b) s = s * 0x5e3f1f + c; - GCHandle h = Decrypt(d, s); - - var f = (byte[])h.Target; + byte[] f = Decrypt(d, s); Assembly a = Assembly.Load(f); Array.Clear(f, 0, f.Length); - h.Free(); Array.Clear(d, 0, d.Length); return a;