前言
就是为了学这个事情所以去学习了linux底层机制,包括CPU加电直到第一个main函数的过程
在知晓其机制以前,刚开始是对着镜像文件硬啃,但由于不知道镜像自解压的逻辑,也由于有一部分操作是操作系统参与的,所以当然不会出任何结果,
后面花了一周多去学习(外加一些学校的任务),理清关系,然后这周回到逆向过程,就能很快搞懂整个过程了,其实还挺简单的
因为某些原因只能省略细节,没关系,直接看思路即可
镜像文件结构
根目录
│ bootargs.cfg
│ bzImage
│ diskmap
│ initramfs.img
│ initrd.gz
│ memtest
│
├─dtb
│ *.dtb 若干
│ u-boot-*.dtb 若干
│
├─grub
│ .gdb_history
│ grub.cfg等文件
│
└─initrd_data
│ dec_initrd等文件
│
└─mount
知晓bzImage机制前的探索
Findcrypt启动找到如下密码算法,但是很奇怪:这些算法最终的引用都没有指向?
查看引用,也没有引用该文件的地方,怎么回事呢
实际上这是加解密的库被包装在镜像里面,而且该固件的解密,后来确实并没有使用这几个被检测到的算法,但是当时在这里绕了一阵子圈圈,怀疑过是IDA版本不够高等问题
于是询问师傅,师傅说学习一下initrd那些东西你就懂了,于是网上搜索了bzImage这个关键词,首先看到的是这样一张图
恍然明白了,原来这么长条的灰色(7.7是灰色条,8.3是绿色条),是被压缩的vmlinux.bin空间呀
知晓bzImage机制后
于是课下花去了大概一周左右的时间,断断续续把从CPU加电到整个系统初始化完成的机制都学习了,其中就包括bzImage的解压为vmlinux的过程,再回来看这个固件的解密
这是我在学习的时候画的图,可能有细节问题请见谅,详见本站的文章:
解压bzImage为vmlinux
bzImage可以使用https://blog.csdn.net/qq_40421991/article/details/111241980来解密
当然可以直接使用虚拟机上的
/usr/src/linux-headers-6.5.0-26-generic/scripts/extract-vmlinux
然后就导出来一个文件,拖IDA分析,此时整个bzImage解压后的状态就能分析了、
分析vmlinux
根据这一篇文章https://www.iotsec-zone.com/article/218
可以参照linux/init /initramfs.c后面populate_rootfs前后逻辑来分析其解密逻辑。
发现大量decrypt字样,以及,rc解密算法
但是这是没有引用的,内核上的那种调用关系,ida分析不出来
当然非常幸运的看到了rc4函数,直接查看引用,发现作者是改写了populate_rootfs的代码,直接让其自身具备rc4解密能力
所以跟进到populate_rootfs:
发现这个解压的函数实在是太长了,如图所示:
解密后的结果存放到/initrd.image
接下来有两个方案:
1.跑起来,让这个bzImage自行解压出来(具备一定普适性)
2.直接写破解脚本(麻烦,暂时可以不这么做)
那在这里就直接尝试跑起来这个镜像先吧
镜像加载
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=1024M \
-cpu cortex-a72 \
-smp 4 \
-kernel bzImage \
-initrd initrd.gz \
--append "console=ttyAMA0 rdinit=/linuxrc"
这样直接启动却报错了,仔细观察报错:
按道理,这里执行是没问题的
但是到后面就是不行,显示挂载不到rootfs
Please append a correct "root=" boot option; here are the available partitions
原来,是qemu启动参数里append中root指定有问题
参考grub.cfg或者boot.cfg设置参数即可
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=4096M \
-cpu cortex-a72 \
-smp 4 \
-kernel bzImage \
-initrd initrd.gz \
--append "console=ttyAMA0,115200 hugepagesz=2M hugepages=5120 ramdisk_size=60000 libata.dma=5 root=/dev/ram0 rw bonding.miimon=100 bonding.mode=active-backup pci=noaer pcie_aspm=off earlycon=uart8250,mmio32,0x28001000 rdinit=/linuxrc" \
-dtb ./dtb/u-boot-general.dtb
不过,并没有这么简单,这个固件体积可不小啊,内存不够大
师傅:应该是16g,但可以把pagesize改小点,2g也能跑
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=12416M \
-cpu cortex-a72 \
-smp 4 \
-kernel bzImage \
-initrd initrd.gz \
--append "console=ttyAMA0,115200 hugepagesz=1M hugepages=1M ramdisk_size=60000 libata.dma=5 root=/dev/ram0 rw bonding.miimon=100 bonding.mode=active-backup pci=noaer pcie_aspm=off earlycon=uart8250,mmio32,0x28001000 rdinit=/linuxrc" \
-dtb ./dtb/u-boot-general.dtb
确实如此,可以跑起来了,终于
此时虽然确实可以进系统,但是有这个字样,一度以为实在不行了,问了一下师傅,他说是没有磁盘啊,但是qemu是可以无视这个条件的。如果想进shell,可以加init=/bin/bash启动参数就ok
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=12416M \
-cpu cortex-a72 \
-smp 4 \
-kernel bzImage \
-initrd initrd.gz \
--append "console=ttyAMA0,115200 hugepagesz=1M hugepages=1M ramdisk_size=60000 libata.dma=5 root=/dev/ram0 rw bonding.miimon=100 bonding.mode=active-backup pci=noaer pcie_aspm=off earlycon=uart8250,mmio32,0x28001000 rdinit=/linuxrc init=/bin/bash" \
-dtb ./dtb/u-boot-general.dtb
文件系统导出
看起来/bin/decrypt就是核心的解密程序,跟进就行
有个问题:
这个RC4加密,密钥是什么,加密了什么,怎么导出解密的内容,实际上这里就是翻找各种启动脚本,以及看看它们引用了什么二进制文件,然后就推断出来个大概的样子
主要的启动逻辑在这:DecryptAndMount
好的,谜解开了,其实是因为initrd_data这个文件夹里面的内容是动调内核然后在populate_rootfs下断点然后dump内存解压出来的
QEMU仿真指令
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=12416M \
-cpu cortex-a72 \
-smp 4 \
-kernel bzImage \
-initrd initrd.gz \
--append "console=ttyAMA0,115200 hugepagesz=1M hugepages=1M ramdisk_size=60000 libata.dma=5 root=/dev/ram0 rw bonding.miimon=100 bonding.mode=active-backup pci=noaer pcie_aspm=off earlycon=uart8250,mmio32,0x28001000 rdinit=/linuxrc init=/bin/bash" \
-dtb ./dtb/u-boot-general.dtb \
-virtfs local,path=/mnt/shared,mount_tag=host0,security_model=passthrough,id=host0
Shell上挂载
mkdir -p /mnt/shared sudo mount -t 9p -o trans=virtio,version=9p2000.L host0 /mnt/shared sudo mount -t nfs host0:/ /mnt/shared
如果我们希望能够直接导出文件系统,那么有两个办法,其一是
其一比较麻烦,配置网卡让虚拟机和本机产生网络连接,然后就可以通过scp之类的指令传文件,首先可预先了解一下TAP/TUN是怎么回事,巧了刚好朋友今天分享了相关知识,所以立马理解了。听起来是个很不错的方法,于是跟着
但我认为这个方法本身没有任何问题,倒是不太适合这种,如果是uart串口会不会就适用呢
其二则可以让qemu虚拟机mount某个镜像(建议先了解一下镜像和mount的那些知识,不然搞不明白),然后在qemu跑起来的shell里面操作,把整个文件系统都放进镜像里面,之后直接分析镜像即可,听起来非常靠谱,因为即使固件再阉割,也是需要mount的吧,不可能把这个功能也给阉割了
刚开始试了下qemu使用这个来挂载镜像,失败
-drive format=raw,file=share.img
然后发现/dev/下仍然没有任何新记录
卡了挺久的,试了好多个都不行,再抱着不怕失败的心态尝试一下这个,感谢作者,终于可以了
接下来详细讲讲怎么导出来文件系统的,首先是宿主机:
还是先创建一个镜像
dd if=/dev/zero of=share.img bs=1M count=2048
ext4格式化镜像
mkfs.ext4 share.img
再在宿主机创建一个空间/tmp/share,提供给镜像挂载,
还有先别把这个镜像挂到这个空间上面,因为宿主机和qemu虚拟机同时挂载镜像以后,并不能够同步,重新挂载才能得到最新的文件
mkdir /tmp/share
//先别sudo mount -o loop $PWD/share.img /tmp/share
然后就是qemu跑起来。我的append是根据配置文件来配置的,其实与镜像挂载无关,真正有关的是最后一行
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=12416M \
-cpu cortex-a72 \
-smp 4 \
-kernel bzImage \
-initrd initrd.gz \
--append "console=ttyAMA0,115200 hugepagesz=1M hugepages=1M ramdisk_size=60000 libata.dma=5 root=/dev/ram0 rw bonding.miimon=100 bonding.mode=active-backup pci=noaer pcie_aspm=off earlycon=uart8250,mmio32,0x28001000 rdinit=/linuxrc init=/bin/bash" \
-drive file=$PWD/share.img,if=virtio
然后进shell第一件事是,看看/dev/有没有多东西?有,太好了
多的是/dev/vda,将它挂载到qemu虚拟机里面某个文件夹,
mount -t ext4 /dev/vda /mnt/
至此,这个镜像挂载上了qemu虚拟机里面的/mnt/
这时候,就可以放文件进去了,我们需要打包整个文件系统,以支撑下一步的漏洞研究。
在qemu shell里执行:
tar -czvf rootfs.tar.gz / cp rootfs.tar.gz /mnt/
现在可以退出qemu虚拟机了,回到宿主机,对镜像进行挂载
sudo mount -o loop $PWD/share.img /tmp/share
然后就可以去/tmp/share/找到我们需要从qemu shell导出来的文件了
自此,bzIamge->解密->解压文件系统到内存->qemu shell->导出固件文件系统,这个解密固件的流程是走完了,后面就可以进行进一步的安全研究啦
代码颜色变透明啦,可以调一下哈
噢噢好的,谢谢stone师傅提醒,晚点看看是怎么回事0.0