Skip to content

Ps bof#1

Merged
TGJLS merged 65 commits into
mainfrom
ps-bof
May 23, 2026
Merged

Ps bof#1
TGJLS merged 65 commits into
mainfrom
ps-bof

Conversation

@TGJLS

@TGJLS TGJLS commented May 23, 2026

Copy link
Copy Markdown
Owner

No description provided.

TGJLS and others added 30 commits May 16, 2026 08:04
- Add #include <psapi.h> after #include <winternl.h>
- Add NTDLL process-control section: NtQuerySystemInformation, NtSuspendProcess, NtResumeProcess
- Add PSAPI module-enumeration section: EnumProcessModulesEx, GetModuleFileNameExW, GetModuleInformation
- No existing lines removed; no aliasing macros added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…b sources

- PS-BOF/Makefile: mirrors FS-BOF/Makefile pattern with 6 BOFs (list, kill, run, grep, suspend, resume)
- 6 stub .c files: void go(char *args, int len) {} with #include "bofdefs.h"
- Recipe lines use literal tab characters; 12 compile targets (x64+x32 per BOF)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add PS-BOF to SUBDIRS := FS-BOF Exit-BOF PS-BOF
- make -C PS-BOF: 12/12 [+] lines, 0 [!] lines, all .o files produced
- make from root: recurses into FS-BOF, Exit-BOF, and PS-BOF cleanly
- make clean: removes all three _bin/ directories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents NtQuerySystemInformation loop pattern, GetUserByToken goto-cleanup
translation, BeaconPkgBytes/Int32 output format, bofdefs.h gap analysis
(10 new declarations including MSVCRT$malloc), and adaptix.h creation plan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Plan 23-01 (Wave 1): create _include/adaptix.h (BeaconPkgBytes/Int32 per
D-01); add 10 declarations to _include/bofdefs.h (KERNEL32/ADVAPI32/NTDLL
token query per D-09, plus MSVCRT\$malloc gap fix); port Kharon list.cc to
PS-BOF/list/list.c using goto-cleanup (D-07), manual wide-string concat
(Pitfall 3), and PACKAGE transport (D-02) for PB-01 binary format.

Plan 23-02 (Wave 2, depends on 23-01): port Kharon kill.cc to
PS-BOF/kill/kill.c with PROCESS_TERMINATE access and optional exit_code
via BeaconDataInt.

Plan 23-03 (Wave 2, depends on 23-01): implement suspend.c and resume.c
from scratch per D-08 using PROCESS_SUSPEND_RESUME + NtSuspendProcess /
NtResumeProcess.

All plans include STRIDE threat models (ASVS L1), per-task read_first
files, and concrete acceptance criteria. Covers requirements PS-01,
PS-02, PS-08, PS-09, PB-01.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- create _include/adaptix.h with BeaconPkgBytes and BeaconPkgInt32 (PCHAR UUID param)
- add MSVCRT$malloc alongside existing MSVCRT$calloc in bofdefs.h
- add KERNEL32 process management section: OpenProcess, TerminateProcess, IsWow64Process, GetCurrentProcess
- add ADVAPI32 token/privilege section: OpenProcessToken, LookupAccountSidW, AdjustTokenPrivileges, LookupPrivilegeValueW
- add NTDLL token query section: NtQueryInformationToken
- all 12 PS-BOF targets continue to build cleanly ([+] only)
- replace 5-line stub with full C port of Kharon list.cc
- GetUserByToken helper uses goto cleanup pattern (D-07)
- manual domain\user index-walk loop (no swprintf, Pitfall 3)
- NtQuerySystemInformation sizing + do/while traversal (Patterns 2-3)
- BeaconPkgBytes/BeaconPkgInt32 PACKAGE transport (D-02, Pattern 5)
- PB-01 field order: name wstr, PID, PPID, session, user wstr, arch
- STATUS_BUFFER_TOO_SMALL defined locally (absent from winternl.h)
- no EnableDebugPrivilege (D-05), no BeaconDataParse (Pitfall 6)
- compiles to list.x64.o and list.x32.o; all 12 PS-BOF targets green
- adaptix.h created with BeaconPkgBytes/BeaconPkgInt32 declarations
- bofdefs.h gains 10 declarations (4 KERNEL32, 4 ADVAPI32, 1 NTDLL, 1 MSVCRT)
- list.c ported from Kharon list.cc to C with goto-cleanup, PACKAGE transport, manual wide-string concat
- build: 12 [+], 0 [!] — PS-01 and PB-01 satisfied at source level
- Include order: <windows.h>, "bofdefs.h", "beacon.h" (no adaptix.h)
- BeaconDataParse + BeaconDataInt twice: PID and optional exit_code
- KERNEL32$OpenProcess with PROCESS_TERMINATE (minimum access, T-23-09)
- KERNEL32$TerminateProcess + KERNEL32$CloseHandle before result check
- BeaconPrintf narrow ASCII on both CALLBACK_ERROR and CALLBACK_OUTPUT paths
- 32 lines; build: 12 [+], 0 [!]; kill.x64.o and kill.x32.o produced
- suspend.c: parse PID via BeaconDataInt, OpenProcess(PROCESS_SUSPEND_RESUME), NtSuspendProcess, CloseHandle, BeaconPrintf
- resume.c: identical structure with NtResumeProcess and updated messages
- Both use PROCESS_SUSPEND_RESUME (0x0800) access mask — not PROCESS_ALL_ACCESS
- Both check NT_SUCCESS on the NT call status
- No adaptix.h, no nt_success(), no BeaconPrintfW — D-03/D-06 compliant
- Build: 12 [+] lines, 0 [!] lines; all four artifacts produced
Replace bare wcslen(user_token) with KERNEL32\$lstrlenW — matches
the project's module\$function dynamic-resolution convention for all
runtime string operations. Compile-time literal wcslen(L"...") calls
are unchanged (folded to constants by optimizer).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…conPrintf

Remove adaptix.h and BeaconPkgBytes/BeaconPkgInt32; ps list now outputs
a formatted text table via BeaconPrintf, making it compatible with any
standard BOF-capable C2 framework instead of Kharon only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KERNEL32: CreateProcessW, GetStdHandle, CreatePipe, SetHandleInformation,
InitializeProcThreadAttributeList, UpdateProcThreadAttribute,
DeleteProcThreadAttributeList, DuplicateHandle
ADVAPI32: CreateProcessWithLogonW, CreateProcessWithTokenW
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ons to bofdefs.h

Adds tlhelp32.h include and two new sections: KERNEL32 toolhelp
(CreateToolhelp32Snapshot, Thread32First, Thread32Next) and NTDLL
process info (NtQueryInformationProcess) required by PS-BOF grep.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 25 ps-grep executed: grep.c implemented with token/modules/cmdline/
threads sections; bofdefs.h extended with toolhelp and NtQueryInformationProcess
declarations. All 12 PS-BOF targets compile; 21/21 acceptance criteria pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- registers ps list, kill, run, grep, suspend, resume as beacon-only subcommands
- ps list: zero-arg prehook (no bof_params interpolation)
- ps kill: conditional int32/int32,int32 pack depending on exit_code presence
- ps run: D-05 method auto-detection (token→2, logon creds→1, default→0)
- ps grep/suspend/resume: single int32 pid pack
- group registered on ["beacon"] only per D-08 (not gopher/kharon)
- no on_processbrowser_list or add_session_browser per D-01

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- add ax.script_load(path + "PS-BOF/ps.axs") after Exit-BOF/exit.axs load
- single-line addition per D-09; no other changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SUMMARY: 2 tasks, PS-BOF/ps.axs created, bof-collection.axs updated
- PB-02/PB-03 satisfied by beacon agent ax_config.axs per D-01

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TGJLS and others added 28 commits May 20, 2026 19:38
Plan 27-01: write PS-BOF/README.md and update root README.md with PS-BOF section and Kharon credit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Six h2 sections for list, kill, run, grep, suspend, resume
- ps run has three separate fenced blocks (CreateProcess, WithLogon, WithToken)
- Matches FS-BOF/README.md structure exactly
- Insert PS-BOF table with 6 command rows after Exit-BOF section
- Add Kharon credit line after Extension-Kit in Credits section
Two-plan structure: 28-01 extends Testing-Kit with capture/substitution
feature, 28-02 adds PS-BOF tasks.yaml entries and test.yaml workflow update.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- append PS-BOF section with 8 task entries after existing FS-BOF entries
- ps-list x2 checking for System and lsass.exe (CI-01)
- ps-run-spawn-notepad with PID capture for dependency chain (CI-02/04/05/06 fixture)
- ps-run-pipe-whoami with expected_regex ci_runner (CI-03)
- ps-grep using expected_regex with all four section headers in order (CI-04)
- ps-suspend, ps-resume, ps-kill using not_expected: error (CI-05, CI-06, CI-02)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- copy PS-BOF _bin/*.o, ps.axs, and bof-collection.axs into container after Exit-BOF copy
- mkdir -p for PS-BOF container path (pre-dates Phase 26 in the image)
- add PS-BOF x64 object count verification (workspace and container, expect 6)
- reinstall adaptix-testing from git before server startup to pick up capture feature

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- add 28-01-SUMMARY.md for plan 28-01 (Testing-Kit capture + variable substitution)
- tasks.yaml capture field and {{var}} substitution implemented in Testing-Kit run.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WR-01: replace ordered multi-line expected: blocks for copy-wildcard-match and move-wildcard-match with expected_regex: patterns that accept either file enumeration order, avoiding spurious failures on NTFS volumes with non-deterministic FindFirstFile ordering
- WR-02: remove trailing space after *.xyz in copy-wildcard-no-match expected block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents CI breakage from unvetted commits landing on the Testing-Kit
default branch between runs. Pinned to 9fb3d85b3a7ff2d7a13c8cc1245cb9fd6a053b91.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ing space in copy-wildcard-no-match

- ps.axs: add "gopher" and "kharon" to register_commands_group — matches
  fs.axs and exit.axs pattern; CI uses kharon agent so ps commands were
  not found
- tasks.yaml: restore trailing space after *.xyz in copy-wildcard-no-match
  expected block — BOF output includes the space; WR-02 fix incorrectly
  removed it, breaking the substring match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…I fixture

- run.c: set cb=sizeof(STARTUPINFOW) when ppid=0 — EXTENDED_STARTUPINFO_PRESENT
  is not set in that path so Windows validates cb against STARTUPINFOW size (104
  bytes on x64); using sizeof(STARTUPINFOEXW)=112 returned ERROR_INVALID_PARAMETER
  via CALLBACK_ERROR which adaptix-testing never saw as task completion → 60s timeout
- tasks.yaml: replace notepad.exe with ping -n 999 127.0.0.1 — notepad is a GUI
  app that may not function on headless Windows Server CI runners

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ocesses

With bInheritHandle=TRUE, all inheritable beacon handles (including the
Kharon agent's internal BOF output-collection pipes) were inherited by the
spawned child. Long-running processes like ping.exe held those write ends
open, blocking the agent from collecting BOF output — causing ps run to
time out in CI.

No-pipe case: pass bInheritHandle=FALSE (no handle inheritance needed).
Pipe case: create both pipe ends non-inheritable, then explicitly mark only
pipe_write inheritable before CreateProcessW so only the stdout/stderr
redirect handle reaches the child.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… Testing-Kit pin

cmd_buf[32768] (64 KB) on the stack overflows the limited thread stack that
Kharon allocates for BOF execution. The overflow crashes go() before any output
is produced; Kharon emits CALLBACK_ERROR which Adaptix never marks as task-
completed, so adaptix-testing sees a 60 s timeout instead of a failure.

Move cmd_buf to MSVCRT$malloc with a null-check and free at cleanup. Also
bump the Testing-Kit pin from 9fb3d85 to c53a47d (agent path escaping, capture
regex hardening).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… fallback

- ps.axs: replace "int32" with "int" throughout; framework only recognises
  "int", "short", "wstr", "cstr", "bytes" — "int32" caused unknown-type error
  on every command that takes arguments
- run.c: fix sizeof(cmd_buf)/sizeof(WCHAR) computing pointer size (8/2=4)
  instead of buffer capacity; any command ≥4 chars triggered "command too long"
- list.c: buffer all rows with BeaconFormatAlloc/BeaconFormatPrintf and flush
  via a single BeaconOutput instead of one callback per process; fix undefined
  behaviour in GetUserByToken copy loop (compound di++ assignment)
- grep.c: replace bare GetSidSubAuthority/GetSidSubAuthorityCount calls (ADVAPI32
  exports not resolved by BOF loader) with direct SID struct field access to fix
  beacon crash after printing integrity level; fall back to
  PROCESS_QUERY_LIMITED_INFORMATION when full access is denied so token and
  thread info are still shown for processes the agent does not own

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
char buf[4096] in read_pipe_output triggered a compiler stack-probe
(__chkstk_ms) that the BOF loader cannot resolve, crashing the beacon
whenever --pipe was used. Heap-allocate the buffer instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@TGJLS TGJLS merged commit 5cbcc03 into main May 23, 2026
12 checks passed
@TGJLS TGJLS deleted the ps-bof branch May 23, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant