首页
社区
课程
招聘
[原创]EMBA安装、使用与源代码分析_0
发表于: 2023-5-1 15:56 25165

[原创]EMBA安装、使用与源代码分析_0

2023-5-1 15:56
25165

项目地址:https://github.com/e-m-b-a/emba
本次分析对象的最后一次提交:https://github.com/e-m-b-a/emba/commit/de9dc53980962c8a62ea1239c9039e87962b32e5
克隆项目

EMBA 被设计为渗透测试人员的中央固件分析工具。它支持完整的安全分析过程,从固件提取过程开始,通过仿真进行静态分析和动态分析,最后生成 Web 报告。EMBA 自动发现固件中可能存在的弱点和漏洞。例如不安全的二进制文件、旧的和过时的软件组件、可能易受攻击的脚本或硬编码密码。EMBA是一个命令行工具,可以选择生成易于使用的 Web 报告以供进一步分析。
EMBA 结合了多种成熟的分析工具,可以通过一个简单的命令启动。之后,它会测试固件是否存在可能的安全风险和感兴趣的区域以进行进一步调查。无需手动安装所有帮助程序,执行集成安装脚本后,您就可以测试固件了。
EMBA 旨在协助渗透测试人员,而不是作为一个没有人工交互的独立工具。EMBA 应该提供尽可能多的关于固件的信息,测试人员可以决定重点领域,并负责验证和解释结果。
官方视频:
https://youtu.be/_dvdy3klFFY
项目采用纯Shell编写,非常适合这种需要结合许多外部工具并执行大量命令的工具。
项目统计:
Summary
Date : 2023-04-19 10:38:56
Total : 225 files, 28187 codes, 3873 comments, 4716 blanks, all 36776 lines
Languages

Directories

由代码量可知其主要功能集中在modules文件夹内。通过后面的分析可以知道,EMBA将根据运行参数分别调用各个模块对固件进行静态或动态的分析。

项目结构

描述:

建议:

注意!
国内用户记得配置代理,不然git啥的会报错。另外,sudo运行的命令不会继承当前用户的环境变量。
cve-search 的安装需要配置代理,需要修改安装流程(不然特别特别慢,还容易失败)

这将安装所需要的依赖工具,例如cve-search、下载大约6GB的docker镜像等等。

手动构建:
docker-compose build emba

安装完后测试 cve-search:./external/cve-search/bin/search.py -p busybox
图片描述
默认方式安装和运行操作都将使用 docker-compose 运行 emba

首先加载helpersinstaller文件夹下的helpers_emba_load_strict_settings.sh,并执行其中的load_strict_mode_settings函数用于加载严格模式的设置
安装docker
加载所有installer文件夹下的*.sh(但是一般都是以函数的形式存在,加载时并不会安装)
检查参数个数,必须为1
检查参数cCdDFghlr,设置对应的安装标记,然后根据安装标记进行针对性安装。在此过程还会检查wsl等环境进行针对性安装。
cve-search is always installed on the host,使用IF20_cve_search函数。

cve-search使用mongodb存储,并会使用pip安装redis等组件。

必须指定日志和固件文件:

使用 checksec.shkernel checker仅测试内核配置:

如果添加-f ./firmware,它将忽略-k并在固件中搜索内核配置
checksec相信打过 pwn 的人应该都很熟悉。关于 checksec 的 kernel checker:
checksec 是一种用于检查Linux系统安全性的工具,其中包括一个kernel checker。checksec kernel checker通过检查当前正在运行的内核的各种安全配置选项来帮助用户评估其安全性。
checksec kernel checker可以检查以下配置选项:

通过检查这些配置选项,checksec kernel checker可以提供有关系统安全性的重要信息,并帮助用户确定是否需要采取额外的安全措施。

运行主文件。首先定义了一些函数,然后在最后调用主函数,并传递所有参数:
图片描述
然后解析参数、检查参数、文件、路径、依赖工具等。设置相关环境变量。
监控docker日志和通知:
图片描述
helpers\helpers_emba_print.sh:
图片描述

如果指定了-t参数,则会设置THREADEDMAX_MOD_THREADS参数(MAX_MOD_THREADS为核心数乘2):

预处理HTML输出

-k参数运行:

会创建一个子进程,用来监控剩余空间容量:

运行容器,并执行分析:
图片描述
初始化状态栏:

预检查模块(所有以 P 开头的模块)

关键在run_modules "P" "${THREADED}" "0"这一行,我们看看这个函数的定义:
emba line 111

函数比较长。接收三个参数的作用在声明前写的很清楚。大概功能:根据传入的参数和一些其他条件,动态地加载和运行符合要求的模块脚本,有时候可以进行多线程并发执行。另外,还会检查某个模块是否已经运行过,并在必要时跳过不需要重新运行的模块,提高运行效率。如果开启了 HTML 选项,则会生成报告文件。
固件检查模块(S模块)

同理,会运行所有S模块,并等待子进程结束。必要时会使用多线程。
实时模拟模块(L模块)

会使用单线程运行。并运行所有L开头的模块。
报告模块(F模块)

单线程运行"F"模块的程序,用于处理测试结果并生成报告。如果指定了-B参数则删除状态栏。
在测试结束后,如果指定了-r参数则将产生的临时文件夹和文件删除(只有运行了S模块才会产生)。
总结:主运行文件将检查参数等,并调用各个模块:

因此各个模块源代码的审计将是理解固件分析实现的重点。

给出有关所提供的固件二进制文件的一些非常基本的信息。
主函数:

fw_bin_detector函数:

用patools工具提取zip、tar、tgz

binwalk_deep_extract_helper函数:

提取vmdk镜像

vmdk_extractor函数:

提取D-link加密固件镜像

dlink_SHRS_enc_extractor函数:

dlink_enc_img_extractor函数:

使用Freetz-NG提取AVM固件映像
avm_extractor函数:

显示Uboot映像的内部
P13_uboot_mkimage函数:

挂载和提取extX映像
ext_extractor函数:

提取ubi文件系统
ubi_extractor函数:

用EnGenius提取固件镜像
engenius_enc_extractor函数:

提取没有加密的gpg压缩的固件镜像
gpg_decompress_extractor函数:

从QNAP中提取加密固件映像
首先调用 qnap_enc_extractor 函数:

qnap_extractor函数:

挂载和提取BSD UFS映像
ufs_extractor函数

提取Foscam公司的加密固件映像
首先调用foscam_enc_extractor函数:

foscam_ubi_extractor函数:

提取Buffalo公司的加密固件映像
buffalo_enc_extractor函数:

提取被密码保护的Zyxel固件镜像
zyxel_zip_extractor函数:

binwalk_deep_extract_helper函数(在P60_firmware_bin_extractor.sh文件内定义):

挂载和提取Qemu QCOW2镜像
qcow_extractor函数:

copy_qemu_nbd函数:

提取Android OTA更新文件
android_ota_extractor函数:

提取UEFI镜像与BIOSUtilities
uefi_extractor函数:

分析固件与binwalk,检查熵和提取固件到日志目录
P59_binwalk_extractor函数:

binwalking函数:
对固件进行简单地分析并给出熵图。

detect_root_dir_helper函数:
通过特殊二进制路径来推测文件系统根目录在哪

linux_basic_identification_helper函数:
用于统计linux文件系统中基本的特征文件或文件名

分析固件与binwalk,检查熵和提取固件到日志目录
P60_firmware_bin_extractor函数:

check_disk_space函数:
用于检查磁盘空间

deep_extractor函数:
深度提取模式

deeper_extractor_helper函数:
进行深度提取

识别和提取典型的软件包档案,如deb, apk, ipk
逻辑比较简单,识别文件名然后针对性解包

将带有unblob的固件提取到模块日志目录(仅用于评估)
逻辑比较简单,直接使用unblob进行提取,然后识别文件系统根目录、统计信息。

一些准备工作(检查固件、架构检查等)
检查固件、准备二进制文件列表、架构检查、文件系统根目录识别、设置配置文件路径、准备文件列表。

git clone https://github.com/e-m-b-a/emba.git
git clone https://github.com/e-m-b-a/emba.git
language files code comment blank total
Shell Script 173 24,559 3,657 4,361 32,577
Properties 22 1,269 70 20 1,359
XML 2 1,034 1 3 1,038
YAML 17 555 92 86 733
CSS 1 343 14 77 434
Markdown 8 309 14 146 469
HTML 1 106 22 15 143
Docker 1 12 3 8 23
 
path files code comment blank total
. 225 28,187 3,873 4,716 36,776
. (Files) 9 812 87 220 1,119
.github 19 549 81 116 746
.github (Files) 2 6 1 15 22
.github\ISSUE_TEMPLATE 2 47 0 19 66
.github\workflows 15 496 80 82 658
config 62 1,395 110 101 1,606
config (Files) 22 1,269 70 20 1,359
config\report_templates 40 126 40 81 247
helpers 26 5,523 729 672 6,924
installer 26 1,734 519 361 2,614
modules 83 18,174 2,347 3,246 23,767
modules (Files) 77 17,704 2,269 3,162 23,135
modules\L10_system_emulation 6 470 78 84 632
 
> tree .                
.
├── check_project.sh
├── CODE_OF_CONDUCT.md
├── config
│   ├── banner
│   │   ├── ...
│   │   └── Vegas_Edt-v1.1.0.txt
│   ├── report_templates
│   │   ├── ...
│   │   └── S99_grepit-pre.sh
│   ├── ...
│   └── trickest_cve-db.txt
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── docker-compose.yml
├── Dockerfile
├── emba
├── helpers
│   ├── ...
│   └── trickest_db_update.sh
├── installer
│   ├── ...
│   └── wickStrictModeFail.sh
├── installer.sh
├── LICENSE
├── licenses
│   └── LICENSE-MIT-firmae-firmadyne.txt
├── modules
│   ├── L10_system_emulation
│   │   ├── ...
│   │   └── run_service.sh
│   ├── ...
│   └── template_module.sh
├── README.md
├── scan-profiles
│   ├── ...
│   └── full-scan.emba
└── SECURITY.md
 
9 directories, 250 files
> tree .                
.
├── check_project.sh
├── CODE_OF_CONDUCT.md
├── config
│   ├── banner
│   │   ├── ...
│   │   └── Vegas_Edt-v1.1.0.txt
│   ├── report_templates
│   │   ├── ...
│   │   └── S99_grepit-pre.sh
│   ├── ...
│   └── trickest_cve-db.txt
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── docker-compose.yml
├── Dockerfile
├── emba
├── helpers
│   ├── ...
│   └── trickest_db_update.sh
├── installer
│   ├── ...
│   └── wickStrictModeFail.sh
├── installer.sh
├── LICENSE
├── licenses
│   └── LICENSE-MIT-firmae-firmadyne.txt
├── modules
│   ├── L10_system_emulation
│   │   ├── ...
│   │   └── run_service.sh
│   ├── ...
│   └── template_module.sh
├── README.md
├── scan-profiles
│   ├── ...
│   └── full-scan.emba
└── SECURITY.md
 
9 directories, 250 files
sudo ./installer.sh -d
sudo ./installer.sh -d
# installer\IF20_cve_search.sh line 38
      # we always need the cve-search stuff:
      if ! [[ -d external/cve-search ]]; then
        git clone https://github.com/EMBA-support-repos/cve-search.git external/cve-search
 
# external\cve-search\lib\Config.py line 37
    default = {
        "redisHost": "localhost",
        "redisPort": 6379,
        "redisQ": 9,
        ...
# installer\IF20_cve_search.sh line 38
      # we always need the cve-search stuff:
      if ! [[ -d external/cve-search ]]; then
        git clone https://github.com/EMBA-support-repos/cve-search.git external/cve-search
 
# external\cve-search\lib\Config.py line 37
    default = {
        "redisHost": "localhost",
        "redisPort": 6379,
        "redisQ": 9,
        ...
 
sudo ./emba.sh -l ./log -f ./firmware
sudo ./emba.sh -l ./log -f ./firmware
sudo ./emba.sh -l ./logs/kernel_conf -k ./kernel.config
sudo ./emba.sh -l ./logs/kernel_conf -k ./kernel.config
# calculate the maximum threads per module
if [[ ${THREADED} -eq 1 ]] && [[ "${MAX_MOD_THREADS}" -eq 0 ]]; then
  # the maximum threads per modules - if this value does not match adjust it via
  # local MAX_MOD_THREADS=123 in module area
  export MAX_MOD_THREADS="$(( 2* "$(grep -c ^processor /proc/cpuinfo)" ))"
fi
# calculate the maximum threads per module
if [[ ${THREADED} -eq 1 ]] && [[ "${MAX_MOD_THREADS}" -eq 0 ]]; then
  # the maximum threads per modules - if this value does not match adjust it via
  # local MAX_MOD_THREADS=123 in module area
  export MAX_MOD_THREADS="$(( 2* "$(grep -c ^processor /proc/cpuinfo)" ))"
fi
# Change log output to color for web report and prepare report
if [[ ${HTML} -eq 1 ]] ; then
  if [[ ${FORMAT_LOG} -eq 0 ]] ; then
    FORMAT_LOG=1
    print_output "[*] Activate colored log for webreport" "no_log"
  fi
  print_output "[*] Prepare webreport" "no_log"
  prepare_report
fi
# Change log output to color for web report and prepare report
if [[ ${HTML} -eq 1 ]] ; then
  if [[ ${FORMAT_LOG} -eq 0 ]] ; then
    FORMAT_LOG=1
    print_output "[*] Activate colored log for webreport" "no_log"
  fi
  print_output "[*] Prepare webreport" "no_log"
  prepare_report
fi
#######################################################################################
# Kernel configuration check
#######################################################################################
if [[ "${KERNEL}" -eq 1 ]]; then
  if [[ ${IN_DOCKER} -eq 1 ]] && [[ -f "${LOG_DIR}"/kernel_config ]]; then
    export KERNEL_CONFIG="${LOG_DIR}"/kernel_config
  fi
 
  if ! [[ -f "${KERNEL_CONFIG}" ]] ; then
    print_output "[-] Invalid kernel configuration file: ${ORANGE}${KERNEL_CONFIG}${NC}" "no_log"
    exit 1
  else
    if [[ ${IN_DOCKER} -eq 0 ]] ; then
      # we copy the kernel config file from outside the container into our log directory
      # further modules are using LOG_DIR/kernel_config for accessing the kernel config
      if [[ -d "${LOG_DIR}" ]] ; then
        cp "${KERNEL_CONFIG}" "${LOG_DIR}"/kernel_config
      else
        print_output "[!] Missing log directory" "no_log"
        exit 1
      fi
    fi
  fi
fi
#######################################################################################
# Kernel configuration check
#######################################################################################
if [[ "${KERNEL}" -eq 1 ]]; then
  if [[ ${IN_DOCKER} -eq 1 ]] && [[ -f "${LOG_DIR}"/kernel_config ]]; then
    export KERNEL_CONFIG="${LOG_DIR}"/kernel_config
  fi
 
  if ! [[ -f "${KERNEL_CONFIG}" ]] ; then
    print_output "[-] Invalid kernel configuration file: ${ORANGE}${KERNEL_CONFIG}${NC}" "no_log"
    exit 1
  else
    if [[ ${IN_DOCKER} -eq 0 ]] ; then
      # we copy the kernel config file from outside the container into our log directory
      # further modules are using LOG_DIR/kernel_config for accessing the kernel config
      if [[ -d "${LOG_DIR}" ]] ; then
        cp "${KERNEL_CONFIG}" "${LOG_DIR}"/kernel_config
      else
        print_output "[!] Missing log directory" "no_log"
        exit 1
      fi
    fi
  fi
fi
disk_space_monitor() {
  local DDISK="$LOG_DIR"
 
  while ! [[ -f "$MAIN_LOG" ]]; do
    sleep 1
  done
 
  while true; do
    # print_output "[*] Disk space monitoring active" "no_log"
    FREE_SPACE=$(df --output=avail "$DDISK" | awk 'NR==2')
    if [[ "$FREE_SPACE" -lt 10000000 ]]; then
      print_ln "no_log"
      print_output "[!] WARNING: EMBA is running out of disk space!" "main"
      print_output "[!] WARNING: EMBA is stopping now" "main"
      df -h || true
      print_ln "no_log"
      # give the container some more seconds for the cleanup process
      [[ "$IN_DOCKER" -eq 0 ]] && sleep 5
      cleaner 1
    fi
 
    if [[ -f "$MAIN_LOG" ]]; then
      if grep -q "Test ended\|EMBA failed" "$MAIN_LOG" 2>/dev/null; then
        break
      fi
    fi
 
    sleep 5
  done
}
disk_space_monitor() {
  local DDISK="$LOG_DIR"
 
  while ! [[ -f "$MAIN_LOG" ]]; do
    sleep 1
  done
 
  while true; do
    # print_output "[*] Disk space monitoring active" "no_log"
    FREE_SPACE=$(df --output=avail "$DDISK" | awk 'NR==2')
    if [[ "$FREE_SPACE" -lt 10000000 ]]; then
      print_ln "no_log"
      print_output "[!] WARNING: EMBA is running out of disk space!" "main"
      print_output "[!] WARNING: EMBA is stopping now" "main"
      df -h || true
      print_ln "no_log"
      # give the container some more seconds for the cleanup process
      [[ "$IN_DOCKER" -eq 0 ]] && sleep 5
      cleaner 1
    fi
 
    if [[ -f "$MAIN_LOG" ]]; then
      if grep -q "Test ended\|EMBA failed" "$MAIN_LOG" 2>/dev/null; then
        break
      fi
    fi
 
    sleep 5
  done
}
initial_status_bar() {
  # PID for box updater threads
  export PID_SYSTEM_LOAD=""
  export PID_STATUS=""
  export PID_MODULES=""
  export PID_STATUS_2=""
  # Path to status tmp file
  # each line is dedicated to a specific function
  # 1: Count of boxes visible
  # 2: CPU load string (needs to be cached, because it takes about a second to get the information)
  # 3: Start time of status bar - not exactly the same as the EMBA timer, but near enough to get an idea
  # 4: Count modules
  export STATUS_TMP_PATH=""
 
  # overwrites $LINES and "$COLUMNS" with the actual values of the window
  shopt -s checkwinsize; (:;:)
  local LINE_POS="$(( LINES - 6 ))"
  printf "\e[%s;1f\e[0J\e[%s;1f" "$LINE_POS" "$LINE_POS"
  reset
 
  # create new tmp file with empty lines
  STATUS_TMP_PATH="$TMP_DIR/status"
  if [[ ! -f "$STATUS_TMP_PATH" && -d "$TMP_DIR" ]] ; then
    echo -e "\\n\\n\\n\\n" > "$STATUS_TMP_PATH"
  fi
  # calculate boxes fitting and draw them
  local INITIAL_STR=""
  INITIAL_STR="\e[${LINE_POS};1f\e[0J\e[0;${LINE_POS}r\e[${LINE_POS};1f"
  if [[ $LINES -gt 10 ]] ; then
    # column has to be increased with 2 characters because of possible arrow column
    local ARROW_POS=0
    STATUS_BAR_BOX_COUNT=0
    if [[ $COLUMNS -ge 27 ]] ; then
      INITIAL_STR+="$(draw_box 26 "SYSTEM LOAD" 0)"
      STATUS_BAR_BOX_COUNT=1
      ARROW_POS=27
    fi
    if [[ $COLUMNS -ge 54 ]] ; then
      INITIAL_STR+="$(draw_box 26 "STATUS" 27)"
      STATUS_BAR_BOX_COUNT=2
      ARROW_POS=53
    fi
    if [[ $COLUMNS -ge 80 ]] ; then
      INITIAL_STR+="$(draw_box 26 "MODULES" 53)"
      STATUS_BAR_BOX_COUNT=3
      ARROW_POS=79
    fi
    if [[ $COLUMNS -ge 104 ]] ; then
      INITIAL_STR+="$(draw_box 26 "STATUS 2" 79)"
      STATUS_BAR_BOX_COUNT=4
    fi
 
    if [[ $STATUS_BAR_BOX_COUNT -lt 4 ]] ; then
      INITIAL_STR+="$(draw_arrows "$ARROW_POS")"
    fi
  fi
  if [[ -f "$STATUS_TMP_PATH" ]] ; then
    sed -i "1s/.*/$STATUS_BAR_BOX_COUNT/" "$STATUS_TMP_PATH" 2> /dev/null || true
  fi
  INITIAL_STR+="\e[H"
  # set cursor and boxes
  printf "%b" "$INITIAL_STR"
  box_updaters
}
initial_status_bar() {
  # PID for box updater threads
  export PID_SYSTEM_LOAD=""
  export PID_STATUS=""
  export PID_MODULES=""
  export PID_STATUS_2=""
  # Path to status tmp file
  # each line is dedicated to a specific function
  # 1: Count of boxes visible
  # 2: CPU load string (needs to be cached, because it takes about a second to get the information)
  # 3: Start time of status bar - not exactly the same as the EMBA timer, but near enough to get an idea
  # 4: Count modules
  export STATUS_TMP_PATH=""
 
  # overwrites $LINES and "$COLUMNS" with the actual values of the window
  shopt -s checkwinsize; (:;:)
  local LINE_POS="$(( LINES - 6 ))"
  printf "\e[%s;1f\e[0J\e[%s;1f" "$LINE_POS" "$LINE_POS"
  reset
 
  # create new tmp file with empty lines
  STATUS_TMP_PATH="$TMP_DIR/status"
  if [[ ! -f "$STATUS_TMP_PATH" && -d "$TMP_DIR" ]] ; then
    echo -e "\\n\\n\\n\\n" > "$STATUS_TMP_PATH"
  fi
  # calculate boxes fitting and draw them
  local INITIAL_STR=""
  INITIAL_STR="\e[${LINE_POS};1f\e[0J\e[0;${LINE_POS}r\e[${LINE_POS};1f"
  if [[ $LINES -gt 10 ]] ; then
    # column has to be increased with 2 characters because of possible arrow column
    local ARROW_POS=0
    STATUS_BAR_BOX_COUNT=0
    if [[ $COLUMNS -ge 27 ]] ; then
      INITIAL_STR+="$(draw_box 26 "SYSTEM LOAD" 0)"
      STATUS_BAR_BOX_COUNT=1
      ARROW_POS=27
    fi
    if [[ $COLUMNS -ge 54 ]] ; then
      INITIAL_STR+="$(draw_box 26 "STATUS" 27)"
      STATUS_BAR_BOX_COUNT=2
      ARROW_POS=53
    fi
    if [[ $COLUMNS -ge 80 ]] ; then
      INITIAL_STR+="$(draw_box 26 "MODULES" 53)"
      STATUS_BAR_BOX_COUNT=3
      ARROW_POS=79
    fi
    if [[ $COLUMNS -ge 104 ]] ; then
      INITIAL_STR+="$(draw_box 26 "STATUS 2" 79)"
      STATUS_BAR_BOX_COUNT=4
    fi
 
    if [[ $STATUS_BAR_BOX_COUNT -lt 4 ]] ; then
      INITIAL_STR+="$(draw_arrows "$ARROW_POS")"
    fi
  fi
  if [[ -f "$STATUS_TMP_PATH" ]] ; then
    sed -i "1s/.*/$STATUS_BAR_BOX_COUNT/" "$STATUS_TMP_PATH" 2> /dev/null || true
  fi
  INITIAL_STR+="\e[H"
  # set cursor and boxes
  printf "%b" "$INITIAL_STR"
  box_updaters
}
#######################################################################################
# Pre-Check (P-modules)
#######################################################################################
if [[ "${PRE_CHECK}" -eq 1 ]] ; then
 
  print_ln "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Pre-checking phase started on ""$(date)""\\n""$(indent "${NC}""Firmware binary path: ""${FIRMWARE_PATH}")" "main"
  else
    print_output "[!] Pre-checking phase started on ""$(date)""\\n""$(indent "${NC}""Firmware binary path: ""${FIRMWARE_PATH}")" "no_log"
  fi
  write_notification "Pre-checking phase started"
 
  # 'main' functions of imported modules
  # in the pre-check phase we execute all modules with P[Number]_Name.sh
 
  run_modules "P" "${THREADED}" "0"
 
  # if we running threaded we ware going to wait for the slow guys here
  [[ ${THREADED} -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"
 
  print_ln "no_log"
 
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Pre-checking phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
  else
    print_output "[!] Pre-checking phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_notification "Pre-checking phase finished"
 
  # useful prints for debugging:
  # print_output "[!] Firmware value: ${FIRMWARE}"
  # print_output "[!] Firmware path: ${FIRMWARE}_PATH"
  # print_output "[!] Output dir: $OUTPUT_DIR"
  # print_output "[!] LINUX_PATH_COUNTER: $LINUX_PATH_COUNTER"
  # print_output "[!] LINUX_PATH_ARRAY: ${#ROOT_PATH[@]}"
fi
#######################################################################################
# Pre-Check (P-modules)
#######################################################################################
if [[ "${PRE_CHECK}" -eq 1 ]] ; then
 
  print_ln "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Pre-checking phase started on ""$(date)""\\n""$(indent "${NC}""Firmware binary path: ""${FIRMWARE_PATH}")" "main"
  else
    print_output "[!] Pre-checking phase started on ""$(date)""\\n""$(indent "${NC}""Firmware binary path: ""${FIRMWARE_PATH}")" "no_log"
  fi
  write_notification "Pre-checking phase started"
 
  # 'main' functions of imported modules
  # in the pre-check phase we execute all modules with P[Number]_Name.sh
 
  run_modules "P" "${THREADED}" "0"
 
  # if we running threaded we ware going to wait for the slow guys here
  [[ ${THREADED} -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"
 
  print_ln "no_log"
 
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Pre-checking phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
  else
    print_output "[!] Pre-checking phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_notification "Pre-checking phase finished"
 
  # useful prints for debugging:
  # print_output "[!] Firmware value: ${FIRMWARE}"
  # print_output "[!] Firmware path: ${FIRMWARE}_PATH"
  # print_output "[!] Output dir: $OUTPUT_DIR"
  # print_output "[!] LINUX_PATH_COUNTER: $LINUX_PATH_COUNTER"
  # print_output "[!] LINUX_PATH_ARRAY: ${#ROOT_PATH[@]}"
fi
# $1: module group letter [P, S, L, F]
# $2: 0=single thread 1=multithread
# $3: HTML=1 - generate html file
run_modules()
{
  MODULE_GROUP="${1:-}"
  printf -v THREADING_SET '%d\n' "${2}" 2>/dev/null
  THREADING_MOD_GROUP="${THREADING_SET}"
 
  local SELECT_PRE_MODULES_COUNT=0
 
  for SELECT_NUM in "${SELECT_MODULES[@]}" ; do
    if [[ "${SELECT_NUM}" =~ ^["${MODULE_GROUP,,}","${MODULE_GROUP^^}"]{1} ]]; then
...
# $1: module group letter [P, S, L, F]
# $2: 0=single thread 1=multithread
# $3: HTML=1 - generate html file
run_modules()
{
  MODULE_GROUP="${1:-}"
  printf -v THREADING_SET '%d\n' "${2}" 2>/dev/null
  THREADING_MOD_GROUP="${THREADING_SET}"
 
  local SELECT_PRE_MODULES_COUNT=0
 
  for SELECT_NUM in "${SELECT_MODULES[@]}" ; do
    if [[ "${SELECT_NUM}" =~ ^["${MODULE_GROUP,,}","${MODULE_GROUP^^}"]{1} ]]; then
...
#######################################################################################
# Firmware-Check (S modules)
#######################################################################################
WAIT_PIDS=()
if [[ ${FIRMWARE} -eq 1 ]] ; then
  print_output "\n=================================================================\n" "no_log"
 
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Testing phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
  else
    print_output "[!] Testing phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "no_log"
  fi
  write_notification "Testing phase finished"
  write_grep_log "$(date)" "TIMESTAMP"
 
  run_modules "S" "${THREADED}" "${HTML}"
 
  [[ ${THREADED} -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"
 
  print_ln "no_log"
 
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Testing phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
  else
    print_output "[!] Testing phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_notification "Testing phase ended"
 
  TESTING_DONE=1
fi
#######################################################################################
# Firmware-Check (S modules)
#######################################################################################
WAIT_PIDS=()
if [[ ${FIRMWARE} -eq 1 ]] ; then
  print_output "\n=================================================================\n" "no_log"
 
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Testing phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
  else
    print_output "[!] Testing phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "no_log"
  fi
  write_notification "Testing phase finished"
  write_grep_log "$(date)" "TIMESTAMP"
 
  run_modules "S" "${THREADED}" "${HTML}"
 
  [[ ${THREADED} -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"
 
  print_ln "no_log"
 
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Testing phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
  else
    print_output "[!] Testing phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_notification "Testing phase ended"
 
  TESTING_DONE=1
fi
#######################################################################################
# Live Emulation - Check (L-modules)
#######################################################################################
if [[ "${FULL_EMULATION}" -eq 1 ]] ; then
  print_output "\n=================================================================\n" "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] System emulation phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
  else
    print_output "[!] System emulation phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "no_log"
  fi
  write_notification "System emulation phase started"
 
  write_grep_log "$(date)" "TIMESTAMP"
  # these modules are not threaded!
  run_modules "L" "0" "${HTML}"
 
  print_ln "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] System emulation phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
  else
    print_output "[!] System emulation ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_notification "System emulation phase ended"
fi
#######################################################################################
# Live Emulation - Check (L-modules)
#######################################################################################
if [[ "${FULL_EMULATION}" -eq 1 ]] ; then
  print_output "\n=================================================================\n" "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] System emulation phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
  else
    print_output "[!] System emulation phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "no_log"
  fi
  write_notification "System emulation phase started"
 
  write_grep_log "$(date)" "TIMESTAMP"
  # these modules are not threaded!
  run_modules "L" "0" "${HTML}"
 
  print_ln "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] System emulation phase ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
  else
    print_output "[!] System emulation ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_notification "System emulation phase ended"
fi
#######################################################################################
# Reporting (F-modules)
#######################################################################################
if [[ -d "${LOG_DIR}" ]]; then
  print_output "[!] Reporting phase started on ""$(date)""\\n" "main"
else
  print_output "[!] Reporting phase started on ""$(date)""\\n" "no_log"
fi
write_notification "Reporting phase started"
 
run_modules "F" "0" "${HTML}"
 
[[ ${DISABLE_STATUS_BAR} -eq 0 ]] && remove_status_bar
 
write_notification "Reporting phase ended"
 
if [[ "${TESTING_DONE}" -eq 1 ]]; then
  if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/firmware ]]; then
    print_output "[*] Removing temp firmware directory\\n" "no_log"
    rm -r "${LOG_DIR}"/firmware 2>/dev/null
  fi
  if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/p61_unblob_eval/unblob_extracted ]]; then
    print_output "[*] Removing unblob firmware directory\\n" "no_log"
    rm -r "${LOG_DIR}"/p61_unblob_eval/unblob_extracted 2>/dev/null
  fi
  print_ln "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Test ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
    write_notification "EMBA finished analysis"
    rm -r "${TMP_DIR}" 2>/dev/null || true
  else
    print_output "[!] Test ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_grep_log "$(date)" "TIMESTAMP"
  write_grep_log "$(date -d@"${SECONDS}" -u +%H:%M:%S)" "DURATION"
else
  print_output "[!] No extracted firmware found" "no_log"
  print_output "$(indent "Try using binwalk or something else to extract the firmware")"
  exit 1
fi
 
[[ "${HTML}" -eq 1 ]] && update_index
 
if [[ -f "${HTML_PATH}"/index.html ]] && [[ "${IN_DOCKER}" -eq 0 ]]; then
  print_output "[*] Web report created HTML report in ${ORANGE}${LOG_DIR}/html-report${NC}\\n" "main"
  print_output "[*] Open the web-report with${ORANGE} firefox $(abs_path "${HTML_PATH}/index.html")${NC}\\n" "main"
fi
 
# we need to change the permissions of the LOG_DIR to the orig. user from the host
[[ "${IN_DOCKER}" -eq 1 ]] && restore_permissions
cleaner 0
#######################################################################################
# Reporting (F-modules)
#######################################################################################
if [[ -d "${LOG_DIR}" ]]; then
  print_output "[!] Reporting phase started on ""$(date)""\\n" "main"
else
  print_output "[!] Reporting phase started on ""$(date)""\\n" "no_log"
fi
write_notification "Reporting phase started"
 
run_modules "F" "0" "${HTML}"
 
[[ ${DISABLE_STATUS_BAR} -eq 0 ]] && remove_status_bar
 
write_notification "Reporting phase ended"
 
if [[ "${TESTING_DONE}" -eq 1 ]]; then
  if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/firmware ]]; then
    print_output "[*] Removing temp firmware directory\\n" "no_log"
    rm -r "${LOG_DIR}"/firmware 2>/dev/null
  fi
  if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/p61_unblob_eval/unblob_extracted ]]; then
    print_output "[*] Removing unblob firmware directory\\n" "no_log"
    rm -r "${LOG_DIR}"/p61_unblob_eval/unblob_extracted 2>/dev/null
  fi
  print_ln "no_log"
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] Test ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "main"
    write_notification "EMBA finished analysis"
    rm -r "${TMP_DIR}" 2>/dev/null || true
  else
    print_output "[!] Test ended on ""$(date)"" and took about ""$(date -d@"${SECONDS}" -u +%H:%M:%S)"" \\n" "no_log"
  fi
  write_grep_log "$(date)" "TIMESTAMP"
  write_grep_log "$(date -d@"${SECONDS}" -u +%H:%M:%S)" "DURATION"
else
  print_output "[!] No extracted firmware found" "no_log"
  print_output "$(indent "Try using binwalk or something else to extract the firmware")"
  exit 1
fi
 
[[ "${HTML}" -eq 1 ]] && update_index
 
if [[ -f "${HTML_PATH}"/index.html ]] && [[ "${IN_DOCKER}" -eq 0 ]]; then
  print_output "[*] Web report created HTML report in ${ORANGE}${LOG_DIR}/html-report${NC}\\n" "main"
  print_output "[*] Open the web-report with${ORANGE} firefox $(abs_path "${HTML_PATH}/index.html")${NC}\\n" "main"
fi
 
# we need to change the permissions of the LOG_DIR to the orig. user from the host
[[ "${IN_DOCKER}" -eq 1 ]] && restore_permissions
cleaner 0
P02_firmware_bin_file_check.sh    给出有关所提供的固件二进制文件的一些非常基本的信息。
P05_patools_init.sh    用patools工具提取zip、tar、tgz
P10_vmdk_extractor.sh    提取vmdk镜像
P11_dlink_SHRS_enc_extract.sh    提取D-link加密固件镜像
P12_avm_freetz_ng_extract.sh    使用Freetz-NG提取AVM固件映像
P13_uboot_mkimage.sh    显示Uboot映像的内部
P14_ext_mounter.sh    挂载和提取extX映像
P15_ubi_extractor.sh    提取ubi文件系统
P16_EnGenius_decryptor.sh    用EnGenius提取固件镜像
P17_gpg_decompress.sh    提取没有加密的gpg压缩的固件镜像
P18_qnap_decryptor.sh    从QNAP中提取加密固件映像
P19_bsd_ufs_mounter.sh    挂载和提取BSD UFS映像
P20_foscam_decryptor.sh    提取Foscam公司的加密固件映像
P21_buffalo_decryptor.sh    提取Buffalo公司的加密固件映像
P22_Zyxel_zip_decrypt.sh    提取被密码保护的Zyxel固件镜像
P23_qemu_qcow_mounter.sh    挂载和提取Qemu QCOW2镜像
P25_android_ota.sh    提取Android OTA更新文件
P35_UEFI_extractor.sh    提取UEFI镜像与BIOSUtilities
P59_binwalk_extractor.sh    分析固件与binwalk,检查熵和提取固件到日志目录
P60_firmware_bin_extractor.sh    和上面一样
P65_package_extractor.sh    识别和提取典型的软件包档案,如deb, apk, ipk
P70_unblob.sh    将带有unblob的固件提取到模块日志目录(仅用于评估)
P99_prepare_analyzer.sh    一些准备工作(检查固件、架构检查等)
P02_firmware_bin_file_check.sh    给出有关所提供的固件二进制文件的一些非常基本的信息。
P05_patools_init.sh    用patools工具提取zip、tar、tgz
P10_vmdk_extractor.sh    提取vmdk镜像
P11_dlink_SHRS_enc_extract.sh    提取D-link加密固件镜像
P12_avm_freetz_ng_extract.sh    使用Freetz-NG提取AVM固件映像
P13_uboot_mkimage.sh    显示Uboot映像的内部
P14_ext_mounter.sh    挂载和提取extX映像
P15_ubi_extractor.sh    提取ubi文件系统
P16_EnGenius_decryptor.sh    用EnGenius提取固件镜像
P17_gpg_decompress.sh    提取没有加密的gpg压缩的固件镜像
P18_qnap_decryptor.sh    从QNAP中提取加密固件映像
P19_bsd_ufs_mounter.sh    挂载和提取BSD UFS映像
P20_foscam_decryptor.sh    提取Foscam公司的加密固件映像
P21_buffalo_decryptor.sh    提取Buffalo公司的加密固件映像
P22_Zyxel_zip_decrypt.sh    提取被密码保护的Zyxel固件镜像
P23_qemu_qcow_mounter.sh    挂载和提取Qemu QCOW2镜像
P25_android_ota.sh    提取Android OTA更新文件
P35_UEFI_extractor.sh    提取UEFI镜像与BIOSUtilities
P59_binwalk_extractor.sh    分析固件与binwalk,检查熵和提取固件到日志目录
P60_firmware_bin_extractor.sh    和上面一样
P65_package_extractor.sh    识别和提取典型的软件包档案,如deb, apk, ipk
P70_unblob.sh    将带有unblob的固件提取到模块日志目录(仅用于评估)
P99_prepare_analyzer.sh    一些准备工作(检查固件、架构检查等)
S02_UEFI_FwHunt.sh    使用FwHunt来识别可能的UEFI固件中的漏洞
S03_firmware_bin_base_analyzer.sh    对操作系统或架构进行识别
S05_firmware_details.sh    统计可执行文件
S06_distribution_identification.sh    识别发行版
S08_package_mgmt_extractor.sh    搜索已知位置的包管理信息
S09_firmware_base_version_check.sh    搜索二进制文件版本
S100_command_inj_check.sh    搜索web文件并搜索可以代码执行的东西
S106_deep_key_search.sh    搜素文件里的特殊字符串
S107_deep_password_search.sh    搜索文件里的特殊密码
S108_stacs_password_search.sh    使用stacs搜索密码
S109_jtr_local_pw_cracking.sh    破解文件哈希
S10_binaries_basic_check.sh    搜索二进制的风险
S110_yara_check.sh    用yara搜索
S115_usermode_emulator.sh    模拟执行固件获取版本信息(实验功能,需要-E单独开启)
S116_qemu_version_detection.sh    该模块从S115的结果中提取版本信息
S120_cwe_checker.sh    用Ghidra扫描二进制文件中的常见风险
S12_binary_protection.sh    搜索二进制文件的保护机制
S13_weak_func_check.sh    和S10类似
S14_weak_func_radare_check.sh    和S10类似,使用radare
S15_bootloader_check.sh    扫描设备树等
S17_apk_check.sh    识别apk包并用APKHunt扫漏洞
S20_shell_check.sh    扫描shell脚本的漏洞
S21_python_check.sh    扫描python脚本的漏洞
S22_php_check.sh    扫描php脚本的漏洞和php配置文件
S24_kernel_bin_identifier.sh    识别内核文件
S25_kernel_check.sh    扫漏洞,包括版本、描述、内核配置等
S26_kernel_vuln_verifier.sh    内核漏洞识别
S35_http_file_check.sh    搜索http相关文件
S36_lighttpd.sh    识别lighttpd配置文件等
S40_weak_perm_check.sh    检查系统用户相关配置、密码文件的风险
S45_pass_file_check.sh    检查密码相关文件
S50_authentication_check.sh    检查身份验证等
S55_history_file_check.sh    检查历史文件
S60_cert_file_check.sh    检查证书文件
S65_config_file_check.sh    扫描典型的配置文件
S70_hidden_file_check.sh    隐藏文件搜索
S75_network_check.sh    检查网络相关的配置文件
S80_cronjob_check.sh    检查所有定时配置文件
S85_ssh_check.sh    检查ssh相关配置文件
S90_mail_check.sh    搜素邮箱文件
S95_interesting_binaries_check.sh    搜索一些二进制文件
S99_grepit.sh    用grepit扫风险
S02_UEFI_FwHunt.sh    使用FwHunt来识别可能的UEFI固件中的漏洞
S03_firmware_bin_base_analyzer.sh    对操作系统或架构进行识别
S05_firmware_details.sh    统计可执行文件
S06_distribution_identification.sh    识别发行版
S08_package_mgmt_extractor.sh    搜索已知位置的包管理信息
S09_firmware_base_version_check.sh    搜索二进制文件版本
S100_command_inj_check.sh    搜索web文件并搜索可以代码执行的东西
S106_deep_key_search.sh    搜素文件里的特殊字符串
S107_deep_password_search.sh    搜索文件里的特殊密码
S108_stacs_password_search.sh    使用stacs搜索密码
S109_jtr_local_pw_cracking.sh    破解文件哈希
S10_binaries_basic_check.sh    搜索二进制的风险
S110_yara_check.sh    用yara搜索
S115_usermode_emulator.sh    模拟执行固件获取版本信息(实验功能,需要-E单独开启)
S116_qemu_version_detection.sh    该模块从S115的结果中提取版本信息
S120_cwe_checker.sh    用Ghidra扫描二进制文件中的常见风险
S12_binary_protection.sh    搜索二进制文件的保护机制
S13_weak_func_check.sh    和S10类似
S14_weak_func_radare_check.sh    和S10类似,使用radare
S15_bootloader_check.sh    扫描设备树等
S17_apk_check.sh    识别apk包并用APKHunt扫漏洞
S20_shell_check.sh    扫描shell脚本的漏洞
S21_python_check.sh    扫描python脚本的漏洞
S22_php_check.sh    扫描php脚本的漏洞和php配置文件
S24_kernel_bin_identifier.sh    识别内核文件
S25_kernel_check.sh    扫漏洞,包括版本、描述、内核配置等
S26_kernel_vuln_verifier.sh    内核漏洞识别
S35_http_file_check.sh    搜索http相关文件
S36_lighttpd.sh    识别lighttpd配置文件等
S40_weak_perm_check.sh    检查系统用户相关配置、密码文件的风险
S45_pass_file_check.sh    检查密码相关文件
S50_authentication_check.sh    检查身份验证等
S55_history_file_check.sh    检查历史文件
S60_cert_file_check.sh    检查证书文件
S65_config_file_check.sh    扫描典型的配置文件
S70_hidden_file_check.sh    隐藏文件搜索
S75_network_check.sh    检查网络相关的配置文件
S80_cronjob_check.sh    检查所有定时配置文件
S85_ssh_check.sh    检查ssh相关配置文件
S90_mail_check.sh    搜素邮箱文件
S95_interesting_binaries_check.sh    搜索一些二进制文件
S99_grepit.sh    用grepit扫风险
L10_system_emulation    文件夹
L10_system_emulation.sh    基于firmadyne和firmAE进行固件模拟(实验模块,需要-Q单独激活。)
L15_emulated_checks_nmap.sh    测试是否成功模拟
L20_snmp_checks.sh    同上
L25_web_checks.sh    测试模拟的web服务
L30_routersploit.sh    测试
L35_metasploit_check.sh    用metasploit测试
L99_cleanup.sh    停止并清理模拟环境
L10_system_emulation    文件夹
L10_system_emulation.sh    基于firmadyne和firmAE进行固件模拟(实验模块,需要-Q单独激活。)
L15_emulated_checks_nmap.sh    测试是否成功模拟
L20_snmp_checks.sh    同上
L25_web_checks.sh    测试模拟的web服务
L30_routersploit.sh    测试
L35_metasploit_check.sh    用metasploit测试
L99_cleanup.sh    停止并清理模拟环境
F10_license_summary.sh    收集许可证信息
F20_vul_aggregator.sh    收集漏洞
F21_cyclonedx_sbom.sh    生成json
F50_base_aggregator.sh    生成一个所有模块的概览
F10_license_summary.sh    收集许可证信息
F20_vul_aggregator.sh    收集漏洞
F21_cyclonedx_sbom.sh    生成json
F50_base_aggregator.sh    生成一个所有模块的概览
P02_firmware_bin_file_check() {
  module_log_init "${FUNCNAME[0]}"
  module_title "Binary firmware file analyzer"
 
  set_p02_default_exports
 
  local FILE_BIN_OUT
  # we set this var global to 1 if we find something UEFI related
  export UEFI_DETECTED=0
 
  write_csv_log "Entity" "data" "Notes"
  write_csv_log "Firmware path" "$FIRMWARE_PATH" "NA"
  if [[ -f "$FIRMWARE_PATH" ]]; then
 
    # 计算固件文件哈希
    SHA512_CHECKSUM="$(sha512sum "$FIRMWARE_PATH" | awk '{print $1}')"
    write_csv_log "SHA512" "${SHA512_CHECKSUM:-}" "NA"
    SHA1_CHECKSUM="$(sha1sum "$FIRMWARE_PATH" | awk '{print $1}')"
    write_csv_log "SHA1" "${SHA1_CHECKSUM:-}" "NA"
    MD5_CHECKSUM="$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')"
    write_csv_log "MD5" "${MD5_CHECKSUM:-}" "NA"
 
    # 使用 ent 计算文件的熵信息
    ENTROPY="$(ent "$FIRMWARE_PATH" | grep Entropy | sed -e 's/^Entropy\ \=\ //')"
    write_csv_log "Entropy" "${ENTROPY:-}" "NA"
 
    print_output "[*] Entropy testing with binwalk ... "
    # we have to change the working directory for binwalk, because everything except the log directory is read-only in
    # Docker container and binwalk fails to save the entropy picture there
    if [[ $IN_DOCKER -eq 1 ]] ; then
      cd "$LOG_DIR" || return
 
        # 使用 binwalk 提取固件,并输出 json 的结果
      print_output "$(binwalk -E -F -J "$FIRMWARE_PATH")"
      mv "$(basename "$FIRMWARE_PATH".png)" "$LOG_DIR"/firmware_entropy.png 2> /dev/null || true
      cd /emba || return
    else
      print_output "$(binwalk -E -F -J "$FIRMWARE_PATH")"
      mv "$(basename "$FIRMWARE_PATH".png)" "$LOG_DIR"/firmware_entropy.png 2> /dev/null || true
    fi
  fi
 
  local FILE_LS_OUT
  FILE_LS_OUT=$(ls -lh "$FIRMWARE_PATH")
 
  print_ln
  print_output "[*] Details of the firmware file:"
  print_ln
  print_output "$(indent "$FILE_LS_OUT")"
  print_ln
  if [[ -f "$FIRMWARE_PATH" ]]; then
    print_ln
 
      # 使用 file 命令提取基本信息
    print_output "$(indent "$(file "$FIRMWARE_PATH")")"
    print_ln
 
      # 使用 hexdump 输出文件前面的十六进制和ASCII信息
    hexdump -C "$FIRMWARE_PATH"| head | tee -a "$LOG_FILE" || true
    print_ln
    print_output "[*] SHA512 checksum: $ORANGE$SHA512_CHECKSUM$NC"
    print_ln
    print_output "$(indent "$ENTROPY")"
    print_ln
    if [[ -x "$EXT_DIR"/pixde ]]; then
 
      # 可视化二进制文件
      print_output "[*] Visualized firmware file (first 2000 bytes):\n"
      "$EXT_DIR"/pixde -r-0x2000 "$FIRMWARE_PATH" | tee -a "$LOG_DIR"/p02_pixd.txt
      print_ln
 
      python3 "$EXT_DIR"/pixd_png.py -i "$LOG_DIR"/p02_pixd.txt -o "$LOG_DIR"/pixd.png -p 10 > /dev/null
      write_link "$LOG_DIR"/pixd.png
    fi
 
      # 对二进制文件进行检测(将在下方进行分析)
    fw_bin_detector "$FIRMWARE_PATH"
 
      # 备份所有的变量
    backup_p02_vars
  fi
 
  module_end_log "${FUNCNAME[0]}" 1
}
P02_firmware_bin_file_check() {
  module_log_init "${FUNCNAME[0]}"
  module_title "Binary firmware file analyzer"
 
  set_p02_default_exports
 
  local FILE_BIN_OUT
  # we set this var global to 1 if we find something UEFI related
  export UEFI_DETECTED=0
 
  write_csv_log "Entity" "data" "Notes"
  write_csv_log "Firmware path" "$FIRMWARE_PATH" "NA"
  if [[ -f "$FIRMWARE_PATH" ]]; then
 
    # 计算固件文件哈希
    SHA512_CHECKSUM="$(sha512sum "$FIRMWARE_PATH" | awk '{print $1}')"
    write_csv_log "SHA512" "${SHA512_CHECKSUM:-}" "NA"
    SHA1_CHECKSUM="$(sha1sum "$FIRMWARE_PATH" | awk '{print $1}')"
    write_csv_log "SHA1" "${SHA1_CHECKSUM:-}" "NA"
    MD5_CHECKSUM="$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')"
    write_csv_log "MD5" "${MD5_CHECKSUM:-}" "NA"
 
    # 使用 ent 计算文件的熵信息
    ENTROPY="$(ent "$FIRMWARE_PATH" | grep Entropy | sed -e 's/^Entropy\ \=\ //')"
    write_csv_log "Entropy" "${ENTROPY:-}" "NA"
 
    print_output "[*] Entropy testing with binwalk ... "
    # we have to change the working directory for binwalk, because everything except the log directory is read-only in
    # Docker container and binwalk fails to save the entropy picture there
    if [[ $IN_DOCKER -eq 1 ]] ; then
      cd "$LOG_DIR" || return
 
        # 使用 binwalk 提取固件,并输出 json 的结果
      print_output "$(binwalk -E -F -J "$FIRMWARE_PATH")"
      mv "$(basename "$FIRMWARE_PATH".png)" "$LOG_DIR"/firmware_entropy.png 2> /dev/null || true
      cd /emba || return
    else
      print_output "$(binwalk -E -F -J "$FIRMWARE_PATH")"
      mv "$(basename "$FIRMWARE_PATH".png)" "$LOG_DIR"/firmware_entropy.png 2> /dev/null || true
    fi
  fi
 
  local FILE_LS_OUT
  FILE_LS_OUT=$(ls -lh "$FIRMWARE_PATH")
 
  print_ln
  print_output "[*] Details of the firmware file:"
  print_ln
  print_output "$(indent "$FILE_LS_OUT")"
  print_ln
  if [[ -f "$FIRMWARE_PATH" ]]; then
    print_ln
 
      # 使用 file 命令提取基本信息
    print_output "$(indent "$(file "$FIRMWARE_PATH")")"
    print_ln
 
      # 使用 hexdump 输出文件前面的十六进制和ASCII信息
    hexdump -C "$FIRMWARE_PATH"| head | tee -a "$LOG_FILE" || true
    print_ln
    print_output "[*] SHA512 checksum: $ORANGE$SHA512_CHECKSUM$NC"
    print_ln
    print_output "$(indent "$ENTROPY")"
    print_ln
    if [[ -x "$EXT_DIR"/pixde ]]; then
 
      # 可视化二进制文件
      print_output "[*] Visualized firmware file (first 2000 bytes):\n"
      "$EXT_DIR"/pixde -r-0x2000 "$FIRMWARE_PATH" | tee -a "$LOG_DIR"/p02_pixd.txt
      print_ln
 
      python3 "$EXT_DIR"/pixd_png.py -i "$LOG_DIR"/p02_pixd.txt -o "$LOG_DIR"/pixd.png -p 10 > /dev/null
      write_link "$LOG_DIR"/pixd.png
    fi
 
      # 对二进制文件进行检测(将在下方进行分析)
    fw_bin_detector "$FIRMWARE_PATH"
 
      # 备份所有的变量
    backup_p02_vars
  fi
 
  module_end_log "${FUNCNAME[0]}" 1
}
fw_bin_detector() {
 
    # 固件路径
  local CHECK_FILE="${1:-}"
  local FILE_BIN_OUT=""
  local DLINK_ENC_CHECK=""
  local QNAP_ENC_CHECK=""
  local AVM_CHECK=0
  local UEFI_CHECK=0
 
  set_p02_default_exports
 
  FILE_BIN_OUT=$(file "$CHECK_FILE")
 
    # 输出第一行用于检查是否为DLINK公司产品
  DLINK_ENC_CHECK=$(hexdump -C "$CHECK_FILE" | head -1 || true)
 
    # 从固件中所有的字符串中检查AVM正则出现的行数。用于判断固件是否为AVM公司产品。
  AVM_CHECK=$(strings "$CHECK_FILE" | grep -c "AVM GmbH .*. All rights reserved.\|(C) Copyright .* AVM" || true)
 
    # 搜索字符串并只显示包含指定字符串的结果,用于判断是否为QNAP加密文件系统
    QNAP_ENC_CHECK=$(binwalk -y "qnap encrypted" "$CHECK_FILE")
  # we are running binwalk on the file to analyze the output afterwards:
  binwalk "$CHECK_FILE" > "$TMP_DIR"/s02_binwalk_output.txt
  UEFI_CHECK=$(grep -c "UEFI" "$TMP_DIR"/s02_binwalk_output.txt || true)
  UEFI_CHECK=$(( "$UEFI_CHECK" + "$(grep -c "UEFI" "$CHECK_FILE" || true)" ))
 
  if [[ -f "$KERNEL_CONFIG" ]] && [[ "$KERNEL" -eq 1 ]]; then
    # we set the FIRMWARE_PATH to the kernel config path if we have only -k parameter
    if [[ "$(md5sum "$KERNEL_CONFIG" | awk '{print $1}')" == "$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')" ]]; then
      print_output "[+] Identified Linux kernel configuration file"
      write_csv_log "kernel config" "yes" "NA"
      export SKIP_PRE_CHECKERS=1
      return
    fi
  fi
 
  if [[ "$UEFI_CHECK" -gt 0 ]]; then
    print_output "[+] Identified possible UEFI firmware - using fwhunt-scan vulnerability scanning module"
    export UEFI_DETECTED=1
    UEFI_AMI_CAPSULE=$(grep -c "AMI.*EFI.*capsule" "$TMP_DIR"/s02_binwalk_output.txt || true)
    if [[ "$UEFI_AMI_CAPSULE" -gt 0 ]]; then
      print_output "[+] Identified possible UEFI-AMI capsule firmware - using capsule extractors"
    fi
    write_csv_log "UEFI firmware detected" "yes" "NA"
  fi
  if [[ "$AVM_CHECK" -gt 0 ]] || [[ "$FW_VENDOR" == *"AVM"* ]]; then
    print_output "[+] Identified AVM firmware - using AVM extraction module"
    export AVM_DETECTED=1
    write_csv_log "AVM firmware detected" "yes" "NA"
  fi
  # if we have a zip, tgz, tar archive we are going to use the patools extractor
  if [[ "$FILE_BIN_OUT" == *"gzip compressed data"* || "$FILE_BIN_OUT" == *"Zip archive data"* || \
    "$FILE_BIN_OUT" == *"POSIX tar archive"* || "$FILE_BIN_OUT" == *"ISO 9660 CD-ROM filesystem data"* || \
    "$FILE_BIN_OUT" == *"7-zip archive data"* || "$FILE_BIN_OUT" == *"XZ compressed data"* || \
    "$FILE_BIN_OUT" == *"bzip2 compressed data"* ]]; then
    # as the AVM images are also zip files we need to bypass it here:
    if [[ "$AVM_DETECTED" -ne 1 ]]; then
      print_output "[+] Identified gzip/zip/tar/iso/xz/bzip2 archive file - using patools extraction module"
      export PATOOLS_INIT=1
      write_csv_log "basic compressed (patool)" "yes" "NA"
    fi
  fi
  if [[ "$FILE_BIN_OUT" == *"QEMU QCOW2 Image"* ]]; then
    print_output "[+] Identified Qemu QCOW image - using QCOW extraction module"
    export QCOW_DETECTED=1
    write_csv_log "Qemu QCOW firmware detected" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"VMware4 disk image"* ]]; then
    print_output "[+] Identified VMWware VMDK archive file - using VMDK extraction module"
    export VMDK_DETECTED=1
    write_csv_log "VMDK" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"UBI image"* ]]; then
    print_output "[+] Identified UBI filesystem image - using UBI extraction module"
    export UBI_IMAGE=1
    write_csv_log "UBI filesystem" "yes" "NA"
  fi
 
    # 识别DLINK固件
  if [[ "$DLINK_ENC_CHECK" == *"SHRS"* ]]; then
    print_output "[+] Identified D-Link SHRS encrpyted firmware - using D-Link extraction module"
    export DLINK_ENC_DETECTED=1
    write_csv_log "D-Link SHRS" "yes" "NA"
  fi
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ 00\ 00\ 00\ 00\ 00\ 00\ 0.\ ..\ \ 00\ 00\ 0.\ ..\ 31\ 32\ 33\ 00 ]]; then
    print_output "[+] Identified EnGenius encrpyted firmware - using EnGenius extraction module"
    export ENGENIUS_ENC_DETECTED=1
    write_csv_log "EnGenius encrypted" "yes" "NA"
  fi
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ 00\ 00\ 00\ 00\ 00\ 00\ 01\ 01\ \ 00\ 00\ 0.\ ..\ 33\ 2e\ 3[89]\ 2e ]]; then
    print_output "[+] Identified EnGenius encrpyted firmware - using EnGenius extraction module"
    export ENGENIUS_ENC_DETECTED=1
    write_csv_log "EnGenius encrypted" "yes" "NA"
  fi
  if [[ "$DLINK_ENC_CHECK" == *"encrpted_img"* ]]; then
    print_output "[+] Identified D-Link encrpted_img encrpyted firmware - using D-Link extraction module"
    export DLINK_ENC_DETECTED=2
    write_csv_log "D-Link encrpted_img encrypted" "yes" "NA"
  fi
 
    # 识别u-boot固件
  if [[ "$FILE_BIN_OUT" == *"u-boot legacy uImage"* ]]; then
    print_output "[+] Identified u-boot firmware - using u-boot module"
    export UBOOT_IMAGE=1
    write_csv_log "Uboot image" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Unix Fast File system [v2]"* ]]; then
    print_output "[+] Identified UFS filesytem - using UFS filesytem extraction module"
    export BSD_UFS=1
    write_csv_log "BSD UFS filesystem" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Linux rev 1.0 ext2 filesystem data"* ]]; then
    print_output "[+] Identified Linux ext2 filesytem - using EXT filesytem extraction module"
    export EXT_IMAGE=1
    write_csv_log "EXT2 filesystem" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Linux rev 1.0 ext3 filesystem data"* ]]; then
    print_output "[+] Identified Linux ext3 filesytem - using EXT filesytem extraction module"
    export EXT_IMAGE=1
    write_csv_log "EXT3 filesystem" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Linux rev 1.0 ext4 filesystem data"* ]]; then
    print_output "[+] Identified Linux ext4 filesytem - using EXT filesytem extraction module"
    export EXT_IMAGE=1
    write_csv_log "EXT4 filesystem" "yes" "NA"
  fi
  if [[ "$QNAP_ENC_CHECK" == *"QNAP encrypted firmware footer , model"* ]]; then
    print_output "[+] Identified QNAP encrpyted firmware - using QNAP extraction module"
    export QNAP_ENC_DETECTED=1
    write_csv_log "QNAP encrypted filesystem" "yes" "NA"
  fi
  # probably we need to take a deeper look to identify the gpg compressed firmware files better.
  # Currently this detection mechanism works quite good on the known firmware images
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ a3\ 01\  ]]; then
    GPG_CHECK="$(gpg --list-packets "$FIRMWARE_PATH" | grep "compressed packet:")"
    if [[ "$GPG_CHECK" == *"compressed packet: algo="* ]]; then
      print_output "[+] Identified GPG compressed firmware - using GPG extraction module"
      export GPG_COMPRESS=1
      write_csv_log "GPG compressed firmware" "yes" "NA"
    fi
  fi
  if [[ "$DLINK_ENC_CHECK" == *"CrAU"* ]]; then
    print_output "[+] Identified Android OTA payload.bin update file - using Android extraction module"
    export ANDROID_OTA=1
    write_csv_log "Android OTA update" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"openssl enc'd data with salted password"* ]]; then
    print_output "[+] Identified OpenSSL encrypted file - trying OpenSSL module for Foscam firmware"
    export OPENSSL_ENC_DETECTED=1
    write_csv_log "OpenSSL encrypted" "yes" "NA"
  fi
  # This check is currently only tested on one firmware - further tests needed:
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ 62\ 67\ 6e\ 00\ 00\ 00\ 00\ 00\ \ 00\ 00\ 00\ b9\ 01\  ]]; then
    print_output "[+] Identified Buffalo encrpyted firmware - using Buffalo extraction module"
    export BUFFALO_ENC_DETECTED=1
    write_csv_log "Buffalo encrypted" "yes" "NA"
  fi
  if [[ "$(basename "$CHECK_FILE")" =~ .*\.ri ]] && [[ "$FILE_BIN_OUT" == *"data"* ]]; then
    # ri files are usually used by zyxel
    if [[ $(find "$LOG_DIR"/firmware -name "$(basename -s .ri "$CHECK_FILE")".bin | wc -l) -gt 0 ]]; then
      # if we find a bin file with the same name then it is a Zyxel firmware image
      print_output "[+] Identified ZyXel encrpyted ZIP firmware - using ZyXel extraction module"
      export ZYXEL_ZIP=1
      write_csv_log "ZyXel encrypted ZIP" "yes" ""
    fi
  fi
  print_ln
}
fw_bin_detector() {
 
    # 固件路径
  local CHECK_FILE="${1:-}"
  local FILE_BIN_OUT=""
  local DLINK_ENC_CHECK=""
  local QNAP_ENC_CHECK=""
  local AVM_CHECK=0
  local UEFI_CHECK=0
 
  set_p02_default_exports
 
  FILE_BIN_OUT=$(file "$CHECK_FILE")
 
    # 输出第一行用于检查是否为DLINK公司产品
  DLINK_ENC_CHECK=$(hexdump -C "$CHECK_FILE" | head -1 || true)
 
    # 从固件中所有的字符串中检查AVM正则出现的行数。用于判断固件是否为AVM公司产品。
  AVM_CHECK=$(strings "$CHECK_FILE" | grep -c "AVM GmbH .*. All rights reserved.\|(C) Copyright .* AVM" || true)
 
    # 搜索字符串并只显示包含指定字符串的结果,用于判断是否为QNAP加密文件系统
    QNAP_ENC_CHECK=$(binwalk -y "qnap encrypted" "$CHECK_FILE")
  # we are running binwalk on the file to analyze the output afterwards:
  binwalk "$CHECK_FILE" > "$TMP_DIR"/s02_binwalk_output.txt
  UEFI_CHECK=$(grep -c "UEFI" "$TMP_DIR"/s02_binwalk_output.txt || true)
  UEFI_CHECK=$(( "$UEFI_CHECK" + "$(grep -c "UEFI" "$CHECK_FILE" || true)" ))
 
  if [[ -f "$KERNEL_CONFIG" ]] && [[ "$KERNEL" -eq 1 ]]; then
    # we set the FIRMWARE_PATH to the kernel config path if we have only -k parameter
    if [[ "$(md5sum "$KERNEL_CONFIG" | awk '{print $1}')" == "$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')" ]]; then
      print_output "[+] Identified Linux kernel configuration file"
      write_csv_log "kernel config" "yes" "NA"
      export SKIP_PRE_CHECKERS=1
      return
    fi
  fi
 
  if [[ "$UEFI_CHECK" -gt 0 ]]; then
    print_output "[+] Identified possible UEFI firmware - using fwhunt-scan vulnerability scanning module"
    export UEFI_DETECTED=1
    UEFI_AMI_CAPSULE=$(grep -c "AMI.*EFI.*capsule" "$TMP_DIR"/s02_binwalk_output.txt || true)
    if [[ "$UEFI_AMI_CAPSULE" -gt 0 ]]; then
      print_output "[+] Identified possible UEFI-AMI capsule firmware - using capsule extractors"
    fi
    write_csv_log "UEFI firmware detected" "yes" "NA"
  fi
  if [[ "$AVM_CHECK" -gt 0 ]] || [[ "$FW_VENDOR" == *"AVM"* ]]; then
    print_output "[+] Identified AVM firmware - using AVM extraction module"
    export AVM_DETECTED=1
    write_csv_log "AVM firmware detected" "yes" "NA"
  fi
  # if we have a zip, tgz, tar archive we are going to use the patools extractor
  if [[ "$FILE_BIN_OUT" == *"gzip compressed data"* || "$FILE_BIN_OUT" == *"Zip archive data"* || \
    "$FILE_BIN_OUT" == *"POSIX tar archive"* || "$FILE_BIN_OUT" == *"ISO 9660 CD-ROM filesystem data"* || \
    "$FILE_BIN_OUT" == *"7-zip archive data"* || "$FILE_BIN_OUT" == *"XZ compressed data"* || \
    "$FILE_BIN_OUT" == *"bzip2 compressed data"* ]]; then
    # as the AVM images are also zip files we need to bypass it here:
    if [[ "$AVM_DETECTED" -ne 1 ]]; then
      print_output "[+] Identified gzip/zip/tar/iso/xz/bzip2 archive file - using patools extraction module"
      export PATOOLS_INIT=1
      write_csv_log "basic compressed (patool)" "yes" "NA"
    fi
  fi
  if [[ "$FILE_BIN_OUT" == *"QEMU QCOW2 Image"* ]]; then
    print_output "[+] Identified Qemu QCOW image - using QCOW extraction module"
    export QCOW_DETECTED=1
    write_csv_log "Qemu QCOW firmware detected" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"VMware4 disk image"* ]]; then
    print_output "[+] Identified VMWware VMDK archive file - using VMDK extraction module"
    export VMDK_DETECTED=1
    write_csv_log "VMDK" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"UBI image"* ]]; then
    print_output "[+] Identified UBI filesystem image - using UBI extraction module"
    export UBI_IMAGE=1
    write_csv_log "UBI filesystem" "yes" "NA"
  fi
 
    # 识别DLINK固件
  if [[ "$DLINK_ENC_CHECK" == *"SHRS"* ]]; then
    print_output "[+] Identified D-Link SHRS encrpyted firmware - using D-Link extraction module"
    export DLINK_ENC_DETECTED=1
    write_csv_log "D-Link SHRS" "yes" "NA"
  fi
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ 00\ 00\ 00\ 00\ 00\ 00\ 0.\ ..\ \ 00\ 00\ 0.\ ..\ 31\ 32\ 33\ 00 ]]; then
    print_output "[+] Identified EnGenius encrpyted firmware - using EnGenius extraction module"
    export ENGENIUS_ENC_DETECTED=1
    write_csv_log "EnGenius encrypted" "yes" "NA"
  fi
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ 00\ 00\ 00\ 00\ 00\ 00\ 01\ 01\ \ 00\ 00\ 0.\ ..\ 33\ 2e\ 3[89]\ 2e ]]; then
    print_output "[+] Identified EnGenius encrpyted firmware - using EnGenius extraction module"
    export ENGENIUS_ENC_DETECTED=1
    write_csv_log "EnGenius encrypted" "yes" "NA"
  fi
  if [[ "$DLINK_ENC_CHECK" == *"encrpted_img"* ]]; then
    print_output "[+] Identified D-Link encrpted_img encrpyted firmware - using D-Link extraction module"
    export DLINK_ENC_DETECTED=2
    write_csv_log "D-Link encrpted_img encrypted" "yes" "NA"
  fi
 
    # 识别u-boot固件
  if [[ "$FILE_BIN_OUT" == *"u-boot legacy uImage"* ]]; then
    print_output "[+] Identified u-boot firmware - using u-boot module"
    export UBOOT_IMAGE=1
    write_csv_log "Uboot image" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Unix Fast File system [v2]"* ]]; then
    print_output "[+] Identified UFS filesytem - using UFS filesytem extraction module"
    export BSD_UFS=1
    write_csv_log "BSD UFS filesystem" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Linux rev 1.0 ext2 filesystem data"* ]]; then
    print_output "[+] Identified Linux ext2 filesytem - using EXT filesytem extraction module"
    export EXT_IMAGE=1
    write_csv_log "EXT2 filesystem" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Linux rev 1.0 ext3 filesystem data"* ]]; then
    print_output "[+] Identified Linux ext3 filesytem - using EXT filesytem extraction module"
    export EXT_IMAGE=1
    write_csv_log "EXT3 filesystem" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"Linux rev 1.0 ext4 filesystem data"* ]]; then
    print_output "[+] Identified Linux ext4 filesytem - using EXT filesytem extraction module"
    export EXT_IMAGE=1
    write_csv_log "EXT4 filesystem" "yes" "NA"
  fi
  if [[ "$QNAP_ENC_CHECK" == *"QNAP encrypted firmware footer , model"* ]]; then
    print_output "[+] Identified QNAP encrpyted firmware - using QNAP extraction module"
    export QNAP_ENC_DETECTED=1
    write_csv_log "QNAP encrypted filesystem" "yes" "NA"
  fi
  # probably we need to take a deeper look to identify the gpg compressed firmware files better.
  # Currently this detection mechanism works quite good on the known firmware images
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ a3\ 01\  ]]; then
    GPG_CHECK="$(gpg --list-packets "$FIRMWARE_PATH" | grep "compressed packet:")"
    if [[ "$GPG_CHECK" == *"compressed packet: algo="* ]]; then
      print_output "[+] Identified GPG compressed firmware - using GPG extraction module"
      export GPG_COMPRESS=1
      write_csv_log "GPG compressed firmware" "yes" "NA"
    fi
  fi
  if [[ "$DLINK_ENC_CHECK" == *"CrAU"* ]]; then
    print_output "[+] Identified Android OTA payload.bin update file - using Android extraction module"
    export ANDROID_OTA=1
    write_csv_log "Android OTA update" "yes" "NA"
  fi
  if [[ "$FILE_BIN_OUT" == *"openssl enc'd data with salted password"* ]]; then
    print_output "[+] Identified OpenSSL encrypted file - trying OpenSSL module for Foscam firmware"
    export OPENSSL_ENC_DETECTED=1
    write_csv_log "OpenSSL encrypted" "yes" "NA"
  fi
  # This check is currently only tested on one firmware - further tests needed:
  if [[ "$DLINK_ENC_CHECK" =~ 00000000\ \ 62\ 67\ 6e\ 00\ 00\ 00\ 00\ 00\ \ 00\ 00\ 00\ b9\ 01\  ]]; then
    print_output "[+] Identified Buffalo encrpyted firmware - using Buffalo extraction module"
    export BUFFALO_ENC_DETECTED=1
    write_csv_log "Buffalo encrypted" "yes" "NA"
  fi
  if [[ "$(basename "$CHECK_FILE")" =~ .*\.ri ]] && [[ "$FILE_BIN_OUT" == *"data"* ]]; then
    # ri files are usually used by zyxel
    if [[ $(find "$LOG_DIR"/firmware -name "$(basename -s .ri "$CHECK_FILE")".bin | wc -l) -gt 0 ]]; then
      # if we find a bin file with the same name then it is a Zyxel firmware image
      print_output "[+] Identified ZyXel encrpyted ZIP firmware - using ZyXel extraction module"
      export ZYXEL_ZIP=1
      write_csv_log "ZyXel encrypted ZIP" "yes" ""
    fi
  fi
  print_ln
}
export PRE_THREAD_ENA=0
 
P05_patools_init() {
  local NEG_LOG=0
 
  if [[ "$PATOOLS_INIT" -eq 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    module_title "Initial extractor of different archive types via patools"
    pre_module_reporter "${FUNCNAME[0]}"
 
    EXTRACTION_DIR="$LOG_DIR"/firmware/patool_extraction/
 
    patools_extractor "$FIRMWARE_PATH" "$EXTRACTION_DIR"
 
    if [[ "$FILES_PATOOLS" -gt 0 ]]; then
      MD5_DONE_DEEP+=( "$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')" )
      export FIRMWARE_PATH="$LOG_DIR"/firmware/
      backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    fi
 
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
 
patools_extractor() {
  sub_module_title "Patool filesystem extractor"
 
  local FIRMWARE_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  FILES_PATOOLS=0
  local DIRS_PATOOLS=0
  local FIRMWARE_NAME_=""
 
  if ! [[ -f "$FIRMWARE_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
  FIRMWARE_NAME_="$(basename "$FIRMWARE_PATH_")"
 
  [[ "$STRICT_MODE" -eq 1 ]] && set +e
 
    # 使用patool测试
  patool -v test "$FIRMWARE_PATH_" | tee -a "$LOG_PATH_MODULE"/paextract_test_"$FIRMWARE_NAME_".log
 
  [[ "$STRICT_MODE" -eq 1 ]] && set -e
 
  cat "$LOG_PATH_MODULE"/paextract_test_"$FIRMWARE_NAME_".log >> "$LOG_FILE"
 
  if ! [[ -d "$EXTRACTION_DIR_" ]]; then
    mkdir "$EXTRACTION_DIR_"
  fi
 
  if grep -q "patool: ... tested ok." "$LOG_PATH_MODULE"/paextract_test_"$FIRMWARE_NAME_".log ; then
 
    print_ln
    print_output "[*] Valid compressed file detected - extraction process via patool started"
 
      # 提取
    patool -v extract "$FIRMWARE_PATH_" --outdir "$EXTRACTION_DIR_" | tee -a "$LOG_PATH_MODULE"/paextract_extract_"$FIRMWARE_NAME_".log
    cat "$LOG_PATH_MODULE"/paextract_extract_"$FIRMWARE_NAME_".log >> "$LOG_FILE"
 
  else
    # Fallback if unzip does not work:
    print_ln
    print_output "[*] No valid compressed file detected - extraction process via binwalk started"
 
      # P60里的函数,下方给出分析
    binwalk_deep_extract_helper 0 "$FIRMWARE_PATH_" "$EXTRACTION_DIR_"
  fi
 
  print_ln
  print_output "[*] Using the following firmware directory ($ORANGE$EXTRACTION_DIR_$NC) as base directory:"
  find "$EXTRACTION_DIR_" -xdev -maxdepth 1 -ls | tee -a "$LOG_FILE"
  print_ln
 
  FILES_PATOOLS=$(find "$EXTRACTION_DIR_" -type f | wc -l)
  DIRS_PATOOLS=$(find "$EXTRACTION_DIR_" -type d | wc -l)
  print_output "[*] Extracted $ORANGE$FILES_PATOOLS$NC files and $ORANGE$DIRS_PATOOLS$NC directories from the firmware image."
  write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
  write_csv_log "Patool extractor" "$FIRMWARE_PATH_" "$EXTRACTION_DIR_" "$FILES_PATOOLS" "$DIRS_PATOOLS" "NA"
  print_ln
}
export PRE_THREAD_ENA=0
 
P05_patools_init() {
  local NEG_LOG=0
 
  if [[ "$PATOOLS_INIT" -eq 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    module_title "Initial extractor of different archive types via patools"
    pre_module_reporter "${FUNCNAME[0]}"
 
    EXTRACTION_DIR="$LOG_DIR"/firmware/patool_extraction/
 
    patools_extractor "$FIRMWARE_PATH" "$EXTRACTION_DIR"
 
    if [[ "$FILES_PATOOLS" -gt 0 ]]; then
      MD5_DONE_DEEP+=( "$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')" )
      export FIRMWARE_PATH="$LOG_DIR"/firmware/
      backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    fi
 
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
 
patools_extractor() {
  sub_module_title "Patool filesystem extractor"
 
  local FIRMWARE_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  FILES_PATOOLS=0
  local DIRS_PATOOLS=0
  local FIRMWARE_NAME_=""
 
  if ! [[ -f "$FIRMWARE_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
  FIRMWARE_NAME_="$(basename "$FIRMWARE_PATH_")"
 
  [[ "$STRICT_MODE" -eq 1 ]] && set +e
 
    # 使用patool测试
  patool -v test "$FIRMWARE_PATH_" | tee -a "$LOG_PATH_MODULE"/paextract_test_"$FIRMWARE_NAME_".log
 
  [[ "$STRICT_MODE" -eq 1 ]] && set -e
 
  cat "$LOG_PATH_MODULE"/paextract_test_"$FIRMWARE_NAME_".log >> "$LOG_FILE"
 
  if ! [[ -d "$EXTRACTION_DIR_" ]]; then
    mkdir "$EXTRACTION_DIR_"
  fi
 
  if grep -q "patool: ... tested ok." "$LOG_PATH_MODULE"/paextract_test_"$FIRMWARE_NAME_".log ; then
 
    print_ln
    print_output "[*] Valid compressed file detected - extraction process via patool started"
 
      # 提取
    patool -v extract "$FIRMWARE_PATH_" --outdir "$EXTRACTION_DIR_" | tee -a "$LOG_PATH_MODULE"/paextract_extract_"$FIRMWARE_NAME_".log
    cat "$LOG_PATH_MODULE"/paextract_extract_"$FIRMWARE_NAME_".log >> "$LOG_FILE"
 
  else
    # Fallback if unzip does not work:
    print_ln
    print_output "[*] No valid compressed file detected - extraction process via binwalk started"
 
      # P60里的函数,下方给出分析
    binwalk_deep_extract_helper 0 "$FIRMWARE_PATH_" "$EXTRACTION_DIR_"
  fi
 
  print_ln
  print_output "[*] Using the following firmware directory ($ORANGE$EXTRACTION_DIR_$NC) as base directory:"
  find "$EXTRACTION_DIR_" -xdev -maxdepth 1 -ls | tee -a "$LOG_FILE"
  print_ln
 
  FILES_PATOOLS=$(find "$EXTRACTION_DIR_" -type f | wc -l)
  DIRS_PATOOLS=$(find "$EXTRACTION_DIR_" -type d | wc -l)
  print_output "[*] Extracted $ORANGE$FILES_PATOOLS$NC files and $ORANGE$DIRS_PATOOLS$NC directories from the firmware image."
  write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
  write_csv_log "Patool extractor" "$FIRMWARE_PATH_" "$EXTRACTION_DIR_" "$FILES_PATOOLS" "$DIRS_PATOOLS" "NA"
  print_ln
}
binwalk_deep_extract_helper() {
  # Matryoshka mode is first parameter: 1 - enable, 0 - disable
  local MATRYOSHKA_="${1:-0}"
  local FILE_TO_EXTRACT_="${2:-}"
  local DEST_FILE_="${3:-}"
 
  if ! [[ -f "$FILE_TO_EXTRACT_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
    # 前面通过检查binwalk的版本,使用不同方法提取
  if [[ "$BINWALK_VER_CHECK" == 1 ]]; then
    if [[ "$MATRYOSHKA_" -eq 1 ]]; then
 
        # 使用binwalk提取
      binwalk --run-as=root --preserve-symlinks --dd='.*' -e -M -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    else
      # no more Matryoshka mode ... we are doing it manually and check the files every round via MD5
      binwalk --run-as=root --preserve-symlinks --dd='.*' -e -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    fi
  else
    if [[ "$MATRYOSHKA_" -eq 1 ]]; then
      binwalk --dd='.*' -e -M -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    else
      binwalk --dd='.*' -e -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    fi
  fi
}
binwalk_deep_extract_helper() {
  # Matryoshka mode is first parameter: 1 - enable, 0 - disable
  local MATRYOSHKA_="${1:-0}"
  local FILE_TO_EXTRACT_="${2:-}"
  local DEST_FILE_="${3:-}"
 
  if ! [[ -f "$FILE_TO_EXTRACT_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
    # 前面通过检查binwalk的版本,使用不同方法提取
  if [[ "$BINWALK_VER_CHECK" == 1 ]]; then
    if [[ "$MATRYOSHKA_" -eq 1 ]]; then
 
        # 使用binwalk提取
      binwalk --run-as=root --preserve-symlinks --dd='.*' -e -M -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    else
      # no more Matryoshka mode ... we are doing it manually and check the files every round via MD5
      binwalk --run-as=root --preserve-symlinks --dd='.*' -e -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    fi
  else
    if [[ "$MATRYOSHKA_" -eq 1 ]]; then
      binwalk --dd='.*' -e -M -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    else
      binwalk --dd='.*' -e -C "$DEST_FILE_" "$FILE_TO_EXTRACT_" | tee -a "$LOG_FILE" || true
    fi
  fi
}
P10_vmdk_extractor() {
  local NEG_LOG=0
 
  if [[ "${VMDK_DETECTED-0}" -eq 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    module_title "VMDK (Virtual Machine Disk) extractor"
    EXTRACTION_DIR="$LOG_DIR"/firmware/vmdk_extractor/
 
      # 提取vmdk
    vmdk_extractor "$FIRMWARE_PATH" "$EXTRACTION_DIR"
 
    if [[ "$VMDK_FILES" -gt 0 ]]; then
      MD5_DONE_DEEP+=( "$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')" )
      export FIRMWARE_PATH="$LOG_DIR"/firmware/
      backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    fi
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
P10_vmdk_extractor() {
  local NEG_LOG=0
 
  if [[ "${VMDK_DETECTED-0}" -eq 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    module_title "VMDK (Virtual Machine Disk) extractor"
    EXTRACTION_DIR="$LOG_DIR"/firmware/vmdk_extractor/
 
      # 提取vmdk
    vmdk_extractor "$FIRMWARE_PATH" "$EXTRACTION_DIR"
 
    if [[ "$VMDK_FILES" -gt 0 ]]; then
      MD5_DONE_DEEP+=( "$(md5sum "$FIRMWARE_PATH" | awk '{print $1}')" )
      export FIRMWARE_PATH="$LOG_DIR"/firmware/
      backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    fi
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
vmdk_extractor() {
  local VMDK_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  local MOUNT_DEV=""
  local DEV_NAME=""
  local TMP_VMDK_MNT="$TMP_DIR/vmdk_mount_$RANDOM"
  local VMDK_DIRS=0
  local RET=0
  VMDK_FILES=0
 
  if ! [[ -f "$VMDK_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
    # 设置子模块title
  sub_module_title "VMDK (Virtual Machine Disk) extractor"
 
  print_output "[*] Enumeration of devices in VMDK images $ORANGE$VMDK_PATH_$NC"
  disable_strict_mode "$STRICT_MODE" 0
 
  # 使用virt工具输出文件系统信息
  virt-filesystems -a "$VMDK_PATH_" > "$TMP_DIR"/vmdk.log
  RET="$?"
 
  if [[ "$RET" -ne 0 ]]; then
 
      # 用7z解压vmdk(7z挺强大)
    # backup with 7z
    7z x -o"$EXTRACTION_DIR_" "$VMDK_PATH_"
 
      # 获取7z结果返回的状态码,并进行错误处理
    RET="$?"
    if [[ "$RET" -ne 0 ]]; then
      print_output "[-] WARNING: VMDK filesystem not enumerated"
      enable_strict_mode "$STRICT_MODE" 0
      return
    fi
  else
    mapfile -t VMDK_VIRT_FS < "$TMP_DIR"/vmdk.log
    for MOUNT_DEV in "${VMDK_VIRT_FS[@]}"; do
      print_output "[*] Found device $ORANGE$MOUNT_DEV$NC"
    done
  fi
  enable_strict_mode "$STRICT_MODE" 0
 
  mkdir -p "$TMP_VMDK_MNT" || true
 
  for MOUNT_DEV in "${VMDK_VIRT_FS[@]}"; do
    DEV_NAME=$(basename "$MOUNT_DEV")
 
      # 尝试挂载vmdk
    print_output "[*] Trying to mount $ORANGE$MOUNT_DEV$NC to $ORANGE$TMP_VMDK_MNT$NC directory"
    # if troubles ahead with vmdk mount, remove the error redirection
    guestmount -a "$VMDK_PATH_" -m "$MOUNT_DEV" --ro "$TMP_VMDK_MNT" 2>/dev/null || true
    if mount | grep -q vmdk_mount; then
      print_output "[*] Copying $ORANGE$MOUNT_DEV$NC to firmware directory $ORANGE$EXTRACTION_DIR_/$DEV_NAME$NC"
      mkdir -p "$EXTRACTION_DIR_"/"$DEV_NAME"/ || true
      cp -pri "$TMP_VMDK_MNT"/* "$EXTRACTION_DIR_"/"$DEV_NAME"/ || true
      umount "$TMP_VMDK_MNT"
    fi
  done
 
    # 统计提取出的文件和文件夹数量
  if [[ -d "$EXTRACTION_DIR_" ]]; then
    VMDK_FILES=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    VMDK_DIRS=$(find "$EXTRACTION_DIR_" -type d | wc -l)
  fi
 
  # 输出结果
  if [[ "$VMDK_FILES" -gt 0 ]]; then
    print_ln
    print_output "[*] Extracted $ORANGE$VMDK_FILES$NC files and $ORANGE$VMDK_DIRS$NC directories from the firmware image."
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "VMDK extractor" "$VMDK_PATH_" "$EXTRACTION_DIR_" "$VMDK_FILES" "$VMDK_DIRS" "NA"
    # currently unblob has issues with VMDKs. We need to disable it for this extraction process
    safe_echo 0 > "$TMP_DIR"/unblob_disable.cfg
  fi
 
    # 删除临时用于挂载的文件夹
  rm -r "$TMP_VMDK_MNT" || true
}
vmdk_extractor() {
  local VMDK_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  local MOUNT_DEV=""
  local DEV_NAME=""
  local TMP_VMDK_MNT="$TMP_DIR/vmdk_mount_$RANDOM"
  local VMDK_DIRS=0
  local RET=0
  VMDK_FILES=0
 
  if ! [[ -f "$VMDK_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
    # 设置子模块title
  sub_module_title "VMDK (Virtual Machine Disk) extractor"
 
  print_output "[*] Enumeration of devices in VMDK images $ORANGE$VMDK_PATH_$NC"
  disable_strict_mode "$STRICT_MODE" 0
 
  # 使用virt工具输出文件系统信息
  virt-filesystems -a "$VMDK_PATH_" > "$TMP_DIR"/vmdk.log
  RET="$?"
 
  if [[ "$RET" -ne 0 ]]; then
 
      # 用7z解压vmdk(7z挺强大)
    # backup with 7z
    7z x -o"$EXTRACTION_DIR_" "$VMDK_PATH_"
 
      # 获取7z结果返回的状态码,并进行错误处理
    RET="$?"
    if [[ "$RET" -ne 0 ]]; then
      print_output "[-] WARNING: VMDK filesystem not enumerated"
      enable_strict_mode "$STRICT_MODE" 0
      return
    fi
  else
    mapfile -t VMDK_VIRT_FS < "$TMP_DIR"/vmdk.log
    for MOUNT_DEV in "${VMDK_VIRT_FS[@]}"; do
      print_output "[*] Found device $ORANGE$MOUNT_DEV$NC"
    done
  fi
  enable_strict_mode "$STRICT_MODE" 0
 
  mkdir -p "$TMP_VMDK_MNT" || true
 
  for MOUNT_DEV in "${VMDK_VIRT_FS[@]}"; do
    DEV_NAME=$(basename "$MOUNT_DEV")
 
      # 尝试挂载vmdk
    print_output "[*] Trying to mount $ORANGE$MOUNT_DEV$NC to $ORANGE$TMP_VMDK_MNT$NC directory"
    # if troubles ahead with vmdk mount, remove the error redirection
    guestmount -a "$VMDK_PATH_" -m "$MOUNT_DEV" --ro "$TMP_VMDK_MNT" 2>/dev/null || true
    if mount | grep -q vmdk_mount; then
      print_output "[*] Copying $ORANGE$MOUNT_DEV$NC to firmware directory $ORANGE$EXTRACTION_DIR_/$DEV_NAME$NC"
      mkdir -p "$EXTRACTION_DIR_"/"$DEV_NAME"/ || true
      cp -pri "$TMP_VMDK_MNT"/* "$EXTRACTION_DIR_"/"$DEV_NAME"/ || true
      umount "$TMP_VMDK_MNT"
    fi
  done
 
    # 统计提取出的文件和文件夹数量
  if [[ -d "$EXTRACTION_DIR_" ]]; then
    VMDK_FILES=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    VMDK_DIRS=$(find "$EXTRACTION_DIR_" -type d | wc -l)
  fi
 
  # 输出结果
  if [[ "$VMDK_FILES" -gt 0 ]]; then
    print_ln
    print_output "[*] Extracted $ORANGE$VMDK_FILES$NC files and $ORANGE$VMDK_DIRS$NC directories from the firmware image."
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "VMDK extractor" "$VMDK_PATH_" "$EXTRACTION_DIR_" "$VMDK_FILES" "$VMDK_DIRS" "NA"
    # currently unblob has issues with VMDKs. We need to disable it for this extraction process
    safe_echo 0 > "$TMP_DIR"/unblob_disable.cfg
  fi
 
    # 删除临时用于挂载的文件夹
  rm -r "$TMP_VMDK_MNT" || true
}
P11_dlink_SHRS_enc_extract() {
  local NEG_LOG=0
 
  if [[ "$DLINK_ENC_DETECTED" -ne 0 ]] && [[ "$MODULE_DISABLED" -ne 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    module_title "DLink encrypted firmware extractor"
    pre_module_reporter "${FUNCNAME[0]}"
    EXTRACTION_FILE="$LOG_DIR"/firmware/firmware_dlink_dec.bin
 
      # 有不同的加密方式,使用对应的方式解密
    if [[ "$DLINK_ENC_DETECTED" -eq 1 ]]; then
      dlink_SHRS_enc_extractor "$FIRMWARE_PATH" "$EXTRACTION_FILE"n=
    elif [[ "$DLINK_ENC_DETECTED" -eq 2 ]]; then
      dlink_enc_img_extractor "$FIRMWARE_PATH" "$EXTRACTION_FILE"
    fi
 
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
P11_dlink_SHRS_enc_extract() {
  local NEG_LOG=0
 
  if [[ "$DLINK_ENC_DETECTED" -ne 0 ]] && [[ "$MODULE_DISABLED" -ne 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    module_title "DLink encrypted firmware extractor"
    pre_module_reporter "${FUNCNAME[0]}"
    EXTRACTION_FILE="$LOG_DIR"/firmware/firmware_dlink_dec.bin
 
      # 有不同的加密方式,使用对应的方式解密
    if [[ "$DLINK_ENC_DETECTED" -eq 1 ]]; then
      dlink_SHRS_enc_extractor "$FIRMWARE_PATH" "$EXTRACTION_FILE"n=
    elif [[ "$DLINK_ENC_DETECTED" -eq 2 ]]; then
      dlink_enc_img_extractor "$FIRMWARE_PATH" "$EXTRACTION_FILE"
    fi
 
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
dlink_SHRS_enc_extractor() {
  local DLINK_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
  if ! [[ -f "$DLINK_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  local MODULE_DISABLED=1
 
  if [[ "$MODULE_DISABLED" -eq 1 ]]; then
    print_output "[*] Module ${FUNCNAME[0]} is deprecated and will be removed in the future"
    return
  fi
 
  sub_module_title "DLink encrypted firmware extractor"
 
  hexdump -C "$DLINK_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
  print_ln
 
    # 使用DLINK密钥对固件进行AES解密
    # 参考:https://www.bleepingcomputer.com/news/security/d-link-blunder-firmware-encryption-key-exposed-in-unencrypted-image/
  dd if="$DLINK_ENC_PATH_" skip=1756 iflag=skip_bytes|openssl aes-128-cbc -d -p -nopad -nosalt -K "c05fbf1936c99429ce2a0781f08d6ad8" -iv "67c6697351ff4aec29cdbaabf2fbe346" --nosalt -in /dev/stdin -out "$EXTRACTION_FILE_" 2>&1 || true | tee -a "$LOG_FILE"
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    print_output "[+] Decrypted D-Link firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "DLink SHRS decryptor" "$DLINK_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    if [[ -z "${FW_VENDOR:-}" ]]; then
      FW_VENDOR="D-Link"
      backup_var "FW_VENDOR" "$FW_VENDOR"
    fi
  else
    print_output "[-] Decryption of D-Link firmware file failed"
  fi
}
dlink_SHRS_enc_extractor() {
  local DLINK_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
  if ! [[ -f "$DLINK_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  local MODULE_DISABLED=1
 
  if [[ "$MODULE_DISABLED" -eq 1 ]]; then
    print_output "[*] Module ${FUNCNAME[0]} is deprecated and will be removed in the future"
    return
  fi
 
  sub_module_title "DLink encrypted firmware extractor"
 
  hexdump -C "$DLINK_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
  print_ln
 
    # 使用DLINK密钥对固件进行AES解密
    # 参考:https://www.bleepingcomputer.com/news/security/d-link-blunder-firmware-encryption-key-exposed-in-unencrypted-image/
  dd if="$DLINK_ENC_PATH_" skip=1756 iflag=skip_bytes|openssl aes-128-cbc -d -p -nopad -nosalt -K "c05fbf1936c99429ce2a0781f08d6ad8" -iv "67c6697351ff4aec29cdbaabf2fbe346" --nosalt -in /dev/stdin -out "$EXTRACTION_FILE_" 2>&1 || true | tee -a "$LOG_FILE"
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    print_output "[+] Decrypted D-Link firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "DLink SHRS decryptor" "$DLINK_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    if [[ -z "${FW_VENDOR:-}" ]]; then
      FW_VENDOR="D-Link"
      backup_var "FW_VENDOR" "$FW_VENDOR"
    fi
  else
    print_output "[-] Decryption of D-Link firmware file failed"
  fi
}
dlink_enc_img_extractor(){
  local TMP_DIR="$LOG_DIR""/tmp"
  local DLINK_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
  local TMP_IMAGE_FILE="$TMP_DIR/image.bin"
  if ! [[ -f "$DLINK_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
  local IMAGE_SIZE=0
  local OFFSET=0
  local ITERATION=0
 
  local MODULE_DISABLED=1
 
  if [[ "$MODULE_DISABLED" -eq 1 ]]; then
    print_output "[*] Module ${FUNCNAME[0]} is deprecated and will be removed in the future"
    return
  fi
 
  sub_module_title "DLink encrpted_image extractor"
 
  hexdump -C "$DLINK_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
    # 从偏移量为 16 的位置开始复制到另外一个文件 $TMP_IMAGE_FILE 中
  dd if="$DLINK_ENC_PATH_" skip=16 iflag=skip_bytes of="$TMP_IMAGE_FILE" 2>&1 | tee -a "$LOG_FILE"
 
  IMAGE_SIZE=$(stat -c%s "$TMP_IMAGE_FILE")
  (( ROOF=IMAGE_SIZE/131072 ))
 
    # 每 131072(128KB) 为一块进行处理
  for ((ITERATION=0; ITERATION<ROOF; ITERATION++)); do
    if [[ "$ITERATION" -eq 0 ]]; then
      OFFSET=0
    else
      (( OFFSET=131072*ITERATION ))
    fi
 
      # aes解密
    dd if="$TMP_IMAGE_FILE" skip="$OFFSET" iflag=skip_bytes count=256| openssl aes-256-cbc -d -in /dev/stdin  -out /dev/stdout \
    -K "6865392d342b4d212964363d6d7e7765312c7132613364316e26322a5a5e2538" -iv "4a253169516c38243d6c6d2d3b384145" --nopad \
    --nosalt | dd if=/dev/stdin of="$EXTRACTION_FILE_" oflag=append conv=notrunc 2>&1 | tee -a "$LOG_FILE"
  done
  # Now it should be a .ubi file thats somewhat readable and extractable via ubireader
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    UBI_OUT=$(file "$EXTRACTION_FILE_")
    if [[ "$UBI_OUT" == *"UBI image, version"* ]]; then
      print_output "[+] Decrypted D-Link firmware file to $ORANGE$EXTRACTION_FILE_$NC"
      print_ln
      print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
      write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
      write_csv_log "DLink enc_img decryptor" "$DLINK_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
      export FIRMWARE_PATH="$EXTRACTION_FILE_"
      backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
      if [[ -z "${FW_VENDOR:-}" ]]; then
        FW_VENDOR="D-Link"
        backup_var "FW_VENDOR" "$FW_VENDOR"
      fi
      EXTRACTION_DIR="$LOG_DIR/firmware/dlink_ubi_extracted"
      mkdir -p "$EXTRACTION_DIR" || true
 
        # ubi提取
      ubi_extractor "$FIRMWARE_PATH" "$EXTRACTION_DIR"
    else
      print_output "[-] Further extraction of D-Link firmware file via deep extraction"
    fi
  else
    print_output "[-] Decryption of D-Link firmware file failed"
  fi
}
dlink_enc_img_extractor(){
  local TMP_DIR="$LOG_DIR""/tmp"
  local DLINK_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
  local TMP_IMAGE_FILE="$TMP_DIR/image.bin"
  if ! [[ -f "$DLINK_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
  local IMAGE_SIZE=0
  local OFFSET=0
  local ITERATION=0
 
  local MODULE_DISABLED=1
 
  if [[ "$MODULE_DISABLED" -eq 1 ]]; then
    print_output "[*] Module ${FUNCNAME[0]} is deprecated and will be removed in the future"
    return
  fi
 
  sub_module_title "DLink encrpted_image extractor"
 
  hexdump -C "$DLINK_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
    # 从偏移量为 16 的位置开始复制到另外一个文件 $TMP_IMAGE_FILE 中
  dd if="$DLINK_ENC_PATH_" skip=16 iflag=skip_bytes of="$TMP_IMAGE_FILE" 2>&1 | tee -a "$LOG_FILE"
 
  IMAGE_SIZE=$(stat -c%s "$TMP_IMAGE_FILE")
  (( ROOF=IMAGE_SIZE/131072 ))
 
    # 每 131072(128KB) 为一块进行处理
  for ((ITERATION=0; ITERATION<ROOF; ITERATION++)); do
    if [[ "$ITERATION" -eq 0 ]]; then
      OFFSET=0
    else
      (( OFFSET=131072*ITERATION ))
    fi
 
      # aes解密
    dd if="$TMP_IMAGE_FILE" skip="$OFFSET" iflag=skip_bytes count=256| openssl aes-256-cbc -d -in /dev/stdin  -out /dev/stdout \
    -K "6865392d342b4d212964363d6d7e7765312c7132613364316e26322a5a5e2538" -iv "4a253169516c38243d6c6d2d3b384145" --nopad \
    --nosalt | dd if=/dev/stdin of="$EXTRACTION_FILE_" oflag=append conv=notrunc 2>&1 | tee -a "$LOG_FILE"
  done
  # Now it should be a .ubi file thats somewhat readable and extractable via ubireader
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    UBI_OUT=$(file "$EXTRACTION_FILE_")
    if [[ "$UBI_OUT" == *"UBI image, version"* ]]; then
      print_output "[+] Decrypted D-Link firmware file to $ORANGE$EXTRACTION_FILE_$NC"
      print_ln
      print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
      write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
      write_csv_log "DLink enc_img decryptor" "$DLINK_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
      export FIRMWARE_PATH="$EXTRACTION_FILE_"
      backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
      if [[ -z "${FW_VENDOR:-}" ]]; then
        FW_VENDOR="D-Link"
        backup_var "FW_VENDOR" "$FW_VENDOR"
      fi
      EXTRACTION_DIR="$LOG_DIR/firmware/dlink_ubi_extracted"
      mkdir -p "$EXTRACTION_DIR" || true
 
        # ubi提取
      ubi_extractor "$FIRMWARE_PATH" "$EXTRACTION_DIR"
    else
      print_output "[-] Further extraction of D-Link firmware file via deep extraction"
    fi
  else
    print_output "[-] Decryption of D-Link firmware file failed"
  fi
}
avm_extractor() {
  local AVM_FW_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  if ! [[ -f "$AVM_FW_PATH_" ]]; then
    return
  fi
  local FRITZ_DIRS=0
  local FIT_IMAGES=()
  local FIT_IMAGE=""
  local RAM_DISKS=()
  local RAM_DISK=""
  local RAM_DISK_NAME=""
  export FRITZ_FILE=0
  export FRITZ_VERSION=""
 
  local MODULE_DISABLED=1
 
  if [[ "$MODULE_DISABLED" -eq 1 ]]; then
    print_output "[*] Module ${FUNCNAME[0]} is deprecated and will be removed in the future"
    return
  fi
 
  sub_module_title "AVM freetz-ng firmware extractor"
 
  # read only filesystem bypass:
    # 将 freetz-ng 工具链的 .config 文件复制到临时目录中
  cp "$EXT_DIR"/freetz-ng/.config "$TMP_DIR"/.config
    # 使用 freetz-ng/fwmod 工具从 AVM 固件中提取文件
  "$EXT_DIR"/freetz-ng/fwmod -u -i "$TMP_DIR"/.config -d "$EXTRACTION_DIR_" "$AVM_FW_PATH_" | tee -a "$LOG_FILE" || true
 
  if [[ -d "$EXTRACTION_DIR_" ]]; then
 
      # FRITZ 是来自德国 AVM 公司生产的一系列家用网络设备,包括路由器、网关、电话和其他相关产品。
      # 统计提取出的文件和文件夹数量
    FRITZ_FILES=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    FRITZ_DIRS=$(find "$EXTRACTION_DIR_" -type d | wc -l)
      # 提取版本信息
    FRITZ_VERSION=$(grep "detected firmware version:" "$LOG_FILE" | cut -d ":" -f2- || true)
    if [[ -z "$FRITZ_VERSION" ]]; then
      FRITZ_VERSION="NA"
    else
      print_output "[+] Detected Fritz version: $ORANGE$FRITZ_VERSION$NC"
    fi
 
    # fitimages are handled here with fitimg - binwalk and unblob are also able to handle these images
    # but it is currently more beautiful doing the AVM extraction in one place here
    mapfile -t FIT_IMAGES < <(find "$EXTRACTION_DIR_" -type f -name "fit-image")
      # 在提取的文件中,该函数会检测是否存在名为 "fit-image" 的文件
        # 如果存在则调用 fitimg 工具对其解压缩
        # 解压缩结果输出到 EXTRACTION_DIR/fit-image-extraction 目录中
        # 并从中提取 RAM_DISK
    if [[ "${#FIT_IMAGES[@]}" -gt 0 ]]; then
      if [[ -f "$EXT_DIR"/fitimg-0.8/fitimg ]]; then
        for FIT_IMAGE in "${FIT_IMAGES[@]}"; do
          print_output "[*] Detected fit-image: $ORANGE$FIT_IMAGE$NC"
          print_output "[*] Extracting fit-image with fitimg to $ORANGE$EXTRACTION_DIR/fit-image-extraction$NC"
          mkdir -p "$EXTRACTION_DIR/fit-image-extraction"
          "$EXT_DIR"/fitimg-0.8/fitimg -x "$FIT_IMAGE" -d "$EXTRACTION_DIR"/fit-image-extraction || true
          mapfile -t RAM_DISKS < <(find "$EXTRACTION_DIR_"/fit-image-extraction -type f -name "*ramdisk")
          print_ln
        done
      else
        print_output "[-] Fitimg installation not available - check your installation"
      fi
    fi
    if [[ "${#RAM_DISKS[@]}" -gt 0 ]]; then
      for RAM_DISK in "${RAM_DISKS[@]}"; do
        print_output "[*] Detected AVM ramdisk: $ORANGE$RAM_DISK$NC"
        RAM_DISK_NAME="$(basename "$RAM_DISK")"
 
          # 对RAM_DISK进行深度提取
        binwalk_deep_extract_helper 1 "$RAM_DISK" "$EXTRACTION_DIR_"/fit-image-extraction/"$RAM_DISK_NAME"_binwalk
        print_ln
      done
    fi
 
    if [[ "$FRITZ_FILES" -gt 0 ]]; then
      print_ln
      print_output "[*] Extracted $ORANGE$FRITZ_FILES$NC files and $ORANGE$FRITZ_DIRS$NC directories from the firmware image."
      write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
      write_csv_log "AVM extractor" "$AVM_FW_PATH_" "$EXTRACTION_DIR_" "$FRITZ_FILES" "$FRITZ_DIRS" "$FRITZ_VERSION"
      export DEEP_EXTRACTOR=1
      MD5_DONE_DEEP+=( "$(md5sum "$AVM_FW_PATH_" | awk '{print $1}')" )
 
      if [[ -z "${FW_VENDOR:-}" ]]; then
        FW_VENDOR="AVM"
        backup_var "FW_VENDOR" "$FW_VENDOR"
      fi
      if [[ -z "${FW_VERSION:-}" && "$FRITZ_VERSION" != "NA" ]]; then
        FW_VERSION="$FRITZ_VERSION"
      fi
    fi
  fi
}
avm_extractor() {
  local AVM_FW_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  if ! [[ -f "$AVM_FW_PATH_" ]]; then
    return
  fi
  local FRITZ_DIRS=0
  local FIT_IMAGES=()
  local FIT_IMAGE=""
  local RAM_DISKS=()
  local RAM_DISK=""
  local RAM_DISK_NAME=""
  export FRITZ_FILE=0
  export FRITZ_VERSION=""
 
  local MODULE_DISABLED=1
 
  if [[ "$MODULE_DISABLED" -eq 1 ]]; then
    print_output "[*] Module ${FUNCNAME[0]} is deprecated and will be removed in the future"
    return
  fi
 
  sub_module_title "AVM freetz-ng firmware extractor"
 
  # read only filesystem bypass:
    # 将 freetz-ng 工具链的 .config 文件复制到临时目录中
  cp "$EXT_DIR"/freetz-ng/.config "$TMP_DIR"/.config
    # 使用 freetz-ng/fwmod 工具从 AVM 固件中提取文件
  "$EXT_DIR"/freetz-ng/fwmod -u -i "$TMP_DIR"/.config -d "$EXTRACTION_DIR_" "$AVM_FW_PATH_" | tee -a "$LOG_FILE" || true
 
  if [[ -d "$EXTRACTION_DIR_" ]]; then
 
      # FRITZ 是来自德国 AVM 公司生产的一系列家用网络设备,包括路由器、网关、电话和其他相关产品。
      # 统计提取出的文件和文件夹数量
    FRITZ_FILES=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    FRITZ_DIRS=$(find "$EXTRACTION_DIR_" -type d | wc -l)
      # 提取版本信息
    FRITZ_VERSION=$(grep "detected firmware version:" "$LOG_FILE" | cut -d ":" -f2- || true)
    if [[ -z "$FRITZ_VERSION" ]]; then
      FRITZ_VERSION="NA"
    else
      print_output "[+] Detected Fritz version: $ORANGE$FRITZ_VERSION$NC"
    fi
 
    # fitimages are handled here with fitimg - binwalk and unblob are also able to handle these images
    # but it is currently more beautiful doing the AVM extraction in one place here
    mapfile -t FIT_IMAGES < <(find "$EXTRACTION_DIR_" -type f -name "fit-image")
      # 在提取的文件中,该函数会检测是否存在名为 "fit-image" 的文件
        # 如果存在则调用 fitimg 工具对其解压缩
        # 解压缩结果输出到 EXTRACTION_DIR/fit-image-extraction 目录中
        # 并从中提取 RAM_DISK
    if [[ "${#FIT_IMAGES[@]}" -gt 0 ]]; then
      if [[ -f "$EXT_DIR"/fitimg-0.8/fitimg ]]; then
        for FIT_IMAGE in "${FIT_IMAGES[@]}"; do
          print_output "[*] Detected fit-image: $ORANGE$FIT_IMAGE$NC"
          print_output "[*] Extracting fit-image with fitimg to $ORANGE$EXTRACTION_DIR/fit-image-extraction$NC"
          mkdir -p "$EXTRACTION_DIR/fit-image-extraction"
          "$EXT_DIR"/fitimg-0.8/fitimg -x "$FIT_IMAGE" -d "$EXTRACTION_DIR"/fit-image-extraction || true
          mapfile -t RAM_DISKS < <(find "$EXTRACTION_DIR_"/fit-image-extraction -type f -name "*ramdisk")
          print_ln
        done
      else
        print_output "[-] Fitimg installation not available - check your installation"
      fi
    fi
    if [[ "${#RAM_DISKS[@]}" -gt 0 ]]; then
      for RAM_DISK in "${RAM_DISKS[@]}"; do
        print_output "[*] Detected AVM ramdisk: $ORANGE$RAM_DISK$NC"
        RAM_DISK_NAME="$(basename "$RAM_DISK")"
 
          # 对RAM_DISK进行深度提取
        binwalk_deep_extract_helper 1 "$RAM_DISK" "$EXTRACTION_DIR_"/fit-image-extraction/"$RAM_DISK_NAME"_binwalk
        print_ln
      done
    fi
 
    if [[ "$FRITZ_FILES" -gt 0 ]]; then
      print_ln
      print_output "[*] Extracted $ORANGE$FRITZ_FILES$NC files and $ORANGE$FRITZ_DIRS$NC directories from the firmware image."
      write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
      write_csv_log "AVM extractor" "$AVM_FW_PATH_" "$EXTRACTION_DIR_" "$FRITZ_FILES" "$FRITZ_DIRS" "$FRITZ_VERSION"
      export DEEP_EXTRACTOR=1
      MD5_DONE_DEEP+=( "$(md5sum "$AVM_FW_PATH_" | awk '{print $1}')" )
 
      if [[ -z "${FW_VENDOR:-}" ]]; then
        FW_VENDOR="AVM"
        backup_var "FW_VENDOR" "$FW_VENDOR"
      fi
      if [[ -z "${FW_VERSION:-}" && "$FRITZ_VERSION" != "NA" ]]; then
        FW_VERSION="$FRITZ_VERSION"
      fi
    fi
  fi
}
P13_uboot_mkimage() {
  local NEG_LOG=0
  if [[ "$UBOOT_IMAGE" -eq 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    local IMAGE_NAME=""
    local IMAGE_TYPE=""
    module_title "Uboot image details"
    pre_module_reporter "${FUNCNAME[0]}"
 
      # 使用mkimage工具读取固件
    mkimage -l "$FIRMWARE_PATH" | tee -a "$LOG_FILE"
    IMAGE_NAME=$(grep "Image Name" "$LOG_FILE" 2>/dev/null | awk '{print $3,$4,$5,$6,$7,$8,$9,$10}' || true)
    IMAGE_TYPE=$(grep "Image Type" "$LOG_FILE" 2>/dev/null | awk '{print $3,$4,$5,$6,$7,$8,$9,$10}' || true)
    write_csv_log "Identifier" "Value"
    write_csv_log "ImageName" "$IMAGE_NAME"
    write_csv_log "ImageType" "$IMAGE_TYPE"
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
P13_uboot_mkimage() {
  local NEG_LOG=0
  if [[ "$UBOOT_IMAGE" -eq 1 ]]; then
    module_log_init "${FUNCNAME[0]}"
    local IMAGE_NAME=""
    local IMAGE_TYPE=""
    module_title "Uboot image details"
    pre_module_reporter "${FUNCNAME[0]}"
 
      # 使用mkimage工具读取固件
    mkimage -l "$FIRMWARE_PATH" | tee -a "$LOG_FILE"
    IMAGE_NAME=$(grep "Image Name" "$LOG_FILE" 2>/dev/null | awk '{print $3,$4,$5,$6,$7,$8,$9,$10}' || true)
    IMAGE_TYPE=$(grep "Image Type" "$LOG_FILE" 2>/dev/null | awk '{print $3,$4,$5,$6,$7,$8,$9,$10}' || true)
    write_csv_log "Identifier" "Value"
    write_csv_log "ImageName" "$IMAGE_NAME"
    write_csv_log "ImageType" "$IMAGE_TYPE"
    NEG_LOG=1
    module_end_log "${FUNCNAME[0]}" "$NEG_LOG"
  fi
}
ext_extractor() {
  local EXT_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  local TMP_EXT_MOUNT="$TMP_DIR""/ext_mount_$RANDOM"
  local DIRS_EXT_MOUNT=0
  FILES_EXT_MOUNT=0
 
  if ! [[ -f "$EXT_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  sub_module_title "EXT filesystem extractor"
 
  mkdir -p "$TMP_EXT_MOUNT" || true
  print_output "[*] Trying to mount $ORANGE$EXT_PATH_$NC to $ORANGE$TMP_EXT_MOUNT$NC directory"
  mount -o ro "$EXT_PATH_" "$TMP_EXT_MOUNT"
  if mount | grep -q ext_mount; then
    print_output "[*] Copying $ORANGE$TMP_EXT_MOUNT$NC to firmware tmp directory ($EXTRACTION_DIR_)"
    mkdir -p "$EXTRACTION_DIR_"
    cp -pri "$TMP_EXT_MOUNT"/* "$EXTRACTION_DIR_"
    print_ln
    print_output "[*] Using the following firmware directory ($ORANGE$EXTRACTION_DIR_$NC) as base directory:"
    find "$EXTRACTION_DIR_" -xdev -maxdepth 1 -ls | tee -a "$LOG_FILE"
    print_ln
    print_output "[*] Unmounting $ORANGE$TMP_EXT_MOUNT$NC directory"
 
    FILES_EXT_MOUNT=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    DIRS_EXT_MOUNT=$(find "$EXTRACTION_DIR_" -type d | wc -l)
    print_output "[*] Extracted $ORANGE$FILES_EXT_MOUNT$NC files and $ORANGE$DIRS_EXT_MOUNT$NC directories from the firmware image."
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "EXT filesystem extractor" "$EXT_PATH_" "$EXTRACTION_DIR_" "$FILES_EXT_MOUNT" "$DIRS_EXT_MOUNT" "NA"
    umount "$TMP_EXT_MOUNT" || true
  fi
  rm -r "$TMP_EXT_MOUNT"
}
ext_extractor() {
  local EXT_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  local TMP_EXT_MOUNT="$TMP_DIR""/ext_mount_$RANDOM"
  local DIRS_EXT_MOUNT=0
  FILES_EXT_MOUNT=0
 
  if ! [[ -f "$EXT_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  sub_module_title "EXT filesystem extractor"
 
  mkdir -p "$TMP_EXT_MOUNT" || true
  print_output "[*] Trying to mount $ORANGE$EXT_PATH_$NC to $ORANGE$TMP_EXT_MOUNT$NC directory"
  mount -o ro "$EXT_PATH_" "$TMP_EXT_MOUNT"
  if mount | grep -q ext_mount; then
    print_output "[*] Copying $ORANGE$TMP_EXT_MOUNT$NC to firmware tmp directory ($EXTRACTION_DIR_)"
    mkdir -p "$EXTRACTION_DIR_"
    cp -pri "$TMP_EXT_MOUNT"/* "$EXTRACTION_DIR_"
    print_ln
    print_output "[*] Using the following firmware directory ($ORANGE$EXTRACTION_DIR_$NC) as base directory:"
    find "$EXTRACTION_DIR_" -xdev -maxdepth 1 -ls | tee -a "$LOG_FILE"
    print_ln
    print_output "[*] Unmounting $ORANGE$TMP_EXT_MOUNT$NC directory"
 
    FILES_EXT_MOUNT=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    DIRS_EXT_MOUNT=$(find "$EXTRACTION_DIR_" -type d | wc -l)
    print_output "[*] Extracted $ORANGE$FILES_EXT_MOUNT$NC files and $ORANGE$DIRS_EXT_MOUNT$NC directories from the firmware image."
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "EXT filesystem extractor" "$EXT_PATH_" "$EXTRACTION_DIR_" "$FILES_EXT_MOUNT" "$DIRS_EXT_MOUNT" "NA"
    umount "$TMP_EXT_MOUNT" || true
  fi
  rm -r "$TMP_EXT_MOUNT"
}
ubi_extractor() {
  local UBI_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  local UBI_FILE=""
  local UBI_INFO=""
  local UBI_1st_ROUND=""
  local UBI_DATA=""
  local DIRS_UBI_EXT=0
  FILES_UBI_EXT=0
  if ! [[ -f "$UBI_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
  sub_module_title "UBI filesystem extractor"
 
  print_output "[*] Extracts UBI firmware image $ORANGE$UBI_PATH_$NC with ${ORANGE}ubireader_extract_images$NC."
  print_output "[*] File details: $ORANGE$(file "$UBI_PATH_" | cut -d ':' -f2-)$NC"
 
  # 提取ubi镜像
    # 工具仓库:https://github.com/jrspruitt/ubi_reader
  ubireader_extract_images -i -v -w -o "$EXTRACTION_DIR_"/ubi_images "$UBI_PATH_" | tee -a "$LOG_FILE" || true
  FILES_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_images -type f | wc -l)
  DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_images -type d | wc -l)
  print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image via UBI extraction round 1."
 
  print_output "[*] Extracts UBI firmware image $ORANGE$UBI_PATH_$NC with ${ORANGE}ubireader_extract_files$NC."
 
  ubireader_extract_files -i -v -w -o "$EXTRACTION_DIR_"/ubi_files "$UBI_PATH_" | tee -a "$LOG_FILE" || true
  FILES_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_files -type f | wc -l)
  DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_files -type d | wc -l)
  print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image via UBI extraction round 2."
 
    # 深度提取,继续查找UBI文件镜像,然后提取
  if [[ -d "$EXTRACTION_DIR_" ]]; then
    mapfile -t UBI_1st_ROUND < <(find "$EXTRACTION_DIR_" -type f -exec file {} \; | grep "UBI image" || true)
 
    for UBI_DATA in "${UBI_1st_ROUND[@]}"; do
      UBI_FILE=$(safe_echo "$UBI_DATA" | cut -d: -f1)
      UBI_INFO=$(safe_echo "$UBI_DATA" | cut -d: -f2)
      if [[ "$UBI_INFO" == *"UBIfs image"* ]]; then
        sub_module_title "UBIfs deep extraction"
        print_output "[*] Extracts UBIfs firmware image $ORANGE$UBI_PATH_$NC with ${ORANGE}ubireader_extract_files$NC."
        print_output "[*] File details: $ORANGE$(file "$UBI_FILE" | cut -d ':' -f2-)$NC"
        ubireader_extract_files -l -i -w -v -o "$EXTRACTION_DIR_"/UBIfs_extracted "$UBI_FILE" | tee -a "$LOG_FILE" || true
        FILES_UBI_EXT=$(find "$EXTRACTION_DIR_"/UBIfs_extracted -type f | wc -l)
        DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_"/UBIfs_extracted -type d | wc -l)
        print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image via UBI deep extraction."
      fi
    done
 
    print_ln
    FILES_UBI_EXT=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_" -type d | wc -l)
    print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image."
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "UBI filesystem extractor" "$UBI_PATH_" "$EXTRACTION_DIR_" "$FILES_UBI_EXT" "$DIRS_UBI_EXT" "NA"
  else
    print_output "[-] First round UBI extractor failed!"
  fi
}
ubi_extractor() {
  local UBI_PATH_="${1:-}"
  local EXTRACTION_DIR_="${2:-}"
  local UBI_FILE=""
  local UBI_INFO=""
  local UBI_1st_ROUND=""
  local UBI_DATA=""
  local DIRS_UBI_EXT=0
  FILES_UBI_EXT=0
  if ! [[ -f "$UBI_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
  sub_module_title "UBI filesystem extractor"
 
  print_output "[*] Extracts UBI firmware image $ORANGE$UBI_PATH_$NC with ${ORANGE}ubireader_extract_images$NC."
  print_output "[*] File details: $ORANGE$(file "$UBI_PATH_" | cut -d ':' -f2-)$NC"
 
  # 提取ubi镜像
    # 工具仓库:https://github.com/jrspruitt/ubi_reader
  ubireader_extract_images -i -v -w -o "$EXTRACTION_DIR_"/ubi_images "$UBI_PATH_" | tee -a "$LOG_FILE" || true
  FILES_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_images -type f | wc -l)
  DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_images -type d | wc -l)
  print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image via UBI extraction round 1."
 
  print_output "[*] Extracts UBI firmware image $ORANGE$UBI_PATH_$NC with ${ORANGE}ubireader_extract_files$NC."
 
  ubireader_extract_files -i -v -w -o "$EXTRACTION_DIR_"/ubi_files "$UBI_PATH_" | tee -a "$LOG_FILE" || true
  FILES_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_files -type f | wc -l)
  DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_"/ubi_files -type d | wc -l)
  print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image via UBI extraction round 2."
 
    # 深度提取,继续查找UBI文件镜像,然后提取
  if [[ -d "$EXTRACTION_DIR_" ]]; then
    mapfile -t UBI_1st_ROUND < <(find "$EXTRACTION_DIR_" -type f -exec file {} \; | grep "UBI image" || true)
 
    for UBI_DATA in "${UBI_1st_ROUND[@]}"; do
      UBI_FILE=$(safe_echo "$UBI_DATA" | cut -d: -f1)
      UBI_INFO=$(safe_echo "$UBI_DATA" | cut -d: -f2)
      if [[ "$UBI_INFO" == *"UBIfs image"* ]]; then
        sub_module_title "UBIfs deep extraction"
        print_output "[*] Extracts UBIfs firmware image $ORANGE$UBI_PATH_$NC with ${ORANGE}ubireader_extract_files$NC."
        print_output "[*] File details: $ORANGE$(file "$UBI_FILE" | cut -d ':' -f2-)$NC"
        ubireader_extract_files -l -i -w -v -o "$EXTRACTION_DIR_"/UBIfs_extracted "$UBI_FILE" | tee -a "$LOG_FILE" || true
        FILES_UBI_EXT=$(find "$EXTRACTION_DIR_"/UBIfs_extracted -type f | wc -l)
        DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_"/UBIfs_extracted -type d | wc -l)
        print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image via UBI deep extraction."
      fi
    done
 
    print_ln
    FILES_UBI_EXT=$(find "$EXTRACTION_DIR_" -type f | wc -l)
    DIRS_UBI_EXT=$(find "$EXTRACTION_DIR_" -type d | wc -l)
    print_output "[*] Extracted $ORANGE$FILES_UBI_EXT$NC files and $ORANGE$DIRS_UBI_EXT$NC directories from the firmware image."
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "UBI filesystem extractor" "$UBI_PATH_" "$EXTRACTION_DIR_" "$FILES_UBI_EXT" "$DIRS_UBI_EXT" "NA"
  else
    print_output "[-] First round UBI extractor failed!"
  fi
}
engenius_enc_extractor() {
  local ENGENIUS_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
 
  if ! [[ -f "$ENGENIUS_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  sub_module_title "EnGenius encrypted firmware extractor"
 
  hexdump -C "$ENGENIUS_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
  if [[ -f "$EXT_DIR"/engenius-decrypt.py ]]; then
 
      # 运行解密脚本
    python3 "$EXT_DIR"/engenius-decrypt.py "$ENGENIUS_ENC_PATH_" > "$EXTRACTION_FILE_"
  else
    print_output "[-] Decryptor not found - check your installation"
  fi
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    print_output "[+] Decrypted EnGenius firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    MD5_DONE_DEEP+=( "$(md5sum "$ENGENIUS_ENC_PATH_" | awk '{print $1}')" )
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "EnGenius decryptor" "$ENGENIUS_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
    if [[ -z "${FW_VENDOR:-}" ]]; then
      FW_VENDOR="EnGenius"
      backup_var "FW_VENDOR" "$FW_VENDOR"
    fi
  else
    print_output "[-] Decryption of EnGenius firmware file failed"
  fi
}
engenius_enc_extractor() {
  local ENGENIUS_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
 
  if ! [[ -f "$ENGENIUS_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  sub_module_title "EnGenius encrypted firmware extractor"
 
  hexdump -C "$ENGENIUS_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
  if [[ -f "$EXT_DIR"/engenius-decrypt.py ]]; then
 
      # 运行解密脚本
    python3 "$EXT_DIR"/engenius-decrypt.py "$ENGENIUS_ENC_PATH_" > "$EXTRACTION_FILE_"
  else
    print_output "[-] Decryptor not found - check your installation"
  fi
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    print_output "[+] Decrypted EnGenius firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    MD5_DONE_DEEP+=( "$(md5sum "$ENGENIUS_ENC_PATH_" | awk '{print $1}')" )
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "EnGenius decryptor" "$ENGENIUS_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
    if [[ -z "${FW_VENDOR:-}" ]]; then
      FW_VENDOR="EnGenius"
      backup_var "FW_VENDOR" "$FW_VENDOR"
    fi
  else
    print_output "[-] Decryption of EnGenius firmware file failed"
  fi
}
gpg_decompress_extractor() {
  local GPG_FILE_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
 
  if ! [[ -f "$GPG_FILE_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
  sub_module_title "GPG compressed firmware extractor"
 
  # 列出包列表并解密   
  gpg --list-packets "$GPG_FILE_PATH_" 2>/dev/null | tee -a "$LOG_FILE"
  gpg --decrypt "$GPG_FILE_PATH_" > "$EXTRACTION_FILE_" || true
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    print_output "[+] Extracted GPG compressed firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "GPG decompression" "$GPG_FILE_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
  else
    print_output "[-] Extraction of GPG compressed firmware file failed"
  fi
}
gpg_decompress_extractor() {
  local GPG_FILE_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
 
  if ! [[ -f "$GPG_FILE_PATH_" ]]; then
    print_output "[-] No file for extraction provided"
    return
  fi
 
  sub_module_title "GPG compressed firmware extractor"
 
  # 列出包列表并解密   
  gpg --list-packets "$GPG_FILE_PATH_" 2>/dev/null | tee -a "$LOG_FILE"
  gpg --decrypt "$GPG_FILE_PATH_" > "$EXTRACTION_FILE_" || true
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" ]]; then
    print_output "[+] Extracted GPG compressed firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "GPG decompression" "$GPG_FILE_PATH_" "$EXTRACTION_FILE_" "1" "NA" "NA"
  else
    print_output "[-] Extraction of GPG compressed firmware file failed"
  fi
}
qnap_enc_extractor() {
  local QNAP_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
  export QNAP=0
 
  if ! [[ -f "$QNAP_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  sub_module_title "QNAP encrypted firmware extractor"
 
  hexdump -C "$QNAP_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
  if [[ -f "$EXT_DIR"/PC1 ]]; then
    print_ln
    print_output "[*] Decrypting QNAP firmware with leaked key material ..."
    print_ln
 
      # 如果有PC1工具,则用PC1工具提取
    "$EXT_DIR"/PC1 d QNAPNASVERSION4 "$QNAP_ENC_PATH_" "$EXTRACTION_FILE_" | tee -a "$LOG_FILE"
  else
    print_output "[-] QNAP decryptor not found - check your installation"
  fi
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" && "$(file "$EXTRACTION_FILE_")" == *"gzip compressed data"* ]]; then
    print_output "[+] Decrypted QNAP firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    MD5_DONE_DEEP+=( "$(md5sum "$QNAP_ENC_PATH_" | awk '{print $1}')" )
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
 
      # 如果提取出来是gzip文件,那再提取一次,使用下面的函数
    export QNAP=1
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    print_ln
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "QNAP decryptor" "$QNAP_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "gzip compressed data"
    if [[ -z "${FW_VENDOR:-}" ]]; then
      FW_VENDOR="QNAP"
      backup_var "FW_VENDOR" "$FW_VENDOR"
    fi
 
  else
    print_output "[-] Decryption of QNAP firmware file failed"
  fi
}
qnap_enc_extractor() {
  local QNAP_ENC_PATH_="${1:-}"
  local EXTRACTION_FILE_="${2:-}"
  export QNAP=0
 
  if ! [[ -f "$QNAP_ENC_PATH_" ]]; then
    print_output "[-] No file for decryption provided"
    return
  fi
 
  sub_module_title "QNAP encrypted firmware extractor"
 
  hexdump -C "$QNAP_ENC_PATH_" | head | tee -a "$LOG_FILE" || true
 
  if [[ -f "$EXT_DIR"/PC1 ]]; then
    print_ln
    print_output "[*] Decrypting QNAP firmware with leaked key material ..."
    print_ln
 
      # 如果有PC1工具,则用PC1工具提取
    "$EXT_DIR"/PC1 d QNAPNASVERSION4 "$QNAP_ENC_PATH_" "$EXTRACTION_FILE_" | tee -a "$LOG_FILE"
  else
    print_output "[-] QNAP decryptor not found - check your installation"
  fi
 
  print_ln
  if [[ -f "$EXTRACTION_FILE_" && "$(file "$EXTRACTION_FILE_")" == *"gzip compressed data"* ]]; then
    print_output "[+] Decrypted QNAP firmware file to $ORANGE$EXTRACTION_FILE_$NC"
    MD5_DONE_DEEP+=( "$(md5sum "$QNAP_ENC_PATH_" | awk '{print $1}')" )
    export FIRMWARE_PATH="$EXTRACTION_FILE_"
    backup_var "FIRMWARE_PATH" "$FIRMWARE_PATH"
 
      # 如果提取出来是gzip文件,那再提取一次,使用下面的函数
    export QNAP=1
    print_ln
    print_output "[*] Firmware file details: $ORANGE$(file "$EXTRACTION_FILE_")$NC"
    print_ln
    write_csv_log "Extractor module" "Original file" "extracted file/dir" "file counter" "directory counter" "further details"
    write_csv_log "QNAP decryptor" "$QNAP_ENC_PATH_" "$EXTRACTION_FILE_" "1" "NA" "gzip compressed data"
    if [[ -z "${FW_VENDOR:-}" ]]; then
      FW_VENDOR="QNAP"
      backup_var "FW_VENDOR" "$FW_VENDOR"
    fi
 
  else
    print_output "[-] Decryption of QNAP firmware file failed"
  fi
}
qnap_extractor() {
  local DECRYPTED_FW_="${1:-}"
  if ! [[ -f "$DECRYPTED_FW_" ]]; then
    return
  fi
 
  sub_module_title "QNAP firmware extraction"
  print_output "[!] WARNING: This module is in an very early alpha state."
  print_output "[!] WARNING: Some areas of this module are not tested."
 
  # This module is a full copy of https://github.com/max-boehm/qnap-utils/blob/master/extract_qnap_fw.sh
  # some areas of this code are completely untested. Please report bugs via https://github.com/e-m-b-a/emba/issues
 
  QNAP_EXTRACTION_ROOT="$LOG_DIR"/firmware/qnap_extraction
  QNAP_EXTRACTION_ROOT_DST="$QNAP_EXTRACTION_ROOT"/root_filesystem
  mkdir -p "$QNAP_EXTRACTION_ROOT_DST" || true
 
  if file "$DECRYPTED_FW_" | grep -q ": gzip" ; then
    print_output "[*] Extracting $ORANGE$DECRYPTED_FW_$NC into $ORANGE$QNAP_EXTRACTION_ROOT$NC."
    mkdir -p "$QNAP_EXTRACTION_ROOT" || true
 
      # 解压gzip文件
    tar xvf "$DECRYPTED_FW_" -C "$QNAP_EXTRACTION_ROOT" 2>/dev/null || true | tee -a "$LOG_FILE"
    print_ln
    print_output "[*] Extracted firmware structure ($ORANGE$QNAP_EXTRACTION_ROOT$NC):"
 
      # 统计输出
        find "$QNAP_EXTRACTION_ROOT" -xdev -ls | tee -a "$LOG_FILE"
    print_files_dirs
    print_bar ""
  else
    print_output "[-] No QNAP firmware file found"
    return 1
  fi
 
  UIMAGE="$QNAP_EXTRACTION_ROOT/uImage"               # x31,x31+
  UBI="$QNAP_EXTRACTION_ROOT/rootfs2.ubi"             # x31,x31+
  IMAGE="$QNAP_EXTRACTION_ROOT_DST/image"
 
  # initial ramdisk root filesystem
  INITRAMFS="$QNAP_EXTRACTION_ROOT_DST/initramfs"        # x31,x31+
  INITRD="$QNAP_EXTRACTION_ROOT/initrd.boot"          # x10,x12,x19,x20,x21
  if [ ! -e "$INITRD" ]; then
    INITRD="$QNAP_EXTRACTION_ROOT/initrd"             # x51,x53
  fi
 
  ROOTFS2="$QNAP_EXTRACTION_ROOT/rootfs2.tgz"
  ROOTFS2_BZ="$QNAP_EXTRACTION_ROOT/rootfs2.bz"
  ROOTFS2_IMG="$QNAP_EXTRACTION_ROOT/rootfs2.img"
  ROOTFS_EXT="$QNAP_EXTRACTION_ROOT/rootfs_ext.tgz"
  QPKG="$QNAP_EXTRACTION_ROOT/qpkg.tar"
 
  if [ -e "$UBI" ]; then
    ROOTFS2="$QNAP_EXTRACTION_ROOT_DST/rootfs2.tgz"
    ROOTFS_EXT="$QNAP_EXTRACTION_ROOT_DST/rootfs_ext.tgz"
    QPKG="$QNAP_EXTRACTION_ROOT_DST/qpkg.tar"
  fi
 
  SYSROOT="$QNAP_EXTRACTION_ROOT_DST/sysroot"
  mkdir "$SYSROOT"
 
  if [ -e "$UIMAGE" ]; then
    print_ln
    print_output "[*] Scanning $ORANGE$UIMAGE$NC for (gzipped) parts..."
 
      # 识别是否为gzip文件,并定位到首次出现gzip文件头的偏移,用于后面的dd命令
    a=$(od -t x1 -w4 -Ad -v "$UIMAGE" | grep '1f 8b 08 00' | awk '{print $1}')
 
    if [ -n "$a" ]; then
 
        # 按照块大小(block size) $a 跳过第一个块,然后将其余的内容复制到输出文件(output file) $IMAGE.gz 中
      dd if="$UIMAGE" bs="$a" skip=1 of="$IMAGE.gz" status=none
 
        # 解压
      gunzip --quiet "$IMAGE.gz" || [ $? -eq 2 ]
      print_output "[+] Extracted and uncompressed $ORANGE$IMAGE$NC at offset $ORANGE$a$NC"
 
      i=0
      for a in $(od -t x1 -w4 -Ad -v "$IMAGE" | grep '1f 8b 08 00' | awk '{print $1}'); do
        i=$((i+1))
 
          # 再识别IMAGE文件里的所有gzip文件,分别提取所有部分
        dd if="$IMAGE" bs="$a" skip=1 of="$IMAGE.part$i.gz" status=none
        gunzip --quiet "$IMAGE.part$i.gz" || [ $? -eq 2 ]
        print_output "[+] Extracted and uncompressed '$IMAGE.part$i' at offset $a"
      done
 
      if [ $i -gt 0 ]; then
        mv "$IMAGE.part$i" "$INITRAMFS" || true
        print_output "[*] Renamed $ORANGE$IMAGE.part$i$NC to $ORANGE$INITRAMFS$NC"
        rm "$IMAGE" || true
      fi
    fi
    print_files_dirs
    print_bar ""
  fi
 
  if [ -e "$UBI" ]; then
      # rootfs2.ubi
    print_ln
    print_output "[*] Unpacking $ORANGE$UBI$NC."
    # TODO: we should evaluate moving to the EMBA UBI extractor in the future
 
    # see http://trac.gateworks.com/wiki/linux/ubi
    #
    # apt-get install mtd-utils
 
    # 256MB flash
    modprobe -r nandsim || true
    if [ -e /dev/mtdblock0 ]; then
      print_output "[-] /dev/mtdblock0 does already exist! Exiting to not overwrite it."; exit
    fi
    modprobe nandsim first_id_byte=0x2c second_id_byte=0xda third_id_byte=0x90 fourth_id_byte=0x95
 
    print_output "[*] Copy UBI image into simulated flash device"
    # populate NAND with an existing ubi:
    modprobe mtdblock
    dd if="$UBI" of=/dev/mtdblock0 bs=2048 status=none
 
    print_output "[*] Attach simulated flash device"
    # attach ubi
    modprobe ubi
    ubiattach /dev/ubi_ctrl -m0 -O2048
    # ubinfo -a
 
    print_output "[*] Mounting ubifs file system"
    # mount the ubifs to host
    modprobe ubifs
    local TMP_EXT_MOUNT="$TMP_DIR""/ext_mount_$RANDOM"
    mkdir -p "$TMP_EXT_MOUNT" || true
    mount -t ubifs ubi0 "$TMP_EXT_MOUNT"
    if mount | grep -q ext_mount; then
      print_output "[*] Copying contents from UBI mount"
      cp -a "$TMP_EXT_MOUNT"/boot/* "$QNAP_EXTRACTION_ROOT_DST" || true
      print_ln
      print_output "[*] Extracted firmware structure ($ORANGE$QNAP_EXTRACTION_ROOT_DST$NC):"
      find "$QNAP_EXTRACTION_ROOT_DST" -xdev -ls | tee -a "$LOG_FILE"
 
      print_output "[*] UBI cleanup"
      umount "$TMP_EXT_MOUNT"
    else
      print_output "[-] Something went wrong!"
    fi
    rm -r "$TMP_EXT_MOUNT" || true
    ubidetach /dev/ubi_ctrl -m0
    modprobe -r nandsim
    print_files_dirs
    print_bar ""
  fi
 
    # 提取初始内存文件系统
  if [ -e "$INITRAMFS" ]; then
    print_ln
    print_output "[*] Extracting $ORANGE$INITRAMFS$NC."
    (cd "$SYSROOT" && (cpio -i --make-directories||true) ) < "$INITRAMFS"
    print_ln
    print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
    find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
    print_files_dirs
    print_bar ""
  fi
 
  if [ -e "$INITRD" ]; then
    print_ln
 
    # 如果是lzma压缩文件,则解压
    if file "$INITRD" | grep -q LZMA ; then
      print_output "[*] Extracting $ORANGE$INITRD$NC (LZMA)."
      lzma -d <"$INITRD" | (cd "$SYSROOT" && (cpio -i --make-directories||true) )
      print_ln
      print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
      find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
      print_files_dirs
      print_bar ""
    fi
 
      # 如果是gzip,则解压
    if file "$INITRD" | grep -q gzip ; then
      print_ln
      print_output "[*] Extracting $ORANGE$INITRD$NC (gzip)."
      gzip -d <"$INITRD" >"$QNAP_EXTRACTION_ROOT_DST/initrd.$$"
      print_output "[*] Mounting $ORANGE$INITRD$NC."
      local TMP_EXT_MOUNT="$TMP_DIR""/ext_mount_$RANDOM"
      mkdir -p "$TMP_EXT_MOUNT" || true
      mount -t ext2 "$QNAP_EXTRACTION_ROOT_DST/initrd.$$" "$TMP_EXT_MOUNT" -oro,loop
      if mount | grep -q ext_mount; then
        cp -a "$TMP_EXT_MOUNT"/* "$SYSROOT" || true
        umount "$TMP_EXT_MOUNT"
        print_ln
        print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
        find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
        rm "$QNAP_EXTRACTION_ROOT_DST/initrd.$$" || true
      else
        print_output "[-] Something went wrong!"
      fi
      rm -r "$TMP_EXT_MOUNT" || true
      print_files_dirs
      print_bar ""
    fi
  fi
 
    # 用tar解压
  if [ -e "$ROOTFS2" ]; then
    print_ln
    print_output "[*] Extracting $ORANGE$ROOTFS2$NC (gzip, tar)."
    tar -xvzf "$ROOTFS2" -C "$SYSROOT"
    print_ln
    print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
    find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
    print_files_dirs
    print_bar ""
  fi
 
    # 用lzma解压
  if [ -e "$ROOTFS2_BZ" ]; then
    print_ln
    if file "$ROOTFS2_BZ" | grep -q "LZMA"; then
      print_output "[*] Extracting $ORANGE$ROOTFS2_BZ$NC (LZMA)."
      lzma -d <"$ROOTFS2_BZ" | (cd "$SYSROOT" && (cpio -i --make-directories||true) )
    else
      print_output "[*] Extracting $ORANGE$ROOTFS2_BZ$NC (bzip2, tar)."
      tar -xvjf "$ROOTFS2_BZ" -C "$SYSROOT"
    fi
    print_ln
    print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
    find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
    print_files_dirs
    print_bar ""
  fi
 
  if [ -f "$ROOTFS2_IMG" ]; then
    print_ln
    print_output "[*] Extracting $ORANGE$ROOTFS2_IMG$NC (ext2)..."
    local TMP_EXT_MOUNT="$TMP_DIR""/ext_mount_$RANDOM"
    mkdir -p "$TMP_EXT_MOUNT" || true
    mount -t ext2 "$ROOTFS2_IMG" "$TMP_EXT_MOUNT" -oro,loop
    if mount | grep -q ext_mount; then
 
        # 挂载ROOTFS2_IMG,然后解压里面的rootfs2.bz
      tar -xvjf "$TMP_EXT_MOUNT"/rootfs2.bz -C "$SYSROOT"
      print_ln
      print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
      find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
      umount "$TMP_EXT_MOUNT"
    else
      print_output "[-] Something went wrong!"
    fi
    rm -r "$TMP_EXT_MOUNT" || true
    print_files_dirs
    print_bar ""
  fi
 
  if [ -e "$ROOTFS_EXT" ]; then
    print_ln
    print_output "[*] Extracting EXT filesystem $ORANGE$ROOTFS_EXT$NC."
 
      # tar解压ext根文件系统
    tar xzvf "$ROOTFS_EXT" -C "$QNAP_EXTRACTION_ROOT_DST"
    print_output "[*] Mounting EXT filesystem $ORANGE$ROOTFS_EXT$NC."
    local TMP_EXT_MOUNT="$TMP_DIR""/ext_mount_$RANDOM"
    mkdir -p "$TMP_EXT_MOUNT" || true
 
      # 挂载ext根文件系统,然后复制出根文件系统下的所有文件
    mount "$QNAP_EXTRACTION_ROOT_DST"/rootfs_ext.img "$TMP_EXT_MOUNT" -oro,loop
    if mount | grep -q ext_mount; then
      cp -a "$TMP_EXT_MOUNT"/* "$SYSROOT" || true
      umount "$TMP_EXT_MOUNT"
    fi
    print_output "[*] Removing EXT filesystem ${ORANGE}rootfs_ext.img$NC."
    rm "$QNAP_EXTRACTION_ROOT_DST"/rootfs_ext.img || true
    rm -r "$TMP_EXT_MOUNT" || true
    print_ln
    print_output "[*] Extracted firmware structure ($ORANGE$SYSROOT$NC):"
    find "$SYSROOT" -xdev -ls | tee -a "$LOG_FILE"
    print_files_dirs
    print_bar ""
  fi
 
    # 查找根文件系统/opt/source下的所有tgz文件并解压到/usr/local下
  USR_LOCAL=$(find "$SYSROOT/opt/source" -name "*.tgz" 2>/dev/null)
  # if [[ "${#USR_LOCAL[@]}" -gt 0 ]]; then
  if [[ -v USR_LOCAL[@] ]]; then
    print_ln
    for f in "${USR_LOCAL[@]}"; do
      print_output "[*] Extracting $ORANGE$f$NC -> ${ORANGE}sysroot/usr/local$NC ..."
      mkdir -p "$SYSROOT/usr/local" || true
      tar xvzf "$f" -C "$SYSROOT/usr/local"
    done
    print_files_dirs
    print_bar ""
  fi
 
    # "$QNAP_EXTRACTION_ROOT/qpkg.tar"
  if [ -e "$QPKG" ]; then
    print_ln
    print_output "[*] Extracting $ORANGE$QPKG$NC."
    mkdir -p "$QNAP_EXTRACTION_ROOT_DST/qpkg" || true
 
      # 解压
    tar xvf "$QPKG" -C "$QNAP_EXTRACTION_ROOT_DST/qpkg"
      # 从解压出来的文件夹里继续搜tgz解压
    for f in "$QNAP_EXTRACTION_ROOT_DST"/qpkg/*.tgz; do
      if file "$f" | grep -q gzip; then
        print_output "[*] Extracting QPKG $ORANGE$f$NC."
        tar tvzf "$f" > "$f".txt
      fi
    done
    print_files_dirs
    print_bar ""
  fi
 
    # 解压出一些服务的tgz
  for name in apache_php5 mysql5 mariadb5; do
    if [ -e "$QNAP_EXTRACTION_ROOT_DST/qpkg/$name.tgz" ]; then
      print_output "[*] Extracting ${ORANGE}qpkg/$name.tgz$NC -> ${ORANGE}sysroot/usr/local$NC ..."
      tar xvzf "$QNAP_EXTRACTION_ROOT_DST/qpkg/$name.tgz" -C "$SYSROOT/usr/local"
    fi
  done
 
    # Boost C++库解压到$SYSROOT/usr/lib下
  if [ -e "$QNAP_EXTRACTION_ROOT_DST"/qpkg/libboost.tgz ]; then
    print_ln
    print_output "[*] Extracting ${ORANGE}qpkg/libboost.tgz$NC -> ${ORANGE}sysroot/usr/lib$NC."
    mkdir -p "$SYSROOT/usr/lib" || true
    tar xvzf "$QNAP_EXTRACTION_ROOT_DST"/qpkg/libboost.tgz -C "$SYSROOT/usr/lib"
  elif [ -e "$QNAP_EXTRACTION_ROOT_DST"/qpkg/DSv3.tgz ]; then
    print_output "[*] Extracting ${ORANGE}libboost$NC from ${ORANGE}qpkg/DSv3.tgz$NC -> ${ORANGE}sysroot/usr/lib$NC."
    tar tzf "$QNAP_EXTRACTION_ROOT_DST"/qpkg/DSv3.tgz |grep libboost | tar xzf "$QNAP_EXTRACTION_ROOT_DST"/qpkg/DSv3.tgz -C "$SYSROOT" -T -
  fi
 
  if [[ -d "$SYSROOT"/usr/lib ]]; then
    HOME_DIR="$(pwd)"
 
      # 创建软链接
    (cd "$SYSROOT/usr/lib" || exit; for f in libboost*.so.1.42.0; do ln -s "$f" "${f%.1.42.0}"; done)
    cd "$HOME_DIR" || exit
  fi
  print_files_dirs
  print_bar ""
}
qnap_extractor() {
  local DECRYPTED_FW_="${1:-}"
  if ! [[ -f "$DECRYPTED_FW_" ]]; then
    return
  fi
 
  sub_module_title "QNAP firmware extraction"
  print_output "[!] WARNING: This module is in an very early alpha state."
  print_output "[!] WARNING: Some areas of this module are not tested."
 
  # This module is a full copy of https://github.com/max-boehm/qnap-utils/blob/master/extract_qnap_fw.sh
  # some areas of this code are completely untested. Please report bugs via https://github.com/e-m-b-a/emba/issues
 
  QNAP_EXTRACTION_ROOT="$LOG_DIR"/firmware/qnap_extraction
  QNAP_EXTRACTION_ROOT_DST="$QNAP_EXTRACTION_ROOT"/root_filesystem
  mkdir -p "$QNAP_EXTRACTION_ROOT_DST" || true
 
  if file "$DECRYPTED_FW_" | grep -q ": gzip" ; then
    print_output "[*] Extracting $ORANGE$DECRYPTED_FW_$NC into $ORANGE$QNAP_EXTRACTION_ROOT$NC."
    mkdir -p "$QNAP_EXTRACTION_ROOT" || true
 
      # 解压gzip文件
    tar xvf "$DECRYPTED_FW_" -C "$QNAP_EXTRACTION_ROOT" 2>/dev/null || true | tee -a "$LOG_FILE"
    print_ln
    print_output "[*] Extracted firmware structure ($ORANGE$QNAP_EXTRACTION_ROOT$NC):"
 
      # 统计输出
        find "$QNAP_EXTRACTION_ROOT" -xdev -ls | tee -a "$LOG_FILE"
    print_files_dirs
    print_bar ""
  else
    print_output "[-] No QNAP firmware file found"
    return 1
  fi
 
  UIMAGE="$QNAP_EXTRACTION_ROOT/uImage"               # x31,x31+
  UBI="$QNAP_EXTRACTION_ROOT/rootfs2.ubi"             # x31,x31+
  IMAGE="$QNAP_EXTRACTION_ROOT_DST/image"
 
  # initial ramdisk root filesystem
  INITRAMFS="$QNAP_EXTRACTION_ROOT_DST/initramfs"        # x31,x31+
  INITRD="$QNAP_EXTRACTION_ROOT/initrd.boot"          # x10,x12,x19,x20,x21
  if [ ! -e "$INITRD" ]; then
    INITRD="$QNAP_EXTRACTION_ROOT/initrd"             # x51,x53
  fi
 
  ROOTFS2="$QNAP_EXTRACTION_ROOT/rootfs2.tgz"
  ROOTFS2_BZ="$QNAP_EXTRACTION_ROOT/rootfs2.bz"
  ROOTFS2_IMG="$QNAP_EXTRACTION_ROOT/rootfs2.img"
  ROOTFS_EXT="$QNAP_EXTRACTION_ROOT/rootfs_ext.tgz"
  QPKG="$QNAP_EXTRACTION_ROOT/qpkg.tar"
 
  if [ -e "$UBI" ]; then
    ROOTFS2="$QNAP_EXTRACTION_ROOT_DST/rootfs2.tgz"
    ROOTFS_EXT="$QNAP_EXTRACTION_ROOT_DST/rootfs_ext.tgz"
    QPKG="$QNAP_EXTRACTION_ROOT_DST/qpkg.tar"
  fi
 
  SYSROOT="$QNAP_EXTRACTION_ROOT_DST/sysroot"
  mkdir "$SYSROOT"
 
  if [ -e "$UIMAGE" ]; then
    print_ln
    print_output "[*] Scanning $ORANGE$UIMAGE$NC for (gzipped) parts..."
 
      # 识别是否为gzip文件,并定位到首次出现gzip文件头的偏移,用于后面的dd命令
    a=$(od -t x1 -w4 -Ad -v "$UIMAGE" | grep '1f 8b 08 00' | awk '{print $1}')
 
    if [ -n "$a" ]; then
 
        # 按照块大小(block size) $a 跳过第一个块,然后将其余的内容复制到输出文件(output file) $IMAGE.gz 中
      dd if="$UIMAGE" bs="$a" skip=1 of="$IMAGE.gz" status=none
 
        # 解压
      gunzip --quiet "$IMAGE.gz" || [ $? -eq 2 ]
      print_output "[+] Extracted and uncompressed $ORANGE$IMAGE$NC at offset $ORANGE$a$NC"
 
      i=0
      for a in $(od -t x1 -w4 -Ad -v "$IMAGE" | grep '1f 8b 08 00' | awk '{print $1}'); do
        i=$((i+1))
 
          # 再识别IMAGE文件里的所有gzip文件,分别提取所有部分
        dd if="$IMAGE" bs="$a" skip=1 of="$IMAGE.part$i.gz" status=none
        gunzip --quiet "$IMAGE.part$i.gz" || [ $? -eq 2 ]
        print_output "[+] Extracted and uncompressed '$IMAGE.part$i' at offset $a"

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 3059
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-5-2 17:14
1
游客
登录 | 注册 方可回帖
返回
//