ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Zeek Input Framework 深入指南:从文件与 SQLite 导入数据到表与事件流

Zeek Input Framework 深入指南:从文件与 SQLite 导入数据到表与事件流 网络安全网络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 的 Input Framework输入框架提供了一条把此前已存储的数据重新引入 Zeek 运行环境的标准化通道既可以把数据读入 Zeek 表table也可以直接转化为事件流event stream交给脚本处理其模块化的 reader 架构支持从文件、数据库等多种数据源摄取数据。读完本文你将掌握Input::add_table/Input::add_event的完整用法、三种读取模式MANUAL/REREAD/STREAM的差异、谓词过滤与变更事件机制以及 ASCII、Raw、Binary、Benchmark、SQLite 等内置 reader 的选型与实战配置。本文以 input 框架包索引文档 和同主题的完整教程 Input Framework 为骨架结合 框架脚本实现 与 btest 测试用例 展开。需要说明的是Input Framework 本身对 Zeek 的集群架构并无感知任何集群节点上都可直接使用下述机制config 框架和 intelligence 框架正是基于 Input Framework 构建由它们在 manager 节点上执行摄取、再通过事件把数据分发到集群各处。Input Framework 是什么Input Framework 的核心目标是把外部数据文件、数据库查询结果等以两种方式送进 Zeek读入表table数据被填充到一张 Zeek 表中之后可以像普通表一样被查找、遍历适合构建 denylist、情报库、主机/用户映射等按索引查询的数据结构读入事件event每一行数据触发一个 Zeek 事件由脚本自行决定如何处理适合数据结构复杂、数据集过大或需要逐条即时处理的场景。框架采用模块化 reader 架构不同数据源由不同的 reader 负责解析默认的 reader 是 Input::READER_ASCII它理解 Zeek 自身的 TSV 日志格式。此外内置了 Raw、Benchmark、Binary、SQLite 等 readerZeek 包与插件还可以提供更多自定义 reader。从实现上看框架入口定义在 scripts/base/frameworks/input/main.zeek它导出了Input::TableDescription、Input::EventDescription两种流描述记录以及Input::add_table、Input::add_event、Input::remove、Input::force_update等核心函数底层则通过load base/bif/input.bif暴露的__create_table_stream、__create_event_stream、__remove_stream、__force_update等内置函数与 C 实现src/input/ 目录下的 Manager、ReaderBackend、ReaderFrontend 等对接。把数据读入 Zeek 表核心流程读取文件到表是 Input Framework 最常用的场景。默认情况下框架读取的数据格式与 Zeek 日志框架写出的格式一致以 Tab 分隔的 ASCII 文件。假设我们要导入一份 denylist其中包含服务器 IP 地址、封禁时间戳和封禁原因。示例输入文件注意所有字段必须以 Tab 分隔且首行为#fields行声明列名见 doc/frameworks/input/denylist.file#fields ip timestamp reason 192.168.17.1 1333252748 Malware host 192.168.27.2 1330235733 Botnet server 192.168.250.3 1333145108 Virus detected要把文件读入表需要定义两个 record 类型一个描述构成表**键key**的列的类型与名称一个描述构成表**值value**的列的类型与名称。以按 IP 查询为目的键记录只包含服务器 IP其余字段存入值记录type Idx: record { ip: addr; }; type Val: record { timestamp: time; reason: string; };注意record 字段名必须与输入文件#fields行中的列名一一对应本例为ip、timestamp、reason列的顺序无关紧要因为每列按名称识别。随后在zeek_init()中调用Input::add_table把文件读入表global denylist: table[addr] of Val table(); event zeek_init() { Input::add_table([$sourcedenylist.file, $namedenylist, $idxIdx, $valVal, $destinationdenylist]); Input::remove(denylist); }这三行代码做了三件事先创建一个接收数据的空表然后指示框架打开名为 denylist 的输入流并把数据读入该表最后立即调用Input::remove移除该流——因为数据读完之后不再需要它。之所以可以这样连续两行调用得益于框架的异步排队机制见下文。关于键/值 record 与 optional 字段的细节键与值 record 都可以使用optional字段但省略列通常用 - 字符表示需要格外小心键 record 的列会展开为一张值列表用于对目标表进行索引上例中denylist就是被一个普通的addr索引的。由于索引所需的全部值都必须存在实践中不能省略键中的值值 record 中省略列是有意义的但仅允许对带optional属性的列进行省略框架会跳过违反此规则的行并发出警告。另请注意版本差异Zeek 4.1 之前框架曾不安全地接受此类输入从 4.1 起行为收紧。从旧版本升级、且输入数据中存在省略字段的用户可能会观察到加载数据集与以往不一致。异步处理与 end_of_data 事件由于部分数据文件可能很大Input Framework异步工作每条新的输入流都会创建一个新线程该线程负责打开数据文件、把数据转换成内部格式并送回 Zeek 主线程。因此数据不会立即立即可用——根据数据源规模可能需要从几毫秒到几秒不等。这一点意味着如果 Zeek 在没有输入源或处理非常短的抓包文件时运行可能在导入线程完成前就处理完所有报文并退出导致数据尚未进表程序就已结束。对同一输入源的后续调用会被排队直到前一个动作完成——这正是上文add_table与remove可以写在同一事件里连续两行的原因remove 会一直排队等到首次读取完成。当框架从某个数据源读完数据后会触发 Input::end_of_data 事件。收到该事件时输入文件的所有数据已全部进入表event Input::end_of_data(name: string, source: string) { # now all data is in the table print denylist; }在读取进行期间表也可以被使用——只是在该事件触发前表里可能还没有输入文件的全部行。表填充完成后即可像任何普通 Zeek 表一样使用例如测试某个 IP 是否命中 denylistif ( 192.168.18.12 in denylist ) # take action用 set 代替表纯成员判定当数据的核心用途只是判断是否属于某个集合时键/值二元结构并不适用。此时可以把目标数据类型换成set并在Input::add_table中省略$valtype Idx: record { ip: addr; }; global denylist: set[addr] set(); event zeek_init() { Input::add_table([$sourcedenylist.file, $namedenylist, $idxIdx, $destinationdenylist]); Input::remove(denylist); }从 TableDescription 定义 看$val是optional的当它未定义时$destination必须是 set这正是该用法的接口级依据。三种读取模式MANUAL、REREAD 与 STREAM许多数据源例如大量 denylist是持续变化的。Input Framework 提供了几种应对机制其模式枚举 Input::Mode 定义如下模式枚举值行为Input::MANUAL0文件读完后不自动重读默认模式Input::REREAD1每次检测到文件变化时重读整个文件Input::STREAM2文件被追加时从文件末尾读取新数据手动刷新Input::force_update最基础的方法是显式刷新输入流。只要输入流还处于打开状态尚未被Input::remove移除就可以调用 Input::force_updateInput::force_update(denylist);这会触发一次完整的表刷新文件中变化的元素被更新、新元素被添加、已不在输入数据中的元素被删除。更新完成后照例会触发Input::end_of_data事件。REREAD 模式文件变更自动重读也可以在Input::add_table中通过$mode指定非默认读取模式让框架在检测到输入文件变化时自动刷新表内容Input::add_table([$sourcedenylist.file, $namedenylist, $idxIdx, $valVal, $destinationdenylist, $modeInput::REREAD]);使用 REREAD 模式时Zeek 会持续检查输入文件是否变化若变化则重新读取整个文件并更新表中数据以反映当前状态。每次检测到变化、且所有新数据都读入表后都会触发Input::end_of_data事件。STREAM 模式追读追加型文件使用$modeInput::STREAM时Zeek 假定输入是**只追加append-only**的文件新数据不断追加到末尾。Zeek 会持续检查文件末尾是否有新数据并把新行加入表同时检测被跟踪的文件是否被改名或轮转rotation——当tail发现正在读取的文件名的 inode 号变了就会关闭并重新打开该文件。若新行与表中已有行具有相同的表索引新值会覆盖旧值。由于流式读取的特性数据持续不断地进表STREAM 模式下永远不会触发Input::end_of_data事件。变更检测的底层机制变更是通过周期性的心跳事件检测的默认频率为每秒一次由全局常量 Threading::heartbeat_interval 相关机制控制。reader 判定输入文件已变化的依据是自上次检查以来文件的 **inode 或修改时间mtime**发生了改变。接收变更事件精确追踪每一行数据的增删改重读文件时我们往往还想知道源文件中具体哪些行发生了变化。为此框架支持在数据项被添加、移除或修改时逐条触发事件。事件定义如下可以在你自己的 Zeek 脚本中改名event entry(description: Input::TableDescription, tpe: Input::Event, left: Idx, right: Val) { # do something here... print fmt(%s %s, left, right); }在Input::add_table调用中通过$ev指定该事件Input::add_table([$sourcedenylist.file, $namedenylist, $idxIdx, $valVal, $destinationdenylist, $modeInput::REREAD, $eventry]);各参数含义与 Input::Event 枚举 对应description最初传给Input::add_table的参数记录可通过description$name等访问流名等信息tpe发生的变化类型枚举left发生变化的表项的索引right变化前后的值视tpe而定。三种变化类型的行为tpe值触发时机leftrightInput::EVENT_NEW表中原本没有的行被添加新增表项的索引新增表项的值Input::EVENT_CHANGED已有表项在重读/流式读取中被修改被修改表项的索引修改前的值Input::EVENT_REMOVED表项在重读后不再出现而被移除被移除表项的索引被移除表项的值注意EVENT_CHANGED的一个微妙之处事件触发时表已经被更新因此right携带的是旧值如需比较新旧值可再通过当前表值查询获得新值。导入时过滤与改写数据谓词函数predicateInput Framework 允许在导入过程中过滤数据依靠的是谓词函数predicate。谓词函数会在新元素被添加/修改/移除之前被调用通过返回T接受或F拒绝来决定是否执行这次变更此外它还可以在数据写入表之前改写数据。下面的示例过滤会拒绝生成时间超过一个月的新条目同时接受所有对已存在值的修改与移除Input::add_table([$sourcedenylist.file, $namedenylist, $idxIdx, $valVal, $destinationdenylist, $modeInput::REREAD, $pred(tpe: Input::Event, left: Idx, right: Val) { if ( tpe ! Input::EVENT_NEW ) { return T; } return (current_time() - right$timestamp) 30day; }]);谓词函数签名在 TableDescription 中被定义为pred: function(typ: Input::Event, left: any, right: any): bool optional参数含义与变更事件一致。若要改写导入中的元素谓词可以直接操作left和right。由于谓词在变更提交到表之前被调用当发生EVENT_CHANGED时left与right携带的是新值而目标表本例中的denylist里仍是旧值——这使得谓词能够在决定是否放行之前先对比新旧版本。损坏输入数据的处理与报错数据摄取期间出现问题框架会通过两种途径通知你其一reporter 消息最终进入reporter.log指明问题类型与出错文件例如#fields ts level message location 0.000000 Reporter::WARNING denylist.file/Input::READER_ASCII: Did not find requested field ip in input data file denylist.file. (empty)其二错误事件Input::TableDescription与Input::EventDescription记录都提供$error_ev成员用于触发携带上述相同消息与严重级别的事件见 main.zeek 中的 error_ev 定义用法与变更事件一致。当错误级别为 error 时流会被自动关闭。无论哪种方式框架都会抑制针对同一文件的重复消息因此大文件中的错误不会引发消息洪泛。此外ASCII reader 提供了对摄取健壮性的粗粒度控制常量 InputAscii::fail_on_invalid_lines 与 InputAscii::fail_on_file_problem 决定问题是仅触发警告、还是导致处理失败两者默认都是仅警告F。相关 btest 测试见 testing/btest/scripts/base/frameworks/input/ 下的invalid-lines.zeek、invalid-lines-duplicate.zeek、errors.zeek、missing-file.zeek等用例。把数据读入事件流Input::add_event第二种摄取模式是直接从数据生成 Zeek 事件而不是写入表。事件流的工作方式与表流非常相似上文讨论的大多数特性例如谓词过滤同样适用于事件流。用事件流读取前文的 denylisttype Val: record { ip: addr; timestamp: time; reason: string; }; event denylistentry(description: Input::EventDescription, tpe: Input::Event, data: Val) { # do something here... print data:, data; } event zeek_init() { Input::add_event([$sourcedenylist.file, $namedenylist, $fieldsVal, $evdenylistentry]); }事件流与表流有两点关键区别无需分别声明索引与值记录——所有源数据类型放在一个 record 定义中$fields没有数据基线概念框架把输入视为持续不断的事件流无法像表那样与新数据做基线对比因此变化事件类型上例中的tpe永远是Input::EVENT_NEW。除此之外事件流与表流的工作方式完全相同并支持大部分表流支持的选项。值得注意的还有$want_record选项在 EventDescription 中它默认为T事件收到包含全部字段的单个 record 值若设为F事件会把$fields中的每个值作为独立参数接收。内置 Reader 全景Input Framework 支持不同种类的 reader 以应对不同格式的数据源。默认摄取的是符合 Zeek 日志文件格式的 ASCII 文件Tab 分隔值 #fields头行。reader 的选择过程如下全局变量 Input::default_reader 定义默认 reader即Input::READER_ASCII调用Input::add_table/Input::add_event时自动使用可以在传给这些调用的描述记录中设置$reader成员来覆盖默认值相关示例可参见 testing/btest/scripts/base/frameworks/input/ 目录下的测试用例。reader 枚举由 C 侧生成见 src/input/input.bif 及生成的头文件脚本侧则在 main.zeek 引用。下面逐一介绍各 reader 的脚本接口。ASCII Reader默认ASCII reader 默认启用或通过Input::READER_ASCII选择理解 Zeek 的 TSV 日志格式。它实际上理解这些日志文件前导部分的全套指令例如定义列分隔符的指令但很少被用到最常见的输入文件就是前文展示的那样以一行 Tab 分隔的#fields头行开始。其可调接口定义在 scripts/base/frameworks/input/readers/ascii.zeek关键常量如下常量默认值含义InputAscii::separatorInput::separator\t字段分隔符必须恰好 1 个字符InputAscii::set_separatorInput::set_separator,set/vector 元素分隔符必须恰好 1 个字符InputAscii::empty_fieldInput::empty_field(empty)空字段的表示字符串InputAscii::unset_fieldInput::unset_field-未设置的optional字段的表示字符串InputAscii::fail_on_invalid_linesF无效行是仅报告警告跳行还是致命错误中止读取InputAscii::fail_on_file_problemF文件读取问题权限、文件缺失等是仅警告还是失败InputAscii::path_prefix为无路径/相对路径的源文件名添加前缀绝对路径不受影响全局的Input::separator、Input::set_separator、Input::empty_field、Input::unset_field都带有redef属性main.zeek各 reader 可以覆盖它们fail_on_invalid_lines/fail_on_file_problem也可通过$config表按流覆盖。一个重要警告ASCII reader没有文件锁概念包括 UNIX 的 advisory locking。对大型文件而言框架可能处理一个仍在被写入的文件reader 能稳健地处理由此产生的错误例如通过 reporter.log如前文所述但错误仍会发生。为避免这类问题最佳实践是先在旁边生成新的输入文件再原子性地 rename 到框架监控的文件名。Benchmark ReaderBenchmark readerInput::READER_BENCHMARK用于帮助 Zeek 开发者优化输入框架的速度它可以按任意数量生成 Input Framework 所支持的全部 Zeek 数据类型上的半随机数据。其可调参数定义在 scripts/base/frameworks/input/readers/benchmark.zeekfactor每秒乘法因子默认 1.0、spread行间散布因子、autospread自动散布、addfactor每个心跳的加法因子、stopspreadat每个心跳散布到多少行停止、timedspread启用定时散布。Binary ReaderBinary readerInput::READER_BINARY用于文件分析输入流file analysis input streams以摄取文件内容并且是这类流的默认 reader 类型。它按块读取文件内容并转发给文件分析框架。其接口在 scripts/base/frameworks/input/readers/binary.zeekInputBinary::chunk_size每次读取的数据块大小默认 1024 字节与InputBinary::path_prefix路径前缀默认空。对应地框架还提供了Input::add_analysis函数main.zeek它创建一条文件分析输入流读取的数据会自动转发给文件分析框架其AnalysisDescription的$reader默认为Input::READER_BINARY。Raw ReaderRaw readerInput::READER_RAW读取按指定记录分隔符record separator默认换行符\n切分的文件内容逐行作为字符串返回。它可用于读取配置文件等通常只在事件模式下有用不适合读入表。其接口在 scripts/base/frameworks/input/readers/raw.zeekInputRaw::record_separator定义记录分隔符同时定义了InputRaw::process_finished事件用于 raw reader 创建的子进程退出时通知脚本name、source、exit_code、signal_exit。读取 JSON LinesRaw Reader from_jsonASCII reader 目前不支持原生 JSON 摄取但可以组合 raw reader 与内置函数from_json读取 JSON Lines 格式的文件并实例化 Zeek record 值自 Zeek 6.0 起可用。下面示例演示了完整做法——维护两张状态表以便支持对已读数据的移除更新。示例输入文件 denylist.jsonl{ip: 192.168.17.1, timestamp: 1333252748, reason: Malware host} {ip: 192.168.27.2, timestamp: 1330235733, reason: Botnet server} {ip: 192.168.250.3, timestamp: 1333145108, reason: Virus detected}加载脚本 input_json_1.zeek 的核心逻辑module Denylist; type JsonLine: record { s: string; }; type Entry: record { ip: addr; timestamp: time; reason: string; }; global staged_denies: table[addr] of Entry; global active_denies: table[addr] of Entry; event Input::end_of_data(name: string, source: string) { if ( name ! denylist ) return; # Switch active and staging tables when input file has been read. active_denies staged_denies; staged_denies table(); print network_time(), end_of_data() active:, table_keys(active_denies); } event Denylist::json_line(description: Input::EventDescription, tpe: Input::Event, l: string) { local parse_result from_json(l, Entry); # Parsing of JSON may fail, so ignore anything invalid. if ( ! parse_result$valid ) return; local entry parse_result$v as Entry; # ...and populate staging table. staged_denies[entry$ip] entry; } event zeek_init() { Input::add_event([ $sourcedenylist.jsonl, $namedenylist, $readerInput::READER_RAW, $modeInput::REREAD, $fieldsJsonLine, $evDenylist::json_line, $want_recordF, ]); }要点用 raw reader $modeInput::REREAD打开流$want_recordF使事件按独立参数接收每行 JSON 通过from_json(l, Entry)解析为Entryrecord先填充staged_denies临时表在Input::end_of_data中原子切换active与staged两张表从而在 REREAD 模式下实现整表替换式的移除新增更新语义。如果你的输入数据已经是或可以方便地转换成JSON Lines 格式上述方案即可将其加载进 Zeek。相关测试可参考 testing/btest/scripts/base/frameworks/input/from_json.zeek。SQLite ReaderSQLite readerInput::READER_SQLITE让 Zeek 可以直接访问 SQLite 数据库。SQLite 是简单、基于文件、被广泛使用的 SQL 数据库由于其事务性特性同一个数据库可被多个应用同时使用因此非常适合把持续演化的数据集持续提供给 Zeek。与 Zeek 的日志支持一样SQLite 数据读取已内置于 Zeek无需额外配置。其脚本接口在 scripts/base/frameworks/input/readers/sqlite.zeek提供了set_separator、unset_field、empty_field三个可 redef 常量。使用 SQLite reader 时必须在$config表中设置query键给出返回所需数据的 SQL 查询。完整入门介绍见 doc/frameworks/logging-input-sqlite.rst。场景一把查询结果读入表假设数据库保存了各主机 IP 与允许登录该机器的用户账号。建表与填充数据的 SQLcreate table machines_to_users ( host text unique not null, users text not null); insert into machines_to_users values ( 192.168.17.1, johanna,matthias,seth); insert into machines_to_users values ( 192.168.17.2, johanna); insert into machines_to_users values ( 192.168.17.3, seth,matthias);把上述内容存入hosts.sqlite后即可读取结果表type Idx: record { host: addr; }; type Val: record { users: set[string]; }; global hostslist: table[addr] of Val table(); event zeek_init() { Input::add_table([$source/var/db/hosts, $namehosts, $idxIdx, $valVal, $destinationhostslist, $readerInput::READER_SQLITE, $configtable([query] select * from machines_to_users;) ]); Input::remove(hosts); } event Input::end_of_data(name: string, source: string) { if ( name ! hosts ) return; # now all data is in the table print Hosts list has been successfully imported; # List the users of one host. print hostslist[192.168.17.1]$users; }这里值得注意几点$source是数据库文件的路径$name是流名二者不必相同Val中的users: set[string]对应数据库中的逗号分隔字符串——SQLite reader 会用set_separator默认,把字段解析为集合表流模式下users列的逗号分隔值通过 InputSQLite::set_separator 拆分。之后hostslist表就可以用于对照可用用户列表检查主机登录行为。场景二把查询结果变成事件流把输入数据作为事件输出通常有两个理由一是输入数据结构过于复杂不便直接导入表——可在事件中于脚本层构建所需数据结构二是数据集太大无法全部驻留内存——事件驱动式摄取可以按需进行即时检查。以大型恶意软件哈希数据库为例数据库结构如下哈希值唯一且非空create table malware_hashes ( hash text unique not null, description text not null); insert into malware_hashes values (86f7e437faa5a7fce15d1ddcb9eaeaea377667b8, malware a); insert into malware_hashes values (e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98, malware b); insert into malware_hashes values (84a516841ba77a5b4648de2cd0dfcb30ea46dbb4, malware c); insert into malware_hashes values (3c363836cf4e16666669a25da280a1865c2d2874, malware d); insert into malware_hashes values (58e6b3a414a1e090dfc6029add0f3555ccba127f, malware e); insert into malware_hashes values (4a0a19218e082a343a1b17e5333409af9d98f0f5, malware f); insert into malware_hashes values (54fd1711209fb1c0781092374132c66e79e2241b, malware g); insert into malware_hashes values (27d5482eebd075de44389774fce28c69f45c8a75, malware h); insert into malware_hashes values (73f45106968ff8dc51fba105fa91306af1ff6666, ftp-trace);下面的脚本结合文件分析框架获取网络传输文件的 sha1 哈希对每个哈希向 SQLite 发起一次 SQL 查询命中则输出匹配的哈希load frameworks/files/hash-all-files type Val: record { hash: string; description: string; }; event line(description: Input::EventDescription, tpe: Input::Event, r: Val) { print fmt(malware-hit with hash %s, description %s, r$hash, r$description); } global malware_source /var/db/malware; event file_hash(f: fa_file, kind: string, hash: string) { # check all sha1 hashes if ( kindsha1 ) { Input::add_event( [ $sourcemalware_source, $namehash, $fieldsVal, $evline, $want_recordT, $configtable( [query] fmt(select * from malware_hashes where hash%s;, hash) ), $readerInput::READER_SQLITE ]); } } event Input::end_of_data(name: string, source:string) { if ( source malware_source ) Input::remove(name); }注意此处为每个哈希创建独立的事件流$namehash并在Input::end_of_data中按source匹配后逐一Input::remove清理避免流累积。如果对testing/btest/Traces/ftp/ipv4.trace运行该脚本会得到一次命中。更多源码级细节与测试佐证为了让上述用法有据可查这里补充几个关键的实现与验证入口框架脚本接口scripts/base/frameworks/input/main.zeek 定义了TableDescription含$reader、$mode、$idx、$val、$want_record、$ev、$pred、$error_ev、$config等字段、EventDescription与AnalysisDescription三种描述记录以及add_table/add_event/add_analysis/remove/force_update五个全局函数和end_of_data事件C 实现src/input/ 目录下的Manager.cc、ReaderBackend.cc、ReaderFrontend.cc等实现了流的创建、后台线程调度、reader 生命周期管理Input::force_update通过__force_update触发对应流的一次强制刷新btest 测试用例testing/btest/scripts/base/frameworks/input/ 目录下覆盖了大部分场景basic.zeek基础表导入、event.zeek事件流、reread.zeek重读模式、predicate.zeek/predicate-stream.zeek/predicatemodify.zeek谓词过滤与改写、set-event-reread.zeek/set-event-stream.zeekset 目标 变更事件、invalid-lines.zeek/errors.zeek/missing-file.zeek损坏输入、optional.zeekoptional字段、from_json.zeekJSON Lines 摄取、bignumber.zeek/port.zeek/patterns.zeek类型解析边界等是验证各机制实际行为的第一手资料默认常量Input::default_reader READER_ASCII、Input::default_mode MANUALmain.zeek以及accept_unsupported_types F遇到暂不支持的类型如file/function时默认中止可 redef 为T改为仅警告。实战选型建议综合上文可以给出如下选型思路构建按索引查询的静态/半静态数据denylist、情报库、映射表用Input::add_table ASCII/SQLite reader数据一次性读入后Input::remove清理流数据持续变化、需要整表同步$modeInput::REREAD必要时结合$ev变更事件或$pred谓词做增量感知与过滤也可在end_of_data中配合双表切换实现原子整表替换只追加的日志/流水数据$modeInput::STREAM追读文件末尾但注意它不会触发end_of_data格式灵活、需要逐行即时处理JSON Lines、配置文件、命令输出Input::add_event Raw reader from_jsonZeek 6.0查询外部数据库且结果庞大/结构复杂Input::READER_SQLITEadd_event按需发起查询、即时处置向文件分析框架供数Input::add_analysis默认搭配Input::READER_BINARY逐块摄取字节流。以上机制均以当前仓库Zeek 源码树中的文档、脚本与测试为准实际部署时请结合你自己的 Zeek 版本核对Input::Mode、from_json等能力from_json自 6.0 起可用的可用性。赞分享网络安全网络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 Input Framework 实战指南从 TSV 表格导入到 SQLite 数据库查询Zeek Input Framework 实战指南从 TSV 表格导入到 SQLite 数据库查询 Zeek原 Bro提供了一套灵活强大的输入框架Inp网络安全网络IDSZeek SQLite 输入读取与日志写入实战指南基于 Input/Logging 框架的数据库集成Zeek SQLite 输入读取与日志写入实战指南基于 Input/Logging 框架的数据库集成 SQLite 是一款简单、基于文件、被广泛使用的嵌入式网络安全网络IDSDLSS Swapper 完整上手指南不更新游戏即可更换 DLSS、FSR、XeSS 版本DLSS Swapper 完整上手指南不更新游戏即可更换 DLSS、FSR、XeSS 版本 DLSS Swapper 是一款面向 Windows 的免费开源工桌面应用上一篇Qwen2.5-Coder-0.5B-Instruct tokenizer深度解析15万词汇表与特殊标记详解下一篇Qwen3-8B-AWQ双模式切换重构开源大模型部署范式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进