SKILL: Yakit 右键插件 (codec) 使用与编程
AI LOAD INSTRUCTION: codec 插件是 Yakit 里写"小工具"成本最低的方式——只需要一个
handle(input)函数。把插件类型设为codec并打开"用于数据包右键"开关, 它就会出现在 MITM/History/Web Fuzzer 等所有 HTTP 编辑器的右键菜单里, 把当前选中文本作为input传进来, 展示handle的返回值。本页讲 启用方式、编程契约、Web Fuzzer 右键的两层菜单, 以及命令行验证闭环。
0. 相关路由
- 总入口: yak
- 原生插件(yak/mitm) + cli 参数体系: yakit-native-plugin
- 验证工具链: yaklang-toolchain(
yak codec-plugin命令) - 历史数据提取/字典/发包: yakit-data-extract-plugin
- 数据库 / Payload 字典: yaklang-database
- 三类热加载: mitm-hotpatch / webfuzzer-hotpatch / global-hotpatch
1. codec 插件的唯一契约: handle(input)
// 一个最小可用的 codec 插件: 把选中文本 Base64 编码
handle = func(input /* string: 当前选中的文本 */) {
return codec.EncodeBase64(input) // 返回值即右键执行后的结果
}
- 入口函数固定叫
handle, 入参是选中文本 (string), 返回字符串。 - 没有选区时, 传入的是整个编辑器内容。
handle内可以用任意 yaklang 库 (codec/str/json/re/poc...)。- 后端执行就是
handle(input)这一句 (SafeCallYakFunction(ctx, "handle", [input])), CLI 与 Yakit UI 走的是完全相同的执行路径。
2. 怎么让它出现在右键菜单 (关键: 一个 tag)
在 Yakit 插件编辑器里:
- 插件类型选 codec。
- 打开开关 "用于数据包右键"。
这个开关本质是给 YakScript.Tags 加一个 tag:
| 开关文案 | tag 值 | 效果 |
|---|---|---|
| 用于数据包右键 | allow-custom-context-menu-execute |
出现在右键 → 插件扩展 子菜单 (codec 插件) |
| HTTP 数据包变形 | allow-custom-http-packet-mutate |
出现在右键 → HTTP数据包变形 (对整包变形) |
| 单条历史记录处理 | allow-custom-single-history-mutate |
在 History 单条记录右键可用 |
| 多条历史记录处理 | allow-custom-multiple-history-mutate |
在 History 多选记录右键可用 |
编辑器右键时, Yakit 用
QueryYakScript({Type:'codec', Tag:['allow-custom-context-menu-execute']})把所有带该 tag 的 codec 插件拉出来, 注入到 插件扩展 / AI工具 菜单。保存插件后会触发onRefPluginCodecMenu重新加载菜单。
3. 点击后发生了什么 (执行链)
选中文本 → 右键 → 插件扩展 → 你的插件
→ onOpenFuzzerModal { text(选中文本), scriptName }
→ DebugPlugin { Input: text, PluginName, PluginType: 'codec' }
→ 引擎执行脚本并调用 handle(input)
→ 结果流式回显在 "Codec结果" 面板
重要行为差异:
- codec 右键插件的结果展示在面板里 (Codec结果), 不会自动写回编辑器——需要手动复制。
- 内置"编码/解码" (右键 → 编码/解码, 无需插件) 以及 HTTP数据包变形插件
(
allow-custom-http-packet-mutate) 才会直接替换编辑器内容/整包。 - 想要"选中即替换", 在 Web Fuzzer 里用选区浮动菜单的编解码最顺手 (见第 5 节)。
4. 编程技巧 (写出好用的右键插件)
- 保持纯函数:
handle只依赖input, 不读外部状态, 结果可预测、好验证。 - 容错不抛错: 解码失败时返回友好提示字符串, 而不是 panic, 右键体验更好。
handle = func(input) { raw, err = codec.DecodeBase64Url(input) if err != nil { return "decode failed: " + input } return string(raw) } - 善用 codec 库:
EncodeBase64/DecodeBase64/EncodeBase64Url/DecodeBase64Url、UnicodeDecode、 各种 hash/对称加密都在codec里, 多数小工具一行搞定。 - 可逆优先: 编/解码类尽量保证
handle(handle(x))或编解码成对, 方便来回切。 - 先自测再上架: 写
if YAK_MAIN { runSelfTest() }, 用yak file.yak跑断言; 注意handle不要被YAK_MAIN包住 (它要常驻注册), 只把自测放进守卫块。
5. Web Fuzzer 编辑器的右键 (两层菜单)
Web Fuzzer 请求编辑器有两套交互, 都能调用编解码/插件:
A. Monaco 原生右键菜单
右键弹出, 分组包含:
- 编码 / 解码: 内置编解码 (base64/url/hex/...), 选中后可"替换内容"写回编辑器。
- HTTP数据包变形: 调用带
allow-custom-http-packet-mutate的插件, 对整包变形。 - 插件扩展 / AI工具: 调用带
allow-custom-context-menu-execute的 codec 插件 (结果进面板)。 - 插入标签/字典: 插入空字节、临时/Fuzz 字典、
{{yak:dyn(...)}}热加载标签、文件标签等。
B. 选区浮动操作条 (最常用)
选中一段文本时, 旁边浮出操作条:
- 编码: 把选中文字包成 fuzztag 并替换选区, 例如选中
admin→{{base64enc(admin)}}, 发包时由 Fuzzer 引擎渲染。 - 解码 / 智能解码: 走
AutoDecode自动识别编码并解出明文, 点"替换"写回。 - 插件 / AI: 同样把选区文本喂给 codec 插件 (结果进面板)。
经验法则: 想"选中即替换成 fuzztag"用浮动条编码; 想"看一眼解码结果"用浮动条解码/智能解码; 想"跑自己写的复杂转换"用右键插件扩展 → codec 插件。
6. 命令行验证 (与 UI 同款 handle 执行)
# 用 yaklang 源码引擎构建一次
cd /Users/v1ll4n/Projects/yaklang && go build -o /tmp/yak ./common/yak/cmd/yak.go
# 验证 codec 插件: 把 input 喂给 handle, 打印返回值
/tmp/yak codec-plugin --script skills/yakit-rightclick-plugin/examples/codec-base64-wrap.yak --input "admin:123456"
# -> output: YWRtaW46MTIzNDU2
# 也支持从文件读取 input
/tmp/yak codec-plugin --script x.yak --input-file /tmp/payload.bin
yak codec-plugin 与 Yakit 右键执行同样调用 handle(input), 所以"命令跑通 == 右键也能跑通"。
7. History 右键: input 是 flow id, 不是文本(重要契约)
普通 HTTP 编辑器右键时 input 是选中文本; 但在 History 表格行右键 跑 codec 插件时,
Yakit 传进来的 input 是这条记录的 flow id(已核实, 见 yakit 源码
components/HTTPFlowTable/useHTTPFlowTableContextMenu.tsx):
| 场景 | tag(开关) | 传入 handle 的 input |
|---|---|---|
| History 单条右键 | allow-custom-single-history-mutate(用于history右键(单选)) |
单个 id, 如 "1287" |
| History 多选右键 | allow-custom-multiple-history-mutate(用于history右键(多选)) |
逗号拼接 id, 如 "1287,1290,1305" |
前端在
onOpenFuzzerModal事件里: 单选text = \${rowData.Id}`, 多选text = selectedRowKeys.join(','), 同时携带脚本声明的params`(cli 参数)。
拿到 id 后, 用 db 的 Yield 通道反查数据包、再做提取:
handle = func(input) {
ids = parseIds(input) // "1287,1290" -> [1287, 1290]
for flow in db.QueryHTTPFlowsByID(ids...) { // Yield 通道, 底层 yakit.YieldHTTPFlows
host = hostOf(flow.Url)
for f in db.QueryHTTPFlowsByKeyword(host) { // 该 host 的全部历史流量
req = codec.StrconvUnquote(f.Request)~ // flow.Request 是 quote 后的, 需还原
// ... 提取 path / 参数名 / cookie
}
}
}
进度条: yakit.SetProgress
提取量大时, 用 yakit.SetProgress(0~1) 在执行面板显示进度(SetProgressEx(id, f) 可多条):
total = len(hosts); done = 0
for host in hosts {
yakit.SetProgress(float(done) / float(total)) // 注意 yak 的 for i,v in slice 给的是(元素,nil), 索引要自己数
// ... 提取
done++
}
yakit.SetProgress(1.0)
完整可运行: examples/codec-history-extract-paths.yak (
yak <file>自测纯逻辑;--real <id>用真实 History 跑 flow id -> 数据包 -> 提取全链路)。
8. codec 插件 + cli 参数(让用户选"提取什么")
codec 插件也能声明 cli 参数: History 右键时 Yakit 会先弹出参数表单, 用户填完再执行 handle。
用 cli.StringSlice + setSelectOption + setMultipleSelect(false) 做一个"提取项"下拉:
MODE_SEL = cli.StringSlice("mode",
cli.setVerboseName("提取项"),
cli.setSelectOption("提取 Path", "path"),
cli.setSelectOption("提取 参数名", "param"),
cli.setSelectOption("提取 Cookie", "cookie"),
cli.setMultipleSelect(false), cli.setDefault("path"))
cli.check()
MODE = "path"
if len(MODE_SEL) > 0 { MODE = MODE_SEL[0] }
handle = func(input) { // input 仍是 flow id; MODE 由表单决定提取什么
// ... 按 MODE 提取, 可选 db.SavePayload 落字典, db.YieldPayload 回读复用
}
完整可运行: examples/codec-cli-extract-selector.yak (三种模式自测 + Payload 字典
SavePayload/YieldPayloadroundtrip;--real <id> --mode param跑真实库)。 cli 参数体系详见 yakit-native-plugin。
9. Web Fuzzer 选区做"奇怪变换"
在 Web Fuzzer 选中一段(如 payload), 右键 -> 插件扩展 -> codec 插件, input 是选中文本,
可做任意变换返回。一个实用例子: 逐字符混合编码(url %xx / html &#d; / unicode \uXXXX / 原文交替),
得到"语义等价但形态怪异"的变体探测/绕过简单 WAF:
handle = func(input) { return weirdTransform(input) } // "admin" -> a%64m\u0069n
完整可运行: examples/codec-fuzzer-transform.yak。 选区"编码成 fuzztag 并替换"用浮动条编码(见第 5 节 B); 跑复杂变换用右键插件扩展。 提示:
log.info是 printf 风格, 打印含%的内容要用log.info("%s", x)占位, 不能直接字符串拼接。
10. 示例 (examples/)
| 文件 | handle 作用 | input | 验证 |
|---|---|---|---|
| codec-base64-wrap.yak | 选中文本 Base64 编码 | 选中文本 | --input "admin:123456" |
| codec-jwt-decode.yak | JWT 一键解码 header+payload | 选中文本 | --input "eyJ...header.eyJ...payload.sig" |
| codec-unicode-unescape.yak | \uXXXX 还原中文 |
选中文本 | --input '\u4e2d\u6587abc' |
| codec-timestamp-to-date.yak | Unix 时间戳转 UTC 日期 | 选中文本 | --input "1700000000" |
| codec-history-extract-paths.yak | flow id 反查包 -> 提取 host 全部 path/参数名(进度条) | flow id | yak <file> / --real 1 |
| codec-cli-extract-selector.yak | codec + cli 下拉选提取 Path/参数名/Cookie + 落字典 | flow id + cli | yak <file> / --real 1 --mode param |
| codec-fuzzer-transform.yak | Web Fuzzer 选区逐字符混合编码变换 | 选中文本 | --input "<script>alert(1)</script>" |
每个示例都带 YAK_MAIN 自测块, yak <file>.yak 即可自证; 编辑器选区类示例能被
yak codec-plugin 命令复现证据。History 提取类示例用合成数据自测纯逻辑, --real <id> 可跑真实库链路。