Fwanalyzer:文件系统镜像分析工具

2022-04-24 10:16:47 浏览数 (1)

FwAnalyzer是一个使用一组可配置规则分析(ext2/3/4),FAT/VFat,SquashFS,UBIFS文件系统镜像和目录内容的工具。FwAnalyzer依赖于e2tools用于ext文件系统,mtools用于FAT文件系统,squashfs-tools用于SquashFs文件系统,ubi_reader用于UBIFS文件系统。对ext2/3/4镜

FwAnalyzer是一个使用一组可配置规则分析(ext2/3/4),FAT/VFat,SquashFS,UBIFS文件系统镜像和目录内容的工具。FwAnalyzer依赖于e2tools用于ext文件系统,mtools用于FAT文件系统,squashfs-tools用于SquashFs文件系统,ubi_reader用于UBIFS文件系统。对ext2/3/4镜像的SELinux/xattr支持需要修补版本的e2tools。

概述

FwAnalyzer旨在提供一种快速分析文件系统镜像的工具。FwAnalyzer有一个专门的配置文件,该文件定义了文件和目录的各种规则,并针对给定的文件系统镜像运行已配置的检查。FwAnalyzer的输出是一个报告,其中包含违反配置中指定的任何规则的文件列表。该报告还包含有关文件系统镜像的元信息,以及从分析的文件系统中提取的信息(如果已配置)。报告使用JSON格式,因此可以轻松的将其集成到大型的分析步骤当中。

报告示例:

代码语言:javascript复制
{
    "fs_type": "extfs",
    "image_digest": "9d5fd9acc98421b46976f283175cc438cf549bb0607a1bca6e881d3e7f323794",
    "image_name": "test/test.img",
    "current_file_tree_path": "test/oldtree.json.new",
    "old_file_tree_path": "test/oldtree.json",
    "data": {
        "Version": "1.2.3",
        "date1 file": "Mon Oct  1 16:13:05 EDT 2018n"
    },
    "informational": {
        "/bin": [                "CheckFileTree: new file: 40755 1001:1001 1024 0 SeLinux label: -"
        ],
    },
    "offenders": {
        "/bin/elf_arm32": [                "script(check_file_elf_stripped.sh) returned=elf_arm32 is not stripped"
        ],
        "/file1": [                "File not allowed"
        ],
        "/file2": [                "File is WorldWriteable, not allowed",                "File Uid not allowed, Uid = 123"
        ],
    }
}

构建和开发

按照构建中描述的步骤安装所有所需项并构建FwAnalyzer。

使用 FwAnalyzer

命令行选项

cfg:string,配置文件的路径 cfgpath:string,配置文件的路径和包含的文件(可以重复) in:string,文件系统镜像文件或目录路径 out:string,使用’-'将报告输出到文件或标准输出 tree:string,覆盖目录以从中读取filetree文件 ee:如果存在违规,则error退出 invertMatch:反转(invert )正则表达式匹配(用于测试)

示例:

代码语言:javascript复制
fwanalyzer -cfg system_fwa.toml -in system.img -out system_check_output.json

使用存储在scripts目录中的自定义脚本的示例:

代码语言:javascript复制
PATH=$PATH:./scripts fwanalyzer -cfg system_fwa.toml -in system.img -out system_check_output.json

devices文件夹包含用于解包和处理特定设备类型和固件包格式(如Android)的helper脚本。它还包括可以包含在特定于目标的FwAnalyzer配置中的常规配置文件。

scripts文件夹包含可从FwAnalyzer调用的helper脚本,用于文件内容分析和数据提取。

配置选项

全局配置

全局配置用于定义一些常规参数。

FsType(文件系统类型)字段选择用于访问镜像中文件的后端。FsType支持的选项有:

dirfs:从运行fwanalyzer主机上的目录中读取文件(支持的FsTypeOptions:N/A) extfs:读取ext2/3/4文件系统镜像(支持的FsTypeOptions:selinux) squashfs:读取SquashFS文件系统镜像(支持的FsTypeOptions:N/A) ubifs:读取UBIFS文件系统镜像(支持的FsTypeOptions:N/A) vfatfs:读取VFat文件系统镜像(支持的FsTypeOptions:N/A) FsTypeOptions允许调整FsTyp驱动程序。 DigestImage选项将生成已分析的文件系统镜像的SHA-256摘要,该摘要将包含在输出中。

示例:

代码语言:javascript复制
[GlobalConfig]FsType        = "extfs"FsTypeOptions = "selinux"DigestImage   = true

输出示例:

代码语言:javascript复制
"fs_type": "extfs","image_digest": "9d5fd9acc98421b46976f283175cc438cf549bb0607a1bca6e881d3e7f323794","image_name": "test/test.img",

Include

Include语句用于将其他FwAnalyzer配置文件包含到包含该语句的配置中。include语句可以出现在配置的任何部分。-cfgpath参数设置包含文件的搜索路径。

示例:

代码语言:javascript复制
[Include."fw_base.toml"]

全局文件检查

GlobalFileChecks是应用于整个文件系统的更通用的检查。

Suid:bool,(可选)启用后,如果任何文件设置了粘滞位,分析将失败(默认值:false) SuidWhiteList:string array,(可选)允许Suid检查的白名单文件(按完整路径) WorldWrite:bool,(可选)启用后,如果任何用户都可以写入任何文件,则分析将失败(默认值:false) SELinuxLabel:string,(可选)启用后,如果文件没有SeLinux标签,分析将失败 Uids:int array,(可选)指定系统中允许的每个UID,每个文件都需要由此列表中指定的Uid所有 Gids:int array,(可选)指定系统中允许的每个GID,每个文件都需要由此列表中指定的Gid所有 BadFiles:string array,(可选)指定不需要的文件列表,允许使用通配符,如?,* 和 **(此列表中不应存在任何文件) BadFilesInformationalOnly:bool,(可选)BadFile检查的结果将仅为信息(默认值:false)

示例:

代码语言:javascript复制
[GlobalFileChecks]Suid          = trueSuidWhiteList = ["/bin/sudo"]SELinuxLabel  = falseWorldWrite    = trueUids          = [0,1001,1002]Gids          = [0,1001,1002]BadFiles      = ["/file99", "/file1", "*.h"]

输出示例:

代码语言:javascript复制
"offenders": {  "/bin/su": [ "File is SUID, not allowed" ],  "/file1":  [ "File Uid not allowed, Uid = 123" ],  "/world":  [ "File is WorldWriteable, not allowed" ],
}

File Stat Check

FileStatCheck可用于为特定文件或目录建模元数据。任何配置的变化都将被报告为违规(offender)。

AllowEmpty:bool,(可选)定义文件的大小可以为0(默认值:false) Uid:int,(可选)指定文件的UID,不指定UID或指定-1将跳过检查 Gid:int,(可选)指定文件的GID,不指定GID或指定-1将跳过检查 Mode:string,(可选)以八进制指定UN * X文件模式/权限,不指定模式将跳过检查 SELinuxLabel:string,(可选)文件的SELinux标签(如果没有设置,将跳过检查) LinkTarget:string,(可选)符号链接的目标,未指定链接目标将跳过检查。目前仅dirfs,squashfs和ubifs文件系统支持此功能。 Desc:string,(可选)是一个描述性字符串,如果检查失败,将附加到报告中 InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)

示例:

代码语言:javascript复制
[FileStatCheck."/etc/passwd"]AllowEmpty = falseUid        = 0Gid        = 0Mode       = "0644"Desc       = "this need to be this way"

输出示例:

代码语言:javascript复制
"offenders": {  "/file2": [ "File State Check failed: size: 0 AllowEmpyt=false : this needs to be this way" ],
}

文件路径所有者检查

FilePathOwner检查可用于为文件系统的整个tree建模文件/目录所有权。如果给定目录中的任何文件或目录不归指定的Uid和Gid(type:int)所有,则检查失败。

示例:

代码语言:javascript复制
[FilePathOwner."/bin"]Uid = 0Gid = 0

输出示例:

代码语言:javascript复制
"offenders": {  "/dir1/file3": [ "FilePathOwner Uid not allowed, Uid = 1002 should be = 0",                   "FilePathOwner Gid not allowed, Gid = 1002 should be = 0" ],
}

文件内容检查

FileContent检查允许检查文件的内容。可以使用四种不同的方法检查文件的内容。通过将InformationalOnly设置为true(默认为false),可以在非强制模式下运行文件内容检查。InformationalOnly检查将产生信息元素替代违规。

示例:整个文件体上的正则表达式

File:string,文件的完整路径 RegEx:string,posix/golang正则表达式 RegExLineByLine:bool,(可选)逐行应用正则表达式,匹配行将在结果中(默认值:false) 匹配:bool,(可选)指示正则表达式匹配或是不匹配(默认值:false) Desc:string,(可选)是一个描述性字符串,将附加到失败的检查 InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)

示例:

代码语言:javascript复制
[FileContent."RegExTest1"]RegEx = ".*Ver=1337.*"Match = trueFile  = "/etc/version"

示例:通过文件体计算的SHA-256摘要

File:string,文件的完整路径 Digest:string,HEX编码摘要 Desc:string,(可选)是一个描述性字符串,将附加到失败的检查 InformationalOnly:bool,(可选)检查结果仅供参考

示例:

代码语言:javascript复制
[FileContent."DigestTest1"]Digest = "8b15095ed1af38d5e383af1c4eadc5ae73cab03964142eb54cb0477ccd6a8dd4"File   = "/ver"

输出示例:

代码语言:javascript复制
"offenders": {  "/ver": [ "Digest (sha256) did not match found = 44c77e41961f354f515e4081b12619fdb15829660acaa5d7438c66fc3d326df3 should be = 8b15095ed1af38d5e383af1c4eadc5ae73cab03964142eb54cb0477ccd6a8dd4." ],
}

示例:运行一个外部脚本传递文件名到脚本

在执行脚本之前,将文件解压缩到具有临时名称的临时目录中。如果脚本在stdout或stderr上生成输出,则检查会产生违规。

File:string,文件或目录的完整路径 Script: string,脚本的完整路径 ScriptOptions:string array,(可选)第一个元素允许定义包含通配符的模式,如?,* 和 ** 应用于文件名(如果存在)它只会检查与模式匹配的文件,这在目录上运行脚本时非常有用。第二个元素允许传递参数到脚本。 File:string,文件的完整路径,如果路径指向目录,则为目录和子目录中的每个文件运行脚本 Desc:string,(可选)是一个描述性字符串,将附加到失败的检查 InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)

如果–存在,则表示下一个参数来自ScriptOptions[1]。该脚本使用以下参数运行:

代码语言:javascript复制
<tmp filename> <original filename> <uid> <gid> <mode in octal> <selinux label or "-" for no label> [--] [script options argument]

示例:

代码语言:javascript复制
[FileContent."ScriptTest1"]Script = "check_file_x8664.sh"File   = "/bin"

输出示例:

代码语言:javascript复制
"offenders": {  "/bin/elf_arm32": [ "script(check_file_x8664.sh) returned=elf_arm32 not a x86-64 elf file" ],
}

Json 字段对比

File:string,文件的完整路径 Json:string,字段名称,使用点(.)表示法访问对象内的字段,冒号(:)分隔所需的值。所有类型都将转换为字符串并作为字符串进行比较。Json数组可以通过提供索引而不是字段名来进行索引。 Desc:string,(可选)是一个描述性字符串,将附加到失败的检查 InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)

示例:

代码语言:javascript复制
[FileContent."System_Arch"]Json = "System.Arch:arm64"File   = "/system.json"Desc = "arch test"

输出示例:

代码语言:javascript复制
{  "System": {    "Version": 7,    "Arch": "arm32",    "Info": "customized"
  }
}
Example Output:
```json"offenders": {  "/system.json": [ "Json field System.Arch = arm32 did not match = arm64, System.Arch, arch test" ],
}

File Tree Check

FileTree检查生成完整的文件系统树(每个文件和目录的列表),并将其与先前保存的文件树进行比较。该检查将生成一个信息输出,列出新文件,已删除文件和已修改文件。

CheckPath(string array)指定应包含在检查中的路径。如果未设置CheckPath,它将设置为[“/”]并将包含整个文件系统。如果CheckPath设置为[],它将生成文件树,但不会检查任何文件。

OldFileTreePath指定从旧filetree读取的文件名,如果生成了新的filetree(例如因为旧文件树不存在),则新生成的filetree文件为OldFileTreePath,并添加“.new”后缀。

OldFileTreePath是相对于配置文件的。这意味着’-cfg testdir/test.toml’与 OldTreeFilePath = “test.json将尝试读取’testdir/test.json’。-tree命令行选项可用于覆盖路径:’-cfg testdir/test.toml -tree test1′将尝试读取’test1/test.json’。类似地,新生成的filetree文件将存储在同一目录中。

文件修改检查可使用以下参数自定义:

CheckPermsOwnerChange:如果更改了所有者或权限(模式),bool,(可选)会将文件标记为已修改(默认值:false) CheckFileSize:bool,(可选)将标记文件,因为修改后的大小已更改(默认值:false) CheckFileDigest:bool,(可选)会在内容发生变化时将文件标记为已修改(比较它的SHA-256摘要)(默认值:false) SkipFileDigest:bool,(可选)跳过计算文件摘要(用于处理非常大的文件,默认为:false)

示例:

代码语言:javascript复制
[FileTreeCheck]OldTreeFilePath       = "testtree.json"CheckPath             = [ "/etc", "/bin" ]CheckPermsOwnerChange = trueCheckFileSize         = trueCheckFileDigest       = false

输出示例:

代码语言:javascript复制
"informational": {    "/bin/bla": [ "CheckFileTree: new file: 40755 1001:1001 1024 0 SeLinux label: -" ]
}

目录内容检查

DirCheck(目录内容)检查在指定目录中指定一组允许或需要检查的文件。在该目录中找到的任何其他文件或目录都将被报告为违规。如果未找到Allowed文件,则检查将通过。如果找不到Required文件,则会将其报告为违规。

文件项可以包含通配符,如?,*和**。allowed模式已在golang文档中描述。

每个目录只能存在一个DirCheck项。

示例:

代码语言:javascript复制
[DirContent."/home"]Allowed = ["collin", "jon"]Required = ["chris"]

数据提取

DataExtract选项允许从文件中提取数据并将其包含在报告中。可以通过正则表达式,运行外部脚本或读取JSON对象来提取数据。提取的数据之后可由后处理脚本使用。

数据提取功能将数据作为key:value对的映射添加到报表中。key被定义为语句的名称或可选的Name参数。该值是正则表达式或脚本输出的结果。

示例:基于正则表达式的数据提取

正则表达式生成的输出将存储为此语句名称的值,下面的示例名为“Version”。

File:string,文件的完整路径 RegEx:string,带有一个匹配字段的正则表达式 Name:string,(可选)键名 Desc:string,(可选)描述

示例:

键“Version”将包含正则表达式的输出。

代码语言:javascript复制
[DataExtract."Version"]File   = "/etv/versions"RegEx  = ".*Ver=(. )n"Desc   = "Ver 1337 test"

输出示例:

代码语言:javascript复制
"data": {  "Version": "1.2.3",
}

示例:基于脚本的数据提取

脚本生成的输出将存储为此语句名称的值,下面的示例名为LastLine。

File:string,文件的完整路径 Script:string,脚本的完整路径 Name:string,(可选)键名 Desc:string,(可选)描述

该脚本使用以下参数运行:

代码语言:javascript复制
<tmp filename> <original filename> <uid> <gid> <mode in octal> <selinux label or "-" for no label>

示例:

键“script_test”将包含脚本的输出。该语句的名称为“scripttest”

代码语言:javascript复制
[DataExtract.scripttest]File   = "/etc/somefile"Script = "extractscripttest.sh"Name   = "script_test"

输出示例:

代码语言:javascript复制
"data": {  "script_test": "some data",
}

示例:JSON数据提取

脚本生成的输出将存储为此语句名称的值,下面的示例名为LastLine。

File:string,文件的完整路径 Json:string,使用点(.)表示法访问对象中字段的字段名称 Name:string,(可选)键名 Desc:string,(可选)描述

示例:

键“ROS_Info”将包含来自/etc/os_version.json下System对象的Info字段内容。

代码语言:javascript复制
{
  "System": {
    "Version": 7,
    "Arch": "arm32",
    "Info": "customized"
  }
}
代码语言:javascript复制
[DataExtract.OS_Info]File   = "/etc/os_version.json"Json   = "System.Info"Name   = "OSinfo"

输出示例:

代码语言:javascript复制
"data": {  "OSinfo": "customized",
}

可以通过提供索引而不是字段名来索引Json数组。

示例:高级用法

DataExtract语句允许具有相同名称(相同键)的多个条目。这对于配置多种提取相同信息的方法非常有用。生成有效输出的第一个数据提取语句将设置给定键的值。这适用于正则表达式和脚本以及两者同时使用。

下面的示例显示了两个语句,它们都将为键“Version”创建键值对。如果“1”没有产生有效输出,则尝试下一个输出,在本例中为“2”。

示例:

代码语言:javascript复制
[DataExtract."1"]File  = "/etc/versions"RegEx = ".*Ver=(. )n"Name  = "Version"[DataExtract."2"]File  = "/etc/OSVersion"RegEx = ".*OS Version: (. )n"Name  = "Version"

*参考来源:GitHub

0 人点赞