Added 'bof' module for executing object files and fixed handling of optional arguments.

This commit is contained in:
Jakob Friedl
2025-08-29 15:58:26 +02:00
parent 352b8fd8d1
commit 4ceb756cfd
8 changed files with 138 additions and 85 deletions

View File

@@ -19,32 +19,32 @@ const
type
datap* {.bycopy,packed.} = object
original*: ptr char
buffer*: ptr char
original*: PCHAR
buffer*: PCHAR
length*: int
size*: int
formatp* {.bycopy,packed.} = object
original*: ptr char
buffer*: ptr char
original*: PCHAR
buffer*: PCHAR
length*: int
size*: int
# Reference: https://forum.nim-lang.org/t/7352
type va_list* {.importc: "va_list", header: "<stdarg.h>".} = object
proc va_start(format: va_list, args: ptr char) {.stdcall, importc, header: "stdio.h"}
proc va_start(format: va_list, args: PCHAR) {.stdcall, importc, header: "stdio.h"}
proc va_end(ap: va_list) {.stdcall, importc, header: "stdio.h"}
proc vprintf(format: cstring, args: va_list) {.stdcall, importc, header: "stdio.h"}
proc vsnprintf(buffer: cstring; size: int; fmt: cstring; args: va_list): int {.stdcall, importc, dynlib: "msvcrt".}
var beaconCompatibilityOutput: ptr char = nil
var beaconCompatibilityOutput: PCHAR = nil
var beaconCompatibilitySize: int = 0
var beaconCompatibilityOffset: int = 0
#[
Parsing
]#
proc BeaconDataParse(parser: ptr datap, buffer: ptr char, size: int): void {.stdcall.} =
proc BeaconDataParse(parser: ptr datap, buffer: PCHAR, size: int): void {.stdcall.} =
if cast[uint64](parser) == 0:
return
@@ -87,13 +87,13 @@ proc BeaconDataLength(parser: ptr datap): int {.stdcall.} =
return parser.length
proc BeaconDataExtract(parser: ptr datap, size: ptr int): ptr char {.stdcall.} =
proc BeaconDataExtract(parser: ptr datap, size: ptr int): PCHAR {.stdcall.} =
if cast[uint64](parser) == 0:
return
var
length: int32 = 0
outData: ptr char = nil
outData: PCHAR = nil
# Length of prefixed binary blob
if parser.length < 4:
@@ -118,7 +118,7 @@ proc BeaconFormatAlloc(format: ptr formatp, maxsz: int): void {.stdcall.} =
if format == NULL:
return
format.original = cast[ptr char](alloc(maxsz))
format.original = cast[PCHAR](alloc(maxsz))
zeroMem(format.original, maxsz)
format.buffer = format.original
format.length = 0
@@ -144,7 +144,7 @@ proc BeaconFormatFree(format: ptr formatp): void {.stdcall.} =
format.length = 0
format.size = 0
proc BeaconFormatAppend(format: ptr formatp, text: ptr char, len: int): void {.stdcall.} =
proc BeaconFormatAppend(format: ptr formatp, text: PCHAR, len: int): void {.stdcall.} =
if format == NULL or text == NULL:
return
@@ -152,7 +152,7 @@ proc BeaconFormatAppend(format: ptr formatp, text: ptr char, len: int): void {.s
format.buffer += len
format.length += len
proc BeaconFormatPrintf(format: ptr formatp, fmt: ptr char): void {.stdcall, varargs.} =
proc BeaconFormatPrintf(format: ptr formatp, fmt: PCHAR): void {.stdcall, varargs.} =
if format == NULL or fmt == NULL:
return
@@ -172,7 +172,7 @@ proc BeaconFormatPrintf(format: ptr formatp, fmt: ptr char): void {.stdcall, var
format.length += length
format.buffer += length
proc BeaconFormatToString(format: ptr formatp, size: ptr int): ptr char {.stdcall.} =
proc BeaconFormatToString(format: ptr formatp, size: ptr int): PCHAR {.stdcall.} =
if format == NULL or size == NULL:
return
@@ -205,12 +205,12 @@ proc BeaconFormatInt(format: ptr formatp, value: int): void =
#[
Output functions
]#
proc BeaconPrintf(typeArg: int, fmt: ptr char):void{.stdcall, varargs.} =
proc BeaconPrintf(typeArg: int, fmt: PCHAR):void{.stdcall, varargs.} =
if fmt == NULL:
return
var length: int = 0
var tempPtr: ptr char = nil
var tempPtr: PCHAR = nil
var args: va_list
va_start(args, fmt)
vprintf(fmt, args)
@@ -219,7 +219,7 @@ proc BeaconPrintf(typeArg: int, fmt: ptr char):void{.stdcall, varargs.} =
va_start(args, fmt)
length = vsnprintf(NULL, 0, fmt, args)
va_end(args)
tempPtr = cast[ptr char](realloc(beaconCompatibilityOutput,beaconCompatibilitySize + length + 1))
tempPtr = cast[PCHAR](realloc(beaconCompatibilityOutput,beaconCompatibilitySize + length + 1))
if tempPtr == nil:
return
beaconCompatibilityOutput = tempPtr
@@ -230,12 +230,12 @@ proc BeaconPrintf(typeArg: int, fmt: ptr char):void{.stdcall, varargs.} =
beaconCompatibilityOffset += length
va_end(args)
proc BeaconOutput(typeArg: int, data: ptr char, len: int): void {.stdcall.} =
proc BeaconOutput(typeArg: int, data: PCHAR, len: int): void {.stdcall.} =
if data == NULL:
return
var tempPtr: ptr char = nil
tempPtr = cast[ptr char](realloc(beaconCompatibilityOutput,beaconCompatibilitySize + len + 1))
var tempPtr: PCHAR = nil
tempPtr = cast[PCHAR](realloc(beaconCompatibilityOutput,beaconCompatibilitySize + len + 1))
beaconCompatibilityOutput = tempPtr
if tempPtr == nil:
return
@@ -263,7 +263,7 @@ proc BeaconIsAdmin(): BOOL {.stdcall.}=
#[
Spawn+Inject Functions
]#
proc BeaconGetSpawnTo(x86: BOOL, buffer: ptr char, length: int): void {.stdcall.} =
proc BeaconGetSpawnTo(x86: BOOL, buffer: PCHAR, length: int): void {.stdcall.} =
if buffer == NULL:
return
@@ -290,11 +290,11 @@ proc BeaconSpawnTemporaryProcess(x86: BOOL, ignoreToken: BOOL, sInfo: ptr STARTU
return bSuccess
proc BeaconInjectProcess(hProc: HANDLE, pid: int, payload: ptr char, p_len: int, p_offset: int, arg: ptr char, a_len: int): void {.stdcall.} =
proc BeaconInjectProcess(hProc: HANDLE, pid: int, payload: PCHAR, p_len: int, p_offset: int, arg: PCHAR, a_len: int): void {.stdcall.} =
# Not implemented
return
proc BeaconInjectTemporaryProcess(pInfo: ptr PROCESS_INFORMATION, payload: ptr char, p_len: int, p_offset: int, arg: ptr char, a_len: int): void {.stdcall.} =
proc BeaconInjectTemporaryProcess(pInfo: ptr PROCESS_INFORMATION, payload: PCHAR, p_len: int, p_offset: int, arg: PCHAR, a_len: int): void {.stdcall.} =
# Not implemented
return
@@ -305,12 +305,12 @@ proc BeaconCleanupProcess(pInfo: ptr PROCESS_INFORMATION): void {.stdcall.} =
#[
Utility Functions
]#
proc toWideChar(src: ptr char, dst: ptr char, max: int): BOOL {.stdcall.} =
proc toWideChar(src: PCHAR, dst: PCHAR, max: int): BOOL {.stdcall.} =
# Not implemented
return FALSE
proc BeaconGetOutputData*(outSize: ptr int): ptr char {.stdcall.} =
var outData: ptr char = beaconCompatibilityOutput
proc BeaconGetOutputData*(outSize: ptr int): PCHAR {.stdcall.} =
var outData: PCHAR = beaconCompatibilityOutput
if cast[uint64](outSize) != 0:
outsize[] = beaconCompatibilitySize

View File

@@ -1,7 +1,7 @@
import winim/lean
import os, strformat, strutils, ptr_math
import ./beacon
import ../../common/[types, utils]
import ../../common/[types, utils, serialize]
#[
Object file loading involves the following steps
@@ -262,8 +262,7 @@ proc objectProcessSection(objCtx: POBJECT_CTX): bool =
Arguments:
- objCtx: Object context
- entry: Name of the entry function to be executed
- args: Pointer to the address of the arguments passed to the object file
- argc: Size of the arguments passed to the object file
- args: Arguments passed to the object file
]#
proc objectExecute(objCtx: POBJECT_CTX, entry: PSTR, args: seq[byte]): bool =
@@ -300,7 +299,11 @@ proc objectExecute(objCtx: POBJECT_CTX, entry: PSTR, args: seq[byte]): bool =
# Execute BOF entry point
var entryPoint = cast[EntryPoint](cast[uint](secBase) + cast[uint](objSym.Value))
entryPoint(addr args[0], cast[ULONG](args.len()))
if args.len > 0:
entryPoint(addr args[0], cast[ULONG](args.len()))
else:
entryPoint(NULL, 0)
# Revert the memory protection change
if VirtualProtect(secBase, secSize, oldProtect, addr oldProtect) == 0:
@@ -314,12 +317,11 @@ proc objectExecute(objCtx: POBJECT_CTX, entry: PSTR, args: seq[byte]): bool =
Loads, parses and executes a object file in memory
Arguments:
- pObject: Base address of the object file in memory
- sFunction: Name of the function to be executed, usually "go"
- pArgs: Base address of the arguments to be passed to the function
- uArgc: Size of the arguments passed to the function
- objectFile: Bytes of the object file
- args: Bytes of the COFF arguments
- entryFunction: Name of the entry function to look for, usually "go"
]#
proc inlineExecute*(objectFile: seq[byte], args: seq[byte], entryFunction: string = "go"): bool =
proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: string = "go"): bool =
var
objCtx: OBJECT_CTX
@@ -399,7 +401,14 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte], entryFunction: strin
return true
proc inlineExecuteGetOutput*(objectFile: seq[byte], args: seq[byte], entryFunction: string = "go"): string =
#[
Execute a object file in memory and retrieve the output using the BeaconGetOutputData API
Arguments:
- objectFile: Bytes of the object file
- args: Bytes of the COFF arguments
- entryFunction: Name of the entry function to look for, usually "go"
]#
proc inlineExecuteGetOutput*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: string = "go"): string =
if not inlineExecute(objectFile, args, entryFunction):
raise newException(CatchableError, fmt"[-] Failed to inline-execute object file.")
@@ -407,33 +416,21 @@ proc inlineExecuteGetOutput*(objectFile: seq[byte], args: seq[byte], entryFuncti
var output = BeaconGetOutputData(NULL)
return $output
proc HexStringToByteArray(hexString:string,hexLength:int):seq[byte] =
var returnValue:seq[byte] = @[]
for i in countup(0,hexLength-1,2):
try:
#cho hexString[i..i+1]
returnValue.add(fromHex[uint8](hexString[i..i+1]))
except ValueError:
return @[]
#fromHex[uint8]
return returnValue
#[
Process the COFF arguments according to:
https://github.com/trustedsec/COFFLoader/blob/main/beacon_generate.py
]#
proc generateCoffArguments*(args: seq[TaskArg]): seq[byte] =
var packer = Packer.init()
for arg in args:
packer.add(uint32(arg.data.len()))
packer.addData(arg.data)
proc test*() =
# Add terminating NULL byte to the end of string arguments
if arg.argType == uint8(types.STRING):
packer.add(uint8('\0'))
var
fileName = "dir.x64.o"
pObject = readFile(fileName)
uLength: ULONG = cast[ULONG](pObject.len)
let argBytes = packer.pack()
echo fmt"[+] Read file {fileName}: 0x{(addr pObject[0]).toHex()} (Size: {uLength} bytes)"
try:
let args = "130000000f000000433a2f55736572732f6a616b6f6200"
let argsBuffer = HexStringToByteArray(args, args.len)
echo $argsBuffer
echo inlineExecuteGetOutput(string.toBytes(pObject), argsBuffer)
except CatchableError as err:
echo "[-] ", err.msg
return uint32.toBytes(uint32(argBytes.len())) & argBytes

View File

@@ -70,6 +70,4 @@ proc main() =
echo "[-] ", err.msg
when isMainModule:
test()
quit(0)
main()