Sliver 中的 OPFOR:纯 Go 的 Sleep 语言与 Aggressor Script(.cna)运行时解析与实战
发布时间:2026/9/25 2:51:15来源:尧图网络
网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载OPFOR 是随 Sliver 仓库一起 vendored 的独立、纯 Go 的 Sleep 语言与 Aggressor Script.cna运行时同一定位既可作为可嵌入的 Go 包也可作为离线opforCLI 使用。本文以vendor/github.com/sliverarmory/opfor/README.md为骨架结合其源码api.go、internal/opfor/与 Sliver 客户端集成层client/command/opfor/完整讲解如何在应用中嵌入 OPFOR、如何用 CLI 校验与执行 CNA 脚本、如何打通 Sleep 与 Go 的双向调用以及它在 Sliver 客户端中承载 CNA 别名alias的实战用法。读完本文你将能够独立完成 OPFOR 的嵌入、脚本校验、别名注册与底层调用链分析。OPFOR 是什么无需 JVM 的 Sleep/CNA 运行时OPFOR 是一个独立的、纯 Go 实现的运行时目标是让 Sleep 语言和 Aggressor Script.cna脚本在不需要 JVM的环境中运行。它把同一套引擎同时提供给两种使用形态可嵌入的 Go 包应用程序可以托管脚本把宿主自身的状态与能力通过显式的 Aggressor Script 扩展与 provider API 注入运行时离线的opforCLI操作员可以离线评估eval、校验check和执行run脚本。从 doc.go 的包级注释可以看到 OPFOR 的完整能力边界实现了解析、编译、执行、可移植的 Sleep 内建函数、脚本生命周期、事件events、钩子hooks与回调callbacks嵌入应用通过Host、ObjectHost等扩展边界提供宿主数据与效果。需要特别强调的是OPFOR 是独立的兼容实现它不包含 Cobalt Strike 客户端或 Team Server 的实现。在 Sliver 项目中OPFOR 以 vendor 依赖的形式存在vendor/github.com/sliverarmory/opfor/并由客户端的client/command/opfor/包深度集成——这是阅读本文时最值得关注的真实落地场景。嵌入 OPFOR三步跑起第一段 Sleep 代码原文档给出的最小嵌入示例完整且可直接运行。它创建一个运行时然后对一段源码字符串求值package main import ( context log github.com/sliverarmory/opfor ) func main() { ctx : context.Background() runtime, err : opfor.New() if err ! nil { log.Fatal(err) } defer runtime.Close(ctx) if _, err : runtime.Eval(ctx, hello.sl, println(hello from OPFOR);); err ! nil { log.Fatal(err) } }这段程序会打印hello from OPFOR。其中涉及两个关键 API 语义Eval(ctx, name, code)将编译与执行合并为一次调用适合一次性求值当程序需要被复用时应改用CompileString或Runtime.Compile得到不可变的Program再交给Runtime.Execute执行。Program是不可变对象可以被多个相互独立的 Runtime 重复加载这一点在 compile.go 的源码注释中有明确说明。println默认写入进程标准输出。如果想要替换进程的三个标准流OPFOR 提供了对应的函数式选项WithStdin、WithStdout与WithStderr。从 runtime.go 的New实现可以看到默认配置下 stdin 为os.Stdin、stdout 为os.Stdout、stderr 为os.Stderr这些选项允许嵌入方把输出重定向到任意io.WriterSliver 客户端正是用这一机制把脚本输出导向自己的控制台。Runtime 构造选项概览源码依据从 runtime.go 中可以整理出以下常用With*选项选项作用源码位置WithStdin / WithStdout / WithStderr替换进程标准流控制脚本 I/O 去向runtime.go#L401-L433WithHost(host Host)安装未解析函数调用的兜底边界Aggressor 实现通常用它承载 Cobalt 专有函数与谓词runtime.go#L438-L446WithObjectHost(host ObjectHost)安装 Java 风格对象语法的宿主边界宿主返回UnsupportedError时 OPFOR 才处理其内置的极小java.lang标量子集runtime.go#L451-L459WithFunction(name, fn)在构造阶段安装或替换一个 Go 原生函数优先级高于同名可移植默认函数runtime.go#L494-L506RegisterFunction(name, fn)运行时阶段安装或替换 Go 原生函数runtime.go#L509-L525WithInstructionLimit(limit)限制单次顶层执行或回调消耗的 VM 指令数0 表示不设限runtime.go#L344-L349WithBindingObserver(observer)安装事件、钩子、命令、别名、菜单等声明的生命周期通知runtime.go#L464-L472WithEnvironment(keyword, kind)注册嵌入方自定义的 Sleep 环境关键字普通、filter、predicateruntime.go#L477-L489值得注意的是 doc.go 中的一句重要警示这些扩展点不是安全沙箱。可移植的 Sleep 文件系统与进程函数默认会执行本地效果除非嵌入方覆盖它们或在外部约束进程。嵌入方必须自行承担这一安全边界。Sleep 与 Go 双向互调WithFunction与Script.CallOPFOR 的核心价值之一是在 Sleep 与 Go 之间建立双向调用。原文档给出了完整可运行的示例package main import ( context fmt log github.com/sliverarmory/opfor ) func main() { ctx : context.Background() runtime, err : opfor.New( opfor.WithFunction(hello_from_go, func(_ context.Context, call opfor.Invocation) (opfor.Value, error) { name : call.Arg(0).String() return opfor.String(hello name from Go), nil }), ) if err ! nil { log.Fatal(err) } defer runtime.Close(ctx) program, err : runtime.CompileString(bridge.sl, sub hello_from_sleep { return hello . $1 . from Sleep; } println(hello_from_go(Sleep)); ) if err ! nil { log.Fatal(err) } script, err : runtime.Load(ctx, program) if err ! nil { log.Fatal(err) } reply, err : script.Call(ctx, hello_from_sleep, opfor.String(Go)) if err ! nil { log.Fatal(err) } fmt.Println(reply.String()) }这个示例展示了两个方向的调用Go → SleepWithFunction把名为hello_from_go的 Go 函数暴露给 Sleep。脚本加载时执行hello_from_go(Sleep)调用call.Arg(0).String()取出第一个参数返回hello Sleep from Go由println打印。Sleep → GoScript.Call从 Go 侧调用脚本内声明的sub hello_from_sleep传入opfor.String(Go)得到hello Go from Sleep。关键的生命周期语义源码见 script.goRuntime.Load执行Program并保留其全局变量、函数、事件、别名、命令与钩子直到返回的Script被卸载Runtime.Close会卸载被保留的脚本及其函数Load传入的参数会填充 Sleep 的ARGV启动数组但不会成为顶层子程序参数——_为空、$1$n在顶层为 nullscript.go#L324-L327注释。opfor CLI安装与四种操作模式安装原文档给出了三种安装路径# 安装最新发布版需要 Go 1.24 或更高 go install github.com/sliverarmory/opfor/cmd/opforlatest # 从源码检出目录安装当前检出 go install ./cmd/opfor # 在仓库根目录构建 make其中make会生成名为opforWindows 下为opfor.exe的二进制其目标定义见 Makefile。四种操作模式./opfor eval 2 2 ./opfor check examples/01-hello.sl ./opfor run examples/01-hello.sl operator ./opfor repleval对表达式求值并打印结果check只编译、不执行用于校验脚本语法run执行脚本后续参数如示例中的operator作为 Sleep 的ARGV传入repl启动交互式 REPL。REPL 在终端中显示彩色的opfor 提示符评估错误以红色显示当输入输出被重定向时提示符会被省略便于面向行的管道pipeline使用。两个重要事实需要牢记不带任何参数运行./opfor会打印完整的命令帮助CLI 是完全离线的解释器它不连接、也不对外部 Aggressor Script 主机进行认证。原文档提到的examples/目录可运行脚本集合与docs/兼容性参考在当前仓库的 vendor 裁剪中未包含读者可直接查阅vendor/github.com/sliverarmory/opfor/api.go中约 3200 行的类型级文档它详细记录了 provider 目录、生命周期契约与兼容性约定。Makefile 中的差分测试目标锦上添花Makefile 揭示了 OPFOR 的兼容性验证手段make test-sleep-java以严格模式运行官方 Sleep 差分测试要求通过OPFOR_SLEEP_JAR指定真实的 Sleep 2.1 JAR测试会校验其固定的 SHA-256 摘要make bench-sleep对全部BenchmarkSleep*基准做冒烟测试make bench-sleep-compare在同一进程内把 OPFOR 与官方 Sleep 2.1 Java 解释器做等量工作负载对比JAR 在测量前被验证。这些目标表明 OPFOR 以官方 Sleep 2.1 行为作为兼容基准而非凭空定义语言语义。范围界定实现 Sleep 与 Aggressor Script而非 JavaOPFOR 的兼容范围需要精确理解。它实现的是Sleep 语言与 Aggressor Script而不是 Java 本身一个**纯 Go 的小型兼容垫片compatibility shim**覆盖了受支持脚本所需的 Java 形态行为包括字符串String、集合Collections、文件File、随机数Random与 UUID。这些在源码中对应objects_portable_*.go一系文件如objects_portable_string_methods.go、objects_portable_random.go、objects_portable_uuid.go等其他对象行为由嵌入应用提供通过ObjectHost边界嵌入方可以为脚本补充任意 Java 风格对象语法Java 序列化是可选兼容支持只服务于显式使用它的脚本正常的嵌入、执行与回调都不需要它。从实现角度看这个范围还意味着文件系统与进程函数默认在本地执行效果Host是扩展边界而非安全沙箱。OPFOR 不猜测 Cobalt Strike 未公开的 BOF 字节封装见api.go中AggressorBOFExtractor的注释绝不本地解析、链接、重定位或执行对象文件这类行为一律交由 provider 实现。在 Sliver 客户端中的实战集成opfor命令OPFOR 在 Sliver 中最有价值的落地是客户端命令集成位于 client/command/opfor/commands.go。Package opfor integrates OPFOR-compatible CNA scripting with the Sliver client——它让客户端可以直接加载 CNA 脚本、把脚本中的 Beacon 别名注册为 Sliver 命令。命令树opfor根命令挂在 implant 菜单SliverCoreHelpGroup与 server 菜单GenericHelpGroup下其子命令为commands.go#L35-L107子命令用途opfor load script.cna执行并保留CNA 的别名、钩子与回调opfor run script.cna [arguments...]执行一次 CNA 并丢弃其注册一次性 runtimeopfor check script.cna只编译、不执行opfor help alias查看已加载 CNA 别名的帮助opfor unload script.cna卸载脚本并吊销其别名opfor list按绝对路径列出已加载脚本根命令opfor [script.cna]本身是便捷入口单个以.cna结尾的参数会被当作load否则第一个 token 被当作 CNA 别名来执行runManagementCommandcommands.go#L110-L129。所有子命令都支持--timeout/-t持久标志默认超时为 10 分钟aliasInvocationTimeout且必须在动态别名名前放置。动态别名机制源码调用链加载脚本后OPFOR 的BindingObserver会把脚本声明的别名发布给 Managermanager.go#L130-L138 的Registered只关心BindingAlias类型。随后syncRootLocked把这些别名动态挂到opfor命名空间下并刻意用opfor alias形式命名以避免与 Sliver 内建命令如cat冲突commands.go#L271-L373。执行别名时的关键链路是runAlias→invokeAlias→runtime.InvokeConsolecommands.go#L395-L481最终通过manager.runtime.InvokeConsole(ctx, opforengine.ConsoleInvocation{ Kind: opforengine.BindingAlias, Name: name, RawInput: rawInput, ParsedArguments: parsedArguments, SessionID: opforengine.String(targetID), })调用前要求存在活动的交互目标Session 或 Beacon否则报opfor: alias ... requires an active target。这解释了为什么 CNA 别名必须面向当前活动目标执行。Manager 常驻运行时与 Sliver Providersmanager.go 的注释点明了集成精髓每个 Sliver 客户端进程持有唯一一个长生命周期long-livedOPFOR Runtime。保持 Runtime 存活至关重要因为 CNA 的别名、钩子与回调都作用域限定在Runtime.Load返回的Script上。newRuntimemanager.go#L101-L125向引擎注入了整套 Sliver 侧 providerWithAggressorBeaconExecutionProvider把beacon_execute_job、beacon_inline_execute等 Beacon 执行任务转译为 Sliver 的 Beacon 任务WithAggressorSessionQueryProvider向脚本提供会话/Beacon 查询能力WithAggressorBeaconTranscriptSink接收脚本产生的 Beacon 转录记录btask 等WithAggressorPromptProvider为脚本的提示prompt提供 UIWithStdout/WithStderr把脚本的println输出重定向到 Sliver 控制台WithBOFPackByteOrder(opforengine.BOFPackLittleEndian)指定 BOF pack 的字节序。provider 的具体实现如executeBeacon、resolveTarget、beacon 任务轮询beaconTaskPollInterval 250ms集中在 providers.go。值得注意的细节是run子命令使用独立的一次性 Runtime不携带 BindingObserver从而保证临时注册不会污染常驻 Managermanager.go#L207-L240。测试语料仓库自带可运行的 CNA 测试语料位于 client/command/opfor/testdata/corpus/bof_collection/cat.cna、firefoxdump/firefoxdump.cna、operatorskit/finddotnet.cna与findsysmon.cna均附LICENSE/NOTICE。这些脚本可配合opfor load/opfor run直接实验。源码级原理从源码到字节码的编译管线OPFOR 的编译过程在 compile.go 的Compile中一目了然拷贝源码source.Data append([]byte(nil), source.Data...)保证Program不可变即使调用方随后复用了输入缓冲区词法解析parser.ParseWithOptions(lexer.Source(source), config.parser)——默认使用CompatibilityOptions()兼容模式也可用WithStrictSyntax()强制要求文档化的逗号/分号分隔符或用WithCompatibilityWarnings()在兼容解析的同时报告被接受的省略分隔符语义编译compiler.Compile(parsed.Script)生成bytecode.Function静态 import 校验validateStaticImports检查静态导入字面量预编译compileProgramLiterals一次性完成数字与字符串字面量的解析转义解码、静态性判定错误挂载在对应 AST 节点上、在运行时相同的执行点抛出从而保持 Sleep 可观察的执行边界。编译失败时返回*CompileError携带Diagnostics非致命兼容性警告则通过Program.Diagnostics()单独暴露。四个顶层执行入口的差异script.go入口语义Runtime.Eval(ctx, name, code)编译并执行一次不保留声明script.go#L496Runtime.Execute(ctx, program, args...)执行已编译的Program参数进ARGVscript.go#L479Runtime.Load(ctx, program, args...)执行并保留声明返回*Script供后续Call/Unloadscript.go#L328Script.Call(ctx, name, args...)调用脚本内声明的sub/inline 声明script.go#L272从Runtime.Load的源码script.go#L341-L359还能看到一个工程细节已关闭的 Runtime 会拒绝加载并返回ErrRuntimeClosed而独立或跨 Runtime 的Program每次加载都会重新计入源码资源预算防止预编译绕过源码限额。结语OPFOR 为 Sleep 语言与 Aggressor Script 提供了一个无需 JVM 的纯 Go 运行时嵌入式 Go APIEval/Compile/Load/Execute/Script.Call支持应用托管脚本与双向互调离线 CLIeval/check/run/repl支持操作员校验与执行typed provider API 则把 Cobalt 专有行为以清晰的生命周期契约交给宿主实现。在 Sliver 中它被深度集成于客户端一个常驻 Runtime 承载 CNA 别名、钩子与回调opfor load/run/check/help/unload/list命令让.cna脚本成为可操作的实战工具。项目采用 Apache License 2.0见 LICENSE对嵌入方与二次开发者都相当友好。赞分享网络安全【免费下载链接】sliverAdversary Emulation Framework项目地址https://gitcode.com/gh_mirrors/sl/sliver点击查看免费下载相关推荐Sliver 客户端运行 CNA/Aggressor 脚本OPFOR 集成指南Sliver 客户端运行 CNA/Aggressor 脚本OPFOR 集成指南 OPFOR 是 Sliver 客户端内的一个 CNA/Aggressor Sc网络安全Sliver OPFOR CNA 测试语料库Corpus深度解析回归固件、许可证与 BOF 调度验证机制Sliver OPFOR CNA 测试语料库Corpus深度解析回归固件、许可证与 BOF 调度验证机制 导读 本文聚焦 Sliver 仓库中 clien网络安全深入解析 go-sqlite纯 Go 实现的 SQLite 驱动与 Sliver 中的实践应用深入解析 go sqlite纯 Go 实现的 SQLite 驱动与 Sliver 中的实践应用 go sqlite 是一个纯 Go 编写的 SQLite 数据网络安全上一篇Modular Monolith DDD日志脱敏5大敏感数据保护实现策略 ️下一篇10分钟上手Machine Learning Q and AI Book配套代码环境搭建指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网