黑华芸/华硕ASUSTOR ADM嵌入式Linux逆向工程和体验引导工具盘「老骥伏枥-兔年大礼包」

前言

在【牛年大礼包】讲解了威联通NAS系统的动态破解之后,坛友们希望我再写一篇关于华芸NAS系统破解的文章。因此在兔年来临之际,作为又一个嵌入式Linux逆向工程的教学范例。我将再次讲解如何利用逆向工程技术,破解华芸NAS系统的细节并介绍华芸实施反破解的手段。希望大家能更好地学习,研究逆向工程的技术。从中学到有益的东西。

需要强调的是,我不是一个黑客。文章中提到的固件资源均来自于华芸官网公布的系统包和发布的固件。在参考文献中注明了所有引用的出处。本文将深入浅出地介绍如何对原始固件进行分析,提取破解所需的ADM系统固件;破解华芸ADM系统的步骤;以及制作启动工具盘等等。此【兔年大礼包】启动工具盘的理念和启动引导的基础架构设计均绝对是老骥伏枥本人独创,并合法利用开源项目 (Tiny Core Linux Project) [5] 开发的。 也是老骥伏枥通过 NASYUN 论坛在全球独家首发。此工具盘为的是进行教学, 研究和技术交流。 禁止用于任何商业目的。

第一讲 - 华芸官方发布的ADM系统固件初步解析

关于嵌入式Linux逆向工程的常用工具我在“【老骥伏枥-狗年大礼包】嵌入式Linux逆向工程,手把手教你作黑威联通[1]”一文中已经介绍过了。在这里就不赘述了。有兴趣的读者可以参考那篇文章。当时主要介绍了IDA -交互式反汇编工具,但它不是免费的。

近年来又有一款名为Ghidra免费开源逆向工程工具更受欢迎[2]。它是由美国国家安全局 (NSA) 开发的免费开源逆向工程工具。它的一些可执行文件于 2019 年 3 月在 RSA 会议上发布; 一个月后,这些资源在 GitHub 上发布。 许多安全研究人员将 Ghidra 视为 IDA Pro 的竞争对手。 该软件是用 Java 编写的,使用于 GUI 的 Swing 框架。 反编译器组件是用 C++ 编写的。 Ghidra 插件可以用 Java 或 Python 开发(通过 Jython 提供)。Ghidra 的存在最初是在 2017 年 3 月通过维基解密向公众披露的, 但该软件本身在两年后解密并正式发布之前仍然不可用。2019 年 6 月,随着 Ghidra 软件套件的开源发布,Coreboot 开始使用 Ghidra 对固件特定问题进行逆向工程。自 Ghidra 版本10.0 起,Ghidra 才可用作调试器。 Ghidra 的调试器支持通过 WinDbg 调试用户模式 Windows 程序,以及通过 GDB 调试 Linux 程序。更详细的有关Ghidra的介绍超出了本文的范围,有兴趣的读者,可以自行到网上搜索,查阅更详细的资料。

作为教学实践的示范,下面我将一步一步地讲述从下载官方发布的固件开始,以及如何完成固件代码逆向解析。遇到需要补充嵌入式Linux系统结构和相关知识时,会再做详细说明。力求通过本次的教学实践示范,让大家详细了解华芸ADM系统。但我们的目的是教学,研究,不是黑客。禁止利用本教学示范,从事任何商业目的。

首先我们去华芸的官网,选择一个它发布的固件。这次我们选择华芸2022年3月发布的最新版X64_G3_4.0.5.RRS1.img固件。

它的下载地址是:

https://downloadgb.asustor.com/download/adm/X64_G3_4.0.5.RRS1.img

在我写这篇文章的时候,华芸的官网在2022年5月5日又升级了它的固件版本:

https://downloadgb.asustor.com/download/adm/X64_G3_4.0.5.RT42.img

所以我又测试了一下,我的破解方法对上述的两个版本都是适用的。

华芸目前的最新版本是:

Version: ADM 4.0.6.RCR1

Release Date: 2022-12-27

https://downloadgb.asustor.com/download/adm/X64_G3_4.0.6.RCR1.img

我没有测试破解方法对这个版本的适用性,有兴趣的读者可以自行测试。

拿到固件后,首先就要分析华芸官网发布的固件包的格式。通常我会使用binwalk固件分析工具来查一查它,看看有什么线索。

扫描的结果如下:

[#]$ binwalk -v X64_G3_4.0.5. RRS1.img

Scan Time:     2022-03-27 19:11:11

Target File:   X64_G3_4.0.5.RRS1.img

MD5 Checksum:  1278d691a12afaa6e1f3457afba432f9

Signatures:    344

DECIMAL       HEXADECIMAL     DESCRIPTION

--------------------------------------------------------------------------------

0             0x0             Executable script, shebang: "/bin/sh"

449           0x1C1           POSIX tar archive (GNU), owner user name: "s"

从扫描的结果看,这个官方的固件包并没有做任何的加密。它包含一个449字节的可执行script脚本包头,从第450个字节开始直到结束是一个普通的tar包。关于X64_G3_4.0.5.RT42.img的扫描,请读者们自行测试。其结果与X64_G3_4.0.5.RRS1.img是完全一样的。

有了这个振奋人心的扫描结果,接着就可以提取这两个部分,看看它们都内容了。我首先使用如下命令:

head –c 449 X64_G3_4.0.5.RRS1.img > heas_script.sh

提取了这个449字节的可执行script脚本包头,查看了一下认为并无意义。就不赘述了。

接着我再使用如下命令:

tail -c +450 X64_G3_4.0.5.RRS1.img > X64_G3_4.0.5.RRS1.tar

提取了后面的tar包。打开这个包后发现里面一共有如下的九个文件:

-rw-r--r-- 1 132273107 Mar 27 19:16 builtin.tgz

-rw-r--r-- 1        87 Mar 27 19:16 builtin.tgz.cksum

-rw-r--r-- 1       100 Mar 27 19:16 builtin.tgz.md5sum

-rw-r--r-- 1   5257264 Mar 27 06:26 bzImage

-rw-r--r-- 1        80 Mar 27 06:26 bzImage.cksum

-rw-r--r-- 1        96 Mar 27 06:26 bzImage.md5sum

-rw-r--r-- 1  16030368 Mar 27 19:16 initramfs

-rw-r--r-- 1        84 Mar 27 19:16 initramfs.cksum

-rw-r--r-- 1        98 Mar 27 19:16 initramfs.md5sum

从文件名称分析,可以看出只有三个是系统文件,其它的是校验用的文件。其中有两个文件的用途,根据经验是可以确定无疑的。一个是bzImage,另一是initramfs。但现在来判断builtin.tgz这个固件的文件是做什么的,还为时过早。不过从文件的尺寸大小分析,它至少应当是永久性的根文件系统的一部分。

下面让我来解释一下bzImageinitramfs这两个文件:

在linux系统中,vmlinux(vmlinuz)是一个包含linux kernel的静态连结的执行档,档案型态可能是linux接受的执行档格式之一(ELF、COFF或a.out),vmlinuz是一种统称,有两种具体的表现形式zImage和bzImage。bzimage和zImage的区别在于本身的大小,以及加载到内存时的地址不同,zImage在0~640KB,而bzImage则在1M以上的位置。

随着 linux kernel 的成长,核心的内容日益增加超越了原本的限制大小。 bzImage (big zImage) 格式则为了克服此缺点开始发展,利用将核心切割成不连续的记忆体区块来克服大小限制。

bzImage 格式仍然是以zlib 演算法来做压缩,虽然有一些广泛的误解就是因为以bz- 为开头,而让人误以为是使用bzip2 压缩方式(bzip2 套件所带的工具程式通常是以bz- 为开头的,例如bzless, bzcat ...)。bzImage 档案是一个特殊的格式,包含了 bootsect.o + setup.o + misc.o + piggy.o 串接。 piggy.o 包含了一个 gzip 格式的 vmlinux 档案(可以参看 arch/i386/boot/下的 compressed/Makefile piggy.o)

initramfs文件是Linux的初始RAM磁盘文件。初始RAM磁盘(initramfs)是在实际根文件系统可用之前挂载到系统中的一个初始根文件系统[3]。initramfs与内核绑定在一起,并作为内核引导过程的一部分进行加载。内核然后会将这个initramfs文件作为其两阶段引导过程的一部分来加载模块,这样才能稍后使用真正的文件系统,并挂载实际的根文件系统。

initramfs中包含了实现这个目标所需要的目录和可执行程序的最小集合,例如将内核模块加载到内核中所使用的 insmod 工具。在桌面或服务器 Linux 系统中,initramfs是一个临时的文件系统。其生存周期很短,只是作为真实文件系统的一个桥梁。在没有存储设备的嵌入式系统中,initramfs则是永久的根文件系统。

有了这些文件我们就可以做个启动盘试一试了。至少可以建立一个最小系统,作为进一步破解固件的基础。

第二讲 - 启动盘的制作

为了继续破解,我们需要创建一个华芸的启动盘。启动盘接结构和原理与我在【老骥伏枥-鸡年大礼包】[4] 

中创建黑群晖启动盘是一样的, 那篇文章有非常详细的介绍。这里就不赘述了。有兴趣的坛友请参看我的那篇文章。这次我做的华芸启动盘,从原理到风格都与以前的启动盘一致。属于破解系列作品。需要强调的是,这个版本的启动盘必须是UEFI格式的启动盘。否则会增加工作量,需要修改这个本版中对UEFI启动盘的核查。

我创建的华芸启动盘共有三个分区。其中的sd?1分区是 GRUB2引导分区。华芸的ADM系统文件放在sd?2和sd?3分区。另外我还要引进Tinycore Linux[5]做为工具母盘,暂时放在sd?3分区,当完成破解补丁后,可以完全删除Tinycore Linux工具,用第二分区的华芸的ADM系统覆盖掉Tiny Core工具,制成特定机型的华芸破解系统发布盘。其中的?表示linux 的udev设备加载时分配的a-z字符。

为了创建GRUB2引导,需要用一台linux机器。最简单实用的办法是用SystemRescueCD 开源x86_64平台的Linix操作系统[6]。SystemRescueCd是x86_64计算机平台的操作系统,但SystemRescueCD的主要目的是在系统崩溃后修复无法启动或以其他方式损坏的计算机系统。 SystemRescueCd不打算用作永久性操作系统。 它从Live CD,USB闪存驱动器或任何类型的硬盘驱动器运行。 它由FrançoisDupoux领导的团队设计,基于Gentoo Linux发行版。我很喜欢使用这个Linix工具系统。 一张CD盘,不用安装直接启动,就能立即使用。并且包含各种各样的系统救援工具。当然,你可以用任何自己熟悉的方法去创建启动盘。

启动SystemRescueCD后,我将以/dev/sda为例,介绍具体步骤。

首先要挂载/dev/sda1分区到/mnt/boot目录。执行的命令如下:

mount /dev/sda1 /mnt/boot

然后创建GRUB2启动盘。执行的命令如下:

grub2-install --boot-directory=/mnt/boot --efi-directory=/mnt/boot /dev/sda

创建启动盘完成后,还编辑修改grub.cfg文件。指出启动引导的细节。以下是我的启动盘的部分有关细节:

接着需要把固件包中的全部九个文件拷贝到sd?2分区中:

builtin.tgz

builtin.tgz.cksum

builtin.tgz.md5sum

bzImage

bzImage.cksum

bzImage.md5sum

initramfs

initramfs.cksum

initramfs.md5sum

再把tinycore Linux工具包的文件拷贝到sd?3分区中:

在这个分区中,我建立了一个tinycore目录,里面一共有三个文件:

vmlinuz64

core.gz

laojifuli.gz 前两个是tinycore的系统文件,这个laojifuli.gz是我的工具补丁。

破解固件的启动盘就这样制作完成了。马上启动一下试一试吧。

第三讲 - 破解华芸ADM系统的第一颗地雷

用我制作的启动盘启动后,华芸ADM系统并不能直接运行起来,而是陷入不断地重启动死循环中。这是华芸系统反破解的第一颗地雷。

从屏幕上给出的信息看,问题在stormand -startup 运行时会出现错误。并立即重启系统。使用IDA工具做反汇编分析后,于是我决定要先暂时将重启系统的调用代码:

call near __Z13Reboot_Systemv; Reboot_System(void)

stormand这个二进制文件中除去。以便进一步测试和分析。其实,后来的进一步分析证明,完全没有必要修改stormand这个二进制文件。请看这段暂时代码修改前后的对照截图:

1672782573-001

修改后的截图:

1672782597-002

我除去重启系统代码的方法是将call __Z13Reboot_Systemv用五个空操作nop汇编指令代替。

修改后需要将stormand文件放回到initramfs初始RAM文件系统当中。

关于initramfs初始RAM文件系统的解包和打包命令的细节如下:

  1. 解包:(逐行执行下方命令行)

mv initramfs initramfs.xz

unxz initramfs.xz

mkdir ramfs

cd ramfs

cpio -id < ../initramfs
  1. 打包:(逐行执行下方命令行)

cd ramfs

find . | cpio -o --format=newc > ../ramfs

xz ramfs

mv ramfs.xz initramfs

md5sum initramfs > initramfs.md5sum

cksum initramfs > initramfs.cksum

这些都是最基本的处理初始RAM文件系统的命令。小白和菜鸟们请自行到网上搜索学习,这里就不赘述了。

把去除重启系统代码后重新打包的三个文件:

initramfs

initramfs.md5sum

initramfs.cksum

放回到启动盘的第二分区。再次启动试试看,虽然还有错误。但此时系统已经不会再陷入不断地重启动的死循环中了。屏幕截图如下:

1672782658-003

接下来我们需要解决“Try to get the booing device! (-42,0)”这个问题了。

第四讲 - 华芸ADM系统的启动流程分析

为了要解决“Try to get the booing device! (-42,0)”这个问题,就要找到华芸系统是如何查找启动盘的。所以我首先就要分析Probe_Booting_Device函数的流程。因为它在libnasman.so.0.0动态库中,因此分析就必须用到IDA这个工具,对相关的二进制文件进行反汇编的逆向工程。当然也可用Ghidra这个工具,但我比较熟悉IDA这个工具,所有本文还是以IDA作为破解工具。因为反汇编的代码太难解释,因此为了把问题解释清楚,我会使用IDA工具生成的伪码来说明。

让我们来看Probe_Booting_Device函数的伪码:

__int64 __fastcall Probe_Booting_Device(signed int a1, char *a2, signed int a3, char *a4)

{

  char *v4; // r12

  __int64 result; // rax

  int v6; // [rsp+0h] [rbp-1Ch]

 

  if ( a1 <= 9 || !a2 || a3 <= 3 || !a4 )

    return 0xFFFFFFEALL;

  v4 = a4;

  result = Probe_Nas_Pch(&v6);

  if ( (signed int)result >= 0 )

  {

    result = sub_2DB30((unsigned int)a1); => share 内存中获取启动盘位。

    if ( (signed int)result < 0 )

    {

      result = 0xFFFFFFEDLL;

    }

    else

    {

      *(_DWORD *)v4 = '4txe';

      v4[4] = 0;

    }

  }

  return result;

}

这段代码看起来非常简单,它调用了一个内部函数sub_2DB30((unsigned int)a1); 从共享 内存中获取启动盘位。其中还调用了Probe_Nas_Attribute()函数。这个分析过程需要有一定的经验,因为文章的篇幅有限,这里我只讲结论性的结果。

这个“Try to get the booing device! (-42,0)”问题,其实相当复杂。华芸反破解的核心技术手段都包含在解决这个问题的过程中。为了解决这个问题,我们就必须从华芸ADM系统的启动流程开始分析研究才行。

为了清晰起见,本文将仅仅介绍启动时的有关脚本和可执行文件。启动过程中,首先是执行了S11nasmand这个脚本,它启动一个nasmand的驻机deamon程序。其实nasmand这个程序文件很小,它的主要功能性的部分都放在一个名为:libnasman.so..0.0的动态库中。nasmand启动后,该程序要调用一个在libnasman.so.0.0的动态库中的Create_Nas_Summary()的函数,它创建共享内存。但它只是创建,不检查共享内存内容的合法性。这会给破解带来错觉,认为雷区不在这段程序中,而是在产生暴雷程序的地方。但不论如何检查暴雷的程序地方,都很难确定暴雷的原因。而不得不回溯检查,这种设计确实给破解带来不少麻烦。

在它创建共享内存时,首先被调用的函数也是Probe_Nas_Attribute()函数。这个函数非常重要,因此猜想这个函数可能是破解的关键。把它分析清楚了,问题可能就迎刃而解了。

为了本文的叙述方便,我把与破解相关的另外两个函数要先解释一下。

  1. Scan_Pci_Device()函数:

这个函数的功能是,扫描当前硬件的所有PCI设备的信息,并在内存中构建一张表格。不同的机器,这张表格的差距很大。为了让大家能够理解,我给出我在Qemu虚拟机上debug时的一个具体实例来说明:

本次扫描的结果是,该硬件有11个PCI设备,其中彩色的部分是华芸核查硬件时会涉及到的设备。华芸核查硬件都是在程序中用硬编码实施的,因此破解起来比较麻烦。

  1. Find_Pci_Device()函数

上述的彩色部分,是华芸的Find_Pci_Device()函数要检查的。这个检查分两种情况,对CPU PCI Bridge的检查是循环检查CPU是否与硬编码一致。如果不成功,就用Probe_Default_Attribute()函数设定初始值。这个初始值就会造成后续的系统安装时找不到硬盘。对其它设备的检查,是根据硬件的PCI配置情况,决定产品系列的具体型号。但也要能够核查到PCI的配置,如果查不到,也会返回不成功。

再让我们回过头来看Probe_Nas_Attribute()函数的最核心部分的反汇编的伪码:

  if ( (signed int)Probe_CPU_Brief(32, (char *)&v10, 48, (char *)&v11, &v8) < 0

    || (v6 = 768, v1 = Scan_Pci_Device(&v6, &v12), v2 = v1, v9 = v1, (v1 & 0x80000000) != 0)

    || dword_A1240 < 0 )

  {

LABEL_9:

    Probe_Default_Attribute(&v8, a1);

  }

  else

  {

    v3 = (signed int (__fastcall **)(int *, __int64))&unk_A1258; =>根据一张表格的硬编码处理“硬件绑定”

    while ( (signed int)Find_Pci_Device(v3, v2, &v12) <= 0 || v3[1](&v8, a1) < 0 )

    {

      v3 += 5;

      if ( *((_DWORD *)v3 - 6) < 0 )

        goto LABEL_9; =>不能初始化成功,去Probe_Default_Attribute();函数。然后继续后续处理。

      v2 = v9;

    }

  }

注意:其中红颜色的调用某子程序是根据一张表格的硬编码来做“硬件绑定”处理的。

其实这段伪码的翻译是有些问题的,实际的汇编语言中v3蓝色部分是属于while循环之内的,捡查硬编码的“硬件绑定”处理跳转表。关于这段伪码,我再用Ghidra来处理一下,对比着看看,我认为Ghidra的伪码处理的更为清晰。

  iVar2 = Probe_CPU_Brief(0x20,local_380,0x30,local_360,(int *)&local_388);

  if (-1 < iVar2) {

    local_39c = 0x300;

    local_388._4_4_ = Scan_Pci_Device(&local_39c,local_330);

    if ((-1 < local_388._4_4_) && (-1 < DAT_001a1240)) {

      p_Var5 = (_T_PCI_DEVICE_ID_ *)&DAT_001a1258;

      do {

        iVar2 = Find_Pci_Device(p_Var5,local_388._4_4_,local_330);

        if ((0 < iVar2) && (iVar2 = (**(code **)(p_Var5 + 8))(&local_388), -1 < iVar2)) =>根据一张表格的硬编码处理“硬件绑定”

        goto LAB_0012da86; =>初始化成功,继续后续处理。

        piVar1 = (int *)(p_Var5 + 0x10);

        p_Var5 = p_Var5 + 0x28;

      } while (-1 < *piVar1);

    }

  }

  Probe_Default_Attribute((_T_SYSTEM_DEVICES_ *)&local_388,param_1); =>不能初始化成功,调用Probe_Default_Attribute();函数。然后继续后续处理。

LAB_0012da86:

  ...

可以清晰地看出,核查硬件绑定关系表的处理程序是在循环内部,不断地循环检查,直至结束。但这段伪码也有毛病,它不能正确反映出这段查表程序的入口参数。

逆向工程工具给出的伪码只是为了帮助我们分析程序的参考。看伪码要结合原始的反汇编代码一起分析。这需要有一定的经验积累才能得心应手。伪码参考也让写文章解释起来比较方便易懂,但不能追究伪码的语法细则。

第五讲 - 破解 “硬件绑定”初始化程序对应的跳转表

继续研究上一讲中反汇编的伪码的:v3 = (signed int (__fastcall **)(int *, __int64))&unk_A1258;(IDA)或者iVar2 = (**(code **)(p_Var5 + 8))(&local_388);(Ghidra) 。其实这段跳转调用子程序有两个入口参数。第一个参数param1是指向CPU厂家和名字的一个数组。第二个参数param2是指向共享内存的指针。跳转表对应的每一段子程序,将对应于某一个华芸硬件系列的类型,用于处理“硬件绑定”关系的硬编码就在每一段程序中。

华芸的X64_G3_4.0.5.RRS1.img固件“硬件绑定”初始化程序的跳转表一共可以支持九种类型的系列NAS系统。为了清晰起见,我把他们编号为index=0至8。以及它所对应的CPU型号。以后我在编写patch_libnasmen补丁程序时,也会使用相同的编号。

=================================================================

index=0: (AS6 Series) Atom(TM):"D27","D25"

index=1: (AS70 Series) Intel Core i3 dual-core"

index=2: (AS50,AS51 Series) Celeron:"J1800","J1900"

index=3: (AS61,AS62 Series) Celeron:"N30","J30","N31","J31";Pentium:"J37","N37"

index=4: (AS63,AS64 Series) Celeron:"J33","N30","J34"

index=5: (AS52,AS53 Series) Celeron:"J4005","J4025","J4105","J4125"

index=6: (AS65 Series) Atom:"C3538"

index=7: (AS71 Series Gen2) Intel:"Core(TM)","Xeon(R)"

index=8: (AS66,AS67 Series Gen2) Celeron: "N5105"

=================================================================

查表的过程为:首先去调用Find_Pci_Device()函数,获取一个index的值。然后根据index的值调用相应的处理子程序,把设定与硬件相关各个参数写到共享内存中。其中最关键的是确定index的值,根据这个值,提取硬编码的CPU信息,再与上一讲提到的Scan_Pci_Device()函数创建的当前硬件的所有PCI设备信息的表格去比较。如果找到一致的硬件信息,就确定了产品的硬件系列。然后再根据PCI设备数量,品牌,厂家等等综合信息,确定该系列中的各种具体型号。生成硬件绑定关系的有关参数,并将它们写入到共享内存中。

因此,我们需要修改由index值指定的硬编码的CPU信息,就可以破解全系列的华芸ADM系列了。这个信息就是CPU PCI Bridge的信息。它一般由六个字节的信息硬编码组成:( vendor + device + subsystem_device)。现在的关键问题是,这些信息的硬编码在libnasman.so.0.0动态库中的什么地方呢?其实是很容易找到的。你可以使用IDA或者Ghidra去找。但为了给那些不会使用IDA或者Ghidra的读者提供便利。我给大家列出如下的这些硬编码信息在libnasman.so.0.0动态库的定位地址的截图:

1672783092-004

用二进制编辑器打开libnasman.so.0.0动态库,其中的红色部分即硬编码信息的位置。最上边的是index=0,最下边的是index=8。那么如何修改呢?以我在第四讲中列出的Scan_Pci_Device()函数在Qemu虚拟机上扫描结果中的CPU PCI Bridge为例:

00 00 01 00 01 06 86 80 00 70 00 11  <= CPU PCI Bridge

你可以看到该结果的CPU PCI Bridge值是“86 80 00 70 00 11”。例如:如果你想做一个Qemu虚拟机上的index=7: (AS71 Series NAS Gen2)的破解,就用上述的值替换掉index=7位置的“86 80 09 A3 70 72”就可以了啊。这是主要的破解第一步。让CPU PCI Bridge值与硬编码匹配一致。

当然,做到这一步还没有完全破解完成。Find_Pci_Device()函数还要做其它设备的检查,也是根据硬件的PCI配置情况,决定产品系列的具体型号。这将对应另一部分的硬编码信息表。它一般由四个字节的信息硬编码组成:( vendor + device)。我列出如下定位的截图:

1672783187-005

这部分是对应我第四讲中列出的Scan_Pci_Device()函数的扫描结果中的其它设备:

00 00 01 01 01 01 86 80 10 70 00 11  <= IDE controller

00 00 03 00 00 02 86 80 0E 10 00 11  <= Network Adapter

00 00 05 00 06 01 86 80 22 29 00 11  <= SATA controller

这些也必须与你当前的硬件匹配一致才行。上图中的红色部分也必需全部替换。方法与上文一样,就不赘述了。

还有就是除了index=1和index=7这两个跳转表不需要检查核对由第一个参数param1是指向CPU厂家和名称的一个数组外,其它的跳转表index的值都要检查核对CPU的厂家和名称。这部分相关的硬编码也要与你当前的硬件一致。如果你嫌改造太麻烦,可以选择index=1和index=7这两个系列。就不用修改libnasman.so.0.0动态库中的有关代码了。

至此,破解 “硬件绑定”初始化程序对应的跳转表的工作就基本上全部完成了。但还有产品系列号和USB启动盘位的问题,需要再进一步仔细推敲。

第六讲 - 关于华芸产品系列号与禁止重启动死循环的补丁

华芸产品系列号是存放在硬件的I2C中,由一个专门Scan_I2C_Device函数来查找,找到后再读取。如果找不到,虽然能启动成功,但仍不能完成继续安装系统。这个函数也再libnasman.so.0.0动态库中。

为了能方便地自定义系列号,我打算在grub.cfg文件启动时向内核的/proc/cmdline传递一个有关系列号的参数:sn=4660 (这个数字可以从1到65000)。但为了能够解析这个参数,就要对Scan_I2C_Device函数进行大规模的改造。这种改造对使用反汇编语言进行二进制文件的改造就非常困难了。怎么办呢?这就要用逆向工程中常常利用的所谓动态库劫持技术了。下面我先简单地介绍一下这个技术。

关于Linux的所谓动态库劫持技术网上有很多介绍,我的介绍是根据维基自由的百科全书的资料[7]

Liunx的动态链接器是操作系统的一部分,它在可执行文件执行时(在“运行时”)加载和链接可执行文件所需的动态共享库,通过将库的内容从持久存储复制到 RAM,填充跳转 表和重定位指针。 特定的操作系统和可执行格式决定了动态链接器的功能和实现方式。链接通常被称为在编译可执行文件时执行的进程,而动态链接器是操作系统的特殊部分,它将外部动态共享库加载到正在运行的进程中,然后将这些动态共享库动态绑定到正在运行的进程。 这种方法也称为动态链接或后期链接。

ld.so是Unix或类Unix系统上的动态链接器,针对ELF文件格式。当应用程序需要使用动态链接库里的函数时,由ld.so负责加载。搜索动态链接库的顺序依此是:

  • 环境变量LD_AOUT_LIBRARY_PATH(dbg版本)、LD_LIBRARY_PATH(release版本);在Linux中,LD_PRELOAD指定的库具有最高优先权。所谓的动态库劫持技术就是利用LD_PRELOAD的最高优先权。将一个自己的动态库中的同名函数,替换掉其它动态库中的函数。
  • 缓存文件/etc/ld.so.cache。此为上述环境变量指定目录的二进制索引文件。更新缓存的命令是ldconfig
  • 默认目录,先在/lib中寻找,再到/usr/lib中寻找。

为了替换掉libnasman.so.0.0中的Scan_I2C_Device函数,当然不仅仅是Scan_I2C_Device函数,我还要替换掉重启动函数。我将创建一个名为laojifuli.so的动态库。其实这个库的laojifuli.c源代码非常简单。仅仅几行代码而已。

编译命令如下:

gcc -Wall -fPIC -shared -o laojifuli.so laojifuli.c

有了这个补丁,第三讲中的破解华芸ADM系统的第一颗地雷对stormand这个二进制文件的改造就没有必要了。实际上的破解仅仅需要改造libnasman.so.0.0一个文件,再加上这个laojifuli.so的动态库就可以了。

但为了让laojifuli.so动态库生效,需要添加动态库劫持补丁到系统。方法是修改启动脚本 S11nasmandS21stormand。然后,还需要拷贝laojifuili.so文件到文件到解包目录的/usr/lib/laojifuili.so

修改解包目录的/etc/init.d/S11nasmand脚本:(仅仅给出部分相关内容)

修改解包目录的/etc/init.d/S21stormand脚本:(仅仅给出部分相关内容)

对于以上的这些修改,我会使用与【老骥伏枥-牛年大礼包】[8]相同的方法。将上述的四个文件放在patch目录下:

patch/usr/lib/laojifuli.so

patch/usr/lib/libnasman.so.0.0

patch/etc/init.d/S11nasmand

patch/etc/init.d/S21stormand

然后使用如下打包命令:

cd patch

find . | cpio -o --format=newc | gzip > ../patch.gz

把它们打包成一个patch.gz补丁文件,再把补丁包拷贝到启动盘的第二分区。然后编辑grub.cfg文件,加载patch.gz启动补丁。

以上是手工破解的全过程。当然如果要制作成像牛年大礼包那样的启动工具盘,还需要几个文件:

patch_libnasman

patch_install

patch_extract

这些文件除了patch_libnasman之外,牛年大礼包中都有可用的样本。制作patch_libnasman的目的是把上述手工修改libnasman.so.0.0二进制文件的过程,改为程序自动修改就可以了。这里就不赘述了。

第七讲 - 关于USB启动盘位的补丁

华芸的ADM系统的硬件的绑定是非常严格的。对数据盘的盘位,启动盘的盘位都有严格的硬编码规定。我在分析破解的时候,发现了libnasman.so.0.0动态库中有一个非常有用的Dump_Nas-Info()函数。我猜想华芸的技术人员也会使用它来调试固件与硬件的对应关系。但使用这个函数的工具华芸的ADM系统并没有发布提供给用户使用。其实用户也没有这个需要,但我们破解它的系统,这个工具就非常重要了。于是我编写了一个调用该函数的工具,用于查看系统的硬件配置问题。例如:我要配置一台蜗牛矿渣机黑华芸AS7112R,使用这个工具就可用得到如下信息:

===Dump_Nas_Info===

Nas property: (total [180] bytes)

  Model Name: [AS7112R], PCH: [7], Series: [71], SubModel: [1], Chassis: [1], Reversion: [1], Bus: [0x190]

  FW ver: [4.0.5.RRS1], Ability: [0x706], HP: [1], S3: [1], KLS: [1], LCM: [0], MA: [1], OTB: [0], WW: [0]

  Host ID: [52-54-00-7E-45-F1], Serial No: [AX1108014IC1234], Max: [50/41]

  Lan [1], Netif [3], Order: eth0,

  Usb Order [6]: 0x110/210, 0x120/220, 0x160/240, 0x150/230, 0x180/260, 0x170/250,

  Sata Order [12]: 27, 49, 50, 2, 26, 48, 51, 3, 25, 24, 1, 0,

  ESata Order [0]:

  M.2 Order [2]: 253/d, 255/c,

其中的PCH: [7]表示我在第五讲中的index=7。

其中的 Usb Order [6] 表示USB启动盘的盘位必须在给定的六种位置,否则不予接受。

例如我的蜗牛矿渣机的USB硬件接口列表如下:

[ 6118.042255] usb 1-2.3: new high-speed USB device number 7 using xhci_hcd (A款机箱左前USB口)

[ 6601.307307] usb 1-2.2: new high-speed USB device number 9 using xhci_hcd (A款机箱右前USB口)

[ 6706.240317] usb 1-4: new high-speed USB device number 10 using xhci_hcd (A款机箱左后上方)

[ 6925.795334] usb 1-1: new high-speed USB device number 11 using xhci_hcd (A款机箱左后下方,支持usb3.0)

[ 7101.987358] usb 1-2.1: new high-speed USB device number 12 using xhci_hcd (A款机箱右后上方)

[ 7277.858371] usb 1-3: new high-speed USB device number 13 using xhci_hcd (A款机箱右后下方)

翻译成华芸的编码就是:0x123, 0x122, 0x140,0x110,0x121,0x130 只有一个接口能够对上。因此USB启动盘必须在该盘位才能正常启动。就是支持usb3.0的那个USB端口。

另外,数据盘的盘位也必须在规定的位置才能有效。还好蜗牛矿渣机中有2号盘位和3号盘都可用。但它对应的是华芸AS7112R的4号盘位和8号盘位。因此插在蜗牛矿渣机的数据盘反映在华芸AS7112R系统中只能是4号盘位和8号盘位。但如果把硬盘插在蜗牛矿渣机的4号和5号盘位,就会出现找不到硬盘的问题了。虽然蜗牛矿渣机可用插四块数据盘,但只有两块是可用位置。如果四块数据盘都想用怎么办呢?那就只能再次对libnasman.so.0.0动态库进行补充修改。改造成由Dump_Nas_Info查出来的盘位信息才行。

那么这些信息在哪里呢?当然都在第五讲中反汇编的伪码的:v3 = (signed int (__fastcall **)(int *, __int64))&unk_A1258;IDA或者iVar2 = (**(code **)(p_Var5 + 8))(&local_388)(Ghidra) 这段跳转表调用的子程序中。这里我就不细说了。留给有兴趣的读者自行分析研究。

但我还是要详细讲解一下如何去掉对USB启动盘位的限制。以使破解版的启动盘为更为灵活。

这个限制在libnasman.so.0.0动态库的Probe_Usb_Disk_Path()函数中。其相关部分的伪码如下:

 if ( strstr(v14, "/host")

        && strstr(v15, "/block/sd")

        && sscanf(v15, "/usb%*[0-9]/%d-%d/%*[0-9]-%*[0-9].%d", &v21, &v22, &v23) > 1

        && v23 + 16 * v22 + (v21 << 8) == v18 )  <= v21;v22;v23三个参数组合的USB端口地址一定要对应输入参数v18的值才行。

  {

       break;

  }

因此我要去掉这段检查。当然修改不能在伪码中进行,必须修改反汇编的代码。其实就是一个条件跳转指令的改造。截图如下:

1672783540-006

改造后再生成伪码看一看:

if ( strstr(v14, "/host")

    && strstr(v15, "/block/sd")

    && sscanf(v15, "/usb%*[0-9]/%d-%d/%*[0-9]-%*[0-9].%d", &v20, &v21, &v22) > 1 )

{

    break;

}

可以看到,限制已经被去掉了。这个补丁当然是可有可无的。但这样的破解使用起来会更加灵活好用。还有就是华芸ADM系统的这个版本,必须使用USB启动盘。如果再对这个函数更进一步的改造,可以让它不限制在USB盘,而是任何硬盘都可以启动。再看伪码:

v14 = strstr(buf, "/usb");  <= 如果取消对USB的检查限制,

v15 = v14;

if ( v14 )  <= 消对这个if语句就可以了。

{

    if ( strstr(v14, "/host")

        && strstr(v15, "/block/sd")

        && sscanf(v15, "/usb%*[0-9]/%d-%d/%*[0-9]-%*[0-9].%d", &v20, &v21, &v22) > 1 )

    {

        break;

    }

}

这个改造也不难。取消上述的红色部分即可。具体修改反汇编的代码做法就不再赘述了。

第八讲 - 体验工具盘使用与实践范例

为了便于大家学习掌握嵌入式linux逆向工程知识,【兔年大礼包】将为大家提供一个测试华芸ADM系统破解的工具盘。此次的工具盘以vmdk格式提供。该盘中不包含任何原厂的固件代码。读者需要自行去华芸的官网下载相应的固件。该盘的设计理念,系统结构,所包含的程序代码均为老骥伏枥本人独家原创已及合法使用开源的TinyCore系统。全部是拥有自主知识产权的本人作品。请玩家们遵守 GNU General Public License, 未经授权禁止用于商业用途。 该演示用工具盘仅仅为的是进行教学, 研究和技术交流。 禁止用于任何商业目的。

我要说明的是这次为大家提供的测试华芸ADM系统破解的工具盘。并不是上述讲解的X64_G3_4.0.5.RRS1.img版本的手工破解过程的测试盘。而是另一个华芸系列AS-304T的启动盘。

这个启动盘经过部分坛友们的测试,证明可以在虚拟机和蜗牛矿渣机B款上安装成功。作为体验华芸ADM系统破解的逆向工程,哪个系列的版本并不重要,主要是让大家体验一下破解的逆向工程。这个版本也是当前华芸发布的最新版。因为上述介绍的是华芸全系列的破解,体验盘则是仅仅给出了一个系列的破解而已。发布全系列破解的启动盘,对该产品的影响比较大,所有只发布了一个系列让小白和菜鸟们体验一下。如果读者有兴趣做华芸全系列的破解,可以按照本文的详细论述,自行研究,学习逆向工程破解技术。在此老骥伏枥要郑重声明:撰写本文的目的是为了探索,研究,学习嵌入式linux逆向工程技术。禁止利用本文提供的技术和使用本教程从事任何非法商业牟利性活动。

第九讲 - 华芸ADM系统数据盘结构分析

安装完成华芸NAS后,华芸ADM系统数据盘结构是什么样子的呢?让我们来看看它的结构。

1672783664-007

华芸NAS的数据盘全部是gpt格式的四个分区配置。

/dev/sda1:是Linux文件系统"ext4"的分区,尺寸是255M。在这个分区中会复制启动盘第二分区的全部内容。作为ADM系统的一个备份。每一个数据盘上都会有这样一个备份。

/dev/sda2: 是Linux的"linux_raid_member"分区,尺寸是2G。这个分区会组装成/dev/md0,文件系统为"ext4"。启动后,它将挂载为/volume0,里面的内容是解压后的builtin.tgz。实际上就是全部的ADM的根文件系统。第一讲中我们就提到过builtin.tgz这个固件的文件。但没有仔细分析它的内容。

/dev/sda3: 也是Linux的"linux_raid_member"分区,尺寸是2G。这个分区会组装成/dev/md126做为"swap"分区使用。

/dev/sda4: 也是Linux的"linux_raid_member"分区,尺寸是硬盘的所有剩余部分。这个分区会组装成/dev/md1,文件系统可以选择为"ext4"或"btrfs"。启动后会挂载为/volume1,如果选择Single设置数据盘,多个数据盘时,还会挂载为/volume2…3…等等。用户的数据全部都会放在这个分区中。如果NAS系统崩溃了,需要恢复自己的数据时,只有处理这个分区的数据就可以了。

华芸NAS的数据盘的结构设计的非常简洁。因此,管理起来也就非常容易。不会占用很多的系统资源。所以数据处理的速度会比较快。

结束语

 很高兴能与大家分享此次的成果。本教程中详细讲解了破解和启动盘的制作过程中各种细节技术详情。

此次的【兔年大礼包】体验盘则是仅仅给出了AS-304T一个系列的破解而已。这个版本也是当前华芸发布的AS-304T系列的最新版。

文章的最后,老骥伏枥要再次郑重声明:撰写本文与制作【兔年大礼包】体验盘盘的目的是为了探索,研究,学习嵌入式linux逆向工程技术。禁止利用本文提供的技术和使用本教程的工具盘从事任何非法商业牟利性活动。

参考文献:

[1]【老骥伏枥-狗年大礼包】嵌入式linux逆向工程,手把手教你作黑威联通

[2] Ghidra免费开源逆向工程工具

[3]  Linux初始RAM磁盘(initrd)概述

[4] 【老骥伏枥-鸡年大礼包】黑群晖6.02-8451版硬盘自启动,USB启动盘,系统救援盘

[5] Tiny Core Linux -维基百科,自由的百科全书 

[6] SystemRescueCD 

[7] Dynamic linker – Wikipedia  

[8] 【老骥伏枥-牛年大礼包】黑威联通在线升级的若干问题

此文绝对是本人【原创】。如果你对本文的观点有不同意见,可以回帖喷我,本人从不固步自封,固持己见。

温馨提示:本文最后更新于2023-01-14 17:33,因系统或软件版本更新,可能会导致文章内容或资源失效,如有发现请在下方留言或联系社长Mojelly,我们将及时进行更新修复!
本文标题:黑华芸/华硕ASUSTOR ADM嵌入式Linux逆向工程和体验引导工具盘
原文链接:https://www.naslab.club/193.html
© 版权声明
THE END
如果文章对您有帮助可以分享或打赏作者
点赞5 分享
laojifuli的头像-NAS研玩社超级会员
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容