面向网络安全工作的脚本语言
用 Go 实现,单文件 ysc 即可运行,无需编译环境。
把「协议解析 / 抓包分析 / 密码学运算 / 漏洞验证」这类工作从零散的 Shell 命令,
变成可读、可复用、可版本管理的脚本。
$ cat hello.ys
package main
func main() {
println("Hello, YScript!")
}
$ ysc hello.ys
Hello, YScript!
📦 本仓库为发布仓库:只包含可直接阅读、运行的示例脚本与测试套件。 解释器源码与完整中文手册在开发仓库中维护(见文末仓库定位)。
YScript 是一门为安全工作者设计的动态类型脚本语言。它不是通用语言的替代品, 而是补上了安全工具链里长期缺失的一环:用一门真正的编程语言,去表达安全工作中 那些「结构复杂、逻辑密集、步骤繁琐」的任务。
传统做法通常有三种,都不太舒服:
| 做法 | 问题 |
|---|---|
| 拼 Shell 命令 | 解析二进制、状态分支、错误处理都很别扭;bash 处理结构化数据能力弱 |
| 写 Python 脚本 | 环境依赖多(scapy/cryptography 等)、分发困难、启动开销大 |
| 写 C / Go 工具 | 一个几十行的协议解析工具要编译、要建工程,改一行就要重新构建 |
YScript 把这些痛点合并成一个答案:语法接近 Python 的脚本体验, 能力接近 Go 的标准库。
// 1. 协议解析 —— binary 命名空间 100 个成员,大小端/流抽象一应俱全
let head = binary.BigEndian().PutUint16(80) // b"\x00\x50"
let port = binary.Uint16(head) // 80
// 2. 网络通信 —— socket 统一 TCP/UDP/TLS,一个对象三种协议
let s = socket.Socket("tcp")
s.connect("192.168.1.1", 22)
s.set_timeout(3000)
let banner = s.recv_line() // SSH banner
println(binary.UTF8(banner))
// 3. 密码学 —— 45 个 crypto 函数,含 WPA2 全套
let pmk = crypto.WPA2_PMK(passphrase, ssid)
crypto.WPA2_MIC_Verify(pmk, eapol, mic)
// 4. 抓包取证 —— raw + pcap,直接读原始帧
let h = raw.PcapOpen("eth0", "tcp port 80")
let pkt = raw.PcapNext(h) // 实时抓包(libpcap)
let rec = raw.PcapReadFile("cap.pcap") // 或纯 Go 解析文件
// 5. Shell 与系统 —— 反引号即命令,进程/路径/定时器一应俱全
let out = `nmap -sV -p 22,80,443 {target}`
os.exec("aircrack-ng", ["-b", "wpa.cap"])
// 异常:一行兜底,不用写 try/catch 样板代码
let n = parse_int(user_input) catch 0
// 带类型异常分流:连接失败和超时分开处理
catch {
socket.Socket("tcp").connect(host, port)
} match {
ConnectError(e) -> log.error("连接失败: " + e)
TimeoutError(e) -> log.error("超时: " + e)
_ -> log.error("其它错误")
} ensure {
cleanup() // 无论成败都执行
}
// 继承多态:把「基类 - 子类 - 子类」写成清晰的层次
struct Animal {
name: string
}
func this.speak() -> string { return "..." }
struct Dog extends Animal {
breed: string
}
func this.speak() -> string { return "汪汪" }
func this.describe() -> string { return super.describe() + ",是只" + this.breed }
仓库中的示例都是真实场景,可直接阅读:
| 场景 | 文件 |
|---|---|
| WPA2 握手包破解(PMK/PTK 派生 + MIC 校验,支持 GPU 加速) | test/wifi_crack.ys |
| WPA2 破解流程(英文版 / 结果校验) | test/wifi_verify.ys |
| WPA2 字典破解性能测试 | test/wifi_speed.ys |
| 服务端口扫描与指纹识别 | service_scanner.ys |
| 网络协议与 socket 实战 | test/net_ext.ys |
| 二进制格式解析与修补 | test/binary_lib.ys |
安全工具需要执行危险操作,YScript 把权限控制做成语言层面的显式声明:
#!permit file_read:/etc/*, exec:nmap, network
脚本声明所需权限,未授权的操作会被拦截;也可通过命令行策略覆盖。 详见《39 预处理器指令》。
| 能力 | 说明 |
|---|---|
| 密码学 | crypto 45 个函数:SHA/SHA3/SHAKE、AES-GCM、ChaCha20、HMAC、PBKDF2、ECDSA、Ed25519、WPA2 全套 |
| 协议分析 | binary 100 个函数:大小端编解码、字节容器、流抽象、等长安全修补 |
| 网络 | socket 统一 TCP/UDP/TLS 对象;net 底层连接;raw 原始帧 + pcap 抓包;ssl 证书与 TLS |
| 系统与 Shell | 反引号直接执行命令;os 进程管理、sys 系统信息、path 路径处理 |
| 并发 | warp 轻量协程 + sync 全套同步原语(Mutex/RWMutex/Chan/TLS/WaitGroup/Once/WorkerPool) |
| 异常 | expr catch h 一行兜底、catch {} match { T(e) -> } 按类型分流、ensure 保证清理 |
| 面向对象 | struct 方法、接口多态、继承多态(extends + super)、泛型函数/结构体、类型推导与约束 |
| GPU 加速 | cuda 命名空间支持密码学运算批处理,无 GPU 时自动降级 CPU |
| FFI | ffi 真实 ABI 调用动态库(0-8 参数,int/string/void 返回) |
| 工程 | 预处理器、REPL 会话、.ybc 字节码落盘、中英双语 i18n |
本仓库不含可执行文件,需先安装 ysc(YScript 解释器):
ysc hello.ys # 执行脚本
ysc # 无参数 = 进入 REPL 会话
ysc -e 'println(1+1)' # 直接执行一段代码
ysc -c hello.ys # 仅做语法检查,不执行
ysc --emit a.ybc x.ys # 导出字节码
ysc pkg -target linux/amd64 hello.ys # 打包为独立 Linux 可执行程序
ysc test ./test/... # 运行 *_test.ys 测试
ysc fmt ./src/... # 递归格式化 YScript 源文件
ysc fmt -check . # 检查当前目录格式
cat app.ys | ysc fmt -t # 从 stdin 读入并向 stdout 输出,不修改文件
ysc mod init myapp # 初始化项目清单
ysc mod get https://github.com/org/lib.git v1.0.0 # 版本参数可省略,默认锁定当前 HEAD
ysc mod sync # 按 import 同步、校验及清理依赖
ysc doc # 查看当前目录包文档
ysc doc ./pkg Serve # 查看本地包的符号文档
ysc doc Serve # 搜索当前项目和全局库中的符号
ysc doc thirdparty/pkg # 查看全局库仓库中的第三方包
ysc doc net # 查看 ysc 内置标准库命名空间及成员
ysc doc net.LookupHost # 查看内置标准库成员
ysc -h # 查看全部选项.ybc 是可由 ysc 直接执行的编译后字节码文件:文件采用带魔数、格式版本、解压长度与校验和的二进制容器,并压缩保存编译 IR。ysc --emit 与 ysc pkg --ybc 都输出此格式;ysc program.ybc 可直接加载运行。当前运行时也能读取旧版 JSON .ybc,但新编译一律输出二进制格式。ysc pkg 将入口脚本编译为优化后的 .ybc,嵌入 Go 运行时并调用 Go 工具链生成目标平台的独立可执行文件。使用 ysc pkg --ybc 时只生成优化后的 .ybc 中间字节码,不会构建可执行文件,也不需要 Go 构建运行时。普通脚本只打包指定入口及其静态 import 闭包;传入含 ysc.models 的项目目录时,会扫描项目 .ys 文件并自动选择唯一声明 main() 的文件作为入口,不要求文件名为 main.ys。如果项目中有多个 main(),可显式传入入口文件。项目打包会递归同步校验项目及依赖库锁定的依赖,再将入口依赖的项目源码合并进字节码,因此这些静态依赖不需要运行时访问项目文件或依赖缓存。通过 load.load / load.install 等动态路径加载的脚本或模块不会自动嵌入,仍需按程序使用的路径部署。打包时按 import 生成标准库注册表和运行时桥接,仅链接引用的标准库模块;未使用的 eval/动态加载编译器、调试器及源码行映射也会移除。Go 运行时源码已随 ysc 内嵌,无需单独安装或定位 YScript Go 源码仓库;构建独立可执行文件时仍需 Go 工具链和 Go 模块依赖(可联网下载或使用本地缓存)。默认关闭 cgo 以便交叉编译;本机依赖已安装时可用 -cgo 启用,交叉 cgo 还需自行配置目标 C 编译器和目标依赖库。
ysc pkg hello.ys
ysc pkg --ybc hello.ys # 只生成优化后的 hello.ybc
ysc pkg -target linux/amd64 hello.ys
ysc pkg -target linux/amd64 -target windows/amd64 -o dist/hello hello.ys
ysc pkg ./my-project # 自动选择项目中声明 main() 的唯一 .ys 文件
ysc pkg -cgo hello.ys # 启用本机 cgo 能力默认产物写入当前工作目录:单目标时使用脚本名,项目打包时使用 ysc.models 声明的名称;Windows 目标会添加 .exe。指定多个目标时会追加系统和架构后缀以避免覆盖。目标支持 Linux、Windows、macOS(darwin)、FreeBSD、OpenBSD、NetBSD、DragonFly、Solaris 和 illumos,以及 386、amd64、arm、arm64。详情见独立程序打包指南。
--ybc 默认写出 <脚本名或项目名>.ybc 到当前工作目录;可用 -o 指定路径(未写扩展名时自动补 .ybc)。该模式不能与 -target 或 -cgo 同时使用。
ysc doc 按 Go Doc 的包 / 符号查询方式读取 .ys 源码注释。无参数时查看当前目录;传入本地目录或源文件路径可查看本地项目,传入 项目名/包路径 可查看 ~/.ysc/pkg(或 YSC_PKG_DIR)中的第三方库。符号查询可写成 ysc doc <包> <符号>,也可在当前目录使用 ysc doc <符号>。传入 ysc 内置标准库名(如 net、json)可查看命名空间成员;使用 ysc doc net.LookupHost 可查看单个成员。
ysc doc
ysc doc ./src/http
ysc doc mylib/http Client
ysc doc -all ./src/http # 兼容选项;默认已列出全部声明
ysc doc -src ./src/http Get # 显示该符号的源码# 行注释或 #* ... *# 块注释紧邻包声明或符号声明时,会作为文档显示;函数注释也可放在文件任意位置,用 @函数名 HEAD 与 @函数名 END 标记,再由 ysc doc 按函数名关联。注解文档优先于紧邻注释,标记及区块内容不会影响脚本执行。YScript 不以首字母大小写区分导出符号,包概览默认展示所有函数(包括小写函数和 main);-all 为兼容选项。ysc doc -h 查看选项用法。
@banner_text HEAD
# 将服务 Banner 字节转换为可读文本。
@banner_text END
func banner_text(data) {
return string(data)
}
测试文件使用 *_test.ys 命名,并在 package main 中声明 Test 开头、后接大写字母或数字的函数。测试函数可不带参数,也可接收测试上下文 t。测试命令的帮助、选项错误、测试发现/编译错误、测试上下文 API 错误和运行摘要会按当前 locale 显示中文或英文;测试名称、测试日志与断言消息保留脚本原文:
func TestAddition(t) {
if 1 + 1 != 2 {
t.Errorf("1 + 1 = %d, want 2", 1 + 1)
}
}
测试上下文提供 Error / Errorf、Fatal / Fatalf、Fail / FailNow、Failed、Log / Logf、Skip / SkipNow、Skipped、Name、Cleanup、TempDir 和 Run 子测试。运行器按包目录合并测试文件、编译并在同一 VM 中执行测试函数;测试文件不要声明 main()。
ysc test ./test/... # 递归发现并运行测试
ysc test -v ./test/generic # 显示名称、日志和通过结果
ysc test -run 'TestGenericCollections' ./test/generic
ysc test -count=3 ./test/generic # 重复运行
ysc test -list 'TestGeneric' ./test/generic # 列出匹配测试
ysc test ./test/generic/generic_test.ys # 指定测试文件当传入目录时,只发现该目录直接包含的 *_test.ys;... 表示递归包目录。-run 和 -list 接受 Go 风格正则表达式。测试失败会返回非零退出码。
package main
func main() {
let host = "192.168.1.1"
let port = 22
// 探测端口是否开放:一行即可,失败自动返回 false
if socket.tcp_probe(host, port) {
println("SSH 端口开放")
// 连上去读 banner
let s = socket.Socket("tcp")
s.connect(host, port)
s.set_timeout(3000)
println(binary.UTF8(s.recv_line()))
s.close()
} else {
println("端口关闭或被过滤")
}
// 解析失败不中断脚本:一行兜底
let n = parse_int("abc") catch 0
println("解析失败取默认值: " + string(n))
}
对照一下,同样的事如果用 Shell 写,需要 nc/timeout/grep 串联,
且无法优雅地区分「连接被拒」和「超时」——YScript 里这只是一个 match。
service_scanner.ys — 服务端口扫描器
一个可读的完整实战示例:init_services() 建立端口 → 服务名映射表,main() 负责交互和扫描,运行时结合 socket 与 Shell 命令完成端口探测。
func init_services() {
let services = {}
services.set(21, "FTP")
services.set(22, "SSH")
services.set(80, "HTTP")
services.set(443, "HTTPS")
# ... 共 100+ 常用服务
return services
}
func main(args) {
let services = init_services()
# 交互及扫描逻辑
}
test/ 下的脚本既是回归测试,也是按特性组织的语法示例,可直接阅读学习。
| 特性 | 文件 |
|---|---|
| 入口 / 总览 | main.ys · comprehensive.ys · features.ys |
| 继承多态 | poly.ys |
| 异常捕获 | exception_catch.ys · error_codes.ys · safe.ys |
| 切片 | slice.ys |
| 二进制 | binary_lib.ys · memory.ys |
| 网络 | net_ext.ys |
| 并发 | sync.ys |
| 面向对象 | struct.ys · interface.ys · enum.ys · generics.ys |
| 集合 / 迭代 | rf2.ys |
| 文件与系统 | sys.ys · shell_ext.ys · io_ext.ys |
| 文本 / 时间 | time_lib.ys |
| 配置解析 | toml_ext.ys · ini_ext.ys · yaml_ext.ys |
| WiFi 实战 | wifi_crack.ys · wifi_verify.ys · wifi_speed.ys |
继承多态(test/poly.ys)—— Java 风格的 extends / 方法重写 / super 复用:
struct Animal {
name: string
}
func this.speak() -> string { return "..." }
func this.describe() -> string { return "我是 " + this.name }
struct Dog extends Animal {
breed: string
}
func this.speak() -> string { return "汪汪" }
func this.describe() -> string {
return super.describe() + ",是只" + this.breed
}
带类型异常分流(test/exception_catch.ys)—— match 按异常类型分支,ensure 保证清理:
func scan_host(host: string) {
catch {
let sock = connect(host)
println(" 已连接 " + sock)
} match {
NetError(e) -> println(" 连接失败: " + e),
TimeoutError(e) -> println(" 超时: " + e),
} ensure {
println(" 释放资源 " + host)
}
}
ensure在未命中match(异常继续透传)时也会执行, 因此异常无论被谁捕获,资源都一定被释放。
一行兜底:
let n = parse_int("abc") catch 0 // 失败取默认值
let s = connect(host, port) catch return // 失败即返回
40+ 命名空间,按用途分组:
| 分组 | 命名空间 |
|---|---|
| 内建转换 | hex alpha alnum ascii errors |
| 文本 | strings encoding json regex color array |
| 字节流 | binary |
| 文件 / IO | io path stdio os load |
| 序列化 | csv xml yaml toml ini |
| 网络 | socket net raw ssl http url |
| 加密 | crypto aes rsa hash |
| 并发 | thread sync event |
| 系统 | sys time rand log |
| 其它 | ffi cuda reflect iter from compress |
load 支持运行时动态管理 YScript 代码:load.load / load.reload 处理普通函数插件;load.install、load.start、load.stop、load.uninstall 管理可包含全局变量、类型和模块入口的完整脚本模块。完整说明见 doc/YScript.wiki/54_热加载与热插拔.md。
event.wait() 可在 VM 主执行线程等待宿主投递的回调;收到通知后,VM 会在安全点执行回调。event.wait(timeout_ms) 可设置毫秒超时,并在超时返回 false、收到通知时返回 true。该接口用于嵌入式宿主事件循环;普通 CLI 不会自行产生 GUI 事件,且 queued callback 内不能再次调用 event.wait()。
Go 宿主可导入 yscript/host 编译并运行脚本。Bind 将宿主后端操作注册为脚本可调用函数,Post 则按顶层函数名安全投递事件回调;回调参数在入队前转换并复制:
package main
import (
"context"
"yscript/host"
)
func run(source string) error {
script, err := host.New(source, host.Options{Filename: "ui.ys"})
if err != nil { return err }
defer script.Close()
ready := make(chan struct{})
if err := script.Bind("host_ready", func([]any) (any, error) {
close(ready)
return nil, nil
}); err != nil { return err }
runDone := make(chan error, 1)
go func() { runDone <- script.Run() }()
<-ready
task, err := script.Post("on_click", 12, 30)
if err != nil { return err }
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if _, err := task.Wait(ctx); err != nil { return err }
return <-runDone
}每个进程当前只能有一个活动 Host(VM 的标准库桥接回调仍是进程级状态);关闭它后才能创建另一个。Bind 必须在 Run 前完成;Run 和 RunEventLoop 必须串行调用,Post 可从宿主的其他 goroutine 调用。需要生命周期取消时使用 RunContext(ctx);它会在字节码指令边界响应取消,也会解除阻塞的 event.wait()。已经进入的 Go 绑定函数需由该函数自身配合取消;Close 会取消脚本执行、阻止新任务、失败尚在队列中的任务,并唤醒等待。
host.Options 还可注入 Stdin、Stdout、Stderr,分别供 YScript 的 io.Stdin()、io.Stdout()、io.Stderr() 使用;未提供时使用进程标准流。绑定的 Go 函数在 VM 执行线程同步调用;若 GUI 工具包要求固定 UI 线程,宿主函数应将操作转发到该工具包的线程,并通过 Post 把完成事件送回 VM。宿主仍需提供真正的窗口、绘制和输入后端。
// 大小端编解码
binary.BigEndian()
let head = binary.PutUint16(80) // b"\x00\x50"
let port = binary.Uint16(b"\x00\x50") // 80
// 流抽象
let w = binary.NewWriter()
binary.WriteString(w, "hello")
binary.CloseWriter(w)
// 二进制修补(等长覆盖,防损坏)
let off = binary.Index(data, binary.FromUTF8("Hello Word!"))
let out = binary.PatchEqual(data, off, binary.FromUTF8("Hello Word!"),
binary.FromUTF8("Hello PATCH"))
⚠️ Go 把字符串常量无分隔符紧密排列。用Replace做变长替换会移动其后 所有数据 → 运行期 SIGSEGV,且readelf检查不出来。PatchEqual在写入前 强制等长 + 偏移合法 + 内容匹配三重校验。
// TCP 服务端
let srv = socket.Socket("tcp")
let port = srv.listen(8080) # 0 = 由内核分配
let cli = srv.accept() # 返回新的 Socket
cli.send("hello\n")
println(binary.UTF8(cli.recv_line()))
cli.close()
srv.close()
// UDP 请求/响应
let us = socket.Socket("udp")
let up = us.bind("0.0.0.0", 9000)
let uc = socket.Socket("udp")
uc.connect_udp("127.0.0.1", up) # 先固定源端口
uc.send("ping")
let r = uc.recvfrom(2048) # [数据, 来源地址]
// 一行便捷函数
let resp = binary.UTF8(socket.tcp_request("127.0.0.1", 80, "GET / HTTP/1.1\r\n\r\n"))
if socket.tcp_probe("192.168.1.1", 22) { println("SSH 开放") }
失败抛出带类型异常,可按类型分流:
catch {
let s = socket.Socket("tcp")
s.connect(host, port)
} match {
ConnectError(e) -> println("连接失败: " + e)
TimeoutError(e) -> println("超时: " + e)
TLSError(e) -> println("TLS 失败: " + e)
SocketError(e) -> println("其它: " + e)
}
📖 在线阅读(推荐):YScript Wiki → —— 共 67 篇核心中文手册,可直接浏览与搜索;测试用例编写和 ysc test 命令详见测试框架指南,ysc fmt 用法详见代码格式化指南,ysc doc 用法详见文档查询指南,白名单 native 调用详见lstd 指南。
完整手册同时以 Markdown 形式存放于开发仓库的 doc/ 目录(含按命名空间分类的函数速查表:45 命名空间 + 6 类型 / 979 个命名空间成员 + 113 个类型方法):
| 主题 | 章节 |
|---|---|
| 语言基础 | 01 类型系统 · 01a 类型转换 · 02 变量与常量 · 03 流程控制 · 04 函数 · 04a 运算符 · 05 关键字速查 |
| 数据结构 | 06 字符串操作 · 07 字节序列 · 08 集合类型 · 09 数组 · 44 切片 |
| 面向对象 | 10 结构体与方法 · 11 接口 · 46 继承与多态 |
| 异常 | 18 错误处理 · 48 异常捕获 |
| 并发 | 27 并发 · 28 warp 协程 · 29 同步原语 · 47 协程与互斥锁 |
| 字节流 | 20 字节流处理 |
| 网络 | 30 网络通信 · 45 TCP 与 UDP · 31 raw 原始帧 · 32 SSL |
| 系统 | 16 文件与路径 · 25 系统与进程 · 37 日志库 |
| Native 互操作 | 35 FFI 动态库 · 60 lstd 白名单动态库调用 |
| 速查 | 00 实现状态 · 05 关键字速查 · 函数速查表 |
编辑器支持:vim 插件 · VSCode 插件(语法高亮 / LSP 智能补全 / 悬停文档 / 跳转定义)。
| 仓库 | 用途 | 内容 |
|---|---|---|
| YScript(本仓库) | 发布仓库 | 示例脚本、测试套件、许可证 |
| 开发仓库 | 源码维护 | Go 解释器源码、doc/ 完整手册、vim/ vscode/ 插件、Makefile 构建 |
本仓库通过 .gitignore 采用「默认忽略 + 白名单」策略(/* 之后仅放行
service_scanner.ys、test/**、README*),确保发布内容干净可控。
当前版本 v0.2.2。完整变更记录见开发仓库的 doc/Log.md。
Apache-2.0,见 LICENSE。