0%

上一课,文字变成了一个个向量。但还有一个问题:

Transformer 是同时看所有词的,它并不知道谁在前、谁在后。

这一课讲模型怎么知道每个词排在第几个:位置编码。

Read more »

模型在选下一个词的时候,会先给每个候选词打一个分数。可分数有正有负,加起来也不等于 1,没法直接当成概率用。

怎么把一组分数变成概率?答案就是 softmax。要看懂它,先要认识两个老朋友:指数和对数。

Read more »

上一课讲了梯度下降:算出梯度,反着走一小步,误差就变小一点。

但真实的模型有几十层,很多参数藏在很深的地方,离最后的误差隔着好几层。深处那些参数的梯度,是怎么算出来的? 这一课就来回答这个问题。

Read more »

上一课讲了矩阵乘法:模型是怎么算的。这一课回答另一个问题:模型是怎么学会的?

答案藏在三个词里:导数、梯度、梯度下降。我们不从公式讲起,而是从一件很日常的小事说起:调洗澡水。

Read more »

00 篇说,这个系列适合”有 C 基础”的读者。可”C 基础”这个说法太宽了:会写 for 循环、懂指针和结构体,翻开 Suricata 的源码,还是会被一些写法卡住。这篇番外把读 Suricata 时会遇到的、超出入门教材的 C 语言知识挑出来,每一项都配上源码里的真实例子和位置(基于 8.0.7),并说明前面哪几篇用到了它。

先给一个总览。读 Suricata,最常卡住人的是这几类:

  1. **函数指针和”接口表”**:整个系列反复出现的骨架。
  2. 宏:预处理器在 Suricata 里几乎是一门小语言。
  3. 结构体的内存布局:柔性数组、union、位域、packed。
  4. 把字节当结构体看的指针技巧:解码层的日常。
  5. 侵入式数据结构:链表和红黑树的节点长在业务结构体里。
  6. 并发:锁、条件变量、线程局部变量、原子操作。
  7. 编译器提示、错误处理、条件编译:每个文件都有,但很少有人解释。
  8. 和 Rust 打交道:8.0 里越来越多的代码是 Rust 写的。

下面逐个看。

Read more »

这是系列第 10 篇,也是最后一篇。上一篇结尾,一条规则的所有条件都满足了,”产生一条告警”。可告警不是凭空出现在 eve.json 里的:它要先经过一轮收尾,再由日志模块拼成 JSON、写到文件里。00 篇开头那行 http 事件,走的也是这条路。这一篇就把最后这一段走完。

先给答案,输出这一段做了三件事:

  1. 告警先攒起来,再统一收尾。 规则命中时,告警先放进一个队列;整个包检测完,再统一排序、做抑制和阈值检查、执行 drop 这类动作,最后挂到 p->alerts 上。
  2. 日志按类型分发。 包处理的最后一步,OutputLoggerLog() 把包交给几类”根日志器”:包、事务、文件……每一类管着一批日志模块,各模块按自己的条件,或者按事务的进度,决定写不写。
  3. 拼成一行 JSON,写出去。 EVE 的每个日志模块用同一套工具拼 JSON:先拼所有事件共用的头,再加自己的字段,最后写到文件、syslog、unix socket 或 redis。

画出来是这样的:

从规则命中到一行 JSON

Read more »

这是系列第 9 篇。上一篇讲了一条规则怎么从文本变成结构。可真实的规则集动辄几万条,如果每个包都把几万条规则挨个试一遍,再快的机器也跟不上线速。这一篇看检测引擎怎么把”几万条”压到”少数几条”:规则是怎么分组的,多模式匹配(MPM)又是怎么做预过滤的。

先给答案,靠的是两层筛选:

  1. 分组。 启动时按”方向 × 协议 × 服务端端口”把规则分成若干组。一个发往 80 端口的 TCP 包,只需要看”发往服务器、TCP、80 端口”这一组规则,其他组的规则跟它无关。
  2. 预过滤。 每条规则挑出一段最有代表性的内容,叫”快速模式”(fast pattern)。每个组把组里所有规则的快速模式编进一个多模式匹配器,一个包来了,只搜一遍,就知道哪些规则的快速模式出现了。只有这些规则才需要逐条细查。

两层加起来,就是 00 篇第 ⑦ 站说的”先挑出一个规则组(SGH),再用 MPM 一次性搜出所有规则的关键字”:

几万条规则一起跑

Read more »