mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-10-30 21:35:06 +00:00
281 lines
8.2 KiB
Go
281 lines
8.2 KiB
Go
//go:build guest_windows
|
|
|
|
package ntapi
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/mirkobrombin/Raven/pkg/mmu"
|
|
)
|
|
|
|
const User32CallbackWindowProc uint32 = 0
|
|
|
|
const (
|
|
userCallbackRuntimeSize = uint64(mmu.PageSize)
|
|
userCallbackDispatcherOffset = uint64(0)
|
|
userCallbackWindowProcOffset = uint64(0x100)
|
|
userCallbackTableOffset = uint64(0x200)
|
|
windowProcCallback32Size = uint32(32)
|
|
windowProcCallback64Size = uint32(64)
|
|
windowProcCallback32Result = uint64(28)
|
|
windowProcCallback64Result = uint64(56)
|
|
)
|
|
|
|
type UserCallbackRuntime struct {
|
|
Dispatcher uint64
|
|
CallbackTable uint64
|
|
WindowProc uint64
|
|
}
|
|
|
|
type WindowProcCallbackArguments struct {
|
|
Proc uint64
|
|
IsAnsiProc bool
|
|
Wnd uint64
|
|
Msg uint32
|
|
WParam uint64
|
|
LParam uint64
|
|
}
|
|
|
|
func InstallUserCallbackRuntime(mem *mmu.MMU, state *ProcessState, ntCallbackReturn uint64) (UserCallbackRuntime, error) {
|
|
if mem == nil {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: nil memory")
|
|
}
|
|
if state == nil || state.PEBAddr == 0 {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: nil PEB")
|
|
}
|
|
if ntCallbackReturn == 0 {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: nil NtCallbackReturn")
|
|
}
|
|
if state.Is32 && (state.PEBAddr > uint64(^uint32(0)) || ntCallbackReturn > uint64(^uint32(0))) {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install PE32 user callback runtime: address exceeds 32-bit range")
|
|
}
|
|
|
|
base := mem.Mmap(0, userCallbackRuntimeSize, mmu.PermRead|mmu.PermWrite)
|
|
if base == 0 {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: executable allocation failed")
|
|
}
|
|
installed := false
|
|
defer func() {
|
|
if !installed {
|
|
mem.Munmap(base, userCallbackRuntimeSize)
|
|
}
|
|
}()
|
|
if state.Is32 && base+userCallbackRuntimeSize-1 > uint64(^uint32(0)) {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: executable allocation failed")
|
|
}
|
|
|
|
runtime := UserCallbackRuntime{
|
|
Dispatcher: base + userCallbackDispatcherOffset,
|
|
CallbackTable: base + userCallbackTableOffset,
|
|
WindowProc: base + userCallbackWindowProcOffset,
|
|
}
|
|
var (
|
|
dispatcherCode []byte
|
|
windowProcCode []byte
|
|
callbackTable uint64
|
|
err error
|
|
)
|
|
if state.Is32 {
|
|
dispatcherCode = buildUserCallbackDispatcher32(uint32(state.PEBAddr), uint32(ntCallbackReturn))
|
|
windowProcCode = buildWindowProcCallback32(uint32(ntCallbackReturn))
|
|
callbackTable, err = readCallbackPointer(mem, state.PEBAddr+Peb32KernelCallbackTable, true)
|
|
} else {
|
|
dispatcherCode = buildUserCallbackDispatcher64(state.PEBAddr, ntCallbackReturn)
|
|
windowProcCode = buildWindowProcCallback64(ntCallbackReturn)
|
|
callbackTable, err = readCallbackPointer(mem, state.PEBAddr+PebKernelCallbackTable, false)
|
|
}
|
|
if err != nil {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: read callback table: %w", err)
|
|
}
|
|
if err := mem.WriteBytes(runtime.Dispatcher, dispatcherCode); err != nil {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: write dispatcher: %w", err)
|
|
}
|
|
if err := mem.WriteBytes(runtime.WindowProc, windowProcCode); err != nil {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: write window proc callback: %w", err)
|
|
}
|
|
|
|
if callbackTable == 0 {
|
|
if state.Is32 {
|
|
if err := mem.Write32(runtime.CallbackTable, uint32(runtime.WindowProc)); err != nil {
|
|
return UserCallbackRuntime{}, err
|
|
}
|
|
} else {
|
|
if err := mem.Write64(runtime.CallbackTable, runtime.WindowProc); err != nil {
|
|
return UserCallbackRuntime{}, err
|
|
}
|
|
}
|
|
} else {
|
|
runtime.CallbackTable = callbackTable
|
|
}
|
|
|
|
if err := mem.Mprotect(base, userCallbackRuntimeSize, mmu.PermRead|mmu.PermExecute); err != nil {
|
|
return UserCallbackRuntime{}, fmt.Errorf("install user callback runtime: protect code: %w", err)
|
|
}
|
|
if callbackTable == 0 {
|
|
if state.Is32 {
|
|
if err := mem.Write32(state.PEBAddr+Peb32KernelCallbackTable, uint32(runtime.CallbackTable)); err != nil {
|
|
return UserCallbackRuntime{}, err
|
|
}
|
|
} else if err := mem.Write64(state.PEBAddr+PebKernelCallbackTable, runtime.CallbackTable); err != nil {
|
|
return UserCallbackRuntime{}, err
|
|
}
|
|
}
|
|
installed = true
|
|
return runtime, nil
|
|
}
|
|
|
|
func EncodeWindowProcCallbackArguments(is32 bool, args WindowProcCallbackArguments) []byte {
|
|
if is32 {
|
|
buf := make([]byte, windowProcCallback32Size)
|
|
binary.LittleEndian.PutUint32(buf[0:], uint32(args.Proc))
|
|
if args.IsAnsiProc {
|
|
binary.LittleEndian.PutUint32(buf[4:], 1)
|
|
}
|
|
binary.LittleEndian.PutUint32(buf[8:], uint32(args.Wnd))
|
|
binary.LittleEndian.PutUint32(buf[12:], args.Msg)
|
|
binary.LittleEndian.PutUint32(buf[16:], uint32(args.WParam))
|
|
binary.LittleEndian.PutUint32(buf[20:], uint32(args.LParam))
|
|
binary.LittleEndian.PutUint32(buf[24:], uint32(^uint32(0)))
|
|
return buf
|
|
}
|
|
|
|
buf := make([]byte, windowProcCallback64Size)
|
|
binary.LittleEndian.PutUint64(buf[0:], args.Proc)
|
|
if args.IsAnsiProc {
|
|
binary.LittleEndian.PutUint32(buf[8:], 1)
|
|
}
|
|
binary.LittleEndian.PutUint64(buf[16:], args.Wnd)
|
|
binary.LittleEndian.PutUint32(buf[24:], args.Msg)
|
|
binary.LittleEndian.PutUint64(buf[32:], args.WParam)
|
|
binary.LittleEndian.PutUint64(buf[40:], args.LParam)
|
|
binary.LittleEndian.PutUint32(buf[48:], uint32(^uint32(0)))
|
|
return buf
|
|
}
|
|
|
|
func ReadWindowProcCallbackResult(mem *mmu.MMU, is32 bool, callbackReturn UserCallbackReturn) (uint64, error) {
|
|
size := windowProcCallback64Size
|
|
offset := windowProcCallback64Result
|
|
if is32 {
|
|
size = windowProcCallback32Size
|
|
offset = windowProcCallback32Result
|
|
}
|
|
if callbackReturn.Address == 0 || callbackReturn.Length < size {
|
|
return 0, fmt.Errorf("window proc callback returned buffer 0x%x length %d", callbackReturn.Address, callbackReturn.Length)
|
|
}
|
|
if is32 {
|
|
value, err := mem.Read32(callbackReturn.Address + offset)
|
|
return uint64(value), err
|
|
}
|
|
return mem.Read64(callbackReturn.Address + offset)
|
|
}
|
|
|
|
func readCallbackPointer(mem *mmu.MMU, address uint64, is32 bool) (uint64, error) {
|
|
if is32 {
|
|
value, err := mem.Read32(address)
|
|
return uint64(value), err
|
|
}
|
|
return mem.Read64(address)
|
|
}
|
|
|
|
func buildUserCallbackDispatcher32(peb, ntCallbackReturn uint32) []byte {
|
|
code := []byte{
|
|
0x8B, 0x54, 0x24, 0x04,
|
|
0xB8,
|
|
}
|
|
code = appendUint32(code, peb)
|
|
code = append(code,
|
|
0x8B, 0x40, byte(Peb32KernelCallbackTable),
|
|
0xFF, 0x74, 0x24, 0x0C,
|
|
0xFF, 0x74, 0x24, 0x0C,
|
|
0xFF, 0x14, 0x90,
|
|
0x50,
|
|
0x6A, 0x00,
|
|
0x6A, 0x00,
|
|
0xB8,
|
|
)
|
|
code = appendUint32(code, ntCallbackReturn)
|
|
return append(code, 0xFF, 0xD0, 0xCC)
|
|
}
|
|
|
|
func buildWindowProcCallback32(ntCallbackReturn uint32) []byte {
|
|
code := []byte{
|
|
0x53,
|
|
0x56,
|
|
0x8B, 0x5C, 0x24, 0x0C,
|
|
0x8B, 0x74, 0x24, 0x10,
|
|
0xFF, 0x73, 0x14,
|
|
0xFF, 0x73, 0x10,
|
|
0xFF, 0x73, 0x0C,
|
|
0xFF, 0x73, 0x08,
|
|
0xFF, 0x13,
|
|
0x89, 0x43, 0x1C,
|
|
0x6A, 0x00,
|
|
0x56,
|
|
0x53,
|
|
0xB8,
|
|
}
|
|
code = appendUint32(code, ntCallbackReturn)
|
|
return append(code, 0xFF, 0xD0, 0xCC)
|
|
}
|
|
|
|
func buildUserCallbackDispatcher64(peb, ntCallbackReturn uint64) []byte {
|
|
code := []byte{
|
|
0x4C, 0x8B, 0x54, 0x24, 0x20,
|
|
0x44, 0x8B, 0x5C, 0x24, 0x28,
|
|
0x8B, 0x44, 0x24, 0x2C,
|
|
0x49, 0xBC,
|
|
}
|
|
code = appendUint64(code, peb)
|
|
code = append(code,
|
|
0x4D, 0x8B, 0x64, 0x24, byte(PebKernelCallbackTable),
|
|
0x49, 0x8B, 0x04, 0xC4,
|
|
0x4C, 0x89, 0xD1,
|
|
0x44, 0x89, 0xDA,
|
|
0x48, 0x83, 0xEC, 0x20,
|
|
0xFF, 0xD0,
|
|
0x48, 0x83, 0xC4, 0x20,
|
|
0x31, 0xC9,
|
|
0x31, 0xD2,
|
|
0x41, 0x89, 0xC0,
|
|
0x48, 0xB8,
|
|
)
|
|
code = appendUint64(code, ntCallbackReturn)
|
|
return append(code, 0xFF, 0xD0, 0xCC)
|
|
}
|
|
|
|
func buildWindowProcCallback64(ntCallbackReturn uint64) []byte {
|
|
code := []byte{
|
|
0x53,
|
|
0x56,
|
|
0x48, 0x89, 0xCB,
|
|
0x89, 0xD6,
|
|
0x48, 0x8B, 0x03,
|
|
0x48, 0x8B, 0x4B, 0x10,
|
|
0x8B, 0x53, 0x18,
|
|
0x4C, 0x8B, 0x43, 0x20,
|
|
0x4C, 0x8B, 0x4B, 0x28,
|
|
0x48, 0x83, 0xEC, 0x28,
|
|
0xFF, 0xD0,
|
|
0x48, 0x89, 0x43, 0x38,
|
|
0x48, 0x89, 0xD9,
|
|
0x89, 0xF2,
|
|
0x45, 0x31, 0xC0,
|
|
0x48, 0xB8,
|
|
}
|
|
code = appendUint64(code, ntCallbackReturn)
|
|
return append(code, 0xFF, 0xD0, 0xCC)
|
|
}
|
|
|
|
func appendUint32(code []byte, value uint32) []byte {
|
|
var encoded [4]byte
|
|
binary.LittleEndian.PutUint32(encoded[:], value)
|
|
return append(code, encoded[:]...)
|
|
}
|
|
|
|
func appendUint64(code []byte, value uint64) []byte {
|
|
var encoded [8]byte
|
|
binary.LittleEndian.PutUint64(encoded[:], value)
|
|
return append(code, encoded[:]...)
|
|
}
|