├── AntiDebug.sln
├── ConsoleApp1
├── ConsoleApp1.csproj
├── Program.cs
└── Properties
│ └── AssemblyInfo.cs
├── ProtectProcess
├── AntiDebug.cs
├── ProtectProcess.projitems
└── ProtectProcess.shproj
└── README.md
/AntiDebug.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2035
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{997265C1-1342-4D44-ADED-67964A32F859}"
7 | EndProject
8 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ProtectProcess", "ProtectProcess\ProtectProcess.shproj", "{BA1E03C7-85C7-4FFF-AC25-E67BE08BA121}"
9 | EndProject
10 | Global
11 | GlobalSection(SharedMSBuildProjectFiles) = preSolution
12 | ProtectProcess\ProtectProcess.projitems*{997265c1-1342-4d44-aded-67964a32f859}*SharedItemsImports = 4
13 | ProtectProcess\ProtectProcess.projitems*{ba1e03c7-85c7-4fff-ac25-e67be08ba121}*SharedItemsImports = 13
14 | EndGlobalSection
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {997265C1-1342-4D44-ADED-67964A32F859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {997265C1-1342-4D44-ADED-67964A32F859}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {997265C1-1342-4D44-ADED-67964A32F859}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {997265C1-1342-4D44-ADED-67964A32F859}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {FE2B4360-C852-468D-83F3-6891245C71C7}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/ConsoleApp1/ConsoleApp1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {997265C1-1342-4D44-ADED-67964A32F859}
8 | Exe
9 | ConsoleApp1
10 | ConsoleApp1
11 | v3.5
12 | 512
13 |
14 |
15 | AnyCPU
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | AnyCPU
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/ConsoleApp1/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ConsoleApp1
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | int ppid = 0;
10 | if (args != null && args.Length > 0)
11 | {
12 | int.TryParse(args[0], out ppid);
13 | }
14 | ProtectProcess.AntiDebug.DebugSelf(ppid);
15 | Console.WriteLine("Self-Debugging...");
16 | Console.ReadLine();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ConsoleApp1/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ConsoleApp1")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ConsoleApp1")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("997265c1-1342-4d44-aded-67964a32f859")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/ProtectProcess/AntiDebug.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using System.Diagnostics;
5 | using System.ComponentModel;
6 |
7 | namespace ProtectProcess
8 | {
9 | //inspired from here: http://csharptest.net/1051/managed-anti-debugging-how-to-prevent-users-from-attaching-a-debugger/index.html
10 | public static class AntiDebug
11 | {
12 | // Debugging thread main loop
13 | static void DebuggerThread(object arg)
14 | {
15 | DEBUG_EVENT evt = new DEBUG_EVENT();
16 | evt.bytes = new byte[1024];
17 | // Attach to the process we provided the thread as an argument
18 | if (!DebugActiveProcess((int)arg))
19 | throw new Win32Exception();
20 |
21 | while (true)
22 | {
23 | // wait for a debug event
24 | if (!WaitForDebugEvent(out evt, -1))
25 | throw new Win32Exception();
26 | // return DBG_CONTINUE for all events but the exception type
27 | int continueFlag = DBG_CONTINUE;
28 | if (evt.dwDebugEventCode == DebugEventType.EXCEPTION_DEBUG_EVENT)
29 | continueFlag = DBG_EXCEPTION_NOT_HANDLED;
30 | // continue running the debugee
31 | ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, continueFlag);
32 | }
33 | }
34 |
35 | public static void DebugSelf(int ppid)
36 | {
37 | Console.WriteLine("Debugging {0}", ppid);
38 | Process self = Process.GetCurrentProcess();
39 | // Child process?
40 | if (ppid != 0)
41 | {
42 | Console.WriteLine("Child Process");
43 | Process pdbg = Process.GetProcessById(ppid);
44 | new Thread(KillOnExit) { IsBackground = true, Name = "KillOnExit" }.Start(pdbg);
45 | //Wait for our parent to debug us
46 | WaitForDebugger();
47 | //Start debugging our parent process
48 | DebuggerThread(ppid);
49 | //Now is a good time to die.
50 | Environment.Exit(1);
51 | }
52 | else // else we are the Parent process...
53 | {
54 | Console.WriteLine("Parent Process");
55 | ProcessStartInfo psi =
56 | new ProcessStartInfo(Environment.GetCommandLineArgs()[0], self.Id.ToString())
57 | {
58 | UseShellExecute = false,
59 | CreateNoWindow = false,
60 | ErrorDialog = false,
61 | //WindowStyle = ProcessWindowStyle.Hidden
62 | };
63 | // Start the child process
64 | Process pdbg = Process.Start(psi);
65 | if (pdbg == null)
66 | throw new ApplicationException("Unable to debug");
67 | // Monitor the child process
68 | new Thread(KillOnExit) { IsBackground = true, Name = "KillOnExit" }.Start(pdbg);
69 | // Debug the child process
70 | new Thread(DebuggerThread) { IsBackground = true, Name = "DebuggerThread" }.Start(pdbg.Id);
71 | // Wait for the child to debug us
72 | WaitForDebugger();
73 | }
74 | }
75 | static void WaitForDebugger()
76 | {
77 | DateTime start = DateTime.Now;
78 | while (!IsDebuggerPresent())
79 | {
80 | if ((DateTime.Now - start).TotalMinutes > 1)
81 | throw new TimeoutException("Debug operation timeout.");
82 | Thread.Sleep(1);
83 | }
84 | }
85 | static void KillOnExit(object process)
86 | {
87 | ((Process)process).WaitForExit();
88 | Environment.Exit(1);
89 | }
90 |
91 | const int DBG_CONTINUE = 0x00010002;
92 | const int DBG_EXCEPTION_NOT_HANDLED = unchecked((int)0x80010001);
93 |
94 | enum DebugEventType : int
95 | {
96 | CREATE_PROCESS_DEBUG_EVENT = 3, //Reports a create-process debugging event. The value of u.CreateProcessInfo specifies a CREATE_PROCESS_DEBUG_INFO structure.
97 | CREATE_THREAD_DEBUG_EVENT = 2, //Reports a create-thread debugging event. The value of u.CreateThread specifies a CREATE_THREAD_DEBUG_INFO structure.
98 | EXCEPTION_DEBUG_EVENT = 1, //Reports an exception debugging event. The value of u.Exception specifies an EXCEPTION_DEBUG_INFO structure.
99 | EXIT_PROCESS_DEBUG_EVENT = 5, //Reports an exit-process debugging event. The value of u.ExitProcess specifies an EXIT_PROCESS_DEBUG_INFO structure.
100 | EXIT_THREAD_DEBUG_EVENT = 4, //Reports an exit-thread debugging event. The value of u.ExitThread specifies an EXIT_THREAD_DEBUG_INFO structure.
101 | LOAD_DLL_DEBUG_EVENT = 6, //Reports a load-dynamic-link-library (DLL) debugging event. The value of u.LoadDll specifies a LOAD_DLL_DEBUG_INFO structure.
102 | OUTPUT_DEBUG_STRING_EVENT = 8, //Reports an output-debugging-string debugging event. The value of u.DebugString specifies an OUTPUT_DEBUG_STRING_INFO structure.
103 | RIP_EVENT = 9, //Reports a RIP-debugging event (system debugging error). The value of u.RipInfo specifies a RIP_INFO structure.
104 | UNLOAD_DLL_DEBUG_EVENT = 7, //Reports an unload-DLL debugging event. The value of u.UnloadDll specifies an UNLOAD_DLL_DEBUG_INFO structure.
105 | }
106 |
107 | [StructLayout(LayoutKind.Sequential)]
108 | struct DEBUG_EVENT
109 | {
110 | [MarshalAs(UnmanagedType.I4)]
111 | public DebugEventType dwDebugEventCode;
112 | public int dwProcessId;
113 | public int dwThreadId;
114 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
115 | public byte[] bytes;
116 | }
117 |
118 | [DllImport("Kernel32.dll", SetLastError = true)]
119 | static extern bool DebugActiveProcess(int dwProcessId);
120 | [DllImport("Kernel32.dll", SetLastError = true)]
121 | static extern bool WaitForDebugEvent([Out] out DEBUG_EVENT lpDebugEvent, int dwMilliseconds);
122 | [DllImport("Kernel32.dll", SetLastError = true)]
123 | static extern bool ContinueDebugEvent(int dwProcessId, int dwThreadId, int dwContinueStatus);
124 | [DllImport("Kernel32.dll", SetLastError = true)]
125 | public static extern bool IsDebuggerPresent();
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/ProtectProcess/ProtectProcess.projitems:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
5 | true
6 | ba1e03c7-85c7-4fff-ac25-e67be08ba121
7 |
8 |
9 | ProtectProcess
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ProtectProcess/ProtectProcess.shproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ba1e03c7-85c7-4fff-ac25-e67be08ba121
5 | 14.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AntiDebug
2 |
3 | Build in Visual Studio (.net 3.5 so it runs on Win 7+).
4 |
5 | This will spawn a watcher process to monitor a parent, and each will `pinvoke` a debugger thread on each other. If either process is killed (parent or child), they both exit, protecting each other from being debugged.
6 |
7 | This is based on the (now old) concepts written here (click below to read details):
8 | http://csharptest.net/1051/managed-anti-debugging-how-to-prevent-users-from-attaching-a-debugger/index.html
9 |
10 | But with a full implementation and a refactor in more usable form.
11 |
--------------------------------------------------------------------------------