Analyze the ELF disassembly code to obtain the call relationship, collect statistics on the APIs that call the libc library, and then parse the LibC library to obtain the call relationship between the LibC APIs and the system call numbers. In this way, you will obtain the system call numbers used by the ELF file.|
Statistics collection is supported for system calls in abnormal branches. |
Parsing of call relationship is not supported for pointer functions. |
| Strace statistics | Use Strace to trace service processes when the device is running. During the trace, the invoked system calls are recorded into logs. Collect the logs after the trace is complete, and use a script to parse the logs and generate a Seccomp policy file.| Easy to use. | System calls can be completely collected only when all code branches are traversed. |
| Audit statistics | After the Seccomp policy is enabled for a process, Seccomp intercepts invalid system calls and records audit log information containing the system call numbers into kernel logs. Collect the logs after the trace is complete, and use a script to parse the logs and generate a Seccomp policy file.| This method can be used as a supplement to the preceding methods. | Logs may be lost.
System calls can be completely collected only when all code branches are traversed. |
#### Static Analysis
1. Prepare the environment.
1. Prepare a Linux environment.
2. Download the cross compilers arm-linux-musleabi and aarch64-linux-musl.
```shell
wget https://musl.cc/arm-linux-musleabi-cross.tgz
wget https://musl.cc/aarch64-linux-musl-cross.tgz
tar -zxvf arm-linux-musleabi-cross.tgz
tar -zxvf aarch64-linux-musl-cross.tgz
# Add the tool execution path to the environment variable.
export PATH=$PATH:/path/to/arm-linux-musleabi-cross/bin
export PATH=$PATH:/path/to/aarch64-linux-musl-cross/bin
```
3. Download the OpenHarmony source code. For details, see [Obtaining Source Code](../get-code/sourcecode-acquire.md).
2. Compile **seccomp_filter** to obtain the dependency files **libsyscall_to_nr_arm** and **libsyscall_to_nr_arm64**.
**seccomp_filter** is declared in **base/startup/init/services/modules/seccomp/BUILD.gn** and is used to build the basic dynamic policy library of Seccomp. After the compilation is complete, dependency files are generated in **//out/Product name /gen/base/startup/init/services/modules/seccomp/gen_system_filter/**.
```shell
./build.sh --product-name *product name* --ccache --build-target seccomp_filter --target-cpu *specified CPU*
# Copy the dependency files to the tool folder for later use.
cp out/*product name* /gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/
```
3. Copy the **generate_code_from_policy.py** script file to the tool folder. This script file is available at **//base/startup/init/services/modules/seccomp/scripts/**.
```shell
# Go to the root directory of the OpenHarmony source code.
cd /root/to/OpenHarmonyCode;
# Go to the directory where the **generate_code_from_policy.py** script file is located.
cd base/startup/init/services/modules/seccomp/scripts/;
# Copy the **generate_code_from_policy.py** script file.
cp generate_code_from_policy.py tools/;
```
4. Compile ELF files related to the service code. In the 32-bit architecture, the implementation of disassembly code redirection for ELF files is complex. Therefore, ELF files are all compiled into 64-bit ELF files to parse the function call relationship.
```shell
./build.sh --product-name *product file* --ccache --target-cpu arm64 --build-target *target file*
```
5. If full build has not been performed before and the dependent dynamic libraries for step 4 are not in the **//out** directory, copy the related dynamic libraries to the **//out** directory. The following code is for reference only. If other dynamic libraries are involved, copy them in a similar way.
```shell
# Go to the root directory of the source code.
cd /root/to/OpenHarmonyCode
# Create the **aarch64-linux-ohos** folder in **out/*product name*/lib.unstripped/** to store the dependent dynamic libraries.
mkdir out/*product name*/lib.unstripped/aarch64-linux-ohos
# Copy the related dynamic libraries to the //out directory.
cp prebuilts/clang/ohos/${host_platform_dir}/llvm/lib/clang/${clang_version}/lib/aarch64-linux-ohos/*.so out/*product name*/lib.unstripped/aarch64-linux-ohos/
cp prebuilts/clang/ohos/${host_platform_dir}/${clang_version}/llvm/lib/aarch64-linux-ohos/*.so out/*product name*/lib.unstripped/aarch64-linux-ohos/
```
6. Modify the **collect_elf_syscall.py** script file, and change the paths of the objdump and readelf tools to their absolute paths in the Linux environment. This script file is available at **base/startup/init/services/modules/seccomp/scripts/tools/**. The **objdump** and **readelf** tools available at **//prebuilts**.
```python
#modified the path of objdump and readelf path
def get_obj_dump_path():
obj_dump_path = '/path/to/llvm-objdump'
return obj_dump_path
def get_read_elf_path():
read_elf_path = '/path/to/llvm-readelf'
return read_elf_path
```
7. Use the **collect_elf_syscall.py** script file to parse and generate the corresponding policy file **xxx.seccomp.policy**.
**Table 7** Parameters in the collect_elf_syscall.py script file
| Parameter | Description |
| --- | --- |
| --src-elf-path | Folder where the ELF file is located, for example, **~/ohcode/out/rk3568**. Do not end the value with a slash (/).|
| --elf-name| ELF file name, for example, **libmedia_service.z.so**.|
| --src-syscall-path | **libsyscall_to_nr_arm** or **libsyscall_to_nr_arm64**, which corresponds to the architecture specified by **--target-cpu**. |
| --target-cpu | CPU architecture, that is, the architecture for which system calls are collected. This parameter determines the architecture for libC file parsing. Its value can be **arm** or **arm64**. |
| --filter-name | Name of the generated policy file. For example, if the input value is **test**, the generated file name is **test.seccomp.policy**. |
Use **collect_elf_syscall.py** to parse ELF files.
```
# The following example considers **rk3568** as the product and **libmedia_service.z.so** as the ELF file.
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
```
Example result of xxx.seccomp.policy
```
@allowList
getcwd;arm64
eventfd2;arm64
epoll_create1;arm64
epoll_ctl;arm64
dup;arm64
dup3;arm64
fcntl;arm64
ioctl;arm64
...
```
#### Strace Statistics
1. Use the cross compilers arm-linux-musleabi and aarch64-linux-musl to build the Strace tool for the 32-bit and 64-bit architectures, respectively.
2. [Trace the service process](#tracing-the-service-process) to obtain the Strace logs.
3. [Parse Strace logs](#parsing-strace-logs) by using scripts to obtain the Seccomp policy file.
##### Tracing the Service Process
1. Modify the embedded code in the init repository. Specifically, add the following content to **//base/startup/init/services/init/init_common_service.c** before executing the **SetSystemseccompPolicy** function to set the Seccomp policy. If the line starts with a plus sign (+), the line is added; if the line starts with a hyphen (-), the line is deleted. **xxxx** must be the same as the value of **Services name** in the [boot configuration file](subsys-boot-init-cfg.md) of the process.
```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. Perform a full build, and burn the image.
3. Disable SElinux, and push Strace to the device.
```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. Create a folder for storing Strace logs.
```shell
hdc shell mkdir -p /data/strace
```
5. Terminate the service process, and restart it. In the following command, **xxx** indicates the service process name.
```shell
kill -9 $(pidof xxx)
```
6. Perform service operations on the device. Make sure that all code is covered.
7. Obtain Strace logs from **/data/strace** on the device, and save them to the directory where the parsing script is located.
```shell
hdc file recv /data/strace /path/to/base/startup/init/services/modules/seccomp/scripts/tools/
```
##### Parsing Strace Logs
1. Copy the dependency files to the Strace log folder for later use. The dependency files are those generated in step 2 in [Static Analysis](#static-analysis).
```shell
cp out/*product name* /gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/strace/
```
2. Use the **strace_log_analysis.py** script file to parse and generate the corresponding policy file **xxx.seccomp.policy**.
The script file is available at **//base/startup/init/services/modules/seccomp/scripts/tools/**.
**Table 8** Parameters in the strace_log_analysis.py script file
| Parameter | Description |
| --- | --- |
| --src-path | Folder for storing log files. It must contain **libsyscall_to_nr_arm** and **libsyscall_to_nr_arm64**. The folder name must not end with a slash (/), for example, **./strace**.|
| --target-cpu | CPU architecture, which is the same as that of the traced process. Its value can be **arm** or **arm64**. |
| --filter-name | Name of the generated policy file. For example, if the input value is **test**, the generated file name is **test.seccomp.policy**. |
Use the **strace_log_analysis.py** script file to parse Strace logs.
```shell
cd base/startup/init/services/modules/seccomp/scripts/tools;
python3 strace_log_analysis.py --src-path strace --target-cpu *specified CPU* --filter-name xxx
```
Example result of xxx.seccomp.policy
```
@allowList
getcwd;arm64
eventfd2;arm64
epoll_create1;arm64
epoll_ctl;arm64
dup;arm64
dup3;arm64
fcntl;arm64
ioctl;arm64
...
```
#### Audit Statistics
1. Enable the initial Seccomp policy. For details, see [Customizing Seccomp Policies for a Process](#customizing-seccomp-policies-for-a-process).
2. Obtain logs.
1. Create a folder for storing logs.
```shell
mkdir -p /data/audit
```
2. Obtain Seccomp-related audit log information from kernel logs. The logs end with **.audit.log**.
```shell
cat /proc/kmsg | grep type=1326 > /data/audit/media_service.audit.log
```
3. Perform service-related operations and segment fault triggering operations.
1. To trigger a segment fault, add the following code to the service code to call **TriggerSegmentFault** at a certain point to rebuild and burn the image:
```c
static void TriggerSegmentFault(void)
{
pid_t pid_child = fork();
if (pid_child == 0) {
char *test = (char *)0x1234;
*test = 1;
}
}
```
2. After the device is started, run the following shell command to temporarily shut down SELinux and terminate the service process. The process then automatically restarts.
```shell
setenforce 0
```
4. Run the hdc command to obtain audit logs from the **/data/audit** on of the device, and save them to the directory where the parsing script is located.
```shell
hdc file recv /data/audit /path/to/base/startup/init/services/modules/seccomp/scripts/tools/
```
5. Parse the audit logs.
Example audit log:
```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
```
**Table 9** Key parameters in audit logs
| Parameter | Description |
| --- | --- |
| type | Type. The value **1326** indicates that the log is of the Seccomp type. |
| sig | Semaphore. The value **31** indicates **SIGSYS**, which is the signal sent to the process when Seccomp interception occurs. |
| arch | Architecture ID. The value **40000028** indicates **arm**, and the value **c00000b7** indicates **arm64**. |
| syscall | System call ID. |
| compat | The value **1** indicates the compatibility mode, that is, the arm64 kernel uses arm system calls.|
1. Copy the dependency files to the log folder for later use. The dependency files are those generated in step 2 in [Static Analysis](#static-analysis).
```shell
cp out/*product name* /gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/audit/
```
2. Run the **audit_log_analysis.py** script to parse logs and generate **xxx.seccomp.policy**. The tool is available at **//base/startup/init/services/modules/seccomp/scripts/tools/**.
**Table 10** Parameters in the audit_log_analysis.py script file
| Parameter | Description |
| --- | --- |
| --src-path | Folder for storing log files. It must contain **libsyscall_to_nr_arm** and **libsyscall_to_nr_arm64**. The folder name must not end with a slash (/), for example, **./audit**.|
| --filter-name | Name of the generated policy file. For example, if the input value is **test**, the generated file name is **test.seccomp.policy**.|
```shell
cd base/startup/init/services/modules/seccomp/scripts/tools
python3 audit_log_analysis.py --src-path audit --filter-name xxx
```
### Combining Multiple Policy Files
During [colltatistics on system calls](#system-call-statistic-methods), multiple policy files may be generated. In these policy files, system calls may be repeated or disordered. To solve these problems, you can combine policy files to sort system calls by arm64/arm and by system call number in ascending order.
**Table 11** Parameters in the merge_policy.py script file
| Parameter | Description |
| --- | --- |
| --src-files | Files to be processed, including **libsyscall_to_nr_arm** and **libsyscall_to_nr_arm64**.|
| --filter-name | Name of the generated policy file. For example, if the input value is **test**, the generated file name is **test.seccomp.policy**. |
1. Copy the dependency files to the log folder for later use.
```shell
cp out/*product name* /gen/base/startup/init/services/modules/seccomp/gen_system_filter/libsyscall_to_nr_arm* base/startup/init/services/modules/seccomp/scripts/tools/
```
2. Run the **merge_policy.py** script to merge **policy1.seccomp.policy** and **policy2.seccomp.policy** into **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
```