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);
+ }
+}