EMBA 被设计为渗透测试人员的中央固件分析工具。它支持完整的安全分析过程,从固件提取过程开始,通过仿真进行静态分析和动态分析,最后生成 Web 报告。EMBA 自动发现固件中可能存在的弱点和漏洞。例如不安全的二进制文件、旧的和过时的软件组件、可能易受攻击的脚本或硬编码密码。EMBA是一个命令行工具,可以选择生成易于使用的 Web 报告以供进一步分析。
# 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
首先加载helpers或installer文件夹下的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函数。
# 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
预处理HTML输出
1 2 3 4 5 6 7 8 9
# 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
whiletrue; 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
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"
关键在run_modules "P" "${THREADED}" "0"这一行,我们看看这个函数的定义: emba line 111:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# $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 ...
函数比较长。接收三个参数的作用在声明前写的很清楚。大概功能:根据传入的参数和一些其他条件,动态地加载和运行符合要求的模块脚本,有时候可以进行多线程并发执行。另外,还会检查某个模块是否已经运行过,并在必要时跳过不需要重新运行的模块,提高运行效率。如果开启了 HTML 选项,则会生成报告文件。 固件检查模块(S模块)
####################################################################################### # 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"
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"
####################################################################################### # 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}"
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
# 使用 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
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
# 固件路径 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 }
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]}"
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 }
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
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
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
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 中 ddif="$DLINK_ENC_PATH_" skip=16 iflag=skip_bytes of="$TMP_IMAGE_FILE" 2>&1 | tee -a "$LOG_FILE"
# 每 131072(128KB) 为一块进行处理 for ((ITERATION=0; ITERATION<ROOF; ITERATION++)); do if [[ "$ITERATION" -eq 0 ]]; then OFFSET=0 else (( OFFSET=131072*ITERATION )) fi
# aes解密 ddif="$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 | ddif=/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
# 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]}"
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 }
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 }
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
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
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
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 ddif="$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; doln -s "$f""${f%.1.42.0}"; done) cd"$HOME_DIR" || exit fi print_files_dirs print_bar "" }
ufs_extractor() { local UFS_PATH_="${1:-}" local EXTRACTION_DIR_="${2:-}" local TMP_UFS_MOUNT="$TMP_DIR""/ufs_mount_$RANDOM" local DIRS_UFS_MOUNT=0 FILES_UFS_MOUNT=0
if ! [[ -f "$UFS_PATH_" ]]; then print_output "[-] No file for extraction provided" return fi
sub_module_title "UFS filesystem extractor"
mkdir -p "$TMP_UFS_MOUNT" 2>/dev/null || true print_output "[*] Trying to mount $ORANGE$UFS_PATH_$NC to $ORANGE$TMP_UFS_MOUNT$NC directory" # 装载ufs内核模块 modprobe ufs # 挂载ufs镜像 mount -r -t ufs -o ufstype=ufs2 "$UFS_PATH_""$TMP_UFS_MOUNT"
if mount | grep -q ufs_mount; then print_output "[*] Copying $ORANGE$TMP_UFS_MOUNT$NC to firmware tmp directory ($ORANGE$EXTRACTION_DIR_$NC)" mkdir -p "$EXTRACTION_DIR_" 2>/dev/null || true
# 统计文件 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_UFS_MOUNT$NC directory"
FILES_UFS_MOUNT=$(find "$EXTRACTION_DIR_" -type f | wc -l) DIRS_UFS_MOUNT=$(find "$EXTRACTION_DIR_" -type d | wc -l) print_output "[*] Extracted $ORANGE$FILES_UFS_MOUNT$NC files and $ORANGE$DIRS_UFS_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 "UFS filesystem extractor""$UFS_PATH_""$EXTRACTION_DIR_""$FILES_UFS_MOUNT""$DIRS_UFS_MOUNT""NA" umount "$TMP_UFS_MOUNT" 2>/dev/null || true fi rm -r "$TMP_UFS_MOUNT" }
foscam_enc_extractor() { local FOSCAM_ENC_PATH_="${1:-}" local EXTRACTION_FILE_="${2:-}" local FOSCAM_FILE_CHECK="" local KEY_FILE="$CONFIG_DIR/foscam_enc_keys.txt"
if ! [[ -f "$FOSCAM_ENC_PATH_" ]]; then print_output "[-] No file for decryption provided" return fi
# config文件夹下的foscam_enc_keys.txt保存密钥列表 if ! [[ -f "$KEY_FILE" ]]; then print_output "[-] No key file found in config directory" return fi
# TODO: Check if we can improve our ubifs extractor in a way to support this Foscam thing # without the following function! Currently it is working together with our # deep extractor quite fine. foscam_ubi_extractor() { local FIRMWARE_PATH_="${1:-}" local MTD_DEVICE="" local UBI_MNT_PT="$LOG_DIR"/tmp/ubi_mnt_foscam local EXTRACTION_DIR_="$LOG_DIR/firmware/foscam_ubi_extractor" local EXTRACTION_DIR_GZ="$LOG_DIR/firmware/foscam_gz_extractor" local UBI_DEV="" local UBI_DEVS=() FOSCAM_UBI_FILES=0 FOSCAM_UBI_DIRS=0
if ! [[ -f "$FIRMWARE_PATH_" ]]; then print_output "[-] No file for extraction found" return fi
# 创建文件夹,然后解压到文件夹内 tar -xzf "$FIRMWARE_PATH_" -C "$EXTRACTION_DIR_GZ" || true
# check if we have the kernel modules available - special interest in docker if ! [[ -d "/lib/modules/" ]]; then print_output "[-] Kernel modules not mounted from host system - please update your docker-compose file!" return fi
if [[ -f "$EXTRACTION_DIR_GZ"/app_ubifs ]]; then print_output "[*] 2nd extraction round successful - ${ORANGE}app_ubifs$NC found" print_output "[*] Loading nandsim kernel module" if lsmod | grep -q "^nandsim[[:space:]]"; then # we need to load nandsim with some parameters - unload it before modprobe -r nandsim fi # 像前面的模块一样加载内核模块 modprobe nandsim first_id_byte=0x2c second_id_byte=0xac third_id_byte=0x90 fourth_id_byte=0x15 MTD_DEVICE=$(grep "mtd[0-9]" /proc/mtd | cut -d: -f1) print_output "[*] Found $ORANGE/dev/$MTD_DEVICE$NC MTD device" print_output "[*] Erasing $ORANGE/dev/$MTD_DEVICE$NC MTD device" flash_erase /dev/"$MTD_DEVICE" 0 0 || true print_output "[*] Formating $ORANGE/dev/$MTD_DEVICE$NC MTD device" ubiformat /dev/"$MTD_DEVICE" -O 2048 -f "$EXTRACTION_DIR_GZ"/app_ubifs || true if ! lsmod | grep -q "^ubi[[:space:]]"; then print_output "[*] Loading ubi kernel module" modprobe ubi fi print_output "[*] Attaching ubi device" ubiattach -p /dev/"$MTD_DEVICE" -O 2048
# should be only one UBI dev, but just in case ... mapfile -t UBI_DEVS < <(find /dev -iname "ubi[0-9]_[0-9]") for UBI_DEV in"${UBI_DEVS[@]}"; do local UBI_MNT_PT="$UBI_MNT_PT-$RANDOM" print_output "[*] Mounting $ORANGE$UBI_DEV$NC ubi device to $ORANGE$UBI_MNT_PT$NC" mkdir -p "$UBI_MNT_PT" || true
# 挂载ubi设备 mount -t ubifs "$UBI_DEV""$UBI_MNT_PT" print_output "[*] Copy mounted ubi device to $ORANGE$EXTRACTION_DIR_/$UBI_DEV$NC" mkdir -p "$EXTRACTION_DIR_/$UBI_DEV"
# 取消附加、解除内核模块加载 # do some cleanup print_output "[*] Detaching ubi device" ubidetach -d 0 || true print_output "[*] Unloading nandsim module" modprobe -r nandsim || true print_output "[*] Unloading ubi module" modprobe -r ubi || true
if [[ -d "$EXTRACTION_DIR_" ]]; then FOSCAM_UBI_FILES=$(find "$EXTRACTION_DIR_" -type f | wc -l) FOSCAM_UBI_DIRS=$(find "$EXTRACTION_DIR_" -type d | wc -l) fi
if [[ "$FOSCAM_UBI_FILES" -gt 0 ]]; then print_ln print_output "[*] Extracted $ORANGE$FOSCAM_UBI_FILES$NC files and $ORANGE$FOSCAM_UBI_DIRS$NC directories from the firmware image." write_csv_log "Foscam UBI extractor""$FIRMWARE_PATH_""$EXTRACTION_DIR_""$FOSCAM_UBI_FILES""$FOSCAM_UBI_DIRS""NA" export FIRMWARE_PATH="$LOG_DIR"/firmware backup_var "FIRMWARE_PATH""$FIRMWARE_PATH" write_csv_log "Foscam decryptor/extractor""$FIRMWARE_PATH_""$EXTRACTION_DIR_""$FOSCAM_UBI_FILES""$FOSCAM_UBI_DIRS""NA" fi fi }
zyxel_zip_extractor() { local RI_FILE_="${1:-}" local EXTRACTION_DIR_="${2:-}"
local RI_FILE_BIN="" local ZLD_DIR="" local RI_FILE_BIN_PATH="" local ZLD_BINS=() local ZLD_BIN=""
sub_module_title "Zyxel protected ZIP firmware extractor"
if ! [[ -f "$RI_FILE_" ]]; then print_output "[-] Zyxel - No file for extraction provided" return fi if ! [[ "$RI_FILE_" =~ .*\.ri ]]; then print_output "[-] Zyxel - No valid ri file for extraction provided" return fi
for ZLD_BIN in"${ZLD_BINS[@]}"; do local FILES_ZYXEL=0 local DIRS_ZYXEL=0 print_output "[*] Checking $ORANGE$ZLD_BIN$NC"
# 获取文件夹名 ZLD_DIR=$(dirname"$ZLD_BIN")
# 找到第一个名字叫这个的文件 RI_FILE_BIN_PATH=$(find "$LOG_DIR"/firmware -name "$RI_FILE_BIN" | head -1) # => this should be the protected Zip file
# 压缩包是一个加密压缩包,但是密钥肯定在程序执行时会出现在内存中 if [[ $(file "$ZLD_BIN") == *"ELF"* ]] && [[ $(file "$RI_FILE_BIN_PATH") == *"Zip archive data"* ]]; then print_output "[*] Found Zyxel environment in $ORANGE$ZLD_DIR$NC" # now we know that we have an elf for extraction and and unzip binary in the extraction dir # this is everything we need for the key if ( file "$ZLD_BIN" | grep -q "ELF 32-bit MSB executable, MIPS, N32 MIPS64 rel2 version 1" ) ; then # todo: check if Zyxel also uses other architectures local EMULATOR="qemu-mipsn32-static" print_output "[*] Found valid emulator $ORANGE$EMULATOR$NC" else print_output "[-] WARNING: Unsupported architecture for key identification:" print_output "$(indent "$(file "$ZLD_BIN")")" print_output "[-] Please open an issue at https://github.com/e-m-b-a/emba/issues" continue fi
print_output "[*] Running Zyxel emulation for key extraction ..."
if ! [[ -e "$(command -v "$EMULATOR")" ]]; then print_output "[-] No valid emulator ($ORANGE$EMULATOR$NC) found in your environment" return fi
if [[ -f "$LOG_PATH_MODULE"/zld_strace.log ]] && [[ -s "$LOG_PATH_MODULE"/zld_strace.log ]]; then
# 在模拟执行的跟踪日志中匹配密钥 ZIP_KEY=$(grep -a -E "^[0-9]+\ execve.*AABBCCDD\",\"-o""$LOG_PATH_MODULE"/zld_strace.log| cut -d, -f6 | sort -u | sed 's/^\"//' | sed 's/\"$//') else print_output "[-] No qemu strace log generated -> no further processing possible" fi
# 如果能找到密钥 # if we have found a ZIP_KEY: if [[ -v ZIP_KEY ]]; then print_ln print_output "[+] Possible ZIP key detected: $ORANGE$ZIP_KEY$NC""""$LOG_PATH_MODULE/zld_strace.log"
# 使用密钥解压 7z x -p"$ZIP_KEY" -o"$EXTRACTION_DIR_"/firmware_zyxel_extracted "$RI_FILE_BIN_PATH" || true
FILES_ZYXEL=$(find "$EXTRACTION_DIR_"/firmware_zyxel_extracted -type f | wc -l) DIRS_ZYXEL=$(find "$EXTRACTION_DIR_"/firmware_zyxel_extracted -type d | wc -l)
print_ln print_output "[*] Zyxel 1st stage - Extracted $ORANGE$FILES_ZYXEL$NC files and $ORANGE$DIRS_ZYXEL$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 "Zyxel extractor""$RI_FILE_BIN_PATH""$EXTRACTION_DIR_/firmware_zyxel_extracted""$FILES_ZYXEL""$DIRS_ZYXEL""NA" else print_output "[-] No ZIP key detected -> no further processing possible" fi
# if it was possible to extract something with the key: if [[ "$FILES_ZYXEL" -gt 0 ]]; then # compress.img ist the firmware -> letz search for it COMPRESS_IMG=$(find "$EXTRACTION_DIR_"/firmware_zyxel_extracted -type f -name compress.img | sort -u) if [[ $(file "$COMPRESS_IMG") == *"Squashfs"* ]]; then print_output "[+] Found valid ${ORANGE}compress.img$GREEN and extract it now" binwalk_deep_extract_helper 1 "$COMPRESS_IMG""$EXTRACTION_DIR_/firmware_zyxel_extracted/compress_img_extracted" FILES_ZYXEL=$(find "$EXTRACTION_DIR_"/firmware_zyxel_extracted/compress_img_extracted -type f | wc -l) DIRS_ZYXEL=$(find "$EXTRACTION_DIR_"/firmware_zyxel_extracted/compress_img_extracted -type d | wc -l) print_output "[*] Zyxel 2nd stage - Extracted $ORANGE$FILES_ZYXEL$NC files and $ORANGE$DIRS_ZYXEL$NC directories from the firmware image." write_csv_log "Zyxel extractor""$RI_FILE_BIN_PATH""$EXTRACTION_DIR_/firmware_zyxel_extracted/compress_img_extracted""$FILES_ZYXEL""$DIRS_ZYXEL""NA" export FIRMWARE_PATH="$LOG_DIR"/firmware/ backup_var "FIRMWARE_PATH""$FIRMWARE_PATH" print_ln break else print_output "[-] No valid ${ORANGE}compress.img$NC file found" fi else print_output "[-] 1st stage Zip extraction failed" fi print_ln else print_output "[-] No environment for Zyxel decryption found" fi done }
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
if [[ "$BINWALK_VER_CHECK" == 1 ]]; then
# 如果参数是1,则递归提取 if [[ "$MATRYOSHKA_" -eq 1 ]]; then
# -M:Recursively scan extracted files(递归扫描提取的文件) 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 }
qcow_extractor() { local QCOW_PATH_="${1:-}" local EXTRACTION_DIR_="${2:-}" local TMP_QCOW_MOUNT="$TMP_DIR""/qcow_mount_$RANDOM" local DIRS_QCOW_MOUNT=0 FILES_QCOW_MOUNT=0
if ! [[ -f "$QCOW_PATH_" ]]; then print_output "[-] No file for extraction provided" return fi
sub_module_title "Qemu QCOW filesystem extractor"
mkdir -p "$TMP_QCOW_MOUNT" 2>/dev/null || true print_output "[*] Trying to mount $ORANGE$QCOW_PATH_$NC to $ORANGE$TMP_QCOW_MOUNT$NC directory"
# 取消nbd内核模块的挂载 if lsmod | grep -q nbd; then rmmod nbd || true fi
# 加一个锁 if ! [[ -d /var/lock ]]; then mkdir /var/lock || true fi
print_output "[*] Identification of partitions on ${ORANGE}/dev/nbd$NC."
# 确保存在nbd设备 mapfile -t NBD_DEVS < <(fdisk -l /dev/nbd0 | grep "^/dev/" | awk '{print $1}' || true) if [[ "${#NBD_DEVS[@]}" -eq 0 ]]; then # sometimes we are not able to find the partitions with fdisk -> fallback NBD_DEVS+=( "/dev/nbd0" ) fi
print_ln fdisk /dev/nbd0 -l print_ln
for NBD_DEV in"${NBD_DEVS[@]}"; do print_output "[*] Extract data from partition $ORANGE$NBD_DEV$NC"
# 将镜像挂载到nbd设备上 mount "$NBD_DEV""$TMP_QCOW_MOUNT" || true
if mount | grep -q "$NBD_DEV"; then EXTRACTION_DIR_FINAL="$EXTRACTION_DIR_"/"$(basename "$NBD_DEV")"
# 复制所有内容到提取的文件夹。 # -p表示保留文件属性,-r表示递归复制子目录,-i表示覆盖前进行询问。 cp -pri "$SOURCE_CP"/* "$DEST_CP" 2>/dev/null || true print_ln print_output "[*] Using the following firmware directory ($ORANGE$DEST_CP$NC) as base directory:"
# 通过不跨越设备、最大深度1级子目录进行搜索,统计文件。 find "$DEST_CP" -xdev -maxdepth 1 -ls | tee -a "$LOG_FILE" print_ln }
android_ota_extractor() { local OTA_INIT_PATH_="$1" local EXTRACTION_DIR_="$2" local DIRS_OTA=0 FILES_OTA=0
if ! [[ -f "$OTA_INIT_PATH_" ]]; then print_output "[-] No file for extraction provided" return fi
sub_module_title "Android OTA extractor"
hexdump -C "$OTA_INIT_PATH_" | head | tee -a "$LOG_FILE" || true
if [[ -d "$EXT_DIR"/payload_dumper ]]; then print_ln print_output "[*] Extracting Android OTA payload.bin file ..." print_ln
# 使用扩展工具进行提取。 python3 "$EXT_DIR"/payload_dumper/payload_dumper.py --out "$EXTRACTION_DIR_""$OTA_INIT_PATH_" | tee -a "$LOG_FILE"
FILES_OTA=$(find "$EXTRACTION_DIR_" -type f | wc -l) DIRS_OTA=$(find "$EXTRACTION_DIR_" -type d | wc -l) print_output "[*] Extracted $ORANGE$FILES_OTA$NC files and $ORANGE$DIRS_OTA$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 "Android OTA extractor""$OTA_INIT_PATH_""$EXTRACTION_DIR_""$FILES_OTA""$DIRS_OTA""via payload_dumper.py" else print_output "[-] Android OTA payload.bin extractor not found - check your installation" fi }
local FIRMWARE_PATH_="${1:-}" local EXTRACTION_DIR_="${2:-}"
local FIRMWARE_NAME_="" local UEFI_EXTRACT_REPORT_FILE=""
local UEFI_EXTRACT_BIN="$EXT_DIR""/UEFITool/UEFIExtract" local FILES_UEFI=0 local DIRS_UEFI=0 local NVARS=0 local PE32_IMAGE=0 local EFI_ARCH=""
if ! [[ -f "$FIRMWARE_PATH_" ]]; then print_output "[-] No file for extraction provided" return fi
FIRMWARE_NAME_="$(basename "$FIRMWARE_PATH_")" if ! [[ -d "$EXTRACTION_DIR_" ]]; then mkdir -p "$EXTRACTION_DIR_" fi cp"$FIRMWARE_PATH_""$EXTRACTION_DIR_" "$UEFI_EXTRACT_BIN""$EXTRACTION_DIR_"firmware all &> "$LOG_PATH_MODULE"/uefi_extractor_"$FIRMWARE_NAME_".log UEFI_EXTRACT_REPORT_FILE="$EXTRACTION_DIR_"firmware.report.txt mv"$UEFI_EXTRACT_REPORT_FILE""$LOG_PATH_MODULE" UEFI_EXTRACT_REPORT_FILE="$LOG_PATH_MODULE"/firmware.report.txt if [[ -f "$EXTRACTION_DIR_"/firmware ]]; then rm"$EXTRACTION_DIR_"/firmware fi
if [[ -f "$LOG_PATH_MODULE"/uefi_extractor_"$FIRMWARE_NAME_".log ]]; then tee -a "$LOG_FILE" < "$LOG_PATH_MODULE"/uefi_extractor_"$FIRMWARE_NAME_".log fi
print_ln print_output "[*] Using the following firmware directory ($ORANGE${EXTRACTION_DIR_}firmware.dump$NC) as base directory:" find "$EXTRACTION_DIR_"firmware.dump -xdev -maxdepth 1 -ls | tee -a "$LOG_FILE" print_ln
if [[ -n "$EFI_ARCH" ]]; then print_output "[*] Found $ORANGE$PE32_IMAGE$NC PE32 images for architecture $ORANGE$EFI_ARCH$NC drivers." print_output "[+] Possible architecture details found ($ORANGE UEFI Extractor $GREEN): $ORANGE$EFI_ARCH$NC" export EFI_ARCH backup_var "EFI_ARCH""$EFI_ARCH" fi
FILES_UEFI=$(grep -c "File""$UEFI_EXTRACT_REPORT_FILE" || true) DIRS_UEFI=$(find "$EXTRACTION_DIR_" -type d | wc -l) print_output "[*] Extracted $ORANGE$FILES_UEFI$NC files and $ORANGE$DIRS_UEFI$NC directories from the firmware image." print_output "[*] Found $ORANGE$NVARS$NC NVARS and $ORANGE$DRIVER_COUNT$NC drivers." write_csv_log "Extractor module""Original file""extracted file/dir""file counter""directory counter""further details" write_csv_log "UEFI extractor""$FIRMWARE_PATH_""$EXTRACTION_DIR_""$FILES_UEFI""$DIRS_UEFI""NA" print_ln }
# 重新定义unblob下的sasquatch工具指向binwalk # we need to check if sasquatch is the correct one for binwalk: if ! [[ "$(readlink -q -f "$UNBLOB_PATH"/sasquatch)" == "/usr/local/bin/sasquatch_binwalk" ]]; then if [[ -L "$UNBLOB_PATH"/sasquatch ]]; then rm"$UNBLOB_PATH"/sasquatch fi ln -s /usr/local/bin/sasquatch_binwalk "$UNBLOB_PATH"/sasquatch || true fi
# typically FIRMWARE_PATH is only a file if none of the EMBA extractors were able to extract something # This means we are using binwalk in Matryoshka mode here # if we have a directory with multiple files in it we automatically pass here and run into the deep extractor if [[ -f "$FIRMWARE_PATH" ]]; then
# 首先调用binwalking,此函数将在下方给出分析。 # 作用:对固件进行简单地分析并给出熵图。 # we love binwalk ... this is our first chance for extracting everything binwalking "$FIRMWARE_PATH" fi
# 将在下方给出分析 # 功能:通过特殊二进制路径来推测文件系统根目录在哪 # FIRMWARE_PATH_CP is typically /log/firmware - shellcheck is probably confused here # shellcheck disable=SC2153 detect_root_dir_helper "$FIRMWARE_PATH_CP"
if [[ "$BINS" -gt 0 || "$UNIQUE_FILES" -gt 0 ]]; then sub_module_title "Firmware extraction details"
# 将在下方给出分析 # 主要功能:用于统计linux文件系统中基本的特征文件或文件名 linux_basic_identification_helper "$FIRMWARE_PATH_CP" print_ln print_output "[*] Found $ORANGE$FILES_EXT$NC files ($ORANGE$UNIQUE_FILES$NC unique files) and $ORANGE$DIRS_EXT$NC directories at all." print_output "[*] Found $ORANGE$BINS$NC binaries." print_output "[*] Additionally the Linux path counter is $ORANGE$LINUX_PATH_COUNTER$NC." print_ln tree -csh "$FIRMWARE_PATH_CP" | tee -a "$LOG_FILE"
# now it should be fine to also set the FIRMWARE_PATH ot the FIRMWARE_PATH_CP export FIRMWARE_PATH="$FIRMWARE_PATH_CP"
if [[ "${#ROOT_PATH[@]}" -gt 0 ]] ; then write_csv_log "FILES""UNIQUE_FILES""DIRS""Binaries""LINUX_PATH_COUNTER""Root PATH detected" for R_PATH in"${ROOT_PATH[@]}"; do write_csv_log "$FILES_EXT""$UNIQUE_FILES""$DIRS_EXT""$BINS""$LINUX_PATH_COUNTER""$R_PATH" done fi backup_var "FILES_EXT""$FILES_EXT" backup_var "FILES_EXT""$UNIQUE_FILES" backup_var "FILES_EXT""$DIRS_EXT" fi
# this function is for the first round of binwalk # in case no other EMBA extractor did something and the # current firmware file is a file and not multiple files binwalking() { local FIRMWARE_PATH_="${1:-}" export OUTPUT_DIR_BINWALK=""
if ! [[ -f "$FIRMWARE_PATH_" ]]; then print_output "[-] No file for extraction provided" return fi
sub_module_title "Analyze binary firmware blob with binwalk"
# 简单地用binwalk分析一下 print_output "[*] Basic analysis with binwalk" binwalk "$FIRMWARE_PATH_" | tee -a "$LOG_FILE"
print_ln "no_log" # we use the original FIRMWARE_PATH for entropy testing, just if it is a file if [[ -f $FIRMWARE_PATH_BAK ]] && ! [[ -f "$LOG_DIR"/firmware_entropy.png ]]; then 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
print_ln print_output "[*] Extracting firmware to directory $ORANGE$OUTPUT_DIR_BINWALK$NC" # this is not working in background. I have created a new function that gets executed in the background # probably there is a more elegant way # binwalk is executed in Matryoshka mode binwalk_deep_extract_helper 1 "$FIRMWARE_PATH_""$OUTPUT_DIR_BINWALK" & WAIT_PIDS+=( "$!" ) wait_for_extractor WAIT_PIDS=( )
print_output "[*] Root directory auto detection for $ORANGE$SEARCH_PATH$NC (could take some time)\\n" export ROOT_PATH=() local R_PATH local MECHANISM=""
# 获取解释器路径 if [[ "${#INTERPRETER_FULL_PATH[@]}" -gt 0 ]]; then for INTERPRETER_PATH in"${INTERPRETER_FULL_PATH[@]}"; do # now we have a result like this "/lib/ld-uClibc.so.0" # lets escape it # 将/替换为\/ INTERPRETER_ESCAPED=$(echo"$INTERPRETER_PATH" | sed -e 's/\//\\\//g') mapfile -t INTERPRETER_FULL_RPATH < <(find "$SEARCH_PATH" -ignore_readdir_race -wholename "*$INTERPRETER_PATH" 2>/dev/null | sort -u) for R_PATH in"${INTERPRETER_FULL_RPATH[@]}"; do # remove the interpreter path from the full path: R_PATH="${R_PATH//$INTERPRETER_ESCAPED/}" if [[ -v R_PATH ]] && [[ -d "$R_PATH" ]]; then ROOT_PATH+=( "$R_PATH" ) MECHANISM="binary interpreter" fi done done fi
# 找到所有解释器的根目录 # if we can't find the interpreter we fall back to a search for something like "*root/bin/* and take this: mapfile -t ROOTx_PATH < <(find "$SEARCH_PATH" -xdev \( -path "*extracted/bin" -o -path "*root/bin" \) -execdirname {} \; 2>/dev/null) for R_PATH in"${ROOTx_PATH[@]}"; do if [[ -d "$R_PATH" ]]; then ROOT_PATH+=( "$R_PATH" ) if [[ -z "$MECHANISM" ]]; then MECHANISM="dir names" elif [[ -n "$MECHANISM" ]] && ! echo"$MECHANISM" | grep -q "dir names"; then MECHANISM="$MECHANISM / dir names" fi fi done
mapfile -t ROOTx_PATH < <(find "$SEARCH_PATH" -xdev \( -path "*/sbin" -o -path "*/bin" -o -path "*/lib" -o -path "*/etc" -o -path "*/root" -o -path "*/dev" -o -path "*/opt" -o -path "*/proc" -o -path "*/lib64" -o -path "*/boot" -o -path "*/home" \) -execdirname {} \; | sort | uniq -c | sort -r) for R_PATH in"${ROOTx_PATH[@]}"; do CNT=$(echo"$R_PATH" | awk '{print $1}') if [[ "$CNT" -lt 5 ]]; then # we only use paths with more then 4 matches as possible root path continue fi R_PATH=$(echo"$R_PATH" | awk '{print $2}') if [[ -d "$R_PATH" ]]; then ROOT_PATH+=( "$R_PATH" ) if [[ -z "$MECHANISM" ]]; then MECHANISM="dir names" elif [[ -n "$MECHANISM" ]] && ! echo"$MECHANISM" | grep -q "dir names"; then MECHANISM="$MECHANISM / dir names" fi fi done
mapfile -t ROOTx_PATH < <(find "$SEARCH_PATH" -xdev -path "*bin/busybox" | sed -E 's/\/.?bin\/busybox//') for R_PATH in"${ROOTx_PATH[@]}"; do if [[ -d "$R_PATH" ]]; then ROOT_PATH+=( "$R_PATH" ) if [[ -z "$MECHANISM" ]]; then MECHANISM="busybox" elif [[ -n "$MECHANISM" ]] && ! echo"$MECHANISM" | grep -q "busybox"; then MECHANISM="$MECHANISM / busybox" fi fi done
mapfile -t ROOTx_PATH < <(find "$SEARCH_PATH" -xdev -path "*bin/bash" -exec file {} \; | grep "ELF" | cut -d: -f1 | sed -E 's/\/.?bin\/bash//' || true) for R_PATH in"${ROOTx_PATH[@]}"; do if [[ -d "$R_PATH" ]]; then ROOT_PATH+=( "$R_PATH" ) if [[ -z "$MECHANISM" ]]; then MECHANISM="shell" elif [[ -n "$MECHANISM" ]] && ! echo"$MECHANISM" | grep -q "shell"; then MECHANISM="$MECHANISM / shell" fi fi done
mapfile -t ROOTx_PATH < <(find "$SEARCH_PATH" -xdev -path "*bin/sh" -exec file {} \; | grep "ELF" | cut -d: -f1 | sed -E 's/\/.?bin\/sh//' || true) for R_PATH in"${ROOTx_PATH[@]}"; do if [[ -d "$R_PATH" ]]; then ROOT_PATH+=( "$R_PATH" ) if [[ -z "$MECHANISM" ]]; then MECHANISM="shell" elif [[ -n "$MECHANISM" ]] && ! echo"$MECHANISM" | grep -q "shell"; then MECHANISM="$MECHANISM / shell" fi fi done
if [[ ${#ROOT_PATH[@]} -eq 0 ]]; then export RTOS=1 ROOT_PATH+=( "$SEARCH_PATH" ) MECHANISM="last resort" else export RTOS=0 fi
eval"ROOT_PATH=($(for i in "${ROOT_PATH[@]}" ; do echo "\"$i\"" ; done | sort -u))" if [[ -v ROOT_PATH[@] && "$RTOS" -eq 0 ]]; then print_output "[*] Found $ORANGE${#ROOT_PATH[@]}$NC different root directories:" write_link "s05#file_dirs" fi
for R_PATH in"${ROOT_PATH[@]}"; do if [[ "$MECHANISM" == "last resort" ]]; then print_output "[*] Found no real root directory - setting it to: $ORANGE$R_PATH$NC via $ORANGE$MECHANISM$NC." else print_output "[+] Found the following root directory: $ORANGE$R_PATH$GREEN via $ORANGE$MECHANISM$GREEN." fi write_link "s05#file_dirs" done }
linux_basic_identification_helper() { local FIRMWARE_PATH_CHECK="${1:-}" if ! [[ -d "$FIRMWARE_PATH_CHECK" ]]; then LINUX_PATH_COUNTER=0 return fi LINUX_PATH_COUNTER="$(find "$FIRMWARE_PATH_CHECK""${EXCL_FIND[@]}" -xdev -type d -iname bin -o -type f -iname busybox -o -type f -name shadow -o -type f -name passwd -o -type d -iname sbin -o -type d -iname etc 2> /dev/null | wc -l)" backup_var "LINUX_PATH_COUNTER""$LINUX_PATH_COUNTER" }
# If we have not found a linux filesystem we try to do an extraction round on every file multiple times if [[ $RTOS -eq 0 ]] ; then module_end_log "${FUNCNAME[0]}" 0 return fi
# 跟前面一样,检查sasquatch链接 # we need to check if sasquatch is the correct one for binwalk: if ! [[ "$(readlink -q -f "$UNBLOB_PATH"/sasquatch)" == "/usr/local/bin/sasquatch_binwalk" ]]; then if [[ -L "$UNBLOB_PATH"/sasquatch ]]; then rm"$UNBLOB_PATH"/sasquatch fi ln -s /usr/local/bin/sasquatch_binwalk "$UNBLOB_PATH"/sasquatch || true fi
# 用于检查磁盘空间,如果没超过最大的额外空间,就会开启深度提取 check_disk_space if ! [[ "$DISK_SPACE" -gt "$MAX_EXT_SPACE" ]]; then deep_extractor else print_output "[!] $(date) - Extractor needs too much disk space $DISK_SPACE""main" print_output "[!] $(date) - Ending extraction processes - no deep extraction performed""main" DISK_SPACE_CRIT=1 fi
if [[ "$BINS" -gt 0 || "$UNIQUE_FILES" -gt 0 ]]; then export LINUX_PATH_COUNTER=0 linux_basic_identification_helper "$FIRMWARE_PATH_CP" print_ln print_output "[*] Found $ORANGE$FILES_EXT$NC files ($ORANGE$UNIQUE_FILES$NC unique files) and $ORANGE$DIRS_EXT$NC directories at all." print_output "[*] Found $ORANGE$BINS$NC binaries." print_output "[*] Additionally the Linux path counter is $ORANGE$LINUX_PATH_COUNTER$NC." print_ln tree -csh "$FIRMWARE_PATH_CP" | tee -a "$LOG_FILE"
# now it should be fine to also set the FIRMWARE_PATH ot the FIRMWARE_PATH_CP export FIRMWARE_PATH="$FIRMWARE_PATH_CP"
if [[ "${#ROOT_PATH[@]}" -gt 0 ]] ; then write_csv_log "FILES""UNIQUE_FILES""DIRS""Binaries""LINUX_PATH_COUNTER""Root PATH detected" for R_PATH in"${ROOT_PATH[@]}"; do write_csv_log "$FILES_EXT""$UNIQUE_FILES""$DIRS_EXT""$BINS""$LINUX_PATH_COUNTER""$R_PATH" done fi backup_var "FILES_EXT""$FILES_EXT" fi
FILES_BEFORE_DEEP=$(find "$FIRMWARE_PATH_CP" -xdev -type f | wc -l )
# if we run into the deep extraction mode we always do at least one extraction round: if [[ "$DISK_SPACE_CRIT" -eq 0 ]]; then print_output "[*] Deep extraction - 1st round" print_output "[*] Walking through all files and try to extract what ever possible"
# 进行深度提取,将在下方给出分析 deeper_extractor_helper detect_root_dir_helper "$FIRMWARE_PATH_CP" fi
# 进行多轮提取 if [[ $RTOS -eq 1 && "$DISK_SPACE_CRIT" -eq 0 ]]; then print_output "[*] Deep extraction - 2nd round" print_output "[*] Walking through all files and try to extract what ever possible"
deeper_extractor_helper detect_root_dir_helper "$FIRMWARE_PATH_CP" fi
if [[ $RTOS -eq 1 && "$DISK_SPACE_CRIT" -eq 0 ]]; then print_output "[*] Deep extraction - 3rd round" print_output "[*] Walking through all files and try to extract what ever possible"
deeper_extractor_helper detect_root_dir_helper "$FIRMWARE_PATH_CP" fi
if [[ $RTOS -eq 1 && "$DISK_SPACE_CRIT" -eq 0 ]]; then print_output "[*] Deep extraction - 4th round" print_output "[*] Walking through all files and try to extract what ever possible with binwalk matryoshka mode" print_output "[*] WARNING: This is the last extraction round that is executed."
# if we are already that far we do a final matryoshka extraction mode deeper_extractor_helper "M" detect_root_dir_helper "$FIRMWARE_PATH_CP" fi
FILES_AFTER_DEEP=$(find "$FIRMWARE_PATH_CP" -xdev -type f | wc -l )
print_output "[*] Before deep extraction we had $ORANGE$FILES_BEFORE_DEEP$NC files, after deep extraction we have now $ORANGE$FILES_AFTER_DEEP$NC files extracted." }
# 遍历每一个独立文件 for FILE_TMP in"${FILE_ARR_LIMITED[@]}"; do
FILE_MD5="$(md5sum "$FILE_TMP" | awk '{print $1}')" # let's check the current md5sum against our array of unique md5sums - if we have a match this is already extracted # already extracted stuff is now ignored
if [[ ! " ${MD5_DONE_DEEP[*]} " =~ ${FILE_MD5} ]]; then
print_output "[*] Details of file: $ORANGE$FILE_TMP$NC" print_output "$(indent "$(file "$FILE_TMP")")"
# do a quick check if EMBA should handle the file or we give it to binwalk: # fw_bin_detector is a function from p02 fw_bin_detector "$FILE_TMP"
# 进行一轮提取 if [[ "$VMDK_DETECTED" -eq 1 ]]; then if [[ "$THREADED" -eq 1 ]]; then vmdk_extractor "$FILE_TMP""${FILE_TMP}_vmdk_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else vmdk_extractor "$FILE_TMP""${FILE_TMP}_vmdk_extracted" fi elif [[ "$UBI_IMAGE" -eq 1 ]]; then if [[ "$THREADED" -eq 1 ]]; then ubi_extractor "$FILE_TMP""${FILE_TMP}_ubi_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else ubi_extractor "$FILE_TMP""${FILE_TMP}_ubi_extracted" fi elif [[ "$DLINK_ENC_DETECTED" -eq 1 ]]; then if [[ "$THREADED" -eq 1 ]]; then dlink_SHRS_enc_extractor "$FILE_TMP""${FILE_TMP}_shrs_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else dlink_SHRS_enc_extractor "$FILE_TMP""${FILE_TMP}_shrs_extracted" fi elif [[ "$DLINK_ENC_DETECTED" -eq 2 ]]; then if [[ "$THREADED" -eq 1 ]]; then dlink_enc_img_extractor "$FILE_TMP""${FILE_TMP}_enc_img_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else dlink_enc_img_extractor "$FILE_TMP""${FILE_TMP}_enc_img_extracted" fi elif [[ "$EXT_IMAGE" -eq 1 ]]; then if [[ "$THREADED" -eq 1 ]]; then ext_extractor "$FILE_TMP""${FILE_TMP}_ext_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else ext_extractor "$FILE_TMP""${FILE_TMP}_ext_extracted" fi elif [[ "$ENGENIUS_ENC_DETECTED" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then engenius_enc_extractor "$FILE_TMP""${FILE_TMP}_engenius_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else engenius_enc_extractor "$FILE_TMP""${FILE_TMP}_engenius_extracted" fi elif [[ "$BSD_UFS" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then ufs_extractor "$FILE_TMP""${FILE_TMP}_bsd_ufs_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else ufs_extractor "$FILE_TMP""${FILE_TMP}_bsd_ufs_extracted" fi elif [[ "$ANDROID_OTA" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then android_ota_extractor "$FILE_TMP""${FILE_TMP}_android_ota_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else android_ota_extractor "$FILE_TMP""${FILE_TMP}_android_ota_extracted" fi elif [[ "$OPENSSL_ENC_DETECTED" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then foscam_enc_extractor "$FILE_TMP""${FILE_TMP}_foscam_enc_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else foscam_enc_extractor "$FILE_TMP""${FILE_TMP}_foscam_enc_extracted" fi elif [[ "$BUFFALO_ENC_DETECTED" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then buffalo_enc_extractor "$FILE_TMP""${FILE_TMP}_buffalo_enc_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else buffalo_enc_extractor "$FILE_TMP""${FILE_TMP}_buffalo_enc_extracted" fi elif [[ "$ZYXEL_ZIP" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then zyxel_zip_extractor "$FILE_TMP""${FILE_TMP}_zyxel_enc_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else zyxel_zip_extractor "$FILE_TMP""${FILE_TMP}_zyxel_enc_extracted" fi elif [[ "$QCOW_DETECTED" -ne 0 ]]; then if [[ "$THREADED" -eq 1 ]]; then qcow_extractor "$FILE_TMP""${FILE_TMP}_qemu_qcow_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else qcow_extractor "$FILE_TMP""${FILE_TMP}_qemu_qcow_extracted" fi
else # default case to binwalk if [[ "$THREADED" -eq 1 ]]; then binwalk_deep_extract_helper "$MATRYOSHKA""$FILE_TMP""${FILE_TMP}_binwalk_extracted" & BIN_PID="$!" store_kill_pids "$BIN_PID" disown"$BIN_PID" 2> /dev/null || true WAIT_PIDS_P20+=( "$BIN_PID" ) else binwalk_deep_extract_helper "$MATRYOSHKA""$FILE_TMP""${FILE_TMP}_binwalk_extracted" fi fi
MD5_DONE_DEEP+=( "$FILE_MD5" ) max_pids_protection "$MAX_MOD_THREADS""${WAIT_PIDS_P20[@]}" fi
check_disk_space
FREE_SPACE=$(df --output=avail "$LOG_DIR" | awk 'NR==2') if [[ "$FREE_SPACE" -lt 100000 ]]; then # this stops the complete EMBA test print_output "[!] $(date) - The system is running out of disk space $ORANGE$FREE_SPACE$NC""main" print_output "[!] $(date) - Ending EMBA firmware analysis processes""main" cleaner 1 exit elif [[ "$DISK_SPACE" -gt "$MAX_EXT_SPACE" ]]; then # this stops the deep extractor but not EMBA print_output "[!] $(date) - Extractor needs too much disk space $DISK_SPACE""main" print_output "[!] $(date) - Ending extraction processes""main" DISK_SPACE_CRIT=1 break fi done