0成本搭建摄像头漏洞挖掘环境

固件安全
2021-11-18 02:26
119743

0成本搭建摄像头漏洞挖掘环境

环境说明:
Ubuntu 18.04+QEMU

网络配置:宿主机物理ens18网卡:x.x.x.x/24,虚拟网卡tap0:192.168.0.2/24;

预备知识

嵌入式linux开机引导程序U-boot

U-Boot 是一个主要用于嵌入式系统的BootLoader程序,可以支持ARM、RSIC-V、MIPS、x86等多种不同的计算机系统结构。目前常见的摄像头硬件使用的cpu大都是ARM架构,采用uboot方式引导,大致流程是:`设备上电 -> uboot代码自动执行 -> 加载uImage内核文件 -> 将执行权限交给内核启动`。由于它是一套在GNU通用公共许可证之下发布的自由软件,因此我们可直接从官方下载源码进行交叉编译后对其进行仿真模拟运行。

设备树(DTB)

设备树(Device Tree binary file),是在系统启动的时候由BootLoader程序将保存在flash或其他地方中的DTB copy到内存中用来描述一棵电路板上CPU、总线、设备组成的树信息,然后内核可以识别这棵树, 并根据它展开出Linux内核中的各种设备。它是由一种ASCII 文本格式的Device Tree描述文件.dts文件,通过DTC(Device Tree Compiler)编译而成,变成适合机器处理的Device Tree Blob。

Linux内核常见格式

  1. vmlinux:编译出来后未压缩最原始的内核文件。
  2. Image:通用Linux内核二进制映像文件。原则上Image就可以直接被烧录到Flash上进行启动执行(类似于u-boot.bin),但搞嵌入式的还是嫌它大了。
  3. zImage:自解压的Linux内核映像的压缩版本。是vmlinux经过objcopy gzip压缩后的文件, objcopy实现由vmlinux的elf文件拷贝成纯二进制数据文件,zImage自带了解压缩程序,所以zImage由vmlinux objcopy出来的纯二进制文件以及解压缩程序组成。
  4. uImage:U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的TAG。

vexpress虚拟开发版

资源:
CPU:ARM-Versatile Express ARMv7 Processor
SD卡:mmci-pl18x clcd-pl11x ARM(R) AMBA(R) PrimeCell Multimedia Card
网卡:smsc911x phy:libphy
实时时钟:rtc-pl031 (qemu能显示正确时间)
串口:AMBA PL011 UART driver
看门狗:
GPIO:
LCD:pl111 clcd-pl11x
中断:gic

软件准备

  • 源码包下载:
    uboot
    linux
    busybox

  • 创建单独的vexpress目录, 下载uboot, linux和busybox源码并解压,创建镜像文件目录image

    ubuntu@ubuntu-virtual-machine:~/vexpress$  ls
    busybox     busybox-1.31.1.tar.bz2     image     linux     linux-3.18.30.tar.gz
    u-boot      u-boot-2019.07.tar.bz2
    
  • Ubuntu 软件安装:
    安装交叉编译工具:sudo apt install gcc-arm-linux-gnueabi
    依赖库和软件包安装:sudo apt-get install zlib1g-dev libglib2.0-0 libglib2.0-dev libtool libsdl1.2-dev autoconf flex bison ncurses-dev u-boot-tools

一、安装Qemu

使用 apt-get 安装

sudo apt-get install qemu
sudo apt-get install qemu-system
sudo apt-get install qemu-user-static

Qemu网络功能设置

配置TUN/TAP虚拟网络

sudo apt-get install bridge-utils uml-utilities // 虚拟网桥工具,brctl命令会用到;UML(User-mode linux)工具,tunctl命令会用到

检查是否有/dev/net/tun 设备文件,没有则自行配置安装。

ubuntu@ubuntu-virtual-machine:~$sudo tunctl -t tap0 -u ubuntu // 创建虚拟网卡并给用户赋予权限
// 配置网桥和虚拟网卡ip
ubuntu@ubuntu-virtual-machine:~$sudo ifconfig tap0 192.168.0.2 netmask 255.255.255.0
// 查看配置后的信息
ubuntu@ubuntu-virtual-machine:~$ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether fe:fc:fe:48:ad:1a brd ff:ff:ff:ff:ff:ff
    inet x.x.x.x/24 brd x.x.x.255 scope global noprefixroute ens18
       valid_lft forever preferred_lft forever
    inet6 fe80::4963:be01:4b2f:abfb/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
    link/ether 2e:d3:aa:eb:eb:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 brd 192.168.0.255 scope global tap0
       valid_lft forever preferred_lft forever
    inet6 fe80::2cd3:aaff:feeb:eb57/64 scope link
       valid_lft forever preferred_lft forever
//测试网络连通性
ubuntu@ubuntu-virtual-machine:~$ping -c 3 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.049 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.062 ms

--- 192.168.0.2 ping statistics ---

二、搭建并运行一个uboot+linux+busybox的ARM仿真环境

思路:

我们先通过自己模拟各种嵌入式系统必须的模块,最终模拟一个能联网运行的ARM环境,明白启动过程、系统初始化与文件系统挂载等等流程。

1、交叉编译uboot

ubuntu@ubuntu-virtual-machine:~/vexpress/uboot$export ARCH=arm
ubuntu@ubuntu-virtual-machine:~/vexpress/uboot$export CROSS_COMPILE=arm-linux-gnueabi-
ubuntu@ubuntu-virtual-machine:~/vexpress/uboot$make vexpress_ca9x4_defconfig // 可以在 configs 目录下选择对应的单板默认配置
ubuntu@ubuntu-virtual-machine:~/vexpress/uboot$make -j$(nproc)
ubuntu@ubuntu-virtual-machine:~/vexpress/uboot$cp u-boot ../image/
# 测试uboot的网络功能
ubuntu@ubuntu-virtual-machine:~/vexpress/uboot$qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel u-boot
pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument

U-Boot 2019.07 (Nov 12 2020 - 16:56:44 +0800)

DRAM:  512 MiB
WARNING: Caches not enabled
Flash: 128 MiB
MMC:   MMC: 0
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   smc911x-0
Hit any key to stop autoboot:  0
=> setenv ipaddr 192.168.0.3
=> ping 192.168.0.2
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
Using smc911x-0 device
smc911x: MAC 52:54:00:12:34:56
host 192.168.0.2 is alive  #这里alive结果表示虚拟主板中uboot和宿主机器可通讯
=> 

2、交叉编译linux内核

ubuntu@ubuntu-virtual-machine:~/vexpress/linux$export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
make vexpress_defconfig
make -j$(nproc)
make modules -j$(nproc)
make uImage LOADADDR=0x60000000
make dtbs
cp -f arch/arm/boot/uImage  ../image/
cp -f arch/arm/boot/zImage  ../image/
cp -f arch/arm/boot/dts/vexpress-v2p-ca9.dtb  ../image/device.dtb

3、交叉编译busybox并制作根文件系统

创建脚本并执行:

ubuntu@ubuntu-virtual-machine:~/vexpress/busybox$  cat mkbusybox.sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install
# 将编译好的busybox和一些动态链接库复制到一个ext3文件系统中,制作rootfs
dd if=/dev/zero of=sdcard.ext3 bs=1M count=32
mkfs.ext3 sdcard.ext3
sudo rm -fr rootfs;
mkdir rootfs
sudo mount -t ext3 -o loop sdcard.ext3  rootfs/
cd rootfs;
sudo mkdir lib proc sys dev etc etc/init.d
sudo cp -r ../_install/* .
sudo cp -P /usr/arm-linux-gnueabi/lib/*  lib/

sudo cp ../examples/bootfloppy/etc/inittab  etc/
sudo touch etc/init.d/rcS ;
sudo chmod 0777 etc/init.d/rcS;
sudo echo "#!/bin/sh" >> etc/init.d/rcS
sudo echo "mount -t proc none /proc" >> etc/init.d/rcS
sudo echo "mount -t sysfs none /sys" >> etc/init.d/rcS
sudo echo "mount -t tmpfs none /dev" >> etc/init.d/rcS
sudo echo "/sbin/mdev -s" >> etc/init.d/rcS
sudo chmod 0755 etc/init.d/rcS;

cd ..;
sudo umount rootfs/
cp sdcard.ext3 ../image/

4、搭建tftp服务器(供uboot下载内核启动)

ubuntu@ubuntu-virtual-machine:~/vexpress/$sudo apt-get install tftp-hpa tftpd-hpa
ubuntu@ubuntu-virtual-machine:~/vexpress/$cat /etc/default/tftpd-hpa #修改配置文件
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/ubuntu/vexpress/image"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"
ubuntu@ubuntu-virtual-machine:~/vexpress/$sudo service tftpd-hpa restart

5、运行uboot+linux+busybox文件系统的ARM环境

使用虚拟网卡tap0、引导程序u-boot、内核镜像uImage和根文件系统sdcard.ext3这些自建的零件启动一个带网络的ARM仿真环境

ubuntu@ubuntu-virtual-machine:~/vexpress/image/$qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel u-boot -sd sdcard.ext3
#......
=>setenv ipaddr 192.168.0.3 #这里只是设置uboot的临时ip
=>setenv serverip 192.168.0.2 #设置tftp服务器地址
=>tftp 0x60000000 uImage #从tftp服务器上下载内核镜像,加载到0x60000000地址处
=>tftp 0x70000000 device.dtb #从tftp服务器上下载设备树,加载到0x70000000地址处
=>setenv bootargs "console=ttyAMA0 root=/dev/mmcblk0 init=/linuxrc rw rootwait" #设置启动参数,指定系统初始化程序以及根文件在哪块分区位置等
=>bootm 0x60000000 - 0x70000000 #从0x60000000启动内核(注意-号两边有空格)
#...自动加载启动内核并挂载根文件系统和执行rcS初始化脚本...
/ # ls
bin               lost+found        sys
dev               lib               proc              usr
etc               linuxrc           sbin
/ # ifconfig eth0 192.168.0.4 #这里设置的是虚拟环境的ip,可与宿主机的tap0网卡192.168.0.2通讯

使用zImage内核文件不依赖uboot直接启动:

ubuntu@ubuntu-virtual-machine:~/vexpress/image/$qemu-system-arm -M vexpress-a9 -m 512 -sd sdcard.ext3 -nographic -net nic -net tap,ifname=tap0,script=no -kernel zImage -dtb device.dtb  -append "console=ttyAMA0 rootwait rw root=/dev/mmcblk0 init=/linuxrc"

退出仿真环境:

先按Ctrl+A,然后按x键即可退出qemu仿真环境

三、分析摄像头升级固件并根据固件模拟摄像头环境

思路:通过分析摄像头固件,得到其中的文件,解压出其中的文件后分析启动过程,将如何初始化、修改了哪些文件等等操作都记录下来,然后把提取出来的文件放到自己制作的一个文件系统中,用我们自己的ARM仿真环境去挂载这个根据固件制作出来的文件系统,对此摄像头环境进行仿真模拟。

1、使用binwalk分析提取固件

image-20211117145821263.png

ubuntu@ubuntu-virtual-machine:~/ipc/$binwalk -Me _IPC-HFW1XXX-Alps_Chn_PN_V2.622.0000000.31.R.171229.bin
ubuntu@ubuntu-virtual-machine:~/ipc/_IPC-HX1XXX-Mao-Eris_Chn_PN_V2.680.0000000.3.R.191216.bin.extracted$ls
84.zip     boot.bin.img             Install     _kernel.img.extracted  partition-x.cramfs.img  romfs-x.squashfs.img  sign.img  web-x.squashfs.img
check.img  _boot.bin.img.extracted  kernel.img  pd-x.squashfs.img       romfs-x.squashfs  _romfs-x.squashfs.img.extracted

2、分析各个镜像中文件

通过简单推理与分析得出:

web-x.squashfs.img中保存的是web文件系统

romfs-x.squashfs.img中保存的是根文件系统

kernel.img中保存的是内核镜像

我们将其解压,进入到根文件系统中,查看一下_romfs-x.squashfs.img.extracted/squashfs-root/etc/init.d/rcS文件,分析是如何启动初始化的:

#! /bin/sh

if [ -f /proc/xxxxx/fsmountcmd ]; then
        echo #! /bin/sh > /var/script
        cat /proc/xxxxx/fsmountcmd >> /var/script
        chmod 777 /var/script
        /var/script
        rm /var/script
else
        mount /dev/mtdblock3 /mnt/custom/
        mount /dev/mtdblock4 /mnt/pd/
        mount /dev/mtdblock6 /mnt/web/
        mount /dev/mtdblock8 /usr/
        mnt_jffs2 /dev/mtdblock9 /mnt/syslog jffs2
        mnt_jffs2 /dev/mtdblock10 /mnt/mtd jffs2
        mnt_jffs2 /dev/mtdblock11 /mnt/backup jffs2
fi
... ...
/sbin/telnetd &
/usr/etc/imod

3、使用提取出的文件制作文件系统

根据初始化脚本,大概推测出哪个img挂载到了哪个目录,我们可以直接创建一个根文件系统,并按照对应关系创建相应目录,然后把对应镜像解压复制到对应目录即可

dd if=/dev/zero of=carmfs.ext3 bs=1M count=512
mkfs.ext3 carmfs.ext3
mkdir rootfs
sudo mount -t ext3 -o loop carmfs.ext3  rootfs/
cp -r _romfs-x.squashfs.img.extracted/squashfs-root/ rootfs
cd rootfs
mv squashfs-root/* ../
rm -r squashfs-root/
cd ..
sudo umount rootfs
cp carmfs.ext3 ~/vexprexx/image/

4、使用u-boot的tftp下载uImage并挂载摄像头文件系统方式启动仿真环境

ubuntu@ubuntu-virtual-machine:~/vexpress/image/$qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel u-boot -sd carmfs.ext3
#......
=>setenv ipaddr 192.168.0.3 #这里只是设置uboot的临时ip
=>setenv serverip 192.168.0.2 #设置tftp服务器地址
=>tftp 0x60000000 uImage #从tftp服务器上下载内核镜像,加载到0x60000000地址处
=>tftp 0x70000000 device.dtb #从tftp服务器上下载设备树,加载到0x70000000地址处
=>setenv bootargs "console=ttyAMA0 root=/dev/mmcblk0 init=/linuxrc rw rootwait" #设置启动参数,指定系统初始化程序以及根文件在哪块分区位置等
=>bootm 0x60000000 - 0x70000000 #从0x60000000启动内核(注意-号两边有空格)
#...自动加载启动内核并挂载根文件系统和执行rcS初始化脚本,启动各种服务...

至此,如果一切正常,我们就根据摄像头固件模拟得到一个摄像头环境。

四、实战分析几种固件

实战A:A厂摄像头型号a

从网上下载A厂摄像头型号a的升级固件

1、使用binwalk分析提取固件

4.1.1提取固件

进入提取后的_romfs-x.squashfs.img分区中,能看见这就是一个嵌入式linux的根目录。

根目录

查看_xx_ipc.bin.extracted/_romfs-x.squashfs.img.extracted/squashfs-root/etc/init.d/rcS脚本进行了哪些操作,如将什么分区挂载到了什么目录,最后拉起了什么服务等等

4.1.1的rcS

2、分析并制作文件系统

根据初始化脚本rcS中挂载情况,推出各个分区挂载在到跟文件系统中的目录,我们直接在romfs-x中创建对应目录,然后把相应分区镜像中的文件复制到romfs这个根分区的文件夹中。我们制作一个根文件系统把这些文件都放进去即可,最后在rcS脚本中写入/bin/sh,方便初始化完成后直接进入我们的shell。

制作文件系统

最后得到包含摄像头文件的文件系统ipc_disk.ext3

3、仿真运行

使用上面我们自己编译的zImage内核镜像和从摄像头固件提取出来的文件制作的文件系统开始仿真运行:

ubuntu@ubuntu-virtual-machine:~/vexpress/image/$qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel zImage -append "console=ttyAMA0 rootwait rw root=/dev/mmcblk0 init=/linuxrc" -sd ipc_disk.ext3

启动时内核报错:
Kernel panic - not syncing: Requested init /linuxrc failed (error -8).

image-20211117145934247

通过查看linux内核的错误定义头文件include/uapi/asm-generic/errno-base.h中得知以上错误代码error 8的含义,是由于linuxrc初始化程序报的可执行文件格式错误。

image-20211117150003653

一般情况下linuxrc在嵌入式linux中就是软连接到busybox的,如果提取出来的文件系统中的busybox不兼容我们的虚拟开发板,则必须重新创建能运行目标程序的主板才行。我们将文件系统挂载到本地,分析一下是什么原因:

image-20211117150050502

image-20211117150123649

image-20211117150202759

简单使用了file命令和readelf简单分析了摄像头文件系统中的busybox二进制程序和其他的一些二进制程序,可以发现它们确实是可执行文件格式,但使用了一种摩托罗拉的架构,和本机的X86-64架构、仿真环境的ARM架构完全不一样。我们从中拿出一个小的配置wifi功能的二进制程序iwconfig来尝试逆向分析:

ida识别为mcore架构

可以看到识别出来这个二进制程序是摩托罗拉的一个叫mCore的架构,从来就没有见过这种东西,比较好奇这是什么架构,刚准备继续逆向分析,就发现无情提示“您安装的版本不支持此架构”:

ida无情不支持

现在陷入僵局,无法逆向分析了,就开始思考:a.是否由于厂商可以修改了elf的标志,让常规的普通内核和逆向软件无法识别加载这种特殊格式的可执行程序,只有自家厂商的魔改内核可加载这种程序;b.此版本摄像头硬件就是使用的是摩托罗拉mCore架构芯片,并没有采用大众的ARM架构芯片。

开始验证假设a:恢复elf的arch标志位为ARM架构,将0x0012h处的27 00改为28 00即可,看看能不能在我们的arm仿真环境中正常运行:

image-20211117150527989

将修改为arm架构标志28 00后的elf文件拿到我们的arm仿真环境中结果还是不能正确识别运行,猜想a错误,接下来验证猜想b。

验证猜想b:

查了一下motorola mcore的资料,维基百科这样介绍:

image-20211117151051435

M-CORE是一种摩托罗拉设计的基于RISC指令集的低功耗芯片架构 ... ...,看到推出年代是1997年,几乎和我一般大了,这也很老了,居然从来没听说过。还好这里还给出了这款芯片的开发人员参考手册,可以继续学习一下,方便后续逆向。

指令集介绍

opcode_map

通过阅读这个手册,知道这些关键信息:mCore是采用32位的RISC指令集架构,指令长度固定为16位,得到各指令功能介绍,得到二进制操作码和指令对应关系。

现在回来开始逆向分析二进制程序就思路清晰了,知道如何把二进制代码转换为指令来分析功能了:

image-20211117151244399

我们再看一下上面readelf分析出iwconfig文件,从节头表中找到.text节,得到代码节映射到内存中的起始地址是0xa0f0;此节相对于文件开头的偏移地址是0x20f0,大小为0x6c3e字节,我们静态分析就从这个偏移地址开始:

image-20211117151304797

得到0x00020f0这个地址的值为0x7208,转换16位二进制格式的指令就是0b01110010 00001000

到手册中的 Opcode Map对应关系中查询,只有lrw指令的格式满足0b01110010 00001000 ,转化为汇编代码就是lrw 0010 00001000,继续查阅lrw指令介绍 (Load Relative Word)及后面的数据结构:

image-20211117151330432

image-20211117151353956

最终根据说明,得到0x7208对应的汇编指令为:lrw r0,[0x8],作用就是把末尾的8位左移2(扩大4倍)加上2与PC的值求和后再与0xfffffffc进行逻辑“与”运算,强制将低两位清0后放到rz寄存器,rz通常情况就是栈指针r0。

同理,根据手册就可以翻译出.text节的所有的汇编代码。

难道没有能反汇编mcore架构的工具吗?难道就这样一条条查手册来反汇编吗?要自动化提高效率还得写个脚本去反汇编?这样投入太大了,不划算啊。mcore架构已经推出这么久了,应该有前人已经做过这些工作了呀。

后来同事让我看看r2是否支持,我一试果然还是开源的radare2支持得架构多,确实可以,接下来看看r2的桌面版工具cutter的反汇编情况:

shebei20211117151400.png

果然还是工具强大,各种辅助线,各种注释,节省了很多精力,遗憾不能反编译为伪代码,我们把更多精力放在阅读汇编代码上,这样就可以愉快的进行漏洞挖掘了。

通过按照标准的mcore芯片文档来逆向分析,确定这些摄像头固件里的二进制程序确实没有经过混淆修改,得知此摄像头硬件应该确实是摩托罗拉的芯片,猜想b成立。

接下来就面临一个非常尴尬的情况,目前能静态的逆向分析这些程序了,但是没有动态调试环境。尝试用qemu来模拟出一个mcore架构的运行环境,刚开始查阅qemu官方文档,其中介绍到有支持RISC-V的模块,天真的想到mcore基于RISC指令集,RISC-V是它的第5版,应该向下兼容吧,然后就开始下载qemu源码,手动编译配置添加这个模块,接下来还需要编译RISC-V的内核,然后有巨多的坑。后来才发现ARM也是属于RISC指令集的,因此mCore、ARM、RISC-V都是基于RISC的同一高度的不同架构,根本不是第5版,本地的ARM仿真环境不能运行mcore的程序,肯定编译出来的RISC-V也是互不兼容啊,很遗憾我没找到能模拟mcore程序的运行环境。如果有大佬能解决这个问题,希望可以指点一下。

实战B:A厂摄像头型号b

由于目前不能模拟出mcore架构环境,在A厂的型号a产品上的完美模拟暂时碰壁了,因此我们将目标转向型号b,从网上下载A厂摄像头型号b的升级固件

1、提取分析固件、制作文件系统

由于是同一厂商的不同型号,固件提取和文件系统制作过程都差不多,这里就省略了,直接记录仿真运行中遇到的坑。

2、仿真运行

ubuntu@ubuntu-virtual-machine:~/vexpress/image/$qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel zImage -append "console=ttyAMA0 rootwait rw root=/dev/mmcblk0 init=/linuxrc" -sd carm_sd.ext3

报错:无法创建、解压文件的问题我们可以进入shell后手动操作,提示初始化时文件系统中模块的version magic和当前内核不匹配,因此我们需要编译能加载vermagic为3.18.30 preempt mod_unload ARMv7 p2v8的内核

insmod报错版本不对

解决:下载3.18.30的内核源码,查看include/linux/vermagic.h 得知启用preempt,需要开启CONFIG_PREEMPT配置
Linux内核中启用CONFIG_PREEMPT选项:
make menuconfig生成.config文件,
修改.config,删掉CONFIG_SMP相关配置,启用 CONFIG_PREEMPT=y

vermagic.h

十分幸运修改完成交叉编译好指定版本的ARM架构内核后,基本能成功运行,接下来检查配置网络,测试与宿主机的网络畅通:

基本成功运行

但是通过ps查看进程,没有发现有相应的web服务,netstat命令也没看见有监听的端口,应该是服务没有运行。

ps和netstat查看进程和服务

我们要找到是是谁提供的web服务,看看能不能通过手动启动。思路:在web目录中查看相关文件,看看调用了什么接口,然后全局搜索接口在什么地方出现。

在宿主机上切到web分区目录,开启一个python服务器,模拟一个web服务来提供index页面,通过访问抓包获取登录接口

搜寻web服务程序

image-20211117161043259

然后我们把carm_sd.ext3挂载到宿主机的rootfs目录,在此目录中全局搜索字符串,看看在哪里出现了,通过宿主机器搜索文件相对更快:

ubuntu@ubuntu-virtual-machine:~/vexpress/image$ sudo mount carm_sd.ext3 rootfs/ubuntu@ubuntu-virtual-machine:~/vexpress/image$ grep -r '/RPC2_Login' ./rootfs/匹配到二进制文件 ./rootfs/usr/bin/sonia./rootfs/mnt/web/jsBase/common/rpcBase.js:^Cubuntu@ubuntu-virtual-machine:~/vexpress/image$ file rootfs/usr/bin/soniarootfs/usr/bin/sonia: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.1.1, BuildID[sha1]=06711d7d2dcc3e9bc81e5e416b935420866181be, stripped

把usr/bin/sonia取出来分析一下,找到/RPC2_Login接口对应的代码,确定sonia就是提供web服务的后端功能程序,接下来就可以静态分析上手挖掘了。

RPC2_Login对应后端代码

在仿真环境中单独运行一下sonia,出现了许多错误,虽然ps查看进程在后台运行起来了没有结束,但是还是没有出现web服务。

仿真环境运行一下sonia

不知道为什么出现这些打开文件失败的错误,难道是初始化的时候有些资源没初始化成功?因此回过头,再研究一下启动过程中的初始化操作,一步步手动再执行一遍:

4.1.1的rcS

跟入到/usr/etc/imod脚本,执行第一条安装osa.ko内核模块的时候报错了,还是不能安装这些模块:

imod

insmod报错

看见报错信息是一些熟悉的__stack_chk_guard安全函数,居然报”Unknown symbol“,如果是普通程序找不到这些常见库函数,通过LD_PRELOAD、LD_LIBRARY_PATH这些环境变量指定so库基本可以解决,但现在是内核模块找不到这些符号,内核模块是不能加载普通用户态的so库的;如果在正向开发模块过程中出这个错误可通过编译模块时关闭栈保护来满足内核加载,但现在是内核模块固定,我们需要重新修改内核配置,编译出能加载这种特定的模块才行。前面我们编译特定版本为3.18.30的内核,修改vermagic值等操作都是为了迎合当前的摄像头固件中的模块,现在有这么多符号找不到,难道是加载模块顺序不对?直接去检查了一下摄像头文件系统中的所有ko文件,看看他们的依赖关系:

各内核模块依赖关系

发现他们一家子都在这里了,没有依赖此目录外的其他模块呀。莫非需要慢慢研究重新一条条修改内核配置?能不能分析一下固件中的kernel.img提取其中的配置,或者直接拿它来运行呢?说干就干!

binwalk kernel.img看到,结构是:uImage头+uImage头+zImage内核头+lzo和xz数据+uImage头:

kernel.img

对比一下自己编译的同版本Linux-3.18.30内核,结构是:uImage头+zImage内核头+gzip数据:

uImage.3.18.30

我们知道uImage是zImage添加64字节信息后供uboot识别启动的,仔细研究了一下zImage和uImage格式,看看多的是什么东西:

zImage_uImage-format

尝试从kernel.img中使用dd去掉多余的头,分离出uImage文件来尝试运行,裁剪后结构是:uImage头+zImage内核头+lzo和xz数据:

kernel.img2

使用自己的uboot加载裁剪好的uImage内核,发现还是不能运行。不清楚为什么,就开始用自己编译的同版本内核A同从固件中提取出来的内核B和自己编译的高版本内核C进行熵对比,可以看到头部结构基本相似,只有在固件中提取出来的内核B尾部混乱程度最高,猜测对某些关键部分进行了加密:

熵分析

现在又陷入僵局,无法运行从固件中提取出的内核,也提取不了它的内核配置,很难编译一个一模一样的内核去加载模块,不加载这些模块各种服务就不能起来,就不能动态测试,开始瞎猜:我们手里的固件是升级固件,加载它的可能是被魔改过的uboot,魔改后的uboot完全可以对固件或内核关键部分进行加解密,如果固件的某些部分被特殊处理过,只靠手里下载的升级固件肯定是不能继续下去的了,因此我们必须拿到真实设备中的升级程序才行,要搞清楚如何升级固件并运行的。

好的,找大哥下单买了个摄像头,很快啊,啪的一下就拆开了,看看发生了甚么事:

摄像头flash芯片

看见这个flash芯片是这样焊的,以我手中小拇指大的烙铁和粗糙的技术,如果强行飞线可能会造成无法挽回的损失,保险起见,我在淘宝上买了专业的夹具和对应编程器。

在等待快递的这段时间,尝试修改uboot源码来看看能不能跟入内核,看看魔改后的内核运行到什么程度,是否有特殊自解压操作等。

开启uboot的DEBUG模式,修改include/log.h 添加#define DEBUG 1

开启uboot的debug模式

uboot运行的第一阶段的许多操作在start.S这个汇编代码中实现,主要是对cpu等硬件进行一些初始化,我们不用太关心这个,直接把精力放在第二阶段的arch/arm/lib/bootm.c中,这里有移交控制权给内核操作的代码,比较关键,我们添加两句printf,输出看一下kernel_entry、machid和r2寄存器中的数据和启动参数:

修改bootm.c

可以看见kernel_entry是一个函数指针,来自images->ep;跟入include/image.h看看bootm_headers_t的数据结构,得知images->ep就是内核的入口:

bootm_headers_t结构体

继续看看images中的image_info_t结构体其实就是内核镜像的描述信息;顺便看看旁边mage_header结构体,它就是uImage的结构,上面0x27051956的就是uImage的magic值:

image_info_t结构体

到此各种文件格式基本分析得差不多了,了解了这些结构中一些特殊字段的含义,现在开始编译debug版本的u-boot.debug,用来加载摄像头固件中提取出来diy的kernel.img.uImage.diy内核:

ubuntu@ubuntu-virtual-machine:~/vexpress/image$  qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel u-boot.debug -sd carm_sd.ext3 

kernel.img.uImage.diy

配置好ip地址,使用uboot的tftp功能在宿主机上下载提取出来的内核:

image-20211117145540576

kernel_entry处无法继续

跟到这里,uboot已经把控制权移交给了内核,内核入口地址是0x60000000,执行的是kernel_entry(0, machid, r2);到这里就卡死了,应该是到内核里面去了,uboot再也不打印调试信息了,不知道内核运行到什么程度、是否有特殊自解压操作了。我们对比一下正常的同版本内核加载情况,到了0x60000000内核入口处后直接就能开始运行内核了:

正常的同版本内核加载

现在没法运行魔改的内核,就没法加载定制的各种ko模块,各种服务也就跑不起来,就不是完美模拟摄像头环境,只有等到专业设备到手了试试直接从芯片中读取数据,搞明白究竟咱操作的再来试试。

实战C:B厂摄像头版本a

1、提取分析固件、制作文件系统

使用binwalk -Me ipc_b.bin提取固件得到如下目录:

4.3.1.1binwalk提取的固件

2、分析并制作文件系统

看一下romfs分区中rcS脚本进行了哪些初始化操作,主要看哪些分区挂载到了哪些目录,启动了哪些程序:

b厂a版本摄像头的rcS脚本

根据初始化脚本,创建挂载目录,将对应分区的文件复制到相应文件夹下,然后就可以制作文件系统了;我们还可以修改rcS脚本添加一个shell,将配置网卡、创建目录、解压文件等操作都注释掉,等我们进入shell后手动来执行。最后得到包含摄像头文件的文件系统monitor_sd.ext3

3、仿真运行

qemu-system-arm -M vexpress-a9 -m 512 -sd monitor_sd.ext3 -nographic -net nic -net tap,ifname=tap0,script=no -kernel zImage -dtb device.dtb -append "console=ttyAMA0 rootwait rw root=/dev/mmcblk0 init=/linuxrc"

报错:一些内核模块加载版本不兼容,只需要编译对应能加载这些模块的内核即可;一些目录不存在,我们进入shell后手动添加即可。

4.3.3.1服务成功运行

成功开启telnet服务,尝试与宿主机通讯:

4.3.3.2服务成功运行

从宿主机上看虚拟摄像头环境是否正常启动这些服务,尝试登录:

4.3.3.3与主机通讯

接下来就可以进行静态分析,上手挖掘啦,然后还可以在仿真环境里动态测试,就不用再花小钱钱买真实的摄像头设备了,成功0成本白嫖一套环境。

其他:

遇到过固件程序能被binwalk识别,但是不能提取的情况,这是因为固件被加密了,需要拿到硬件设备,从设备中读取芯片数据,从中找到加密方式才行。

参考文章:

【qemu-vexpress】https://www.qemu.org/docs/master/system/arm/vexpress.html

【基于QEMU搭建完整的虚拟ARM开发环境】https://blog.csdn.net/konga/article/details/79595119

【M.CORE】https://en.wikipedia.org/wiki/M%C2%B7CORE

【M-CORE, microRISC Engine, Programmers Reference Manual】https://web.archive.org/web/20160304090032/

http://www.saladeteletipos.com/pub/SistemasEmbebidos2006/PlacaMotorola/mcore_rm_1.pdf

分享到

参与评论

0 / 200

全部评论 7

Hacking_Hui的头像
学习了
2023-02-01 14:20
tracert的头像
前排学习
2022-09-17 01:28
满戈的头像
内容很详细
2021-12-02 10:30
iot-FL的头像
很详尽,想请教一下,从flash读取固件的时候,如果遇到硬件加密,无法提取固件的时候,有没有什么好的思路
2021-11-20 15:45
有毒的头像
硬件加密的我不是很了解,还是请群里大佬们解答下吧
2021-11-23 09:02
iot-FL的头像
ok,麻烦了
2021-11-24 00:33
观复的头像
硬件加密的情况较少吧,消费级和商业级基本上都是固件加密的。硬件加密受限于🤬能,成本,功耗,稳定🤬,集成难度,适用终端等因素。
2022-01-21 09:14
iot-FL的头像
多谢解答!
2022-04-19 21:41
AJay13的头像
2021-11-20 09:09
Azeng的头像
学废了!
2021-11-18 11:07
八一那个军旗的头像
非常详尽!!
2021-11-18 11:01
投稿
签到
联系我们
关于我们