ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Zeek 分析器框架内部接口解析:深入 `Analyzer::__` 系列 BIF 函数与动态协议分析机制

Zeek 分析器框架内部接口解析:深入 `Analyzer::__` 系列 BIF 函数与动态协议分析机制 网络安全网络IDS【免费下载链接】zeekZeek is a powerful network analysis framework that is much different from the typical IDS you may know.项目地址https://gitcode.com/gh_mirrors/ze/zeek点击查看免费下载导读本文围绕 Zeek 仓库中base/bif/analyzer.bif.zeek这一自动生成的脚本接口文档系统讲解分析器框架Analyzer Framework的 8 个内部 BIF 函数__enable_analyzer、__disable_analyzer、__disable_all_analyzers、__register_for_port、__schedule_analyzer、__name、__tag、__has_tag及其背后的 C 实现。读者将掌握 Zeek 如何动态启用/禁用协议分析器、如何为分析器注册知名端口、如何为即将到来的连接预调度分析器以及这些内部函数如何被Analyzer脚本框架封装成可直接调用的公共接口。一、背景分析器框架与 BIF 的桥接作用Zeek 的分析器Analyzer是解析具体协议的核心组件。每支持一种协议Zeek 就有一个从analyzer::Analyzer派生的类一旦确定某条连接的载荷要按某种协议解析Zeek 就实例化对应的分析器并将其作为子节点加入连接的分析器树analyzer tree。同时每种协议还有一个从analyzer::Component派生的元类描述该分析器并记录其是否处于启用状态src/analyzer/Manager.h。脚本层与 C 内核之间的沟通依靠BIFBuilt-In Function机制src/analyzer/analyzer.bif中定义的函数会被编译进 Zeek 二进制从而在 Zeek 脚本中直接可用。文档base/bif/analyzer.bif.zeek正是由该.bif文件生成的脚本接口文档对应仓库中的 doc/scripts/base/bif/analyzer.bif.zeek.rst文档开头的说明Internal functions and types used by the analyzer framework表明这些函数被定位为框架内部接口——它们通常不直接面向用户脚本而是由 scripts/base/frameworks/analyzer/main.zeek 这一公共框架封装后对外提供。这些函数分布在两个命名空间中Analyzer命名空间__enable_analyzer、__disable_analyzer、__disable_all_analyzers、__register_for_port、__schedule_analyzerGLOBAL命名空间__name、__tag、__has_tag它们面向所有分析器类型包括协议、报文、文件分析器。二、分析器的启用与禁用2.1Analyzer::__enable_analyzer(id: Analyzer::Tag) : bool该函数接受一个Analyzer::Tag如Analyzer::ANALYZER_HTTP并返回bool用于启用某个协议分析器。其 C 实现直接委托给分析器管理器// src/analyzer/analyzer.bif function Analyzer::__enable_analyzer%(id: Analyzer::Tag%) : bool %{ return zeek::analyzer_mgr-EnableAnalyzer(id-AsEnumVal()); %}EnableAnalyzer在 src/analyzer/Manager.cc 中通过Lookup按 Tag 找到对应Component若不存在返回false否则调用p-SetEnabled(true)并返回true。注意Lookup(tag, false)的第二个参数false表示不跟随映射见下文__name的说明即直接操作传入 Tag 对应的组件本身。2.2Analyzer::__disable_analyzer(id: Analyzer::Tag) : bool与启用对称禁用某个协议分析器。其实现调用analyzer_mgr-DisableAnalyzer(id-AsEnumVal())底层同样是Lookup后调用p-SetEnabled(false)src/analyzer/Manager.cc。被禁用的分析器不会再为新的连接实例化但已存在的连接不受影响。一个有意思的细节是在 src/analyzer/Analyzer.cc 的单元测试中Zeek 通过DisableAnalyzer禁用 SSH 分析器并配合AddComponentMapping将 SSH 映射到 IMAP验证了映射机制——禁用后InstantiateAnalyzer(SSH, ...)得到的实际是 IMAP 分析器。这说明禁用与映射这两个机制可以协同工作是框架内部测试直接使用该接口的实证。2.3Analyzer::__disable_all_analyzers() : any无参、返回any实际返回nullptr一次性禁用所有已注册的分析器function Analyzer::__disable_all_analyzers%(%) : any %{ zeek::analyzer_mgr-DisableAllAnalyzers(); return nullptr; %}底层实现遍历GetComponents()返回的全部Component列表并逐个SetEnabled(false)src/analyzer/Manager.cc。这与Analyzer::disable_all选项配合当disable_all T时zeek_init阶段会调用本函数关闭一切分析器再通过requested_analyzers集合选择性启用见 scripts/base/frameworks/analyzer/main.zeek。2.4 脚本框架封装Analyzer::enable_analyzer与disable_analyzer内部__函数不区分分析器类型而脚本框架提供了更友好的统一入口。scripts/base/frameworks/analyzer/main.zeek中的公共函数接受AllAnalyzers::Tag一个包含协议、报文、文件三类分析器 Tag 的全局枚举并按类型分发function enable_analyzer(tag: AllAnalyzers::Tag) : bool { if ( is_packet_analyzer(tag) ) return PacketAnalyzer::__enable_analyzer(tag); if ( is_file_analyzer(tag) ) return Files::__enable_analyzer(tag); return __enable_analyzer(tag); }即报文分析器走PacketAnalyzer::__enable_analyzer、文件分析器走Files::__enable_analyzer、协议分析器才落到本文档的Analyzer::__enable_analyzerscripts/base/frameworks/analyzer/main.zeek。用户在脚本中自定义启用逻辑时应优先使用这套公共接口而非直接调用__内部函数。三、端口注册让知名端口自动激活分析器3.1Analyzer::__register_for_port(id: Analyzer::Tag, p: port) : bool将某个协议分析器与一个知名端口绑定。一旦看到使用该端口的新连接Zeek 会自动为该连接分配对应分析器。签名接受Analyzer::Tag与portport值本身已包含传输层协议如80/tcp、53/udpfunction Analyzer::__register_for_port%(id: Analyzer::Tag, p: port%) : bool %{ return zeek::analyzer_mgr-RegisterAnalyzerForPort(id-AsEnumVal(), p); %}RegisterAnalyzerForPort(EnumVal*, PortVal*)先将 Tag 解析为Component再调用底层重载src/analyzer/Manager.cc。底层实现有一个值得注意的时序约束在InitPostScript完成之前即脚本尚未全部处理完时端口注册无法立即生效会被暂存在pending_analyzers_for_ports集合中待初始化完成后统一处理src/analyzer/Manager.cc随后按 TCP/UDP 找到对应的报文分析器packet_mgr-GetAnalyzer(TCP)/UDP最终调用IPBasedAnalyzer::RegisterAnalyzerForPort真正注册src/analyzer/Manager.cc。这也解释了为什么脚本层通常只在zeek_init阶段注册端口。3.2 脚本框架封装register_for_port与register_for_ports脚本框架在其封装中不仅调用内部函数还维护了一个ports: table[AllAnalyzers::Tag] of set[port]表用于 BPF 过滤与动态协议检测DPDfunction register_for_port(tag: Analyzer::Tag, p: port) : bool { if ( ! __register_for_port(tag, p) ) return F; if ( tag !in ports ) ports[tag] set(); add ports[tag][p]; return T; }而register_for_ports(tag, server_ports, non_server_ports defaultset())则批量注册并自动把server_ports并入likely_server_ports用于启发式服务器端口判断non_server_ports如客户端端口则不会加入scripts/base/frameworks/analyzer/main.zeek。框架注释特别强调注册是追加式的不会替换已有端口。此外analyzer_to_bpf(tag)与get_bpf()会基于该ports表生成对应的 BPF 过滤表达式使 Zeek 可以只捕获与已注册分析器相关的流量scripts/base/frameworks/analyzer/main.zeek。四、连接预调度Analyzer::__schedule_analyzer签名function Analyzer::__schedule_analyzer(orig: addr, resp: addr, resp_p: port, analyzer: Analyzer::Tag, tout: interval) : bool该函数预订一个未来出现的连接当 Zeek 看到与(orig, resp, resp_p)匹配的新连接时会自动把analyzer加入该连接的分析器树。tout是预订的超时时间必须非零超时后请求被清除。其实现为zeek::analyzer_mgr-ScheduleAnalyzer(orig-AsAddr(), resp-AsAddr(), resp_p, analyzer-AsEnumVal(), tout); return true;从 src/analyzer/Manager.h 的数据结构可以看出调度机制的内部细节ConnIndex以(orig, resp, resp_p, proto)四元组为索引其中orig允许用0.0.0.0作通配符匹配任意发起方见ScheduleAnalyzer的注释src/analyzer/Manager.h调度请求存储在conns_mapstd::multimapConnIndex, ScheduledAnalyzer*中同时conns_by_timeout优先队列按queue_timeout排序用于定期过期清理ExpireScheduledAnalyzers当连接真正出现时ApplyScheduledAnalyzers(Connection*, ...)检索并挂载这些预调度分析器GetScheduled返回命中集合src/analyzer/Manager.h。一个硬性约束ScheduleAnalyzer在run_state::network_time尚未建立即网络处理尚未开始时会直接告警 cannot schedule analyzers before processing begins; ignoredsrc/analyzer/Manager.cc因此该函数只能在网络运行期间使用。脚本框架的封装Analyzer::schedule_analyzer(orig, resp, resp_p, analyzer, tout)直接透传内部函数scripts/base/frameworks/analyzer/main.zeek。典型应用场景是检测到某主机将发起某种协议交互如基于先前告警或外部情报提前为其预订分析器。五、名称与 Tag 的相互转换GLOBAL 命名空间文档中另外三个函数位于GLOBAL命名空间接受/返回AllAnalyzers::Tag用于在分析器名称字符串与Tag 枚举值之间转换。它们在 src/analyzer/analyzer.bif 中共享同一个辅助函数component_for_name依次在analyzer_mgr协议分析器、packet_mgr报文分析器、file_mgr文件分析器中按名称查找组件找到则返回对应plugin::Component。5.1__name(atype: AllAnalyzers::Tag) : string返回给定 Tag 对应的分析器规范名称CanonicalName。实现的关键注释是这里不跟随映射Note that we dont want to follow mappings here即用户传入什么 Tag 就返回什么名字即使该 Tag 在内部被映射到别的分析器如前面提到的 SSH→IMAP 映射也不会被改写。若三类管理器都找不到组件则返回字符串errorsrc/analyzer/analyzer.bif。5.2__tag(name: string) : AllAnalyzers::Tag按名称反查 Tag。若名称有效返回对应Component的Tag()否则返回空的zeek::Tag()t.AsVal()会得到一个无效 Tag 值src/analyzer/analyzer.bif。注意AllAnalyzers::Tag是包含协议/报文/文件三类 Tag 的大枚举因此该函数能解析任意类型的分析器名称。5.3__has_tag(name: string) : bool检查给定名称是否为已知的分析器名实现即component_for_name(name-CheckString()) ! nullptrsrc/analyzer/analyzer.bif。脚本框架文档建议在调用Analyzer::get_tag之前先调用Analyzer::has_tag验证名称合法性避免得到无效 Tagscripts/base/frameworks/analyzer/main.zeek。5.4 脚本框架封装框架对应提供了Analyzer::name(atype)、Analyzer::has_tag(name)、Analyzer::get_tag(name)以及Analyzer::kind(atype)返回protocol/packet/file/unknown等公共接口全部直接透传上述内部函数scripts/base/frameworks/analyzer/main.zeek。这些接口常被用于把分析器名称写入日志、在策略脚本中按名称动态查找分析器等场景。六、8 个内部函数速查表函数命名空间签名底层调用说明__enable_analyzerAnalyzer(id: Analyzer::Tag) : boolEnableAnalyzer(EnumVal*)启用协议分析器__disable_analyzerAnalyzer(id: Analyzer::Tag) : boolDisableAnalyzer(EnumVal*)禁用协议分析器__disable_all_analyzersAnalyzer() : anyDisableAllAnalyzers()禁用全部分析器__register_for_portAnalyzer(id: Analyzer::Tag, p: port) : boolRegisterAnalyzerForPort(...)为分析器注册知名端口__schedule_analyzerAnalyzer(orig: addr, resp: addr, resp_p: port, analyzer: Analyzer::Tag, tout: interval) : boolScheduleAnalyzer(...)为未来连接预调度分析器__nameGLOBAL(atype: AllAnalyzers::Tag) : stringLookup三类管理器Tag → 名称__tagGLOBAL(name: string) : AllAnalyzers::Tagcomponent_for_name名称 → Tag__has_tagGLOBAL(name: string) : boolcomponent_for_name名称是否有效七、实战建议与调用约束综合以上源码证据使用这套内部接口时有几点值得注意优先使用脚本框架公共接口Analyzer::enable_analyzer、disable_analyzer、register_for_ports、schedule_analyzer、name、kind、has_tag、get_tag等封装scripts/base/frameworks/analyzer/main.zeek已处理了报文/文件分析器的分发与端口表的维护直接使用__内部函数会绕过这些逻辑。时序约束端口注册在InitPostScript之前只会进入待处理队列分析器调度在run_state::network_time建立之前会被拒绝并告警。因此端口注册应放在zeek_init阶段调度操作应放在网络运行期间。配合disable_all与requested_analyzers全禁用后可通过requested_analyzers集合在启动时选择性启用优先级 -5 的zeek_init处理实现默认全关、按需开启的精细化控制scripts/base/frameworks/analyzer/main.zeek。返回值的语义__enable_analyzer/__disable_analyzer/__register_for_port在 Tag 无效或组件缺失时返回false__tag对未知名称返回无效 Tag务必配合__has_tag使用。如需进一步深入可阅读分析器管理器的完整实现 src/analyzer/Manager.cc、其接口声明 src/analyzer/Manager.h以及生成这些内部函数文档的源定义文件 src/analyzer/analyzer.bif而 scripts/base/frameworks/analyzer/main.zeek 则展示了全部公共封装如何在这些内部接口之上构建完整的分析器管理框架。赞分享网络安全网络IDS【免费下载链接】zeekZeek is a powerful network analysis framework that is much different from the typical IDS you may know.项目地址https://gitcode.com/gh_mirrors/ze/zeek点击查看免费下载相关推荐Zeek 协议分析器脚本接口全索引Analyzer::Tag、插件组件与事件函数详解Zeek 协议分析器脚本接口全索引Analyzer::Tag、插件组件与事件函数详解 本篇技术指南以 Zeek 官方自动生成的协议分析器索引 autogen网络安全网络IDSZeek Packet Analyzer BIF 函数全解析注册、检测、启停与校验和忽略机制Zeek Packet Analyzer BIF 函数全解析注册、检测、启停与校验和忽略机制 本篇技术指南以 Zeek 官方 API 参考文档 base/bi网络安全网络IDSZeek base/bif 包深度解析BIF 加载机制与内置功能全景Zeek base/bif 包深度解析BIF 加载机制与内置功能全景 base/bif/__load__.zeek 是 Zeek 脚本层所有内置函数BIF网络安全网络IDS上一篇终极窗口管理革命用FancyZones告别杂乱桌面5分钟打造完美工作区下一篇从零开始构建DeepSeek R1完整指南与核心技术解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表