File: ceccomp-cn.adoc

package info (click to toggle)
ceccomp 3.5-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,528 kB
  • sloc: ansic: 3,154; python: 653; makefile: 240; sh: 226
file content (401 lines) | stat: -rw-r--r-- 13,605 bytes parent folder | download
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
= ceccomp(1)
dbgbgtf <dudududumaxver@outlook.com>; RocketDev <ma2014119@outlook.com>
{VERSION}, {TAG_TIME}
:doctype: manpage
:docdatetime: {TAG_TIME}
:manmanual: Ceccomp 手册
:mansource: ceccomp {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]
                   [-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} 。

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

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

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

|===
|命令|差别

|`seccomp-tools asm`
|使用它自己的语法汇编,有点像脚本

|`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} 。

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

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

TEXT::
一个可选的文件名,其中存放了需要被测试的 _TEXT_ 规则。不设置则从 _标准输入_ 中读取。

|===
|命令|差别

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

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

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

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

使用第一行的命令可以利用调试在 *PROGRAM* 运行中加载过滤器时动态捕获过滤器;
第二行的命令可以从 *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。

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

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

|===
|命令|差别

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

|`ceccomp trace`
|所有过滤器被输出到同一个文件;永远不会杀死 *PROGRAM* ; *PROGRAM* 是直接被执行的,
所以不需要 `./` ;当 fork 时,显式打印事件
|===

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

    ceccomp probe [-c WHEN] [-o FILE] 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 语法参考

一个有效的 _TEXT_ 可以只包含 *伪代码* 如 `$A = $arch` ,但是添加一些多余的
*行号* 可能可以辅助你手写 _TEXT_ 。 *行号* 从1开始,并且永远是十进制的。

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

=== 可选的其他字段

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

    Line  CODE  JT   JF      K
    ---------------------------------
    0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW
    ---------------------------------

以上例子中,只用 `return ALLOW` 这条 *伪代码* 是需要的。

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 = 0b111
    $X = 0777

你还可以将 `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 3

当...跳转:

    if ($A == execve) goto 3
    if ($A != 1234) goto 4
    if ($A & $X) goto 5
    if !($A & 7) goto 6
    if ($A <= $X) goto 7

当条件为真时跳转到...,条件为假时跳转到...:
    
    if ($A > $X) goto 3, else goto 4
    if ($A >= 4567) goto 5, else goto 6

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

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

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

=== 返回码

返回寄存器 `A` 的值:

    return $A

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

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

== ceccomp 示例

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

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

image::trace.png[]

如果设置了 `-o FILE` :

image::output_trick.png[]

PID模式:

image::trace_pid.png[]

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

image::trace_completion.png[]

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

== 仓库

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

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