Skip to content

Commit 21c58ee

Browse files
author
RandomCrocodile
committed
fix: strengthen anti-debug startup detection and polling (#76)
Phase 1 hardening for the anti-debug protection. The core complaint — a debugger (e.g. dnSpy F5) can attach and the app runs before the async worker ever checks — is fixed portably: - Safe and Win32 modes now run a BLOCKING debugger check inside Initialize() (injected at the top of the module cctor), so an assembly launched under a debugger fails immediately, before any user code runs, instead of only being caught later on the background thread. - Win32's startup check also calls the native IsDebuggerPresent(). - Reduced the watchdog poll interval from 1000ms to 200ms and removed the 500ms initial sleep, shrinking the detection race window. Deliberately NOT changing the default mode from Safe to Win32 (issue's proposal 1): Win32 P/Invokes ntdll/kernel32 and would crash cross-platform .NET targets on non-Windows, conflicting with the project's all-frameworks goal. The strengthened Safe mode addresses the reported issue portably; OS-aware mode selection is a larger Phase 2 change. Added AntiDebug.Test (safe + win32) as a regression guard that an anti-debug protected assembly still runs normally when not debugged (START/output/exit 42).
1 parent 2d4c539 commit 21c58ee

5 files changed

Lines changed: 71 additions & 4 deletions

File tree

Confuser.Runtime/AntiDebug.Safe.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ static void Initialize() {
1515
"1".Equals(method.Invoke(null, new object[] { x + "_ENABLE_PROFILING" })))
1616
Environment.FailFast(null);
1717

18+
// Blocking startup check: if a debugger is already attached when the module loads
19+
// (e.g. the assembly was launched under a debugger / dnSpy's F5), fail immediately —
20+
// before any user code runs — rather than only detecting it later on the async worker.
21+
if (Debugger.IsAttached || Debugger.IsLogging())
22+
Environment.FailFast(null);
23+
1824
var thread = new Thread(Worker);
1925
thread.IsBackground = true;
2026
thread.Start(null);
@@ -25,7 +31,6 @@ static void Worker(object thread) {
2531
th = new Thread(Worker);
2632
th.IsBackground = true;
2733
th.Start(Thread.CurrentThread);
28-
Thread.Sleep(500);
2934
}
3035
while (true) {
3136
if (Debugger.IsAttached || Debugger.IsLogging())
@@ -34,7 +39,7 @@ static void Worker(object thread) {
3439
if (!th.IsAlive)
3540
Environment.FailFast(null);
3641

37-
Thread.Sleep(1000);
42+
Thread.Sleep(200);
3843
}
3944
}
4045
}

Confuser.Runtime/AntiDebug.Win32.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ static void Initialize() {
1515
if (here != null && here.ProcessName.IndexOf("dnspy", StringComparison.OrdinalIgnoreCase) >= 0)
1616
Environment.FailFast("");
1717

18+
// Blocking startup check: fail before any user code runs if a debugger is already
19+
// attached when the module loads (managed or native), instead of only catching it
20+
// later on the async worker thread where the app has already started.
21+
if (Debugger.IsAttached || Debugger.IsLogging() || IsDebuggerPresent())
22+
Environment.FailFast("");
23+
1824
var thread = new Thread(Worker);
1925
thread.IsBackground = true;
2026
thread.Start(null);
@@ -97,7 +103,6 @@ static void Worker(object thread) {
97103
th = new Thread(Worker);
98104
th.IsBackground = true;
99105
th.Start(Thread.CurrentThread);
100-
Thread.Sleep(500);
101106
}
102107
while (true) {
103108
// Managed
@@ -129,7 +134,7 @@ static void Worker(object thread) {
129134
if (!th.IsAlive)
130135
Environment.FailFast("");
131136

132-
Thread.Sleep(1000);
137+
Thread.Sleep(200);
133138
}
134139
}
135140
}

Confuser2.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net8", "
205205
EndProject
206206
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net10", "Tests\CrossFramework.Library.Net10\CrossFramework.Library.Net10.csproj", "{4458415A-0F5E-4136-B723-7A67955D6047}"
207207
EndProject
208+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntiDebug.Test", "Tests\AntiDebug.Test\AntiDebug.Test.csproj", "{47197200-B8CB-400A-B1BD-84975FEC8C28}"
209+
EndProject
208210
Global
209211
GlobalSection(SolutionConfigurationPlatforms) = preSolution
210212
Debug|Any CPU = Debug|Any CPU
@@ -1343,6 +1345,18 @@ Global
13431345
{4458415A-0F5E-4136-B723-7A67955D6047}.Release|x64.Build.0 = Release|Any CPU
13441346
{4458415A-0F5E-4136-B723-7A67955D6047}.Release|x86.ActiveCfg = Release|Any CPU
13451347
{4458415A-0F5E-4136-B723-7A67955D6047}.Release|x86.Build.0 = Release|Any CPU
1348+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1349+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|Any CPU.Build.0 = Debug|Any CPU
1350+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x64.ActiveCfg = Debug|Any CPU
1351+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x64.Build.0 = Debug|Any CPU
1352+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x86.ActiveCfg = Debug|Any CPU
1353+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Debug|x86.Build.0 = Debug|Any CPU
1354+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|Any CPU.ActiveCfg = Release|Any CPU
1355+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|Any CPU.Build.0 = Release|Any CPU
1356+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x64.ActiveCfg = Release|Any CPU
1357+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x64.Build.0 = Release|Any CPU
1358+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x86.ActiveCfg = Release|Any CPU
1359+
{47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x86.Build.0 = Release|Any CPU
13461360
EndGlobalSection
13471361
GlobalSection(SolutionProperties) = preSolution
13481362
HideSolutionNode = FALSE
@@ -1434,6 +1448,7 @@ Global
14341448
{3942E3FD-06BC-470C-A1ED-BB18F2332B94} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
14351449
{30DC9F52-E08A-4EF0-B041-04AED6135C3D} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
14361450
{4458415A-0F5E-4136-B723-7A67955D6047} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
1451+
{47197200-B8CB-400A-B1BD-84975FEC8C28} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
14371452
EndGlobalSection
14381453
GlobalSection(ExtensibilityGlobals) = postSolution
14391454
SolutionGuid = {0D937D9E-E04B-4A68-B639-D4260473A388}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net462</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\Confuser.UnitTest\Confuser.UnitTest.csproj" />
10+
<ProjectReference Include="..\AntiTamper\AntiTamper.csproj" />
11+
</ItemGroup>
12+
13+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Threading.Tasks;
2+
using Confuser.Core;
3+
using Confuser.Core.Project;
4+
using Confuser.UnitTest;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
8+
namespace AntiDebug.Test {
9+
public sealed class AntiDebugTest : TestBase {
10+
public AntiDebugTest(ITestOutputHelper outputHelper) : base(outputHelper) { }
11+
12+
// Regression guard: anti-debug injects a startup check plus a background watchdog into
13+
// the module cctor. This verifies that an assembly protected with anti-debug still runs
14+
// normally when it is NOT being debugged — i.e. the strengthened checks (blocking startup
15+
// check, faster polling) do not false-positive on a plain process launch. The test runner
16+
// launches the subject as an ordinary child process with no debugger attached, so a healthy
17+
// build must produce START / <resource> / END and exit code 42.
18+
[Theory]
19+
[InlineData("safe")]
20+
[InlineData("win32")]
21+
[Trait("Category", "Protection")]
22+
[Trait("Protection", "anti debug")]
23+
public Task ProtectAntiDebugAndExecute(string mode) =>
24+
Run("AntiTamper.exe",
25+
new[] { "This is a test." },
26+
new SettingItem<Protection>("anti debug") { { "mode", mode } },
27+
"_antidebug_" + mode);
28+
}
29+
}

0 commit comments

Comments
 (0)