查看原文
其他

对 Parrot SkyController 无人机固件的逆向工程

2017-03-02 Linso 看雪学院

介 绍

ParrotSkyController 是远距离飞行的无人机,有时出售给房地产行业用来3D建模工作。它集成了智能手机和平板电脑客户端,通过 Wifi 连接进行飞行控制,允许从无人机传输实时数据到控制端。换句话说,SkyController 充当飞行器控制软件(智能手机/平板电脑)和实际无人机之间的中间人。在这篇文章中,我们将了解如何逆向 SkyController 的固件,以便能够查看内部操作系统的内容并了解其内部工作原理。

内 容

Parrot Skycontroller 的固件似乎是一个定义明确的结构,可以在该公司的多个产品中重复使用。固件包含2种类型的结构头和一个序列的“数据项”。头文件位于文件的开头,后面是包含配置和文件系统信息的多个条目。我们将在下文讨论这两种结构。

固件头以四个 magic 字节开始,输出“ PLF!“。因此,这些可用于标识有效的Parrot 固件文件。下面包含一个Python代码段作为示例

def is_valid_parrot_file(self, _file):      magic = None      with open(_file, “rb”) as f:          magic = struct.unpack("4s", f.read(4))[0]      return magic == b'PLF!'

紧接在 magic 字节之后,十三个32位无符号整数的序列提供关于固件的附加信息。定义为一个 ·C 文件,头文件如下:

typedef struct sPLFFile {      u32 dwMagic;      u32 dwHdrVersion;      u32 dwHeaderSize;      u32 dwEntryHeaderSize;      u32 uk_0x10;      u32 uk_0x14;      u32 uk_0x18;      u32 uk_0x1C;      u32 uk_0x20;      u32 dwVersionMajor;      u32 dwVersionMinor;      u32 dwVersionRevision;      u32 uk_0x30;      u32 dwFileSize; }

头文件指定用于固件头的格式类型。在 skycontroller 固件中,该字段的值为“0x0d”,可能说明头文件有14种变化。头文件大小提供包括在报头中的字节数,而条目头文件大小指定固件内的条目的报头中的字节数。接下来的5个整数是未定义的,和前面的最后一个整数一样。其中之一可能是 CRC32 校验和。dwVersionMajordwVersionMinor 和 dwVersionRevision 定义固件的版本。最后,头包含文件的大小(以字节为单位)。

条 目

每个条目都有一个恒定大小的头,它在固件头中由 dwEntrySize 值指定。在 SkyController 中,每个条目由20个字节组成,使用如下:

typedef struct sPLFEntryTag {     u32 dwSectionType;     u32 dwEntrySize;     u32 dwCRC32;     u32 uk_0x0C;     u32 dwUncompressedSize; }

字节类型指定条目中包含的数据类型。这些数据可以是配置数据,分区详细信息或者文件信息。这些将在下面的文字中进一步详细解释。条目大小包含条目内的字节数,不考虑空字节填充。此字段后跟一个 CRC32 值。目前还不清楚这个值是如何计算的。最后已知的整数是未压缩的数据的大小。如果此值为0,则不压缩内容。每种类型的条目具有定义的另外的字段,并且这些都将在下文进行解释。


固 件 条 目

固件中有多种类型的条目。在本节中,我们将描述我们在我们分析的文件中遇到的一个。

引导加载程序和引导配置(0X03,0X07)

条目0x03包含一个二进制文件,它似乎是基于包含在其中的字符串的系统的引导加载程序,但是需要更多的分析来了解它实际上是如何工作的。在SkyController中,观察到一个PLF文件,但在该PLF文件中,此条目具有二进制数据。再比如,条目0x07也看起来与引导过程相关,如字符串“ ecos-bootloader-p7-start-246-ge30badf ”所示,“ecos”引用eCos操作系统

40:7D40h: 28 01 10 00 00 00 00 00 65 63 6F 73 2D 62 6F 6F (.......ecos-boo  40:7D50h: 74 6C 6F 61 64 65 72 2D 70 37 2D 73 74 61 72 74 tloader-p7-start  40:7D60h: 2D 32 34 36 2D 67 65 33 30 62 61 64 66 00 00 00 -246-ge30badf...

文件系统数据(0X09)

固件中包含的每个文件都有一个“File System Data”条目。因此,这个条目是最常见的。根据文件是否被压缩,此条目的结构稍有不同:当其内容未压缩时,此条目以文件名(或目录名)开始,这是一个零终止的字符串。该名称后面接着3个无字符整数。

typedef struct sFileEntryTag {     u32 dwFlags;     u32 uk_0x04;     u32 uk_0x08; } sFileEntry;

第一个整数包含指定文件类型的标志,它可以是目录,普通文件或符号链接。这是通过读取位12到15来指定的。其他12位包含文件的权限。转换为八进制形式,最后12位将提供与Linux中使用格式相同的格式。例如:

从固件条目中提取文件权限

压缩时,flag 位于 Gzip 压缩数据中。因此,必须首先解压缩数据,然后使用相同的过程:文件名将是一个以空字符结尾的字符串,随着在3个无符号整数之后,第一个包含类型和权限。其余的字节是文件的内容。在下面的例子中,文件名是system / pulsar / etc / boxinit.hosted。flag是0x000081A0(小端),文件的内容从0x32开始。

00000000  73 79 73 74 65 6d 2f 70  75 6c 73 61 72 2f 65 74  |system/pulsar/et| 00000010  63 2f 62 6f 78 69 6e 69  74 2e 68 6f 73 74 65 64  |c/boxinit.hosted| 00000020  70 63 2e 72 63 00 a0 81  00 00 00 00 00 00 00 00  |pc.rc...........| 00000030  00 00 23 20 2d 2a 2d 20  6d 6f 64 65 3a 73 68 20  |..# -*- mode:sh | 00000040  2d 2a 2d 0a 23 20 54 68  69 73 20 66 69 6c 65 20  |-*-.# This file | 00000050  73 68 6f 75 6c 64 20 63  6f 6e 74 61 69 6e 20 63  |should contain c| 00000060  6f 6d 6d 61 6e 64 73 20  76 61 6c 69 64 20 6f 6e  |ommands valid on| 00000070  20 61 20 4c 69 6e 75 78  20 50 43 20 70 6c 61 74  | a Linux PC plat| 00000080  66 6f 72 6d 0a 0a 6f 6e  20 65 61 72 6c 79 2d 66  |form..on early-f|

分区数据(0X0B)

0x0B条目包含有关设备上的分区的信息。此条目的标题由10个值组成。前4个整数是版本信息,接下来的5个是未知的,最后一个无符号整数是条目中定义的分区数。

typedef struct sPartitionSectionTag {     u32 dwTblVersion;     u32 dwVersionMajor;     u32 dwVersionMinor;     u32 dwVersionBugfix;     u32 uk_0x10;     u32 uk_0x14;     u32 uk_0x18;     u32 uk_0x1C;     u32 uk_0x20;     u32 dwNumEntries; }

对于在条目内定义的每个分区,可以使用包含有关分区的更多信息的子条目:设备ID,卷数据和目标设备上的装载点。此条目的定义结构是:

typedef struct sPartitionEntryTag {   u16 wDevice;   u16 wVolumeType;   u16 wVolume;   u16 uk_0x06;   u32 dwVolumeSize;   u32 wVolumeAction;   u8  cVolumeName[32];   u8  cMountName[32]; } sPartitionEntry;

Volume类型可以是RAW,STATIC和DYNAMIC。原始分区不包含任何文件系统,例如交换分区。静态分区是只读的,例如引导分区,最后,动态分区是读写的,并包含可修改的文件。

安装数据(0X0C)

此条目早在文件中包含,并在其数据中包含另一个PLF文件。此PLF文件包含0x00条目,未知,0x03条目和0x07条目。0x07条目包含引导选项:

40:7894h: 07 00 00 00 9F 00 00 00 56 79 0A BF 00 00 70 80  ....Ÿ...Vy.¿..p€ 40:78A4h: 00 00 00 00 6D 74 64 70 61 72 74 73 3D 6E 61 6E  ....mtdparts=nan 40:78B4h: 64 30 3A 38 4D 28 50 62 6F 6F 74 6C 6F 61 64 65  d0:8M(Pbootloade 40:78C4h: 72 29 2C 31 36 4D 28 50 6D 61 69 6E 5F 62 6F 6F  r),16M(Pmain_boo 40:78D4h: 74 29 2C 38 4D 28 50 66 61 63 74 6F 72 79 29 2C  t),8M(Pfactory), 40:78E4h: 38 4D 28 50 63 75 73 74 6F 6D 65 72 5F 70 72 6F  8M(Pcustomer_pro 40:78F4h: 64 29 2C 31 30 30 37 31 30 34 4B 28 50 73 79 73  d),1007104K(Psys 40:7904h: 74 65 6D 29 2C 35 31 32 4B 28 50 6D 74 64 6F 6F  tem),512K(Pmtdoo 40:7914h: 70 73 29 20 63 6F 6E 73 6F 6C 65 3D 74 74 79 50  ps) console=ttyP 40:7924h: 41 33 2C 31 31 35 32 30 30 20 6C 6F 67 6C 65 76  A3,115200 loglev 40:7934h: 65 6C 3D 38 20 70 69 6E 73 74 3A 75 70 64 61 74  el=8 pinst:updat 40:7944h: 65 20 00 00                                     e ..


结论

我已经上传了一个Python程序 ,它包含了从SkyController固件中提取的信息和文件。它还成功地提取了一个Bebop2固件的文件,我觉得它将会适用于其他最近的固件。进行其他的编译工作,它或许还可以逆向旧版本的固件。重新包装解压缩的固件应该是可行的,但我没有任何设备来进行测试。在这样做之前,需要确定如何计算头文件和每个条目的CRC值。以后的工作将包括测试额外的固件文件和实施程序重新打包未打包和修改的固件。最后,可以阅读eCos文档来了解许多未识别的值。

原文:

本文由看雪翻译小组 Linso 编译


相 关 阅 读:


智能硬件入门

汽车 ECU 升级初探

HG533路由器分析教程之找到硬件调试接口

......

更多优秀文章点击左下角“关注原文”查看!

看雪论坛:http://bbs.pediy.com/

微信公众号 ID:ikanxue

微博:看雪安全

投稿、合作:www.kanxue.com

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存