├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── antidbg.sln └── antidbg ├── AntiDBG.asm ├── AntiDBG.cpp ├── antidbg.h ├── antidbg.vcxproj ├── antidbg.vcxproj.filters └── gauntlet.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | Properties/launchSettings.json 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # Visual Studio code coverage results 115 | *.coverage 116 | *.coveragexml 117 | 118 | # NCrunch 119 | _NCrunch_* 120 | .*crunch*.local.xml 121 | nCrunchTemp_* 122 | 123 | # MightyMoose 124 | *.mm.* 125 | AutoTest.Net/ 126 | 127 | # Web workbench (sass) 128 | .sass-cache/ 129 | 130 | # Installshield output folder 131 | [Ee]xpress/ 132 | 133 | # DocProject is a documentation generator add-in 134 | DocProject/buildhelp/ 135 | DocProject/Help/*.HxT 136 | DocProject/Help/*.HxC 137 | DocProject/Help/*.hhc 138 | DocProject/Help/*.hhk 139 | DocProject/Help/*.hhp 140 | DocProject/Help/Html2 141 | DocProject/Help/html 142 | 143 | # Click-Once directory 144 | publish/ 145 | 146 | # Publish Web Output 147 | *.[Pp]ublish.xml 148 | *.azurePubxml 149 | # TODO: Comment the next line if you want to checkin your web deploy settings 150 | # but database connection strings (with potential passwords) will be unencrypted 151 | *.pubxml 152 | *.publishproj 153 | 154 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 155 | # checkin your Azure Web App publish settings, but sensitive information contained 156 | # in these scripts will be unencrypted 157 | PublishScripts/ 158 | 159 | # NuGet Packages 160 | *.nupkg 161 | # The packages folder can be ignored because of Package Restore 162 | **/packages/* 163 | # except build/, which is used as an MSBuild target. 164 | !**/packages/build/ 165 | # Uncomment if necessary however generally it will be regenerated when needed 166 | #!**/packages/repositories.config 167 | # NuGet v3's project.json files produces more ignoreable files 168 | *.nuget.props 169 | *.nuget.targets 170 | 171 | # Microsoft Azure Build Output 172 | csx/ 173 | *.build.csdef 174 | 175 | # Microsoft Azure Emulator 176 | ecf/ 177 | rcf/ 178 | 179 | # Windows Store app package directories and files 180 | AppPackages/ 181 | BundleArtifacts/ 182 | Package.StoreAssociation.xml 183 | _pkginfo.txt 184 | 185 | # Visual Studio cache files 186 | # files ending in .cache can be ignored 187 | *.[Cc]ache 188 | # but keep track of directories ending in .cache 189 | !*.[Cc]ache/ 190 | 191 | # Others 192 | ClientBin/ 193 | ~$* 194 | *~ 195 | *.dbmdl 196 | *.dbproj.schemaview 197 | *.jfm 198 | *.pfx 199 | *.publishsettings 200 | node_modules/ 201 | orleans.codegen.cs 202 | 203 | # Since there are multiple workflows, uncomment next line to ignore bower_components 204 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 205 | #bower_components/ 206 | 207 | # RIA/Silverlight projects 208 | Generated_Code/ 209 | 210 | # Backup & report files from converting an old project file 211 | # to a newer Visual Studio version. Backup files are not needed, 212 | # because we have git ;-) 213 | _UpgradeReport_Files/ 214 | Backup*/ 215 | UpgradeLog*.XML 216 | UpgradeLog*.htm 217 | 218 | # SQL Server files 219 | *.mdf 220 | *.ldf 221 | 222 | # Business Intelligence projects 223 | *.rdl.data 224 | *.bim.layout 225 | *.bim_*.settings 226 | 227 | # Microsoft Fakes 228 | FakesAssemblies/ 229 | 230 | # GhostDoc plugin setting file 231 | *.GhostDoc.xml 232 | 233 | # Node.js Tools for Visual Studio 234 | .ntvs_analysis.dat 235 | 236 | # Visual Studio 6 build log 237 | *.plg 238 | 239 | # Visual Studio 6 workspace options file 240 | *.opt 241 | 242 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 243 | *.vbw 244 | 245 | # Visual Studio LightSwitch build output 246 | **/*.HTMLClient/GeneratedArtifacts 247 | **/*.DesktopClient/GeneratedArtifacts 248 | **/*.DesktopClient/ModelManifest.xml 249 | **/*.Server/GeneratedArtifacts 250 | **/*.Server/ModelManifest.xml 251 | _Pvt_Extensions 252 | 253 | # Paket dependency manager 254 | .paket/paket.exe 255 | paket-files/ 256 | 257 | # FAKE - F# Make 258 | .fake/ 259 | 260 | # JetBrains Rider 261 | .idea/ 262 | *.sln.iml 263 | 264 | # CodeRush 265 | .cr/ 266 | 267 | # Python Tools for Visual Studio (PTVS) 268 | __pycache__/ 269 | *.pyc 270 | 271 | # Cake - Uncomment if you are using it 272 | # tools/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AntiDBG 2 | 3 | AntiDBG is a collection of Windows Anti Debugging techniques. The techniques are categorized by the methods they use to find a debugger. 4 | 5 | - Memory 6 | - CPU 7 | - Timing 8 | - Forced Exceptions 9 | - Other 10 | 11 | ### Demos! 12 | 13 | Want to see this stuff in action? Check out the [playlist on YouTube](https://www.youtube.com/playlist?list=PLxgggb3Nxh7s0uLlDinGVAsbi6o0pWv2X). 14 | 15 | ### AntiDBG API 16 | AntiDBG is written in C and requires only a single source file and header. Nearly all of these methods are designed to take no input and produce no output. They aim to be self-contained debugger checks that will automatically detach debuggers. 17 | 18 | ### Obfuscation 19 | AntiDBG is designed to be *readable* so the user can learn about the techniques. If you choose to use these methods in your own project, you will benefit greatly by adding obfuscation on top of these methods. Obfuscation is not the aim of this project. 20 | 21 | ### The Gauntlet 22 | The Gauntlet is a simple application that runs each AntiDBG check one after the other. It's purpose is to test your ability to bypass the anti-debugging methods and make it to the end of The Gauntlet while running under a debugger. 23 | 24 | Want to make The Gauntlet *harder*? Undefine SHOW_DEBUG_MESSAGES (defined by default in AntiDBG.cpp). This option produces a message box when you get caught with information about the check that got you. 25 | 26 | ### FAQ & Troubleshooting 27 | 28 | > Help! X method doesn't seem to work. 29 | 30 | Many anti-debugging checks focus on odd edge cases. Some require you to single step past, some require a specific debugger to be used, some require you to pass the exception to the debugger, etc. 31 | 32 | All methods in AntiDBG have been tested under the conditions which they are designed work on Windows 10 64-bit. Most (if not all) should work on all other versions of Windows as well. 33 | 34 | > Help! This thing won't compile! 35 | 36 | AntiDBG was developed and tested using Microsoft Visual Studio 2019. As long as you're using 2019, please submit an issue with details and I'd be happy to help. 37 | 38 | > Why is x86 assembly inline while x64 variants are in a .asm file? 39 | 40 | Microsoft thought it would be a great idea to stop allowing developers to write inline assembly for x64. I don't know why, but the common reason I see cited around the internet is that developers suck at writing assembly and compilers are way better. While I don't disagree with this, I doubt that's the real reason. Whatever the reason, we now have to jump through hoops to do something even remotely similar. Huge thanks to [lallouslab](http://lallouslab.net/2016/01/11/introduction-to-writing-x64-assembly-in-visual-studio/) and [onipot](https://onipot.altervista.org/how-to-create-assembly-project-visual-studio-2019-64-bit/) for guiding me through this minefield. 41 | 42 | > I have more questions. 43 | 44 | I'd be happy to answer them! Please submit a GitHub issue with your questions and I'll try my best to help as soon as possible. 45 | 46 | ### Thanks 47 | 48 | Thanks to the [contributors](https://github.com/HackOvert/AntiDBG/graphs/contributors) and everyone who has provided feedback in the past on this project. 49 | -------------------------------------------------------------------------------- /antidbg.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30907.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AntiDBG", "AntiDBG\AntiDBG.vcxproj", "{07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Debug|x64.ActiveCfg = Debug|x64 17 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Debug|x64.Build.0 = Debug|x64 18 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Debug|x86.ActiveCfg = Debug|Win32 19 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Debug|x86.Build.0 = Debug|Win32 20 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Release|x64.ActiveCfg = Release|x64 21 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Release|x64.Build.0 = Release|x64 22 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Release|x86.ActiveCfg = Release|Win32 23 | {07FD03DB-29BB-4C86-AAD6-49A2F8C47BE6}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {00657B1B-CE4C-4A11-B1DB-ADBBDDB8BA66} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /antidbg/AntiDBG.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------------------------------------- 2 | ; Variables (we don't currently use these, this is just an example of how to populate the data segment) 3 | ; ----------------------------------------------------------------------------------------------------------- 4 | _DATA SEGMENT 5 | hello_msg db "Hello world", 0 6 | _DATA ENDS 7 | 8 | ; ----------------------------------------------------------------------------------------------------------- 9 | ; Text or code segment 10 | ; ----------------------------------------------------------------------------------------------------------- 11 | _TEXT SEGMENT 12 | 13 | ; The PUBLIC modifier will make your function visible and callable outside 14 | PUBLIC adbg_BeingDebuggedPEBx64 15 | PUBLIC adbg_NtGlobalFlagPEBx64 16 | PUBLIC adbg_QueryPerformanceCounterx64 17 | PUBLIC adbg_GetTickCountx64 18 | PUBLIC adbg_RDTSCx64 19 | PUBLIC adbg_Int2Dx64 20 | PUBLIC adbg_Int3x64 21 | PUBLIC adbg_SingleStepExceptionx64 22 | 23 | adbg_BeingDebuggedPEBx64 PROC 24 | xor rax, rax ; clear eax 25 | mov rax, gs:[60h] ; reference start of the PEB 26 | mov rax, [rax + 02h] ; PEB+2 points to BeingDebugged 27 | and rax, 0FFh ; only reference one byte 28 | ret ; return into 'rax' which puts BeingDebugged value into 'found' 29 | adbg_BeingDebuggedPEBx64 ENDP 30 | 31 | adbg_NtGlobalFlagPEBx64 PROC 32 | xor rax, rax ; clear eax 33 | mov rax, gs:[60h] ; Reference start of the PEB 34 | mov rax, [rax + 0BCh] ; PEB+0xBC points to NtGlobalFlag 35 | and rax, 70h ; check three flags 36 | ret ; return flag value into 'rax' which puts into 'found' 37 | adbg_NtGlobalFlagPEBx64 ENDP 38 | 39 | adbg_QueryPerformanceCounterx64 PROC 40 | xor rax, rax ; this 41 | push rax ; is 42 | push rcx ; just 43 | pop rax ; junk 44 | pop rcx ; code 45 | sub rcx, rax ; use 46 | shl rcx, 4 ; whatever 47 | ret 48 | adbg_QueryPerformanceCounterx64 ENDP 49 | 50 | adbg_GetTickCountx64 PROC 51 | xor rax, rax ; this 52 | push rax ; is 53 | push rcx ; just 54 | pop rax ; junk 55 | pop rcx ; code 56 | sub rcx, rax ; use 57 | shl rcx, 4 ; whatever 58 | ret 59 | adbg_GetTickCountx64 ENDP 60 | 61 | adbg_RDTSCx64 PROC 62 | ; Note: On Windows, the x64 calling convention places the first argument (a pointer to TimeKeeper) in RCX 63 | ; We must avoid clobbering RCX. If we need to clobber RCX for some reason, we'll have to save it in another 64 | ; register, the stack, or somewhere else. 65 | rdtsc ; First time check! 66 | ; rdtsc stores result across EDX:EAX on x86 67 | ; on x64 rdtsc does roughly the same, but it clears the high-order 32 bits of RDX and RAX 68 | mov [rcx + 00h], rdx ; TimeKeeper.timeUpperA 69 | mov [rcx + 08h], rax ; TimeKeeper.timeLowerA 70 | xor rax, rax ; this 71 | mov rax, 5 ; is 72 | shr rax, 2 ; just 73 | sub rax, rbx ; junk 74 | cmp rax, rcx ; code 75 | rdtsc ; Second time check! 76 | mov [rcx + 10h], rdx ; TimeKeeper.timeUpperB 77 | mov [rcx + 18h], rax ; TimeKeeper.timeLowerB 78 | ret 79 | adbg_RDTSCx64 ENDP 80 | 81 | adbg_Int2Dx64 PROC 82 | int 2Dh ; interrupt 0x2D kernel breakpoint 83 | nop ; 84 | adbg_Int2Dx64 ENDP 85 | 86 | adbg_Int3x64 PROC 87 | int 3 ; 0xCC breakpoint 88 | adbg_Int3x64 ENDP 89 | 90 | adbg_SingleStepExceptionx64 PROC 91 | pushfq ; save RFLAGS register 92 | or byte ptr[rsp + 1], 1 ; set trap flag in RFLAGS 93 | popfq ; restore RFLAGS register 94 | ret; ; 95 | adbg_SingleStepExceptionx64 ENDP 96 | 97 | 98 | _TEXT ENDS 99 | 100 | END 101 | -------------------------------------------------------------------------------- /antidbg/AntiDBG.cpp: -------------------------------------------------------------------------------- 1 | #include "AntiDBG.h" 2 | #include 3 | #define SHOW_DEBUG_MESSAGES 4 | 5 | // ======================================================================= 6 | // Debugging helper 7 | // ======================================================================= 8 | void DBG_MSG(WORD dbg_code, const char* message) 9 | { 10 | #ifdef SHOW_DEBUG_MESSAGES 11 | printf("[MSG-0x%X]: %s\n", dbg_code, message); 12 | MessageBoxA(NULL, message, "GAME OVER!", 0); 13 | #endif 14 | } 15 | 16 | // ======================================================================= 17 | // Memory Checks 18 | // These checks focus on Windows structures containing information which 19 | // can reveal the presence of a debugger. 20 | // ======================================================================= 21 | 22 | /* 23 | * Want to inspect the PEB structure? Launch gauntlet in WinDBG and run 24 | * this command: dt ntdll!_PEB 25 | * Example output: 26 | * 0:000> dt ntdll!_PEB 27 | * +0x000 InheritedAddressSpace : UChar 28 | * +0x001 ReadImageFileExecOptions : UChar 29 | * +0x002 BeingDebugged : UChar <-- This is what we're checking 30 | * ...snip... 31 | */ 32 | void adbg_BeingDebuggedPEB(void) 33 | { 34 | BOOL found = FALSE; 35 | 36 | #ifdef _WIN64 37 | found = adbg_BeingDebuggedPEBx64(); 38 | #else 39 | _asm 40 | { 41 | xor eax, eax; // clear eax 42 | mov eax, fs: [0x30] ; // Reference start of the PEB 43 | mov eax, [eax + 0x02]; // PEB+2 points to BeingDebugged 44 | and eax, 0xFF; // only reference one byte 45 | mov found, eax; // Copy BeingDebugged into 'found' 46 | } 47 | #endif 48 | 49 | if (found) 50 | { 51 | DBG_MSG(DBG_BEINGEBUGGEDPEB, "Caught by BeingDebugged PEB check!"); 52 | exit(DBG_BEINGEBUGGEDPEB); 53 | } 54 | } 55 | 56 | 57 | void adbg_CheckRemoteDebuggerPresent(void) 58 | { 59 | HANDLE hProcess = INVALID_HANDLE_VALUE; 60 | BOOL found = FALSE; 61 | 62 | hProcess = GetCurrentProcess(); 63 | CheckRemoteDebuggerPresent(hProcess, &found); 64 | 65 | if (found) 66 | { 67 | DBG_MSG(DBG_CHECKREMOTEDEBUGGERPRESENT, "Caught by CheckRemoteDebuggerPresent!"); 68 | exit(DBG_CHECKREMOTEDEBUGGERPRESENT); 69 | } 70 | } 71 | 72 | void adbg_CheckWindowName(void) 73 | { 74 | BOOL found = FALSE; 75 | HANDLE hWindow = NULL; 76 | const wchar_t* WindowNameOlly = L"OllyDbg - [CPU]"; 77 | const wchar_t* WindowNameImmunity = L"Immunity Debugger - [CPU]"; 78 | 79 | // Check for OllyDBG class name 80 | hWindow = FindWindow(NULL, WindowNameOlly); 81 | if (hWindow) 82 | { 83 | found = TRUE; 84 | } 85 | 86 | // Check for Immunity class name 87 | hWindow = FindWindow(NULL, WindowNameImmunity); 88 | if (hWindow) 89 | { 90 | found = TRUE; 91 | } 92 | 93 | if (found) 94 | { 95 | DBG_MSG(DBG_FINDWINDOW, "Caught by FindWindow (WindowName)!"); 96 | exit(DBG_FINDWINDOW); 97 | } 98 | } 99 | 100 | void adbg_ProcessFileName(void) 101 | { 102 | // detect debugger by process file (for example: ollydbg.exe) 103 | const wchar_t *debuggersFilename[6] = { 104 | L"cheatengine-x86_64.exe", 105 | L"ollydbg.exe", 106 | L"ida.exe", 107 | L"ida64.exe", 108 | L"radare2.exe", 109 | L"x64dbg.exe" 110 | }; 111 | 112 | wchar_t* processName; 113 | PROCESSENTRY32W processInformation{ sizeof(PROCESSENTRY32W) }; 114 | HANDLE processList; 115 | 116 | processList = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 117 | processInformation = { sizeof(PROCESSENTRY32W) }; 118 | if (!(Process32FirstW(processList, &processInformation))) 119 | printf("[Warning] It is impossible to check process list."); 120 | else 121 | { 122 | do 123 | { 124 | for (const wchar_t *debugger : debuggersFilename) 125 | { 126 | processName = processInformation.szExeFile; 127 | if (_wcsicmp(debugger, processName) == 0) { 128 | DBG_MSG(DBG_PROCESSFILENAME, "Caught by ProcessFileName!"); 129 | exit(DBG_PROCESSFILENAME); 130 | } 131 | } 132 | } while (Process32NextW(processList, &processInformation)); 133 | } 134 | CloseHandle(processList); 135 | } 136 | 137 | void adbg_CheckWindowClassName(void) 138 | { 139 | BOOL found = FALSE; 140 | HANDLE hWindow = NULL; 141 | const wchar_t* WindowClassNameOlly = L"OLLYDBG"; // OllyDbg 142 | const wchar_t* WindowClassNameImmunity = L"ID"; // Immunity Debugger 143 | 144 | // Check for OllyDBG class name 145 | hWindow = FindWindow(WindowClassNameOlly, NULL); 146 | if (hWindow) 147 | { 148 | found = TRUE; 149 | } 150 | 151 | // Check for Immunity class name 152 | hWindow = FindWindow(WindowClassNameImmunity, NULL); 153 | if (hWindow) 154 | { 155 | found = TRUE; 156 | } 157 | 158 | if (found) 159 | { 160 | DBG_MSG(DBG_FINDWINDOW, "Caught by FindWindow (ClassName)!"); 161 | exit(DBG_FINDWINDOW); 162 | } 163 | } 164 | 165 | void adbg_IsDebuggerPresent(void) 166 | { 167 | BOOL found = FALSE; 168 | found = IsDebuggerPresent(); 169 | 170 | if (found) 171 | { 172 | DBG_MSG(DBG_ISDEBUGGERPRESENT, "Caught by IsDebuggerPresent!"); 173 | exit(DBG_ISDEBUGGERPRESENT); 174 | } 175 | } 176 | 177 | /* 178 | * Want to inspect the value of something in the PEB? Launch WinDBG, 179 | * Attach to, or launch a process and run this command: 180 | * dt ntdll!_PEB @$peb -r 181 | * Want more info on NtGlobalFlag? See these resources: 182 | * https://www.aldeid.com/wiki/PEB-Process-Environment-Block/NtGlobalFlag 183 | * https://www.geoffchappell.com/studies/windows/win32/ntdll/api/rtl/regutil/getntglobalflags.htm 184 | */ 185 | void adbg_NtGlobalFlagPEB(void) 186 | { 187 | BOOL found = FALSE; 188 | 189 | #ifdef _WIN64 190 | found = adbg_NtGlobalFlagPEBx64(); 191 | #else 192 | _asm 193 | { 194 | xor eax, eax; // clear eax 195 | mov eax, fs: [0x30] ; // Reference start of the PEB 196 | mov eax, [eax + 0x68]; // PEB+0x68 points to NtGlobalFlag 197 | and eax, 0x00000070; // check three flags: 198 | // FLG_HEAP_ENABLE_TAIL_CHECK (0x10) 199 | // FLG_HEAP_ENABLE_FREE_CHECK (0x20) 200 | // FLG_HEAP_VALIDATE_PARAMETERS (0x40) 201 | mov found, eax; // Copy result into 'found' 202 | } 203 | #endif 204 | 205 | if (found) 206 | { 207 | DBG_MSG(DBG_NTGLOBALFLAGPEB, "Caught by NtGlobalFlag PEB check!"); 208 | exit(DBG_NTGLOBALFLAGPEB); 209 | } 210 | } 211 | 212 | 213 | void adbg_NtQueryInformationProcess(void) 214 | { 215 | HANDLE hProcess = INVALID_HANDLE_VALUE; 216 | PROCESS_BASIC_INFORMATION pProcBasicInfo = {0}; 217 | ULONG returnLength = 0; 218 | 219 | // Get a handle to ntdll.dll so we can import NtQueryInformationProcess 220 | HMODULE hNtdll = LoadLibraryW(L"ntdll.dll"); 221 | if (hNtdll == INVALID_HANDLE_VALUE || hNtdll == NULL) 222 | { 223 | return; 224 | } 225 | 226 | // Dynamically acquire the addres of NtQueryInformationProcess 227 | _NtQueryInformationProcess NtQueryInformationProcess = NULL; 228 | NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess"); 229 | 230 | if (NtQueryInformationProcess == NULL) 231 | { 232 | return; 233 | } 234 | 235 | hProcess = GetCurrentProcess(); 236 | 237 | // Note: There are many options for the 2nd parameter NtQueryInformationProcess 238 | // (ProcessInformationClass) many of them are opaque. While we use ProcessBasicInformation (0), 239 | // we could also use: 240 | // ProcessDebugPort (7) 241 | // ProcessDebugObjectHandle (30) 242 | // ProcessDebugFlags (31) 243 | // There are likely others. You can find many other options for ProcessInformationClass over at PINVOKE: 244 | // https://www.pinvoke.net/default.aspx/ntdll/PROCESSINFOCLASS.html 245 | // Keep in mind that NtQueryInformationProcess will return different things depending on the ProcessInformationClass used. 246 | // Many online articles using NtQueryInformationProcess for anti-debugging will use DWORD types for NtQueryInformationProcess 247 | // paramters. This is fine for 32-builds with some ProcessInformationClass values, but it will cause some to fail on 64-bit builds. 248 | // In the event of a failure NtQueryInformationProcess will likely return STATUS_INFO_LENGTH_MISMATCH (0xC0000004). 249 | 250 | // Query ProcessDebugPort 251 | NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pProcBasicInfo, sizeof(pProcBasicInfo), &returnLength); 252 | if (NT_SUCCESS(status)) { 253 | PPEB pPeb = pProcBasicInfo.PebBaseAddress; 254 | if (pPeb) 255 | { 256 | if (pPeb->BeingDebugged) 257 | { 258 | DBG_MSG(DBG_NTQUERYINFORMATIONPROCESS, "Caught by NtQueryInformationProcess (ProcessDebugPort)!"); 259 | exit(DBG_NTQUERYINFORMATIONPROCESS); 260 | } 261 | } 262 | } 263 | } 264 | 265 | 266 | void adbg_NtSetInformationThread(void) 267 | { 268 | THREAD_INFORMATION_CLASS ThreadHideFromDebugger = (THREAD_INFORMATION_CLASS)0x11; 269 | 270 | // Get a handle to ntdll.dll so we can import NtSetInformationThread 271 | HMODULE hNtdll = LoadLibraryW(L"ntdll.dll"); 272 | if (hNtdll == INVALID_HANDLE_VALUE || hNtdll == NULL) 273 | { 274 | return; 275 | } 276 | 277 | // Dynamically acquire the addres of NtSetInformationThread and NtQueryInformationThread 278 | _NtSetInformationThread NtSetInformationThread = NULL; 279 | NtSetInformationThread = (_NtSetInformationThread)GetProcAddress(hNtdll, "NtSetInformationThread"); 280 | 281 | if (NtSetInformationThread == NULL) 282 | { 283 | return; 284 | } 285 | 286 | // There is nothing to check here after this call. 287 | NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, 0, 0); 288 | } 289 | 290 | 291 | void adbg_DebugActiveProcess(const char* cpid) 292 | { 293 | BOOL found = FALSE; 294 | STARTUPINFOA si = { 0 }; 295 | PROCESS_INFORMATION pi = { 0 }; 296 | si.cb = sizeof(si); 297 | TCHAR szPath[MAX_PATH]; 298 | DWORD exitCode = 0; 299 | 300 | CreateMutex(NULL, FALSE, L"antidbg"); 301 | if (GetLastError() != ERROR_SUCCESS) 302 | { 303 | // If we get here we are in the child process 304 | if (DebugActiveProcess((DWORD)atoi(cpid))) 305 | { 306 | // No debugger found. 307 | return; 308 | } 309 | else 310 | { 311 | // Debugger found, exit child with a unique code we can check for. 312 | exit(555); 313 | } 314 | } 315 | 316 | // parent process 317 | DWORD pid = GetCurrentProcessId(); 318 | GetModuleFileName(NULL, szPath, MAX_PATH); 319 | 320 | char cmdline[MAX_PATH + 1 + sizeof(int)]; 321 | snprintf(cmdline, sizeof(cmdline), "%ws %d", szPath, pid); 322 | 323 | // Start the child process. 324 | BOOL success = CreateProcessA( 325 | NULL, // path (NULL means use cmdline instead) 326 | cmdline, // Command line 327 | NULL, // Process handle not inheritable 328 | NULL, // Thread handle not inheritable 329 | FALSE, // Set handle inheritance to FALSE 330 | 0, // No creation flags 331 | NULL, // Use parent's environment block 332 | NULL, // Use parent's starting directory 333 | &si, // Pointer to STARTUPINFO structure 334 | &pi); // Pointer to PROCESS_INFORMATION structure 335 | 336 | // Wait until child process exits and get the code 337 | WaitForSingleObject(pi.hProcess, INFINITE); 338 | 339 | // Check for our unique exit code 340 | GetExitCodeProcess(pi.hProcess, &exitCode); 341 | if (exitCode == 555) 342 | { 343 | found = TRUE; 344 | } 345 | 346 | // Close process and thread handles. 347 | CloseHandle(pi.hProcess); 348 | CloseHandle(pi.hThread); 349 | 350 | if (found) 351 | { 352 | DBG_MSG(DBG_DEBUGACTIVEPROCESS, "Caught by DebugActiveProcess!"); 353 | exit(DBG_DEBUGACTIVEPROCESS); 354 | } 355 | } 356 | 357 | 358 | // ======================================================================= 359 | // Timing Checks 360 | // These checks focus on comparison of time stamps between a portion 361 | // of code which is likely to be analyzed under a debugger. The goal 362 | // is to determine with high probability that a debugger is allowing 363 | // single step control, or that a breakpoint had been hit between 364 | // the time check locations. 365 | // ======================================================================= 366 | 367 | void adbg_RDTSC(void) 368 | { 369 | BOOL found = FALSE; 370 | 371 | #ifdef _WIN64 372 | uint64_t timeA = 0; 373 | uint64_t timeB = 0; 374 | TimeKeeper timeKeeper = { 0 }; 375 | adbg_RDTSCx64(&timeKeeper); 376 | 377 | timeA = timeKeeper.timeUpperA; 378 | timeA = (timeA << 32) | timeKeeper.timeLowerA; 379 | 380 | timeB = timeKeeper.timeUpperB; 381 | timeB = (timeB << 32) | timeKeeper.timeLowerB; 382 | 383 | // 0x100000 is purely empirical and is based on the CPU clock speed 384 | // This value should be change depending on the length and complexity of 385 | // code between each RDTSC operation. 386 | 387 | if (timeB - timeA > 0x100000) 388 | { 389 | found = TRUE; 390 | } 391 | 392 | #else 393 | int timeUpperA = 0; 394 | int timeLowerA = 0; 395 | int timeUpperB = 0; 396 | int timeLowerB = 0; 397 | int timeA = 0; 398 | int timeB = 0; 399 | 400 | _asm 401 | { 402 | // rdtsc stores result across EDX:EAX 403 | rdtsc; 404 | mov [timeUpperA], edx; 405 | mov [timeLowerA], eax; 406 | 407 | // Junk code to entice stepping through or a breakpoint 408 | xor eax, eax; 409 | mov eax, 5; 410 | shr eax, 2; 411 | sub eax, ebx; 412 | cmp eax, ecx; 413 | 414 | rdtsc; 415 | mov [timeUpperB], edx; 416 | mov [timeLowerB], eax; 417 | } 418 | 419 | timeA = timeUpperA; 420 | timeA = (timeA << 32) | timeLowerA; 421 | 422 | timeB = timeUpperB; 423 | timeB = (timeB << 32) | timeLowerB; 424 | 425 | // 0x100000 is purely empirical and is based on the CPU clock speed 426 | // This value should be change depending on the length and complexity of 427 | // code between each RDTSC operation. 428 | 429 | if (timeB - timeA > 0x10000) 430 | { 431 | found = TRUE; 432 | } 433 | 434 | #endif 435 | 436 | if (found) 437 | { 438 | DBG_MSG(DBG_RDTSC, "Caught by RDTSC!"); 439 | exit(DBG_RDTSC); 440 | } 441 | } 442 | 443 | 444 | void adbg_QueryPerformanceCounter(void) 445 | { 446 | BOOL found = FALSE; 447 | LARGE_INTEGER t1; 448 | LARGE_INTEGER t2; 449 | 450 | QueryPerformanceCounter(&t1); 451 | 452 | #ifdef _WIN64 453 | adbg_QueryPerformanceCounterx64(); 454 | #else 455 | // Junk or legit code. 456 | _asm 457 | { 458 | xor eax, eax; 459 | push eax; 460 | push ecx; 461 | pop eax; 462 | pop ecx; 463 | sub ecx, eax; 464 | shl ecx, 4; 465 | } 466 | #endif 467 | 468 | QueryPerformanceCounter(&t2); 469 | 470 | // 30 is an empirical value 471 | if ((t2.QuadPart - t1.QuadPart) > 30) 472 | { 473 | found = TRUE; 474 | } 475 | 476 | if (found) 477 | { 478 | DBG_MSG(DBG_QUERYPERFORMANCECOUNTER, "Caught by QueryPerformanceCounter!"); 479 | exit(DBG_QUERYPERFORMANCECOUNTER); 480 | } 481 | } 482 | 483 | 484 | void adbg_GetTickCount(void) 485 | { 486 | BOOL found = FALSE; 487 | DWORD t1; 488 | DWORD t2; 489 | 490 | t1 = GetTickCount(); 491 | 492 | #ifdef _WIN64 493 | adbg_GetTickCountx64(); 494 | #else 495 | // Junk or legit code. 496 | _asm 497 | { 498 | xor eax, eax; 499 | push eax; 500 | push ecx; 501 | pop eax; 502 | pop ecx; 503 | sub ecx, eax; 504 | shl ecx, 4; 505 | } 506 | #endif 507 | 508 | t2 = GetTickCount(); 509 | 510 | // 30 milliseconds is an empirical value 511 | if ((t2 - t1) > 30) 512 | { 513 | found = TRUE; 514 | } 515 | 516 | if (found) 517 | { 518 | DBG_MSG(DBG_GETTICKCOUNT, "Caught by GetTickCount!"); 519 | exit(DBG_GETTICKCOUNT); 520 | } 521 | } 522 | 523 | 524 | // ======================================================================= 525 | // CPU Checks 526 | // These checks focus on aspects of the CPU, including hardware break- 527 | // points, special interrupt opcodes, and flags. 528 | // ======================================================================= 529 | 530 | void adbg_HardwareDebugRegisters(void) 531 | { 532 | BOOL found = FALSE; 533 | CONTEXT ctx = { 0 }; 534 | HANDLE hThread = GetCurrentThread(); 535 | 536 | ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; 537 | if (GetThreadContext(hThread, &ctx)) 538 | { 539 | if ((ctx.Dr0 != 0x00) || (ctx.Dr1 != 0x00) || (ctx.Dr2 != 0x00) || (ctx.Dr3 != 0x00) || (ctx.Dr6 != 0x00) || (ctx.Dr7 != 0x00)) 540 | { 541 | found = TRUE; 542 | } 543 | } 544 | 545 | if (found) 546 | { 547 | DBG_MSG(DBG_HARDWAREDEBUGREGISTERS, "Caught by a Hardware Debug Register Check!"); 548 | exit(DBG_HARDWAREDEBUGREGISTERS); 549 | } 550 | } 551 | 552 | 553 | void adbg_MovSS(void) 554 | { 555 | BOOL found = FALSE; 556 | 557 | #ifdef _WIN64 558 | // This method does not work on x64 559 | #else 560 | _asm 561 | { 562 | push ss; 563 | pop ss; 564 | pushfd; 565 | test byte ptr[esp + 1], 1; 566 | jne fnd; 567 | jmp end; 568 | fnd: 569 | mov found, 1; 570 | end: 571 | nop; 572 | } 573 | #endif 574 | 575 | if (found) 576 | { 577 | DBG_MSG(DBG_MOVSS, "Caught by a MOV SS Single Step Check!"); 578 | exit(DBG_MOVSS); 579 | } 580 | } 581 | 582 | 583 | // ======================================================================= 584 | // Exception Checks 585 | // These checks focus on exceptions that occur when under the control of 586 | // a debugger. In several cases, there are certain exceptions that will 587 | // be thrown only when running under a debugger. 588 | // ======================================================================= 589 | 590 | 591 | void adbg_CloseHandleException(void) 592 | { 593 | HANDLE hInvalid = (HANDLE)0xBEEF; // an invalid handle 594 | DWORD found = FALSE; 595 | 596 | __try 597 | { 598 | CloseHandle(hInvalid); 599 | } 600 | __except (EXCEPTION_EXECUTE_HANDLER) 601 | { 602 | found = TRUE; 603 | } 604 | 605 | if (found) 606 | { 607 | DBG_MSG(DBG_CLOSEHANDLEEXCEPTION, "Caught by an CloseHandle exception!"); 608 | exit(DBG_CLOSEHANDLEEXCEPTION); 609 | } 610 | } 611 | 612 | 613 | void adbg_SingleStepException(void) 614 | { 615 | DWORD found = TRUE; 616 | 617 | // In this method we force an exception to occur. If it occurs 618 | // outside of a debugger, the __except() handler is called setting 619 | // found to FALSE. If the exception occurs inside of a debugger, the 620 | // __except() will not be called (in certain cases) leading to 621 | // found being TRUE. 622 | 623 | __try 624 | { 625 | #ifdef _WIN64 626 | adbg_SingleStepExceptionx64(); 627 | #else 628 | _asm 629 | { 630 | pushfd; // save EFFLAGS register 631 | or byte ptr[esp + 1], 1; // set trap flag in EFFLAGS 632 | popfd; // restore EFFLAGS register 633 | } 634 | #endif 635 | } 636 | __except (EXCEPTION_EXECUTE_HANDLER) 637 | { 638 | found = FALSE; 639 | } 640 | 641 | if (found) 642 | { 643 | DBG_MSG(DBG_SINGLESTEPEXCEPTION, "Caught by a Single Step Exception!"); 644 | exit(DBG_SINGLESTEPEXCEPTION); 645 | } 646 | } 647 | 648 | 649 | void adbg_Int3(void) 650 | { 651 | BOOL found = TRUE; 652 | 653 | __try 654 | { 655 | #ifdef _WIN64 656 | adbg_Int3x64(); 657 | #else 658 | _asm 659 | { 660 | int 3; // 0xCC standard software breakpoint 661 | } 662 | #endif 663 | } 664 | 665 | __except (EXCEPTION_EXECUTE_HANDLER) 666 | { 667 | found = FALSE; 668 | } 669 | 670 | if (found) 671 | { 672 | DBG_MSG(DBG_INT3CC, "Caught by a rogue INT 3!"); 673 | exit(DBG_INT3CC); 674 | } 675 | } 676 | 677 | 678 | void adbg_PrefixHop(void) 679 | { 680 | BOOL found = TRUE; 681 | 682 | __try 683 | { 684 | #ifdef _WIN64 685 | // TODO: Not yet implemented in x64 686 | found = FALSE; 687 | #else 688 | _asm 689 | { 690 | __emit 0xF3; // 0xF3 0x64 is the prefix 'REP' 691 | __emit 0x64; 692 | __emit 0xCC; // this gets skipped over if being debugged 693 | } 694 | #endif 695 | } 696 | 697 | __except (EXCEPTION_EXECUTE_HANDLER) 698 | { 699 | found = FALSE; 700 | } 701 | 702 | if (found) 703 | { 704 | DBG_MSG(DBG_PREFIXHOP, "Caught by a Prefix Hop!"); 705 | exit(DBG_PREFIXHOP); 706 | } 707 | } 708 | 709 | 710 | void adbg_Int2D(void) 711 | { 712 | BOOL found = TRUE; 713 | 714 | __try 715 | { 716 | #ifdef _WIN64 717 | adbg_Int2Dx64(); 718 | #else 719 | _asm 720 | { 721 | int 0x2D; 722 | nop; 723 | } 724 | #endif 725 | } 726 | 727 | __except (EXCEPTION_EXECUTE_HANDLER) 728 | { 729 | found = FALSE; 730 | } 731 | 732 | if (found) 733 | { 734 | DBG_MSG(DBG_NONE, "Caught by a rogue INT 2D!"); 735 | exit(DBG_NONE); 736 | } 737 | } 738 | 739 | // ======================================================================= 740 | // Other Checks 741 | // Other kinds of checks that don't fit into the normal categories. 742 | // ======================================================================= 743 | 744 | void adbg_CrashOllyDbg(void) 745 | { 746 | // crash OllyDbg v1.x by exploit 747 | __try { 748 | OutputDebugString(TEXT("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")); 749 | } 750 | __except (EXCEPTION_EXECUTE_HANDLER) { ; } 751 | } 752 | -------------------------------------------------------------------------------- /antidbg/antidbg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | // Error Codes 8 | enum DBG_CATCH 9 | { 10 | DBG_NONE = 0x0000, 11 | 12 | // Memory Codes (0x1000 range) 13 | DBG_BEINGEBUGGEDPEB = 0x1000, 14 | DBG_CHECKREMOTEDEBUGGERPRESENT = 0x1001, 15 | DBG_ISDEBUGGERPRESENT = 0x1002, 16 | DBG_NTGLOBALFLAGPEB = 0x1003, 17 | DBG_NTQUERYINFORMATIONPROCESS = 0x1004, 18 | DBG_FINDWINDOW = 0x1005, 19 | DBG_OUTPUTDEBUGSTRING = 0x1006, 20 | DBG_NTSETINFORMATIONTHREAD = 0x1007, 21 | DBG_DEBUGACTIVEPROCESS = 0x1008, 22 | DBG_PROCESSFILENAME = 0x1009, 23 | 24 | // CPU Codes (0x2000 range) 25 | DBG_HARDWAREDEBUGREGISTERS = 0x2000, 26 | DBG_MOVSS = 0x2001, 27 | 28 | // Timing Codes (0x3000 range) 29 | DBG_RDTSC = 0x3000, 30 | DBG_QUERYPERFORMANCECOUNTER = 0x3001, 31 | DBG_GETTICKCOUNT = 0x3002, 32 | 33 | // Exception Codes (0x4000 range) 34 | DBG_CLOSEHANDLEEXCEPTION = 0x4000, 35 | DBG_SINGLESTEPEXCEPTION = 0x4001, 36 | DBG_INT3CC = 0x4002, 37 | DBG_PREFIXHOP = 0x4003, 38 | }; 39 | 40 | // Debugging messages 41 | void DBG_MSG(WORD dbg_code, char* message); 42 | 43 | // Dynamically resolved functions 44 | typedef NTSTATUS(__stdcall* _NtQueryInformationProcess)(_In_ HANDLE, _In_ unsigned int, _Out_ PVOID, _In_ ULONG, _Out_ PULONG); 45 | typedef NTSTATUS(__stdcall* _NtSetInformationThread)(_In_ HANDLE, _In_ THREAD_INFORMATION_CLASS, _In_ PVOID, _In_ ULONG); 46 | 47 | typedef struct timeKeeper { 48 | uint64_t timeUpperA; 49 | uint64_t timeLowerA; 50 | uint64_t timeUpperB; 51 | uint64_t timeLowerB; 52 | } TimeKeeper; 53 | 54 | #ifdef _WIN64 55 | extern "C" 56 | { 57 | int adbg_BeingDebuggedPEBx64(void); 58 | int adbg_NtGlobalFlagPEBx64(void); 59 | void adbg_GetTickCountx64(void); 60 | void adbg_QueryPerformanceCounterx64(void); 61 | void adbg_RDTSCx64(TimeKeeper*); 62 | void adbg_Int2Dx64(void); 63 | void adbg_Int3x64(void); 64 | void adbg_SingleStepExceptionx64(void); 65 | }; 66 | #endif 67 | 68 | // Memory 69 | void adbg_BeingDebuggedPEB(void); 70 | void adbg_CheckRemoteDebuggerPresent(void); 71 | void adbg_CheckWindowClassName(void); 72 | void adbg_CheckWindowName(void); 73 | void adbg_ProcessFileName(void); 74 | void adbg_IsDebuggerPresent(void); 75 | void adbg_NtGlobalFlagPEB(void); 76 | void adbg_NtQueryInformationProcess(void); 77 | void adbg_NtSetInformationThread(void); 78 | void adbg_DebugActiveProcess(const char*); 79 | 80 | // CPU 81 | void adbg_HardwareDebugRegisters(void); 82 | void adbg_MovSS(void); 83 | 84 | // Timing 85 | void adbg_RDTSC(void); 86 | void adbg_QueryPerformanceCounter(void); 87 | void adbg_GetTickCount(void); 88 | 89 | // Other 90 | void adbg_CrashOllyDbg(void); 91 | 92 | // Exception 93 | void adbg_CloseHandleException(void); 94 | void adbg_SingleStepException(void); 95 | void adbg_Int3(void); 96 | void adbg_Int2D(void); 97 | void adbg_PrefixHop(void); 98 | -------------------------------------------------------------------------------- /antidbg/antidbg.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {07fd03db-29bb-4c86-aad6-49a2f8c47be6} 25 | AntiDBG 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | 88 | Level3 89 | true 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | true 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | Level3 101 | true 102 | true 103 | true 104 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | true 106 | 107 | 108 | Console 109 | true 110 | true 111 | true 112 | 113 | 114 | 115 | 116 | Level3 117 | true 118 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 119 | true 120 | 121 | 122 | Console 123 | true 124 | 125 | 126 | 127 | 128 | Level3 129 | true 130 | true 131 | true 132 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 133 | true 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | legacy_stdio_definitions.lib;legacy_stdio_wide_specifiers.lib;%(AdditionalDependencies) 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | Document 153 | ml64 /c /Fo$(OutDir)\AntiDBG.obj AntiDBG.asm 154 | $(OutDir)/AntiDBG.obj 155 | ml64 /c /Fo$(OutDir)\AntiDBG.obj AntiDBG.asm 156 | $(OutDir)/AntiDBG.obj 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /antidbg/antidbg.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | Source Files 28 | 29 | 30 | 31 | 32 | Source Files 33 | 34 | 35 | -------------------------------------------------------------------------------- /antidbg/gauntlet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AntiDBG is a collection of Windows anti-debugging tricks. 3 | * The "gaultlet" execuable is a sample application you can test under a debugger. 4 | * Most anti-debugging methods are located in AntiDBG.cpp 5 | * Any x64 anti-debugging methods using inline assembly have their assembly 6 | * located in AntiDBG.asm 7 | */ 8 | 9 | #include "AntiDBG.h" 10 | 11 | // ======================================================================= 12 | // The Gauntlet 13 | // ======================================================================= 14 | int main(int argc, char* argv[]) 15 | { 16 | /* 17 | This sample application calls all included anti-debugging methods 18 | one after the other. Your goal is to start from the entry point, 19 | and debug your way to the end without the debugger closing on you. 20 | 21 | Make sure to try the gauntlet out in different debuggers: 22 | - OllyDBG 23 | - Immunity 24 | - x64dbg 25 | - WinDBG 26 | - IDA Pro 27 | - etc. 28 | 29 | Different debuggers will handle exceptions their own way by default. 30 | For example, some of our breakpoint exceptions will not work in x64dbg 31 | under default setting. 32 | 33 | Good Luck! 34 | */ 35 | 36 | // ------------------------------------------------------------------- 37 | // -- Memory Checks -------------------------------------------------- 38 | // ------------------------------------------------------------------- 39 | adbg_IsDebuggerPresent(); 40 | adbg_BeingDebuggedPEB(); 41 | adbg_NtGlobalFlagPEB(); 42 | adbg_CheckRemoteDebuggerPresent(); 43 | adbg_NtQueryInformationProcess(); 44 | adbg_CheckWindowClassName(); 45 | adbg_CheckWindowName(); 46 | adbg_ProcessFileName(); 47 | adbg_NtSetInformationThread(); 48 | adbg_DebugActiveProcess(argv[1]); 49 | 50 | // ------------------------------------------------------------------- 51 | // -- CPU Checks ----------------------------------------------------- 52 | // ------------------------------------------------------------------- 53 | adbg_HardwareDebugRegisters(); 54 | adbg_MovSS(); 55 | 56 | // ------------------------------------------------------------------- 57 | // -- Timing Checks -------------------------------------------------- 58 | // ------------------------------------------------------------------- 59 | adbg_RDTSC(); 60 | adbg_QueryPerformanceCounter(); 61 | adbg_GetTickCount(); 62 | 63 | // ------------------------------------------------------------------- 64 | // -- Exception Checks ----------------------------------------------- 65 | // ------------------------------------------------------------------- 66 | adbg_CloseHandleException(); 67 | adbg_SingleStepException(); 68 | adbg_Int3(); 69 | adbg_Int2D(); 70 | adbg_PrefixHop(); 71 | 72 | // ------------------------------------------------------------------- 73 | // -- Other ---------------------------------------------------------- 74 | // ------------------------------------------------------------------- 75 | adbg_CrashOllyDbg(); 76 | 77 | // Your goal is to get here in a debugger without modifying EIP yourself. 78 | MessageBoxA(NULL, "Congratulations! You made it!", "You Win!", 0); 79 | 80 | return 0; 81 | } 82 | --------------------------------------------------------------------------------