首页
社区
课程
招聘
[翻译]HUBCAP:攻破ChromeCast 第一部分
2017-11-5 14:14 3734

[翻译]HUBCAP:攻破ChromeCast 第一部分

2017-11-5 14:14
3734

如果你正在寻找本文的根源,最早是发布在这里的:


基本立足点

        我原本想在这里告诉你们关于Chromecast的所有内容,但我认为维基百科已经涵盖了这部分内容,所以相关信息请查阅Wikipedia 为了达到我们的目的,您只需要知道这是一个基于ARMv7的设备,具有WiFiHDMI接口和微型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_DEVICE32个。这意味着这个数组大约为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地址从0127.该代码没笨拙的处理其他所有东西并无法正确设置USB EHCI控制器。

        我们可以将dev_index设置为-1126的任意值。 该数组有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_HUB16)。 但是它使用一个有符号的整数来实现,所以如果我们提供一个负的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虚拟机自动化脱壳的方法

收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 6818
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
聖blue 2017-11-7 00:14
2
0
不错!
游客
登录 | 注册 方可回帖
返回