如果你正在寻找本文的根源,最早是发布在这里的:
基本立足点
我原本想在这里告诉你们关于Chromecast的所有内容,但我认为维基百科已经涵盖了这部分内容,所以相关信息请查阅Wikipedia。 为了达到我们的目的,您只需要知道这是一个基于ARMv7的设备,具有WiFi,HDMI接口和微型USB端口形式的维护端口。 此外,您可以打开盒子得到一个串行调试端口。 设备所运行的是一个没有Dalvik引擎的精简版Android。
Chromecast处理器支持“安全”引导,这意味着它只会启动Google授权的引导加载程序和内核。 但是如果我想运行我自己的内核呢? 那么,我们的故事就从这里开始了。 Chromecast的独特之处在于Google向大多数系统发布了源代码,包括第二阶段的引导程序。 该引导程序是由SoC制造商Marvell维护的U-Boot的分叉版本。为了启动Android映像,对其做出了相应的修改。
因为我们可以访问源代码,所以我们可以简单地对它进行审计。 如果不打开盒子,只有通过维护端口这唯一的方法可以将数据导入引导加载程序。 那么,在这种情况下,该端口被配置为USB主设备,所以让我们来看看负责处理设备插入的代码。 在负责解析USB配置描述符的函数中,我们发现:
head = (struct usb_descriptor_header *) &buffer[0];
if (head->bDescriptorType != USB_DT_CONFIG) {
printf(" ERROR: NOT USB_CONFIG_DESC %x\n",
head->bDescriptorType);
return -1;
}
memcpy(&dev->config, buffer, buffer[0]);
le16_to_cpu(&(dev->config.desc.wTotalLength));
dev->config.no_of_if = 0;
index = dev->config.desc.bLength;
/* Ok the first entry must be a configuration entry,
* now process the others */
head = (struct usb_descriptor_header *) &buffer[index];
while (index + 1 < dev->config.desc.wTotalLength) {
switch (head->bDescriptorType) {
case USB_DT_INTERFACE:
if (((struct usb_interface_descriptor *) \
&buffer[index])->bInterfaceNumber != curr_if_num) {
/* this is a new interface, copy new desc */
ifno = dev->config.no_of_if;
dev->config.no_of_if++;
memcpy(&dev->config.if_desc[ifno],
&buffer[index], buffer[index]);
dev->config.if_desc[ifno].no_of_ep = 0;
dev->config.if_desc[ifno].num_altsetting = 1;
curr_if_num =
dev->config.if_desc[ifno].desc.bInterfaceNumber;
} else {
/* found alternate setting for the interface */
dev->config.if_desc[ifno].num_altsetting++;
}
break;
第7行和第23行显示的memcpy调用是有界(255字节)调用。 但更重要的是,负责计算接口描述符数量的“ifno”变量也不受限制。 您可以根据需要,提供尽可能多的接口头文件,因为代码不会察觉。 看起来我们可以在这个“设备”结构之外溢出一个合适数量的设备。 所以我们插入一个经过变形的配置描述符的USB设备,我们就赢了,对不对? 那么...当然这还不完全。 这些设备结构体的分配在usb_alloc_new_device中完成:
struct usb_device *usb_alloc_new_device(void)
{
int i;
USB_PRINTF("New Device %d\n", dev_index);
if (dev_index == USB_MAX_DEVICE) {
printf("ERROR, too many USB Devices, max=%d\n", USB_MAX_DEVICE);
return NULL;
}
/* default Address is 0, real addresses start with 1 */
usb_dev[dev_index].devnum = dev_index + 1;
usb_dev[dev_index].maxchild = 0;
for (i = 0; i = USB_MAXCHILDREN; i++)
usb_dev[dev_index].children[i] = NULL;
usb_dev[dev_index].parent = NULL;
dev_index++;
return &usb_dev[dev_index - 1];
}
usb_dev中的每个条目大约为1300个字节,而USB_MAX_DEVICE为32个。这意味着这个数组大约为42k。 我们的设备只是第二个插入的设备(第一个是根集线器),所以我们后面有大约40k的未使用的内存。 我们不能溢出那么多的内存! 我们将不得不找出一个增加dev_index的方法,直到我们能够找到一些有趣的东西。
硬件
要介绍的是Teensy 2.0 ++,我们将用它来攻击这个平台的硬件。 我使用这个方便设备的部分原因是因为我已经有了它,另一部分原因是我已经熟悉它的环境,我之前已经使用它进行了USB开发。 我们将利用Teensy的灵活性来增加device_index,从而到达一个数组之外的某一处内存。
我们只需要插入大量的“假”设备。 我们可以假装成一个USB集线器,并插入大量的设备。 这里涉及到一些小花招,因为我们必须同时成为集线器和设备,但Teensy一次只能听一个USB地址。 看代码是如何完成的。 所以我们现在有一个插入到最后一个插槽的设备。 我们赢了,对吧? 还是不完全。 与传统的平台不同,引导程序是特殊的。 通常没有堆,没有动态分配,结构都非常简单。 怎么办? 我们不能破坏堆的元数据,并且附近没有函数指针。 让我们看看我们附近有什么:
RAM:006E2FDC hub_index DCD
RAM:006E2FE8 dev_array DCD
...
RAM:006ED8E8 dev_index DCD
RAM:006ED8EC hub_array DCD
正如你所看到的,在dev_array之后是在usb_alloc_new_device函数中使用的dev_index变量。 我们可以修改这个32位值,并使USB设备的下一个“allocation”指向任意地方! 所以,现在我们赢了,对吧? 仍然没有。 如果您查看上面的代码,您会注意到它也使用dev_index来分配设备的devnum或地址。 USB地址从0到127.该代码没笨拙的处理其他所有东西并无法正确设置USB EHCI控制器。
我们可以将dev_index设置为-1到126的任意值。 该数组有32个条目,所以32-126会在数组之后覆盖内存,-1将允许我们在数组之前进行覆盖。 在dev_index之外的usb_dev数组之后的内存里没有什么有趣的东西(它在内存空间的末尾塞满了)。 这就让我们只能写入usb_dev数组前面的内存。 恰巧这段内存包含了一些有趣的东西。
引导加载程序支持两种设备,USB集线器和USB大容量存储设备。 它有类似于我们的usb_dev阵列的数组来跟踪与这些设备相关的设备特定数据。 与设备阵列类似,它使用索引来跟踪这些设备。 其中一个索引(用于集线器)位于usb_dev数组的前面,因此我们可以覆盖它! 但是,看看集线器分配代码,它有点严格:
static int usb_hub_index;
if (usb_hub_index < USB_MAX_HUB)
return &hub_dev[usb_hub_index++];
它检查usb_hub_index是否小于USB_MAX_HUB(16)。 但是它使用一个有符号的整数来实现,所以如果我们提供一个负的usb_hub_index(在正常的代码流中不会发生的),我们可以在hub_dev数组前面的任何地方进行写操作! 读取引导加载程序使用的链接器脚本,我们得到以下内存映射:
Offset 0 +----------+
| |
| text |
| |
+----------+
| |
| rodata |
| |
+----------+
| |
| data |
| |
+----------+
| |
| bss |
| |
+----------+
| |
| stack |
| |
Top of mem +----------+
.text段位于.data段之前,页面表设置为允许对所有页面进行读取,写入和执行访问。 我们可以覆盖.text段,我们赢了,对不对? 我讨厌失望,但答案是没有。 这种方法有不止一个问题。 第一,我们不知道我们在内存中的位置。我们所拥有的只是内存中某个数组的相对索引。 另外,我们不知道.text是什么样的。 到目前为止,我一直在使用当前版本的源代码,以及在GTVHacker上由我们的朋友友性提供的旧固件转储。 但是我的chromecast上没有bootloader版本的固件转储。 所以,即使我们可以覆盖.text(后面会介绍更多),我们也不知道要覆盖哪里。
我们可以解决这个问题,但这是第二部分的事了,溢出。
原文地址:https://fail0verflow.com/blog/2014/hubcap-chromecast-root-pt1/
本文由看雪翻译小组rainbow翻译
[培训]《安卓高级研修班(网课)》月薪三万计划,掌
握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法