可以统计到异常分支的系统调用。 |
不支持解析指针函数的调用关系。 |
| Strace工具统计 | 设备运行时,使用Strace跟踪业务进程或者跟踪测试进程。跟踪过程中会将系统调用的执行记录下来。收集日志后使用脚本解析日志生成Seccomp策略文件。 | 操作简单 | 代码分支全部覆盖才能完整统计使用的系统调用。 |
| Audit统计 | 进程使能Seccomp策略后,Seccomp机制会拦截非法系统调用,在内核日志生成含系统调用号信息的Audit日志。收集日志后使用脚本解析日志生成Seccomp策略文件。 | 可对上面两个方法进行查漏补缺。 | 日志有丢失的风险。
代码分支全部覆盖才能完整统计使用的系统调用。 |
> ![icon-note.gif](public_sys-resources/icon-note.gif)**免责声明:使用第三方软件或网站**
> 我们可能会推荐使用其它公司拥有或运营的软件、信息、产品或网站。我们通过超链接或其它方法作此推荐,以帮助您访问第三方资源。
> 虽然我们努力将您引导至有用的、值得信赖的资源,但我们无法保证由第三方资源提供或在第三方资源处提供的软件、信息、产品或服务,也无法跟踪这些资源的变化。因此,我们不对任何第三方资源的内容或结果的合规性、准确性、完整性负责,也不对因使用第三方资源提供的产品或服务或由第三方资源提供的产品或服务的使用或故障而导致的任何损失或损害负责。
#### 静态分析
1. 环境准备
1. 准备linux环境。
2. 下载交叉编译器arm-linux-musleabi与aarch64-linux-musl。
```shell
# 将执行工具路径加入环境变量
export PATH=$PATH:/path/to/arm-linux-musleabi-cross/bin
export PATH=$PATH:/path/to/aarch64-linux-musl-cross/bin
```
3. 下载openharmony源代码,
见[下载说明](../get-code/sourcecode-acquire.md)。
2. 通过编译seccomp_filter得到静态分析的依赖文件libsyscall_to_nr_arm与libsyscall_to_nr_arm64。
seccomp_filter在base/startup/init/services/modules/seccomp/BUILD.gn中声明,用于构建Seccomp的基础策略动态库。依赖文件最终会生成在//out/产品名称/gen/base/startup/init/services/modules/seccomp/gen_system_filter/路径中。
```shell
./build.sh --product-name 产品名称 --ccache --build-target seccomp_filter --target-cpu 指定CPU
# 将统计所依赖的文件复制到工具文件夹以备使用
cp out/产品名称/gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/
```
3. 复制generate_code_from_policy.py到统计系统调用工具的文件夹内。该脚本存在于//base/startup/init/services/modules/seccomp/scripts/路径下
```shell
# 进入Openharmony代码根目录
cd /root/to/OpenharmonyCode;
# 进入generate_code_from_policy.py所在目录
cd base/startup/init/services/modules/seccomp/scripts/;
# 复制generate_code_from_policy.py
cp generate_code_from_policy.py tools/;
```
4. 编译业务代码相关的ELF文件,由于32位架构的ELF文件反汇编代码跳转机制较复杂,故统一编译成64位ELF文件用来解析函数调用关系。
```shell
./build.sh --product-name 产品文件 --ccache --target-cpu arm64 --build-target 目标文件
```
5. 修改collect_elf_syscall.py脚本文件,将objdump与readelf工具的路径从空修改为工具在linux环境下的绝对路径。工具路径存放在//prebuilts文件夹下,具体路径一般在//prebuilts/clang/ohos/linux-x86_64/15.0.4/llvm/bin文件夹下。
```python
#modified the path of objdump and readelf path
def get_obj_dump_path():
obj_dump_path = '/path/to/prebuilts/clang/ohos/linux-x86_64/15.0.4/llvm/bin/llvm-objdump'
return obj_dump_path
def get_read_elf_path():
read_elf_path = '/path/to/prebuilts/clang/ohos/linux-x86_64/15.0.4/llvm/bin/llvm-readelf'
return read_elf_path
```
6. 使用脚本解析生成对应的策略文件xxx.seccomp.policy
脚本collect_elf_syscall.py在//base/startup/init/services/modules/seccomp/scripts/tools/路径下
**表7** collect_elf_syscall.py的参数说明
| 参数 | 说明 |
| --- | --- |
| --src-elf-path | ELF文件所在文件夹,例如,~/ohcode/out/rk3568,不以'/'结尾|
| --elf-name| ELF文件名,例如,libmedia_service.z.so|
| --src-syscall-path | libsyscall_to_nr_arm或libsyscall_to_nr_arm64,与--target-cpu架构对应 |
| --target-cpu | 架构号,表示需要统计系统调用的对应架构,该参数影响解析何种架构的libc文件,arm或arm64 |
| --filter-name | 生成的策略文件名名称,例如,输入值为test,生成的文件名为test.seccomp.policy |
使用collect_elf_syscall.py解析ELF文件。
```
# 产品以rk3568,ELF文件以libmedia_service.z.so为示例
python3 collect_elf_syscall.py --src-elf-path ~/ohcode/out/rk3568 --elf-name libmedia_service.z.so --src-syscall-path libsyscall_to_nr_arm64 --target-cpu arm64 --filter-name media_service
```
xxx.seccomp.policy结果示例
```
@allowList
getcwd;arm64
eventfd2;arm64
epoll_create1;arm64
epoll_ctl;arm64
dup;arm64
dup3;arm64
fcntl;arm64
ioctl;arm64
...
```
#### Strace统计
1. 使用arm-linux-musleabi与aarch64-linux-musl交叉编译器分别构建32位与64的Strace工具。
2. [跟踪测试进程](#跟踪测试进程),得到Strace日志。
3. [跟踪业务进程](#跟踪业务进程),得到Strace日志。
4. 利用脚本[解析Strace日志](#解析strace日志文件),得到Seccomp策略文件。
##### 跟踪测试进程
1. 将Strace工具推入设备中。
```shell
hdc shell mount -rw -o remount /
hdc file send /path/to/strace /system/bin/
hdc shell chmod a+x /system/bin/strace
```
2. 在[开发者测试框架](https://gitee.com/openharmony/testfwk_developer_test)本地代码进行嵌入式修改,使得执行测试套会执行Strace跟踪测试进程。
在src/core/driver/drivers.py修改_init_gtest函数与_run_gtest函数的内容,以下为修改内容。其中行首符号为“+”,则该行为新增内容,若符号为“-”,则是需删除的内容。
```python
--- a/src/core/driver/drivers.py
+++ b/src/core/driver/drivers.py
@@ -500,6 +500,8 @@ class CppTestDriver(IDriver):
"rm -rf %s" % self.config.target_test_path)
self.config.device.execute_shell_command(
"mkdir -p %s" % self.config.target_test_path)
+ self.config.device.execute_shell_command(
+ "mkdir -p /data/strace")
self.config.device.execute_shell_command(
"mount -o rw,remount,rw /")
if "fuzztest" == self.config.testtype[0]:
@@ -539,10 +541,11 @@ class CppTestDriver(IDriver):
test_para,
seed)
else:
- command = "cd %s; rm -rf %s.xml; chmod +x *; ./%s %s" % (
+ command = "cd %s; rm -rf %s.xml; chmod +x *; strace -ff -o /data/strace/%s.strace.log ./%s %s" % (
self.config.target_test_path,
filename,
filename,
+ filename,
test_para)
else:
coverage_outpath = self.config.coverage_outpath
```
3. 执行相关业务测试用例。
4. 从设备中/data/strace文件夹取出Strace日志。
```shell
hdc file recv /data/strace /path/to/base/startup/init/services/modules/seccomp/scripts/tools/
```
##### 跟踪业务进程
1. 在init仓代码中进行嵌入式修改,修改文件为//base/startup/init/services/init/init_common_service.c,在执行SetSystemseccompPolicy函数设置Seccomp策略前增加以下内容。以下为修改内容。其中行首符号为“+”,则该行为新增内容,若符号为“-”,则是需删除的内容,“xxxx”与进程[引导启动配置文件](subsys-boot-init-cfg.md)中的Services name保持一致。
```c
--- a/services/init/init_common_service.c
+++ b/services/init/init_common_service.c
@@ -155,7 +155,19 @@ static int SetPerms(const Service *service)
// set seccomp policy before setuid
INIT_ERROR_CHECK(SetSystemseccompPolicy(service) == SERVICE_SUCCESS, return SERVICE_FAILURE,
"set seccomp policy failed for service %s", service->name);
-
+ if (strncmp(service->name, "xxxx", strlen("xxxx")) == 0) {
+ pid_t pid = getpid();
+ pid_t pid_child = fork();
+ if (pid_child == 0) {
+ char pidStr[9] = {0};
+ sprintf_s(pidStr, 6, "%d", pid);
+ if (execl("/system/bin/strace", "/system/bin/strace", "-p", (const char *)pidStr, "-ff", "-o", "/data/strace/xxxx.strace.log", NULL) !=0 ) {
+ INIT_LOGE("strace failed");
+ }
+ }
+ sleep(5);
+ }
if (service->servPerm.uID != 0) {
if (setuid(service->servPerm.uID) != 0) {
INIT_LOGE("setuid of service: %s failed, uid = %d", service->name, service->servPerm.uID);
```
2. 修改文件后进行全量编译,并烧写镜像。
3. 关闭SElinux机制,将Strace工具推入设备中。
```shell
hdc shell setenforce 0
hdc shell mount -rw -o remount /
hdc file send /path/to/strace /system/bin/
hdc shell chmod a+x /system/bin/strace
```
4. 创建存放Strace日志的的文件夹。
```shell
hdc shell mkdir -p /data/strace
```
5. 终止业务进程,令其重启。可使用以下命令,xxx为业务进程名。
```shell
kill -9 $(pidof xxx)
```
6. 对设备进行相关业务操作,尽量使代码全量覆盖。
7. 从设备中/data/strace文件夹取出Strace日志到解析脚本的路径下。
```shell
hdc file recv /data/strace /path/to/base/startup/init/services/modules/seccomp/scripts/tools/
```
##### 解析Strace日志文件
1. 将解析日志时所依赖的文件复制到Strace日志文件夹,该依赖文件为[静态分析](#静态分析)第2步的产物。
```shell
cp out/产品名称/gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/strace/
```
2. 使用脚本解析生成对应的策略文件xxx.seccomp.policy
脚本strace_log_analysis.py在//base/startup/init/services/modules/seccomp/scripts/tools/路径下
**表8** strace_log_analysis.py的参数说明
| 参数 | 说明 |
| --- | --- |
| --src-path | 日志文件所在文件夹,需含libsyscall_to_nr_arm与libsyscall_to_nr_arm64例如,./strace,不以'/'结尾|
| --target-cpu | 架构号,与跟踪进程的对应的架构一致,arm或arm64 |
| --filter-name | 生成的策略文件名名称,例如,输入值为test,生成的文件名为test.seccomp.policy |
使用strace_log_analysis.py解析Strace日志。
```shell
cd base/startup/init/services/modules/seccomp/scripts/tools;
python3 strace_log_analysis.py --src-path strace --target-cpu 指定CPU --filter-name xxx
```
xxx.seccomp.policy结果示例
```
@allowList
getcwd;arm64
eventfd2;arm64
epoll_create1;arm64
epoll_ctl;arm64
dup;arm64
dup3;arm64
fcntl;arm64
ioctl;arm64
...
```
#### Audit统计
1. 使能初始Seccomp策略,使能方法见[进程使能个性化Seccomp策略](#进程使能个性化seccomp策略)章节的开发步骤。
2. 获取日志
1. 利用Shell命令创建存放日志的文件夹。
```shell
mkdir -p /data/audit
```
2. 利用Shell命令获取内核日志中与Seccomp相关的Audit日志,存放的日志以“.audit.log”结尾
```shell
cat /proc/kmsg | grep type=1326 > /data/audit/media_service.audit.log
```
3. 进行业务相关操作与触发段错误操作。
1. 执行触发段错误的方法:在业务代码加入以下代码,并在某处调用TriggerSegmentFault,对镜像重新进行构建及烧写。
```c
static void TriggerSegmentFault(void)
{
pid_t pid_child = fork();
if (pid_child == 0) {
char *test = (char *)0x1234;
*test = 1;
}
}
```
2. 设备启动后,利用Shell临时关闭SElinux,并终止业务进程,该进程会自动重启。
```shell
setenforce 0
```
4. 利用hdc命令从设备的/data/audit文件夹取出Audit日志到解析脚本的路径下。
```shell
hdc file recv /data/audit /path/to/base/startup/init/services/modules/seccomp/scripts/tools/
```
5. 解析Audit日志。
Audit日志示例
```shell
<5>[ 198.963101] audit: type=1326 audit(1659528178.748:27): auid=4294967295 uid=0 gid=1000 ses=4294967295 subj=u:r:appspawn:s0 pid=2704 comm="config_dialog_s" exe="/system/bin/appspawn" sig=31 arch=40000028 syscall=
208 compat=1 ip=0xf7b79400 code=0x80000000
```
**表9** Audit日志关键字段说明
| 参数 | 说明 |
| --- | --- |
| type | 类型,值为1326说明是seccomop类型日志 |
| sig | 信号量,31为SIGSYS,表示Seccomp发生拦截时给进程发出的信号 |
| arch | 架构标识,值为40000028表示arm,值为c00000b7表示arm64 |
| syscall | 系统调用号 |
| compat | 1表示为兼容模式,即arm64的内核使用了arm的系统调用 |
1. 将解析日志时所依赖的文件复制到日志文件夹以备使用,该依赖文件为[静态分析](#静态分析)第2步的产物。
```shell
cp out/产品名称/gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/audit/
```
2. 使用audit_log_analysis.py脚本解析日志生成xxx.seccomp.policy。工具路径在//base/startup/init/services/modules/seccomp/scripts/tools/下
**表10** audit_log_analysis.py的参数说明
| 参数 | 说明 |
| --- | --- |
| --src-path | 日志文件所在文件夹,需含libsyscall_to_nr_arm与libsyscall_to_nr_arm64。例如,./audit,不以'/'结尾|
| --filter-name | 生成的策略文件名名称,例如,输入值为test,生成的文件名为test.seccomp.policy |
```shell
cd base/startup/init/services/modules/seccomp/scripts/tools
python3 audit_log_analysis.py --src-path audit --filter-name xxx
```
### 合并多个策略文件
[统计系统调用](#统计系统调用方法)的执行过程中,可能会生成多个策略文件,这些策略文件中系统调用可能会有重复或者乱序。通过以下步骤可将多个策略文件进行合并、去重,按照arm64/arm顺序,系统调用号从小到大排序。
**表11** merge_policy.py的参数说明
| 参数 | 说明 |
| --- | --- |
| --src-files | 需处理的文件,需含libsyscall_to_nr_arm与libsyscall_to_nr_arm64|
| --filter-name | 生成的策略文件名名称,例如,输入值为test,生成的文件名为test.seccomp.policy |
1. 将合并文件所依赖的文件复制到日志文件夹以备使用。
```shell
cp out/产品名称/gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/
```
2. 使用merge_policy.py将policy1.seccomp.policy,policy2.seccomp.policy策略文件合并成xxxx.seccomp.policy
```shell
python3 merge_policy.py --src-files libsyscall_to_nr_arm --src-files libsyscall_to_nr_arm64 --src-files policy1.seccomp.policy --src-files policy2.seccomp.policy --filter-name xxxx
```