├── 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 | --------------------------------------------------------------------------------