diff --git a/Confuser.Runtime/AntiDebug.Safe.cs b/Confuser.Runtime/AntiDebug.Safe.cs index fca4253e8..8fbb126d5 100644 --- a/Confuser.Runtime/AntiDebug.Safe.cs +++ b/Confuser.Runtime/AntiDebug.Safe.cs @@ -15,6 +15,12 @@ static void Initialize() { "1".Equals(method.Invoke(null, new object[] { x + "_ENABLE_PROFILING" }))) Environment.FailFast(null); + // Blocking startup check: if a debugger is already attached when the module loads + // (e.g. the assembly was launched under a debugger / dnSpy's F5), fail immediately — + // before any user code runs — rather than only detecting it later on the async worker. + if (Debugger.IsAttached || Debugger.IsLogging()) + Environment.FailFast(null); + var thread = new Thread(Worker); thread.IsBackground = true; thread.Start(null); @@ -25,7 +31,6 @@ static void Worker(object thread) { th = new Thread(Worker); th.IsBackground = true; th.Start(Thread.CurrentThread); - Thread.Sleep(500); } while (true) { if (Debugger.IsAttached || Debugger.IsLogging()) @@ -34,7 +39,7 @@ static void Worker(object thread) { if (!th.IsAlive) Environment.FailFast(null); - Thread.Sleep(1000); + Thread.Sleep(200); } } } diff --git a/Confuser.Runtime/AntiDebug.Win32.cs b/Confuser.Runtime/AntiDebug.Win32.cs index 10801467e..ddb245b6d 100644 --- a/Confuser.Runtime/AntiDebug.Win32.cs +++ b/Confuser.Runtime/AntiDebug.Win32.cs @@ -15,6 +15,12 @@ static void Initialize() { if (here != null && here.ProcessName.IndexOf("dnspy", StringComparison.OrdinalIgnoreCase) >= 0) Environment.FailFast(""); + // Blocking startup check: fail before any user code runs if a debugger is already + // attached when the module loads (managed or native), instead of only catching it + // later on the async worker thread where the app has already started. + if (Debugger.IsAttached || Debugger.IsLogging() || IsDebuggerPresent()) + Environment.FailFast(""); + var thread = new Thread(Worker); thread.IsBackground = true; thread.Start(null); @@ -97,7 +103,6 @@ static void Worker(object thread) { th = new Thread(Worker); th.IsBackground = true; th.Start(Thread.CurrentThread); - Thread.Sleep(500); } while (true) { // Managed @@ -129,7 +134,7 @@ static void Worker(object thread) { if (!th.IsAlive) Environment.FailFast(""); - Thread.Sleep(1000); + Thread.Sleep(200); } } } diff --git a/Confuser2.sln b/Confuser2.sln index b200f0781..c1c56f613 100644 --- a/Confuser2.sln +++ b/Confuser2.sln @@ -205,6 +205,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net8", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net10", "Tests\CrossFramework.Library.Net10\CrossFramework.Library.Net10.csproj", "{4458415A-0F5E-4136-B723-7A67955D6047}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntiDebug.Test", "Tests\AntiDebug.Test\AntiDebug.Test.csproj", "{47197200-B8CB-400A-B1BD-84975FEC8C28}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1343,6 +1345,18 @@ Global {4458415A-0F5E-4136-B723-7A67955D6047}.Release|x64.Build.0 = Release|Any CPU {4458415A-0F5E-4136-B723-7A67955D6047}.Release|x86.ActiveCfg = Release|Any CPU {4458415A-0F5E-4136-B723-7A67955D6047}.Release|x86.Build.0 = Release|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x64.ActiveCfg = Debug|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x64.Build.0 = Debug|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x86.ActiveCfg = Debug|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x86.Build.0 = Debug|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|Any CPU.Build.0 = Release|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x64.ActiveCfg = Release|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x64.Build.0 = Release|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x86.ActiveCfg = Release|Any CPU + {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1434,6 +1448,7 @@ Global {3942E3FD-06BC-470C-A1ED-BB18F2332B94} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} {30DC9F52-E08A-4EF0-B041-04AED6135C3D} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} {4458415A-0F5E-4136-B723-7A67955D6047} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} + {47197200-B8CB-400A-B1BD-84975FEC8C28} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0D937D9E-E04B-4A68-B639-D4260473A388} diff --git a/Tests/AntiDebug.Test/AntiDebug.Test.csproj b/Tests/AntiDebug.Test/AntiDebug.Test.csproj new file mode 100644 index 000000000..48ef32c65 --- /dev/null +++ b/Tests/AntiDebug.Test/AntiDebug.Test.csproj @@ -0,0 +1,13 @@ + + + + net462 + false + + + + + + + + diff --git a/Tests/AntiDebug.Test/AntiDebugTest.cs b/Tests/AntiDebug.Test/AntiDebugTest.cs new file mode 100644 index 000000000..71bd72d59 --- /dev/null +++ b/Tests/AntiDebug.Test/AntiDebugTest.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Confuser.Core; +using Confuser.Core.Project; +using Confuser.UnitTest; +using Xunit; +using Xunit.Abstractions; + +namespace AntiDebug.Test { + public sealed class AntiDebugTest : TestBase { + public AntiDebugTest(ITestOutputHelper outputHelper) : base(outputHelper) { } + + // Regression guard: anti-debug injects a startup check plus a background watchdog into + // the module cctor. This verifies that an assembly protected with anti-debug still runs + // normally when it is NOT being debugged — i.e. the strengthened checks (blocking startup + // check, faster polling) do not false-positive on a plain process launch. The test runner + // launches the subject as an ordinary child process with no debugger attached, so a healthy + // build must produce START / / END and exit code 42. + [Theory] + [InlineData("safe")] + [InlineData("win32")] + [Trait("Category", "Protection")] + [Trait("Protection", "anti debug")] + public Task ProtectAntiDebugAndExecute(string mode) => + Run("AntiTamper.exe", + new[] { "This is a test." }, + new SettingItem("anti debug") { { "mode", mode } }, + "_antidebug_" + mode); + } +}