本文将简单介绍一下如何开始搭建一个固件调试环境并且使用gdb来调试
系统
文中所用的系统是AttifyOS4,下载后用7zip解压并安装到虚拟机中
Release AttifyOS 4.0 OVA · AttifyOS/AttifyOS · GitHub

工具
binwalk
下载并导入AttifyOS4系统后我们需要下载binwalk
AttifyOS 4.0 里预装的是 binwalk 2.3.3,是snap 包的形式,我选择删除然后下载源码来安装,也可选择使用自带的
删除snap形式的binwalk
sudo snap remove binwalk
hash -r
which -a binwalk
安装系统依赖
sudo apt update
sudo apt install -y git build-essential locales \
mtd-utils gzip bzip2 tar lhasa p7zip p7zip-full cabextract \
cramfsswap squashfs-tools zlib1g-dev liblzma-dev liblzo2-dev \
sleuthkit default-jdk lzop srecord cpio unyaffs
下载binwalk
此处我给出指令的路径是按照我自己存放的路径来给的
mkdir -p /home/attify/Desktop/iot/binwalk
cd /home/attify/Desktop/iot/binwalk
wget https://github.com/ReFirmLabs/binwalk/archive/refs/tags/v2.3.4.tar.gz
tar xzf v2.3.4.tar.gz
cd /home/attify/Desktop/iot/binwalk/binwalk-2.3.4
sudo python3 setup.py install
如果缺少sasquatch
cd /tmp
git clone --quiet --depth 1 --branch master https://github.com/devttys0/sasquatch
cd sasquatch
wget https://github.com/devttys0/sasquatch/pull/51.patch && patch -p1 < 51.patch
sudo ./build.sh
qemu
安装qemu
mkdir -p /home/attify/Desktop/iot/qemu
cd /home/attify/Desktop/iot/qemu
wget https://download.qemu.org/qemu-6.0.0.tar.xz
tar xvJf qemu-6.0.0.tar.xz
sudo apt-get install -y htop gcc g++ libglib2.0-dev libpixman-1-dev make libgtk-3-dev ninja-build
cd /home/attify/Desktop/iot/qemu/qemu-6.0.0
mkdir build
cd build
../configure --gdb=/usr/bin/gdb --enable-debug --prefix=/usr/local
make -j4
sudo make install
qemu-system
我们要使用qemu-systm来仿真,使用qemu-user的话环境不完整
下载图中三个

我存放的路径是

备份
mkdir -p /home/attify/Desktop/iot/backups
chmod 700 /home/attify/Desktop/iot/backups
cp -v debian_wheezy_armhf_standard.qcow2 /home/attify/Desktop/iot/backups/
cp -v vmlinuz-3.2.0-4-vexpress initrd.img-3.2.0-4-vexpress \
/home/attify/Desktop/iot/backups/
chmod 444 /home/attify/Desktop/iot/backups/*.qcow2
ls -l /home/attify/Desktop/iot/backups/
仿真指令,如何使用我给的脚本则不需要这段指令启动
qemu-system-arm \
-M vexpress-a9 \
-kernel vmlinuz-3.2.0-4-vexpress \
-initrd initrd.img-3.2.0-4-vexpress \
-drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \
-append "root=/dev/mmcblk0p2 console=ttyAMA0" \
-net nic \
-net tap,ifname=tap0,script=no,downscript=no \
-nographic
下文我会给出脚本来一键启动和关闭
gdbserver
为了方便调试我们需要安装对应指令集下静态编译的gdbserver,此处我们需要的是
gdbserver-7.7.1-armhf-eabi5-v1-sysv
HatLab Tools Library: 海特实验室IoT安全工具/环境整合 - Gitee.com

我存放的路径如图所示

mygdbinit
建在 gdb 的当前工作目录
cd ~/Desktop/iot/Firmware/SR20/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted
cat > mygdbinit <<'EOF'
---- 架构与字节序 ----
set architecture arm
set endian little
---- 让 gdb 去宿主机上的解包目录找固件的库符号 ----
set sysroot ./squashfs-root
set solib-search-path ./squashfs-root/lib:./squashfs-root/usr/lib
---- 允许对尚未加载的库/函数下断点 ----
set breakpoint pending on
---- daemon 常 fork,跟住子进程 ----
set follow-fork-mode child
set detach-on-fork off
---- 连接 QEMU guest 里运行的 gdbserver ----
target remote 10.10.10.2:12345
EOF
cat mygdbinit
固件
本文使用到的固件是SR20(US)_V1_180518

使用binwalk对文件系统进行提取
binwalk -Me tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin
进入提取出来的文件系统中查看是什么类型,随便file一个就行
cd /home/attify/Desktop/iot/Firmware/SR20/_tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin.extracted/squashfs-root
file ./usr/bin/tddp

我们可以发现是一个 ARM 32 位小端,动态链接 uClibc,符号表已剥离的ELF可执行文件
调试
启动脚本
sudo tee /usr/local/bin/iot-arm >/dev/null <<'EOF'
#!/bin/bash
set -e
DIR="$HOME/Desktop/iot/qemu/qemu-system/armhf"
BASE="$HOME/Desktop/iot/backups/debian_wheezy_armhf_standard.qcow2"
TAP_IP="10.10.10.1/24"
cd "$DIR"
[ -f "$BASE" ] || { echo "base 镜像缺失: $BASE"; exit 1; }
[ -f work.qcow2 ] || qemu-img create -f qcow2 -b "$BASE" -F qcow2 work.qcow2
if ! ip link show tap0 &>/dev/null; then
sudo ip tuntap add dev tap0 mode tap user "$USER"
sudo ip link set tap0 up
fi
# 幂等:缺地址才加,已有同网段则跳过
ip -4 addr show tap0 | grep -q "10.10.10.1/24" || sudo ip addr add "$TAP_IP" dev tap0
exec qemu-system-arm \
-M vexpress-a9 \
-kernel vmlinuz-3.2.0-4-vexpress \
-initrd initrd.img-3.2.0-4-vexpress \
-drive if=sd,file=work.qcow2 \
-append "root=/dev/mmcblk0p2 console=ttyAMA0" \
-net nic -net tap,ifname=tap0,script=no,downscript=no \
-nographic
EOF
sudo chmod +x /usr/local/bin/iot-arm
指令
iot-arm # 启动qume-system-arm
关闭脚本
sudo tee /usr/local/bin/iot-arm-stop >/dev/null <<'EOF'
#!/bin/bash
# 关闭 Attify 宿主机上运行的 qemu-system-arm
set -u
PATTERN="${PATTERN:-qemu-system-arm}"
GUEST_IP="${GUEST_IP:-10.10.10.2}"
GUEST_USER="${GUEST_USER:-root}"
TAP_IF="${TAP_IF:-tap0}"
GRACE="${GRACE:-5}" # 发完 poweroff 后等 guest 收尾的秒数
WAIT_SOFT="${WAIT_SOFT:-20}" # 等 QEMU 自然退出的上限
WAIT_TERM="${WAIT_TERM:-10}" # 等 SIGTERM 生效的上限
FORCE=0
DOWN_TAP=0
SSH_OPTS=(-o StrictHostKeyChecking=no
-o UserKnownHostsFile=/dev/null
-o ConnectTimeout=5
-o NumberOfPasswordPrompts=1
-o LogLevel=ERROR
-o HostKeyAlgorithms=+ssh-rsa
-o PubkeyAcceptedKeyTypes=+ssh-rsa)
usage() {
cat <<'EOT'
用法: iot-arm-stop [选项]
-f, --force 跳过 guest 正常关机,直接终止 QEMU
--down-tap 同时删除 tap0(默认保留,下次启动更快)
-h, --help 显示帮助
环境变量: PATTERN / GUEST_IP / GUEST_USER / TAP_IF / GRACE
EOT
}
qemu_pids() {
pgrep -f "$PATTERN" 2>/dev/null | grep -vx -e "$$" -e "${PPID:-0}" || true
}
alive() { [ -n "$(qemu_pids)" ]; }
down_tap() {
ip link show "$TAP_IF" &>/dev/null || return 0
echo "→ 删除 $TAP_IF"
sudo ip link set "$TAP_IF" down 2>/dev/null || true
sudo ip tuntap del dev "$TAP_IF" mode tap 2>/dev/null \
|| echo " (删除失败,忽略)"
}
while [ $# -gt 0 ]; do
case "$1" in
-f|--force) FORCE=1 ;;
--down-tap) DOWN_TAP=1 ;;
-h|--help) usage; exit 0 ;;
*) echo "未知参数: $1" >&2; usage; exit 2 ;;
esac
shift
done
if ! alive; then
echo "没有运行中的 $PATTERN"
[ "$DOWN_TAP" = 1 ] && down_tap
exit 0
fi
echo "发现 QEMU 进程: $(qemu_pids | tr '\n' ' ')"
# ---- 1) 尽力让 guest 自己干净关机 ----
if [ "$FORCE" = 0 ]; then
if ssh "${SSH_OPTS[@]}" "$GUEST_USER@$GUEST_IP" 'sync' 2>/dev/null; then
echo "→ guest 磁盘已同步,发送 poweroff"
ssh "${SSH_OPTS[@]}" "$GUEST_USER@$GUEST_IP" 'poweroff' &>/dev/null &
sleep "$GRACE"
else
echo "→ 连不上 guest(sshd 未起 / 网络不通 / 取消输密码),跳过正常关机"
fi
fi
# ---- 2) 等 QEMU 自己退出(部分内核 poweroff 会让 QEMU 退出)----
for _ in $(seq "$WAIT_SOFT"); do
alive || break
sleep 1
done
# ---- 3) SIGTERM(QEMU 会正常关闭块设备、刷缓存)----
if alive; then
echo "→ QEMU 仍在运行,发送 SIGTERM"
kill -TERM $(qemu_pids) 2>/dev/null
for _ in $(seq "$WAIT_TERM"); do
alive || break
sleep 1
done
fi
# ---- 4) 最后的 SIGKILL ----
if alive; then
echo "→ SIGTERM 无效,强制 SIGKILL"
kill -KILL $(qemu_pids) 2>/dev/null
sleep 1
fi
if alive; then
echo "✗ 无法终止,请手动检查: $(qemu_pids | tr '\n' ' ')" >&2
exit 1
fi
echo "✓ QEMU 已关闭"
[ "$DOWN_TAP" = 1 ] && down_tap
exit 0
EOF
sudo chmod +x /usr/local/bin/iot-arm-stop
指令
iot-arm-stop # 正常关:通知 guest 落盘 → 终止 QEMU
iot-arm-stop --down-tap # 顺便删掉 tap0(宿主机要断网/重配时用)
iot-arm-stop --force # guest 完全卡死、ssh 不通时直接终止
初次启动
启动qemu-system
在终端中输入下列指令
iot-arm

用户名和密码是root
将gdbserver放入文件系统,打包送入qemu
scp ~/Desktop/iot/arm/gdbserver-7.7.1-armhf-eabi5-v1-sysv \
root@10.10.10.2:/root/squashfs-root/tmp/
guest 内赋权
ls -l /root/squashfs-root/tmp/
chmod +x /root/squashfs-root/tmp/gdbserver-7.7.1-armhf-eabi5-v1-sysv
ls -l /root/squashfs-root/tmp/
使用chroot将根目录变为固件文件系统
cd /root
mkdir -p ./squashfs-root/proc ./squashfs-root/sys ./squashfs-root/dev
mount -t proc proc ./squashfs-root/proc
mount -t sysfs sysfs ./squashfs-root/sys
mount --rbind /dev ./squashfs-root/dev
chroot ./squashfs-root /bin/sh
进 chroot 运行
chroot /root/squashfs-root /bin/sh
/tmp/gdbserver-7.7.1-armhf-eabi5-v1-sysv :12345 /usr/bin/tddp

AttifyOS连 gdb
cd ~/Desktop/iot/Firmware/SR20/*.extracted
gdb-multiarch -q ./squashfs-root/usr/bin/tddp -x mygdbinit


关闭qemu-system
AttifyOS中使用下列指令即可
iot-arm-stop

日常使用
启动qemu
iot-arm
进 chroot 运行
chroot /root/squashfs-root /bin/sh
/tmp/gdbserver-7.7.1-armhf-eabi5-v1-sysv :12345 /usr/bin/tddp
AttifyOS连 gdb
cd ~/Desktop/iot/Firmware/SR20/*.extracted
gdb-multiarch -q ./squashfs-root/usr/bin/tddp -x mygdbinit
关闭qemu-system
AttifyOS中使用下列指令
iot-arm-stop