Yakit 热加载(HotPatch)是 yaklang 平台中一个强大的功能,允许用户在运行时动态加载和执行 Yak 脚本代码,无需重启应用程序。这个功能在 MITM(中间人攻击)代理、Web 模糊测试和安全测试场景中特别有用,能够让安全研究人员快速调试和修改测试逻辑。
热加载的核心概念是将用户编写的 Yak 代码动态编译并注入到正在运行的系统中,实现实时的功能扩展和修改。这种机制使得 Yakit 具有了极高的灵活性和可扩展性。
热加载功能主要由以下几个核心组件构成:
热加载的核心处理逻辑在 buildHotpatchHandler
函数中实现。这个函数接收用户的 Yak 代码,创建一个新的脚本引擎来执行代码,并返回一个处理函数。
处理流程包括:
在 MITM 代理中,热加载功能通过 LoadHotPatch
方法实现:
这个方法执行以下步骤:
在 MITM 服务中,热加载脚本的加载过程如下:
当检测到热加载脚本内容时,系统会:
LoadHotPatch
方法加载脚本热加载功能也集成到了模糊测试的 FuzzTag 系统中:
这里定义了两种热加载 FuzzTag:
yak
:标准热加载标签yak:dyn
:动态热加载标签系统提供了多种热加载配置选项:
Fuzz_WithHotPatch
:添加标准热加载支持Fuzz_WithDynHotPatch
:添加动态热加载支持Fuzz_WithAllHotPatch
:同时添加两种热加载支持Yakit 提供了完整的热加载模板管理系统:
模板管理功能包括:
系统支持灵活的模板更新操作,可以单独更新模板的名称、内容或类型。
以下是一个简单的热加载脚本示例:
// 定义一个处理函数
handle = func(params) {
// 解析参数
if params == "" {
return ["default_value"]
}
// 处理逻辑
result = str.ToUpper(params)
return [result]
}
// 定义一个带回调的处理函数
handleWithCallback = func(params, yield) {
values = str.Split(params, ",")
for value in values {
yield(str.Trim(value))
}
}
在 MITM 场景中的热加载脚本:
// HTTP 请求劫持
hijackHTTPRequest = func(isHttps, url, reqInstance, forward, drop) {
// 修改请求头
reqInstance.Header["X-Custom-Header"] = ["HotPatch-Modified"]
// 记录请求信息
log.info("劫持请求: %s", url)
// 转发请求
forward(reqInstance)
}
// HTTP 响应劫持
hijackHTTPResponse = func(isHttps, url, rspInstance, forward, drop) {
// 修改响应内容
body = rspInstance.GetBody()
if str.Contains(body, "error") {
log.warn("检测到错误响应: %s", url)
}
// 转发响应
forward(rspInstance)
}
在模糊测试中使用热加载:
// 自定义 payload 生成器
generatePayload = func(params) {
base = params
if base == "" {
base = "test"
}
payloads = []
// 生成 SQL 注入 payload
payloads = append(payloads, base + "' OR 1=1--")
payloads = append(payloads, base + "'; DROP TABLE users;--")
// 生成 XSS payload
payloads = append(payloads, base + "")
payloads = append(payloads, base + "javascript:alert('xss')")
return payloads
}
// 动态 payload 生成器
dynamicGenerator = func(params, yield) {
for i = 0; i < 10; i++ {
payload = sprintf("%s_%d", params, i)
yield(payload)
}
}
在热加载脚本中,始终要包含适当的错误处理:
handle = func(params) {
try {
// 主要逻辑
result = processData(params)
return [result]
} catch err {
log.error("热加载脚本错误: %v", err)
return [params] // 返回原始值作为后备
}
}
热加载脚本应该保持轻量级,避免耗时操作:
// 好的做法:快速处理
handle = func(params) {
return [str.ToUpper(params)]
}
// 避免:耗时操作
handle = func(params) {
// 避免在热加载中进行网络请求或文件 I/O
// time.Sleep(5 * time.Second) // 不要这样做
return [params]
}
使用全局变量来维护状态:
// 全局计数器
counter = 0
handle = func(params) {
counter++
result = sprintf("%s_%d", params, counter)
return [result]
}
使用日志来调试热加载脚本:
handle = func(params) {
log.info("热加载输入: %s", params)
result = processData(params)
log.info("热加载输出: %s", result)
return [result]
}
问题:热加载脚本报告函数未找到
解决方案:
问题:传递给热加载函数的参数类型不匹配
解决方案:
问题:热加载脚本执行缓慢
解决方案:
问题:长时间运行后内存使用增加
解决方案:
热加载支持动态注册多个处理函数:
// 注册多个处理器
handlers = {
"encode": func(params) {
return [codec.EncodeBase64(params)]
},
"decode": func(params) {
return [codec.DecodeBase64(params)]
},
"hash": func(params) {
return [codec.Md5(params)]
}
}
// 主处理函数
handle = func(params) {
parts = str.Split(params, "|")
if len(parts) < 2 {
return [params]
}
action = parts[0]
data = parts[1]
if handler, ok = handlers[action]; ok {
return handler(data)
}
return [params]
}
使用配置文件驱动热加载行为:
// 配置对象
config = {
"enabled": true,
"debug": false,
"max_length": 1000,
"allowed_patterns": ["*.php", "*.jsp"]
}
handle = func(params) {
if !config["enabled"] {
return [params]
}
if config["debug"] {
log.info("处理参数: %s", params)
}
if len(params) > config["max_length"] {
return [params[:config["max_length"]]]
}
return [processParams(params)]
}
实现插件化的热加载架构:
// 插件接口
plugins = []
// 注册插件
registerPlugin = func(name, handler) {
plugin = {
"name": name,
"handler": handler
}
plugins = append(plugins, plugin)
}
// 执行所有插件
handle = func(params) {
result = params
for plugin in plugins {
result = plugin["handler"](result)[0]
}
return [result]
}
// 注册具体插件
registerPlugin("uppercase", func(data) {
return [str.ToUpper(data)]
})
registerPlugin("trim", func(data) {
return [str.Trim(data)]
})
Yakit 热加载功能为安全测试提供了强大的动态扩展能力。通过理解其技术架构、掌握使用方法和遵循最佳实践,您可以充分利用这个功能来提高测试效率和灵活性。
热加载的主要优势包括:
随着对热加载功能的深入使用,您将发现它在复杂安全测试场景中的巨大价值。建议从简单的示例开始,逐步掌握高级特性,最终能够构建出符合特定需求的定制化测试解决方案。