破解Kyocera KY-42C

Lobsters Hottest 新闻

摘要

一篇关于解锁引导加载程序和改造Kyocera KY-42C功能手机的详细博客文章,该手机在固件更新后被认为无法获取root权限。作者研究了联发科(MediaTek)的启动过程和漏洞利用。

<p><a href="https://lobste.rs/s/xvzbwu/hacking_kyocera_ky_42c">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/06 20:08

# 破解 Kyocera KY-42C 来源:https://itssho.my/blog/article/unlocking-kyocera-ky42c 发布于 2026 年 7 月 6 日,作者:Shomy ## 引言 在这篇文章中,我将分享解锁 bootloader 和修改 Kyocera KY-42C(DIGNO Keitai 4)的经验。这是一款日本功能手机,社区里都知道这款设备在固件更新后被认为是无法 root 的,但一如既往,这并不能阻止有人尝试 :)。 *Kyocera A202KC,与 KY-42C 类似的设备,图片来自 garahowiki (https://garahowiki.com/phones:kyocera_digno_keitai_4:start)* 在我发布关于 Carbonara(一个 MediaTek Download Agent 漏洞)的博文之后,一位名为 @760ceb3b9c0ba4872cadf3ce35a7a494 (https://github.com/760ceb3b9c0ba4872cadf3ce35a7a494) 的用户留言询问,在固件更新后(据称使其无法 root),是否有可能解锁 KY-42C 的 bootloader。我对这个挑战很感兴趣,于是决定进行调查。 ## 背景 KY-42C 搭载 MediaTek MT6761 处理器,市场名称为“Helio A22”。这是一款 MediaTek 设备(而且是旧款),因此我心中已经有了一些想法。我之前曾处理过解锁 MediaTek 设备的工作,所以对如何入手比较熟悉。 在深入讨论设备的具体细节之前,有必要提及一些关于 MediaTek 设备的概念。MediaTek 设备包含一种 USB 救援模式,用于刷写固件,该模式存在于 `Preloader` 和 `BROM` 中。Brom 是芯片中的掩膜 ROM,是设备开机时首先运行的代码。它负责初始化基本硬件,并加载 `Preloader`——第二级引导加载程序,其职责是初始化其余硬件(如 DRAM)并加载主引导加载程序(LK),后者再加载 Android 操作系统。 MediaTek 启动流程:BootROM 和 Preloader 都允许加载 `Download Agent`(DA),这是一种二进制文件,允许通过专用工具(如 `SP Flash Tool`、`Penumbra` 或 `mtkclient`)刷写固件。要了解更多关于 Download Agent 的信息,建议阅读 R0rt1z2 关于 heapb8 的博文 (https://blog.r0rt1z2.com/posts/exploiting-mediatek-datwo/#download-agents) 以及 penumbra 文档 (https://penumbra.itssho.my/Mediatek/Common/DA/Download-Agent)。 通常 OEM 厂商不提供设备所需的 DA,即使提供,也通常锁定为仅允许刷写签名固件。此外,由于 kamakiri (https://penumbra.itssho.my/Mediatek/Exploits/Kamakiri) 或 linecode 等 BROM 漏洞的存在,OEM 厂商已开始全面封锁 BROM USBDL,只允许通过 Preloader 刷写。KY-42C 就是这种情况,它的 BROM 被锁定,并且没有可用的 DA 来刷写固件。 ## 调查 对于 KY-42C,我们关心的是,该设备在 1.090XX 固件更新后,锁定了 BootROM USBDL。这意味着我们无法使用任何 BROM 漏洞来引导未签名的 Download Agent,只能使用 Preloader 下载模式。但我有一种感觉,BROM USBDL 并非真正永久锁定,有可能暂时访问它。 MediaTek 实现了两种方式来阻止该模式:一种是通过 efuse 永久禁用,不可逆转;另一种是在 Preloader GFH 头中设置一个标志。在启动过程中,BootROM 通过检查几个标志来确定是否允许 USBDL: ```c bool check_brom_cmd_disabled(void) { int iVar1; GFH_BROM_SEC_CFG_v1 *brom_sec_cfg; GFH_BROM_CFG_v3 *brom_cfg; ushort code2; ushort uVar2; bool locked; code2 = 0; uVar2 = 0; iVar1 = efuse_is_brom_cmd_disabled(); locked = iVar1 != 0; if (locked) { code2 = 0x8000; } /* Preloader header 由多个结构体组成,统称为 GFH(通用文件头)。 * 每个 GFH 结构体有一个用途,我们关心的有 GFH_BROM_SEC_CFG 和 GFH_BROM_CFG。 */ brom_sec_cfg = get_bl_gfh(GFH_BROM_SEC_CFG); if ((brom_sec_cfg != NULL) && (brom_sec_cfg->m_cmd_mode_permanent_dis == 0xc975e033)) { code2 = code2 | 0x4000; locked = true; } /* * GFH_BROM_CFG 允许禁用特定的通信模式,此处为 UART1 和 USB。 * 这意味着即使启用了 BROM USBDL,也只能通过 USB 以外的方式进行。 */ brom_cfg = get_bl_gfh(GFH_BROM_CFG); if ((brom_cfg != NULL) && (brom_cfg->m_brom_cmd_via_uart1_disable_magic == 'R')) { code2 = code2 | 1; } if ((brom_cfg != NULL) && (brom_cfg->m_brom_cmd_via_usb_disable_magic == 'U')) { code2 = code2 | 2; } ... iVar1 = efuse_is_brom_cmd_disabled(); if (iVar1 != 0) { code2 = code2 | 0x8000; locked = true; } cmd_dis_bitfield = code2 | uVar2 | cmd_dis_bitfield; if ((cmd_dis_bitfield & 0x3fff) == 3) { cmd_dis_bitfield = cmd_dis_bitfield & 0xfffe; } ... return locked; } ``` 随后,BootROM 会检查是否允许 USBDL,以及允许哪种通信模式。 我的感觉是 efuse 本身并未熔断,而是通过 Preloader 标志禁用了下载模式。为了证实我的理论,我请 760ceb3b9c0ba4872cadf3ce35a7a494 运行一个小脚本,从 Preloader USBDL 中转储熔丝(是的,MediaTek 允许这样做)。 ```python # 脚本基于 moto-experiments: # https://github.com/R0rt1z2/moto-experiments ... device = Device(args.port) logging.info('Waiting for device...') device.find_device() if not args.skip_handshake: device.handshake() device.identify() hrid = device.read32(0x11c50000, 0x1000) with open('efuse.bin', 'wb') as f: f.write(hrid) ``` 感谢 MTK 将 `devinfo` 基址列入 `read32` 命令的白名单,我们成功转储了整个 efuse 区域,尤其是偏移 `0x60` 处的 `sec-ctrl` 熔丝。 sec-ctrl 熔丝 通过查看 BootROM 代码,我们可以轻松确定禁用 BROM USBDL 的位是哪个。 ```c uint efuse_is_brom_cmd_disabled(void) { return (uint)(_DAT_11c50060) >> 8 & 1; } ``` 所以,这个位是位 8,在我们的例子中计算结果为 0!也就是说,efuse 并未熔断,因此理论上我们只需擦除 Preloader 或短路测试点,就可以利用 `linecode` 漏洞加载未签名的 DA。不幸的是,经过多次尝试定位测试点,并且由于设备 fastboot 被锁定而无法擦除 Preloader,只剩下一个选项:希望找到一种通过软件解锁 bootloader 的方法,最好是利用 Preloader 或 lk 漏洞。 ## 固件分析 我首先开始分析 lk,760ceb3b9c0ba4872cadf3ce35a7a494 从旧备份中找到了它。凭借我偶尔为 kaeru (https://github.com/R0rt1z2/kaeru) 贡献而分析 lk 镜像的经验,我很快找到了 `fastboot_init`,所有 fastboot 命令都在这里注册。我寻找某种解锁命令,但看起来 Kyocera 将其完全移除了: ```c ... fastboot_register("flash:",0x4802b461,1,0); iVar1 = codecheck(); if (iVar1 == 0) { fastboot_register("erase:",0x4802b1f1,1,1); } else { fastboot_register("erase:",0x4802b1f1,1,0); } fastboot_register("oem printk-ratelimit",0x48025175,1,0); iVar1 = codecheck(); if (iVar1 == 0) { fastboot_register("continue",&LAB_4802508c+1,0,0); } else { fastboot_register("continue",&LAB_4802508c+1,1,0); } fastboot_register("download:",0x4802b899,1,0); fastboot_publish("max-download-size",0x480baa9c); FUN_48000188(); iVar1 = codecheck(); if (iVar1 == 0) { fastboot_register("oem p2u",0x4802522d,1,1); } else { fastboot_register("oem p2u",0x4802522d,1,0); } fastboot_register("oem dump_pllk_log",0x480250fd,1,0); fastboot_register("oem off-mode-charge",&LAB_48025388+1,0,0); iVar1 = codecheck(); if (iVar1 == 0) { fastboot_register("oem key",0x4802b905,1,1); } else { fastboot_register("oem key",0x4802b905,1,0); } iVar1 = codecheck(); if (iVar1 == 0) { fastboot_register("oem lks",0x4802b961,1,1); } else { fastboot_register("oem lks",0x4802b961,1,0); } ... fastboot_register("oem ultraflash:",0x4802b4cd,1,1); fastboot_register("oem ultraflash_en",0x4802b5b9,1,1); fastboot_register("ultraflash:",0x4802b4cd,1,1); ``` 而且不幸的是,大多数 oem 命令都被 `chkcode` 分区中的一个标志所阻挡,导致安全设备无法访问。 ```c int codecheck(void) { uint uVar1; int local_810; int local_80c; if (ALLOWED? < 0) { uVar1 = partition_read("chkcode",0x800,0,0,&local_810,0x800); if (((uVar1 == 0) || (local_810 != 0x544f4f4c)) || (local_80c != 0x4b434642)) { ALLOWED? = 0; return 0; } ALLOWED? = 1; } return ALLOWED?; } ``` 这再加上严格的安全策略,缩小了在 lk 中利用漏洞的可能性(至少是我愿意投入时间研究的)。因此,是时候转向 Preloader 了。 获取 Preloader 镜像相当具有挑战性,因为 760ceb3b9c0ba4872cadf3ce35a7a494 的转储中不包含它。我等待着他们提供继续所需的文件。他们最终从 OTA 转储中获取了新旧两个镜像,这对于差异分析非常完美。 有了这两个镜像,我首先确认了我最初的推测,即 Preloader 确实是禁用 BROM USBDL 的元凶。 ``` # 旧 preloader BROM_SEC_CFG (v1, 48 bytes) JTAG: disabled Debug: disabled BROM Cmd Perm Dis: no # 新 preloader BROM_SEC_CFG (v1, 48 bytes) JTAG: disabled Debug: disabled BROM Cmd Perm Dis: yes ``` 然后,我开始将两个镜像加载到 Ghidra 中,寻找可利用的路径。 ## 利用 Preloader 在 Preloader 的 USBDL 模式中,设备会进入一个无限循环,等待来自主机的命令。命令通过 USB 发送,在 switch 语句中匹配并执行相应函数。 ```c // 来自 GitHub 上的 `download.c` int usbdl_handler(struct bldr_comport *comport, u32 hshk_tmo_ms) { u8 cmd = 0; ... #if CFG_PRELOADER_AS_DA usbdl_init_image(); #endif while (1) { platform_wdt_kick(); usbdl_get_byte(&cmd); if (cmd != CMD_GET_BL_VER) usbdl_put_byte(cmd); switch (cmd) { ... case CMD_SEND_DA: DBGMSG("%s CMD_SEND_DA\n", MOD); usbdl_send_da(); break; case CMD_JUMP_DA: DBGMSG("%s CMD_JUMP_DA\n", MOD); usbdl_jump_da(); break; #if CFG_PRELOADER_AS_DA case CMD_SEND_IMAGE: DBGMSG("%s CMD_SEND_IMAGE\n", MOD); usbdl_send_image(); break; case CMD_BOOT_IMAGE: DBGMSG("%s CMD_BOOT_IMAGE\n", MOD); usbdl_boot_image(); break; #endif case CMD_READ32: DBGMSG("%s CMD_READ32\n", MOD); usbdl_read32(FALSE); break; case CMD_WRITE32: DBGMSG("%s CMD_WRITE32\n", MOD); usbdl_write32(TRUE); break; ... default: DBGMSG("%s Unhandled CMD 0x%x\n", MOD, cmd); break; } } return 0; } ``` 在查看 Preloader 代码和我在 Ghidra 中加载的代码时,我被“CMD_SEND_IMAGE\n”调试消息吸引住了。这个命令只有在定义 `CFG_PRELOADER_AS_DA` 时才可用,而 KY-42C 似乎定义了它。根据我的经验,这个命令和 `CMD_BOOT_IMAGE` 通常不会出现在生产版 Preloader 中。因此,我想,既然 MTK 默认不启用这些命令,并且还决定完全移除它们,也许是有原因的。 阅读代码后,我意识到,正如我所怀疑的,MediaTek 忘记在这些命令中实现签名验证检查: ```c static void usbdl_send_image(void) { u32 img_addr = 0; u32 img_len = 0; image_index_t id; u16 status = 0; u8 img_name[64] = {0}; u32 checksum32 = 0; u32 my_checksum32 = 0; u8 debug_buf[32] = {0}; usbdl_get_data(img_name, 64); usbdl_get_dword(&img_len); for (id = IMAGE_LK_ID; id < IMAGE_MAX_NUM; id++) { if ((!strcmp(img_name, g_image_list[id].partition_name)) && (img_len > 0) && (img_len <= g_image_list[id].length)) { pal_log_info("%s SEND: Verify PASS\n", MOD); if(id == IMAGE_ATF_ID) img_addr = (u32)tee1_buf; else img_addr = g_image_list[id].start_addr; break; } } if (!img_addr) { pal_log_err("%s Unknown image\n", MOD); } usbdl_put_word(status); /* 这里设备从主机下载镜像数据,并且 * 只进行校验和验证,从未验证签名。 */ usbdl_get_data((u8 *)img_addr, img_len); my_checksum32 = checksum_plain((u8 *)img_addr, img_len); usbdl_get_dword(&checksum32); if (my_checksum32 != checksum32) { pal_log_err("%s checksum mismatch!\n", MOD); return; } if(id == IMAGE_ATF_ID) { // 重定位 ATF 和 TEE } } static void usbdl_boot_image(void) { u8 img_name[64] = {0}; u32 jump_arg; u16 status = 0; usbdl_get_data(img_name, 64); trustzone_pre_init(); g_boot_mode = FASTBOOT; platform_set_boot_args(); trustzone_post_init(); jump_arg = (u32)&bootarg; if (!strcmp(img_name, lk)) { usbdl_put_word(status); pal_log_err("%s Jump to LK\n", MOD); /* 嗨,MTK,谢谢这个 */ bldr_jump(g_image_list[IMAGE_LK_ID].start_addr + PART_HDR_BUF_SIZE, jump_arg, sizeof(boot_arg_t)); } else if (!strcmp(img_name, atf)) { usbdl_put_word(status); pal_log_err("%s Jump to ATF\n", MOD); bldr_jump64(g_image_list[IMAGE_LK_ID].start_addr + PART_HDR_BUF_SIZE, jump_arg, sizeof(boot_arg_t)); } else { status = 1; usbdl_put_word(status); pal_log_err("%s Unknown Jump\n", MOD); } } ``` 这意味着我们只需几行 Python 代码就能在 `EL3` 上实现任意代码执行: ```python path = 'bin/unlock.bin' with open(path, 'rb') as f: data = f.read() device = Device(None) logging.info('Waiting for device...') device.find_device() device.handshake() device.identify() device.send_image('lk', data) device.boot_image('lk') ``` *值得一提的是,此漏洞过去已在 lgk10exploit (https://github.com/arturkow2000/lgk10exploit) 中公开使用过,因此并非新鲜事,而且现在已经被修补了好几年。不过,在发现这个项目之前,自己摸索出这一点还是挺有意思的。* ## 解锁 Bootloader 最初,我们尝试引导一个打过补丁的 lk 镜像,但不幸的是,设备几秒钟后就会崩溃,我们无法找出原因。因此,再加上这个 lk 中根本没有包含 `sec_set_lock_state` 函数,我决定制作自己的 payload,使用我的 mtk-payloads (https://github.com/shomykohai/mtk-payloads) 项目框架。你可以在此处查看 payload 的完整代码 (https://github.com/shomykohai/kyocera-ky-42c-unlock/blob/main/payload/src/main.c),但主要思路是创建一个新的 seccfg,加密其哈希并写入 `seccfg` 分区,同时解锁 `chkcode` 中的 fastboot 功能: ```c int main(void) { ... seccfg.lock_state = LKS_UNLOCK; seccfg.dm_verity = DM_VERITY_OK; sha256_hash(hash, &seccfg, 0x1C); params.length = HASH_SZ; params.anti_clone = true; params.encrypt = true; sp_sej_enc(hash, hash, params); memcpy(seccfg.hash, hash, HASH_SZ); mmc_write_block(&g_mmc_dev, seccfg_start, &seccfg); u32 chkcode_block = gpt_get_start(&gpt, "chkcode") + 4; mmc_read_block(&g_mmc_dev, chkcode_block, chkcode_buf); *(u32 *)&chkcode_buf[0] = CHKCODE_MAGIC1; *(u32 *)&chkcode_buf[4] = CHKCODE_MAGIC2; mmc_write_block(&g_mmc_dev, chkcode_block, chkcode_buf); } ``` 我将 payload 和 Python 脚本发送给了 760ceb

相似文章

VMC2040安全摄像头的Root操作

Lobsters Hottest

本篇博客文章是系列教程的第一部分,内容涵盖硬件检查、UART发现以及初始Bootloader分析,目标是对Arlo VMC2040安全摄像头进行Root操作。

usbliter8 - Apple A12/A13 bootROM漏洞利用

Lobsters Hottest

本文详细介绍了苹果A12/A13 SoC中一种新颖的bootROM漏洞,该漏洞利用USB控制器中的硬件缺陷和配置缺陷,从而破坏了启动链。文中还提供了一个概念验证。

本田思域与邪恶泊车员

Hacker News Top

一位安全研究人员揭示了本田思域车载信息娱乐系统(headunit)会接受使用公开已知的AOSP测试密钥进行的未签名更新,从而使得通过物理USB访问可以实现“邪恶泊车员”(evil valet)攻击。还发布了如ota-builder和apk-rebuilder等工具,以方便进一步的逆向工程。