File: ceccomp.zh_CN.adoc

package info (click to toggle)
ceccomp 4.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 1,604 kB
  • sloc: ansic: 6,470; python: 1,039; makefile: 248; sh: 145
file content (404 lines) | stat: -rw-r--r-- 17,199 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
= ceccomp(1)
dbgbgtf <dudududumaxver@outlook.com>; RocketDev <ma2014119@outlook.com>
:doctype: manpage
:docdatetime: {TAG_TIME}
:manmanual: Ceccomp Manual
:mansource: ceccomp {VERSION}
:manversion: {VERSION}
:imagesdir: images/
:stylesheet: boot-slate.css

== 名称

ceccomp - 一个分析安全计算 (seccomp) 过滤器的工具

== 大纲

    usage: ceccomp <asm|disasm|emu|trace|probe|version|help> [FILE] [-q|--quiet]
                   [-f|--format FMT] [-a|--arch ARCH] [-p|--pid PID] [-s|--seize]
                   [-o|--output FILE] [-c|--color WHEN] ...

== 概念

内核使用BPF过滤器来限制系统调用规则,并使用 `seccomp` 和 `prctl` 两个系统调用来安装过滤器。以下是一个以十六进制表示的限制 `execve` 系统调用的简单过滤器:

    1: 20 00 00 00 00 00 00 00     $A = $syscall_nr
    2: 15 00 00 01 3b 00 00 00     if ($A != execve) goto 4
    3: 06 00 00 00 00 00 00 00     return KILL
    4: 06 00 00 00 00 00 ff 7f     return ALLOW

以上十六进制的部分就是内核收到的过滤器,而 `ceccomp` 负责把它拿来反汇编为人类可读的文本。 例如左侧的 *行号* 和右侧的 *伪代码* 。

IMPORTANT: 之后我会使用 _TEXT_ 作为BPF过滤器人类可读文本(伪代码)的缩写, 使用 _RAW_ 作为BPF过滤器原始格式的缩写,请记住这个约定。

== 描述

`ceccomp` 有5个主要的功能,它基本上是C版本的 `seccomp-tools` ,然而, 有一些不同的地方你需要知道,它们会在在每个子命令的章节中被注明。

=== asm - 汇编

    ceccomp asm [-c WHEN] [-a ARCH] [-f FMT] [TEXT]

将 _TEXT_ 汇编为 _RAW_ 。适用于将手写的过滤器规则嵌入到C代码中, 或希望观察一些 _TEXT_ 对应的原始字节码。

WHEN::
决定了 `ceccomp` 何时输出有颜色的文本。当设置为 _auto_ 时, `ceccomp` 会在输出目标是一个“tty”时打印颜色。可以是 _auto_ 、 _never_ 或者 _always_ 。 默认值是 _auto_ 。

ARCH::
可以设置为任何 libseccomp 支持的架构。它将被用于决定系统调用名称对应的系统调用号。 例如,在 x86_64 上,就像上面的基本示例,你可以写 `"execve"` 而不是数字 `59` 指代系统调用号。如果不设置这个参数,则通过 `uname` 提取当前系统的架构。 你的系统上的默认值是 {ARCH} 。

NOTE: 从 _4.0 版本_ 开始考虑端序。如果目标架构 *ARCH* 的端序与机器端序不同,则会在输出前反转过滤器(CODE 和 K)。

FMT::
决定了 `ceccomp` 如何输出二进制格式的BPF字节码。可以是 _hexfmt_ 、 _hexline_ 或者 _raw_ 。你可以在 <<ceccomp 示例>> 节中找到示例输出。默认值是 _hexline_ 。

TEXT::
一个可选的文件名,其中存放了需要被汇编的 _TEXT_ 。不设置则从 _标准输入_ 中读取。`-` 被视作 _标准输入_。

IMPORTANT: _4.0 版本_ 大幅修改了汇编的语法,请查阅下面的语法参考!

查看 <<TEXT 语法参考>> 一节可以找到如何手写规则。一些示例会在 <<ceccomp 示例>> 一节中展示。

|===
|命令|差别

|`seccomp-tools asm`
|使用它自己的语法汇编,有点像脚本;可以汇编会被内核拒绝的错误 _TEXT_

|`ceccomp asm`
|你可以直接拿着 `disasm` 的输出来汇编,不需要学习新语法;默认使用 _标准输入_ 作为输入
|===

=== disasm - 反汇编

    ceccomp disasm [-c WHEN] [-a ARCH] [RAW]

反汇编 _RAW_ 为 _TEXT_ 。适用于当你无法使用 `trace` 看到过滤器时,必须手动提取过滤器, 然后检查其含义。

WHEN::
参数描述可以在 <<asm - 汇编>> 一节中找到。 `disasm` 可能会打印更多有颜色的文本,包括针对 _TEXT_ 的语法高亮。

ARCH::
可以设置为任何 libseccomp 支持的架构。它将被用于决定 _RAW_ 中的系统调用号如何被翻译为系统调用名。 例如,在 x86_64 上,在比较系统调用号时,数字 `0x3b` 将被翻译为 `execve` ,可以看上面的基本示例。 你的系统上的默认值是 {ARCH} 。

RAW::
包含原始 BPF 代码的二进制文件。如果不设置则将 _标准输入_ 作为输入。`-` 被视作 _标准输入_。这个文件是与架构有关的,所以它在不同架构之间可能不是通用的。

NOTE: 从 _4.0 版本_ 开始考虑端序。如果目标架构 *ARCH* 的端序与机器端序不同,则会在解码前反转过滤器(CODE 和 K)。

NOTE: 当且仅当在某一行的架构可以被确定时,ceccomp 才会尝试用那个架构解析那个系统调用号。 如果是外部架构(不等于你设置的架构),它会被附加到系统调用名前。你可能会注意到在一些情况下, seccomp-tools 能解析一些系统调用名,而 ceccomp 不能,这可能是因为此时架构不能被确定。

|===
|命令|差别

|`seccomp-tools disasm`
|用它自己的语法反汇编;永远不会检查 _RAW_ 是否合法

|`ceccomp disasm`
|用 ceccomp 语法反汇编,并且默认将 _标准输入_ 作为输入;严格检查架构,
并且永远打印外部架构名
|===

=== emu - 模拟

    ceccomp emu [-c WHEN] [-a ARCH] [-q] TEXT SYSCALL_NAME/SYSCALL_NR [ARGS[0] ARGS[1] ... ARGS[5] PC]

按照 _TEXT_ 中描述的规则,模拟从 `PC` 调用 `syscall(SYSCALL_NR, ARGS[0], ARGS[1], ..., ARGS[5])` 的结果。适用于在不实际运行程序或不想手动检查规则时,查看触发系统调用的结果。 这个子命令适合用来自动检测一个过滤器。

WHEN::
参数描述可以在 <<asm - 汇编>> 一节中找到。 `emu` 可能会打印更多有颜色的文本,包括针对 _TEXT_ 的语法高亮以及跳过的伪代码。

SYSCALL_NAME/SYSCALL_NR::
如果你设置了 *SYSCALL_NAME* (比如 `execve` ),那么它会基于 *ARCH* 先被翻译为对应的 *SYSCALL_NR* 。或者你可以直接设置 *SYSCALL_NR* (例如 `59` )。然后会测试这个系统调用号经过 BPF 过滤器处理后的输出并打印出来。这个参数是必填的。

ARGS[0-5] 和 PC::
当调用系统调用时对应寄存器的值。例如,在 x86_64 上,它们分别对应了 `rdi` 、 `rsi` 、 `rdx` 、 `r10` 、 `r8` 、 `r9` 和 `rip` 。它们的默认值都是0。

ARCH::
参数描述可以在 <<asm - 汇编>> 一节中找到。

TEXT::
一个文件名,其中存放了需要被测试的 _TEXT_ 规则。注意文件名 不能 被忽略,因为 ceccomp 无法判断一个位置参数是系统调用号还是文件名。使用 `-` 可以指代 _标准输入_。

-q, --quiet::
只打印过滤器的模拟结果。例如,最后一行模拟的伪代码是 `return KILL`,那么将会打印 `KILL`。

|===
|命令|差别

|`seccomp-tools emu`
|用 _RAW_ 作为输入

|`ceccomp emu`
|用 _TEXT_ 作为输入,并默认将 _标准输入_ 作为输入;可以设置 *PC*
|===

=== trace - 运行时捕获过滤器

    ceccomp trace [-c WHEN] [-o FILE] PROGRAM [program-args]
                  [-c WHEN] -p PID [-s]

使用第一行的命令可以利用调试在 *PROGRAM* 运行中加载过滤器时动态捕获过滤器; 第二行的命令可以从 *PID* 对应的进程中提取出 seccomp 过滤器,或通过调试 *PID* 捕获后续的 seccomp 过滤器;一旦捕获到了过滤器, 将会以 _TEXT_ 的格式将它打印出来。你可以从两个格式中选择一个使用。 适用于运行一个程序是捕获BPF过滤器最简单的方式或者一个安装了 seccomp 过滤器的程序正在等待输入。

WHEN::
参数描述可以在 <<asm - 汇编>> 一节中找到。 `trace` 可能会打印更多有颜色的文本,包括针对 _TEXT_ 的语法高亮。

FILE::
当 *PROGRAM* 会产生很多输出到 _标准错误_ 时可能很有用。 `ceccomp` 允许用户关闭 _标准输入_ 和 _标准输出_ 来限制 *PROGRAM* 的输入和输出,所以 当运行 *PROGRAM* 时 `ceccomp` 使用 _标准错误_ 来打印消息。如果你想在别的文件中看见 _TEXT_ 的话请设置 *FILE* 。`-` 被视作 _标准输出_。

PROGRAM::
设置为你想运行的程序,并且 *program-args* 将作为它的参数, 就像运行 shell 命令 `exec PROGRAM program-args` 。

PID::
设置为你想检查的 pid。 *PID* 和 *PROGRAM* 相冲突;你只能在一条命令中动态运行一个程序, 或者检查一个 pid。没有 `-s` 标志,trace pid 会尝试使用 `ptrace(PTRACE_SECCOMP_GET_FILTER)` 从 *PID* 中提取 seccomp 过滤器,这个操作在一些系统上可能不可用。

-s, --seize::
*只适用于 TRACE PID 模式。* 设置这个标志将会覆盖 trace pid 的行为为像 trace prog 模式一样把调试器挂到 *PID* 上并持续跟踪 seccomp 过滤器的加载。_这个标志引入于 4.0 版本。_

-q, --quiet::
设置这个标志将会在检测到进程分叉、退出或加载 seccomp 过滤器时抑制多余的 *[INFO]* 输出。 _这个标志引入于 4.0 版本。_

NOTE: 要想从 *PID* 中提取过滤器,你需要 `CAP_SYS_ADMIN` (没有 `-s` 标志),同时还可能需要 `CAP_SYS_PTRACE` ,最简单的获取它们的方法是用 `sudo` 运行 `ceccomp` 。

NOTE: 从 _3.1 版本_ 开始引入了多进程支持,并且当被调试进程 fork/resolve/exit 时,将会打印一条额外的 INFO 信息。你可以使用像 `ceccomp trace -q PROG 2>/dev/null` 这样的命令丢弃它。

|===
|命令|差别

|`seccomp-tools dump`
|可以设置输出格式;每一个过滤器可以输出到不同的文件;当 *PROGRAM*
加载了 *LIMIT* 个过滤器后就杀死程序;将 *PROGRAM* 包装在 `sh -c` 中运行

|`ceccomp trace`
|所有过滤器被输出到同一个文件;永远不会杀死 *PROGRAM* ; *PROGRAM* 是直接被执行的,
所以不需要 `./` ;当 fork 时,显式打印事件;能够附加调试器到 pid 上动态捕捉 seccomp 过滤器
|===

=== probe - 快速测试常见的系统调用

    ceccomp probe [-c WHEN] [-o FILE] [-q] PROGRAM [program-args]

以 *program-args* 为参数运行 *PROGRAM* 来捕获 *第一个* seccomp 过滤器, 然后杀死所有子进程。适用于快速测试一个程序的规则并检测潜在的 seccomp 规则问题。

所有参数描述都可以在 <<trace - 运行时捕获过滤器>> 一节中找到。

这个子命令的输出是一系列常见的系统调用的模拟结果,例如 `execve` 、 `open` 等。 如果过滤器本身并不能阻拦系统调用,那你一眼就能看出来。

这个子命令的典型输出如下所示,更多完整的实例可以在 <<ceccomp 示例>> 一节中找到。

    open      -> ALLOW
    read      -> ALLOW
    write     -> ALLOW
    execve    -> KILL
    execveat  -> KILL
    mmap      -> ALLOW
    mprotect  -> ALLOW
    openat    -> ALLOW
    sendfile  -> ALLOW
    ptrace    -> ERRNO(1)
    fork      -> ALLOW

NOTE: `seccomp-tools` 没有等价的子命令。

== TEXT 语法参考

IMPORTANT: _4.0 版本_ 提高了词法分析器代码的可读性,伴随着大幅修改的语法。前缀为 `#` 的行现在是注释了。同时 _行号_ 被替换为 _标签_,现在词法分析器根据标签定义来决定跳转到哪里,而不是根据 _TEXT_ 文件中的行号。

这里有正确的 _TEXT_ 格式,以类 EBNF 语法描述:https://github.com/dbgbgtf1/Ceccomp/issues/17#issuecomment-3610531705。 没兴趣了解 EBNF?请继续阅读以下的例子。

其余未描述到的BPF操作都被内核禁止了。

=== 注释与标签

`ceccomp disasm` 展示了很多东西,但对于 asm 来说有些是可选的。

    #Label  CODE  JT   JF      K
    #---------------------------------
     L0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW
    #---------------------------------


任何在 `#` 之后的文本将被 asm 丢弃,就像一些脚本语言一样。

允许空行。

标签声明是一个从行首开始并以 `:` 结尾的标识符,例如 `L0001`。标识符是一个以字母为首,中间只包含字母、数字和下划线 `_` 的字符串。标签只在它是 `goto` 的目标时才是必要的,由 disasm 添加的多余的标签知识为了可读性。例如在 `if ($A == 0) goto somewhere` 中,`somewhere` 是一个标签并且必须在这行伪代码后声明。标签声明可以单独占据一行,也可以放置在伪代码的前面。

由 disasm 生成的 `CODE`、`JT`、`JF` 和 `K` 值会被 asm 丢弃,asm 之解析 `K` 之后的有效伪代码。

NOTE: `ceccomp disasm` 和 `seccomp-tools disasm` 的输出之间有很多细微的差别, 以下是一个典型的输出示例。同时有些伪代码是不同的,所以不要盲目将 seccomp-tools 的输出管道给 ceccomp。

    line  CODE  JT   JF      K
    =================================
    0000: 0x06 0x00 0x00 0x7fff0000  return ALLOW

=== 赋值

`A` 可以直接赋值为 seccomp 属性。由于内核限制, `X` 不能直接赋值为 seccomp 属性。

    $A = $arch
    $A = $syscall_nr

要给 `A` 赋值为这些64位长的字段,必须使用 `low_` 或者 `high_` 的前缀。

    $A = $low_pc
    $A = $high_pc
    $A = $low_args[0]
    $A = $high_args[0]
    ...
    $A = $low_args[5]
    $A = $high_args[5]

一个特殊的属性是 `sizeof(struct seccomp_data)` ,它可以直接赋值给 `A` 或 `X` 。

    $A = $scmp_data_len
    $X = $scmp_data_len

临时内存是32位的,要想访问它们,你可以使用十六进制或者十进制的索引。 `A` 和 `X` 都是可赋值的。给 `A` 或 `X` 赋值为立即数接受任意格式的数字,只要你使用 "0x" 或者 "0b" 等前缀正确表达数字是几进制的。

    $X = $mem[0]
    $A = $mem[0xf]
    $A = $mem[15] # both hex and dec index are OK
    $A = 0
    $X = 0x3b
    $A = 0b1111
    $A = 0333

你还可以将 `X` 赋值给 `A` 或者反过来。将 `X` 或 `A` 赋值给临时内存当然可以。

    $A = $X
    $X = $A
    $mem[3] = $X
    $mem[0x4] = $A

=== 数学运算

你可以以多种方式操作 `A` 。

    $A += 30
    $A -= 4
    $A *= 9
    $A /= 1
    $A &= 7
    $A >>= 6

右值也可以是 `X` 。

    $A &= $X
    $A |= $X
    $A ^= $X
    $A <<= $X

想要对 `A` 取反可以这么做。

    $A = -$A

=== 当...时向下跳转

无条件跳转:

    goto L3

当...跳转:

    if ($A == execve) goto L3
    if ($A != 1234) goto L4
    if ($A & $X) goto L5
    if !($A & 7) goto L6
    if ($A <= $X) goto L7

当条件为真时跳转到...,条件为假时跳转到...:
    
    if ($A > $X) goto L3, else goto L4
    if ($A >= 4567) goto L5, else goto L6

*只有* 在做条件判断时,你才能将数字替换为系统调用号或架构名。在以上的例子中, `0x3b` 被 `execve` 替换。所有系统调用名将会以你设置的架构解析为系统调用号。 如果你希望解析外部架构(不等于你设置的架构)的系统调用名, 请在前面附加架构名和一个点。例如,你设置的架构是 x86_64,并且你正在写 _aarch64_ 架构的规则,请这样写:

    if ($A == aarch64.read) goto 5

注意当你手动使用 `-a aarch64` 将架构设置为 _aarch64_ 时, 你可以在伪代码中忽略 `aarch64.` 。

=== 返回码

返回寄存器 `A` 的值:

    return $A

或者返回一个立即数,多余的字段放在 `()` 里。 `TRACE` 、 `TRAP` 和 `ERRNO` 接受一个额外的字段,如果没有 `()` ,它们将被视为 `行为(0)` 。

    return 0x13371337
    return KILL
    return KILL_PROCESS
    return TRAP(123)
    return ERRNO(0)
    return TRACE
    return TRACE(3)
    return LOG
    return NOTIFY

=== 简短的例子

下面的 _TEXT_ 对与 asm 来说是正确的,这段 _TEXT_ 阻止了 amd64 的 `execve` 和 `execveat` 系统调用:

    $A = $syscall_nr
    if ($A == execve) goto forbid
    if ($A == execveat) goto forbid
    return ALLOW
    forbid: return KILL

== 限制

为了更好的性能,Ceccomp asm 对 _TEXT_ 有一些限制。

1. 由于 _TEXT_ 是个文本文件,`'\0'` 不能出现在 _TEXT_ 中。
2. 一行必须短于 384 *字节*。
3. 一个 _TEXT_ 文件必须短于 4096 行。
4. 一个 _TEXT_ 文家必须小于 1 MiB。

并且对于 asm 和 disasm 来说,有效的伪代码(能被编码或解码为 BPF 的)必须少于等于 1024 条,这是内核规定的。

一个关于 ceccomp asm 有趣的知识:任何在 _TEXT_ 文件中基本的 ANSI 颜色,例如 `\x1b[31m`,会在处理时被丢弃。

== ceccomp 示例

ifdef::backend-manpage[]
手册不能显示图片,因此如果想看示例请参阅html版本。
endif::[]

ifndef::backend-manpage[]
=== asm 示例
image::asm.webp[]
=== disasm 示例
image::disasm.webp[]
=== emu 示例
image::emu.webp[]
=== trace 示例
运行程序:

image::trace.webp[]

如果设置了 `-o FILE` :

image::output_trick.webp[]

PID 模式:

image::trace_pid.webp[]

跟踪 PID 模式:

image::trace_seize.webp[]

zsh下PID模式可以使用补全:

image::trace_completion.webp[]

=== probe 示例
image::probe.webp[]
endif::[]

== 仓库

在 https://github.com/dbgbgtf1/Ceccomp 可以找到源代码。 欢迎提交 Pull Requests 和 Issues !

Copyright (C) 2025-现在,基于 GPLv3 或更新版本分发。