以彼之矛-攻彼之盾:通过伪造运行环境无损解压RedBendEFDPackage固件包
今天刚好看到看雪上有人在求解包这个东西,并且引用了本博客的一篇博文作为启发点,作为互联网上为数不多真的研究过这个引擎的人,今天把以前逆向的部分结果继续逆向,最终得出此文。看雪原始链接:https://bbs.kanxue.com/thread-285879-1.htm
0x00 前言
如果你折腾过日本手机或某些车机系统的固件,你一定遇到过 .mld、.vbe、.dat 或 .delta 后缀的文件。这些是 RedBend的vDirect Mobile设备MDM管理方案所产生差分升级包。
与普通的 zip 不同,RedBend vDM并不只是简单的压缩,它是一个基于块的差分引擎。想要从中提取差分包或镜像数据,通常的做法是逆向其解压算法。
但当我把从某平板中得到的二进制扔进IDA后,发现事情并没有那么简单。
0x01 劝退现场:为什么不尝试还原算法?
一开始,按照思维惯性,在得到的刷机包里看到了一个名为rb_ua的可执行文件和一个名为UpatePackage.mld的更新包本体(MAGIC为RedBendEFDPackage),就知道这把玄了(虽然至少证明了这是RB的公版实现,其对厂商应该是SDK交付,日本厂商一般都会进行大量二开)。不过还好这个平板采用的是Intel Atom SoC,其本身就是x86_64架构,为直接拉起这个二进制和后续分析提供了方便。
在基于updater-script里面的传参进行分析之后,在IDA里追来追去,找到了其核心差分引擎的函数(sub_41FF10),发现RedBend vDM并不是一个我之前认为的简单的换了字典的LZMA解压器,而是一个完整的针对 Delta 数据流的虚拟机!
更可怕的是,为了在移动设备上达到极致的更新速度,RedBend在开发中大量使用了SIMD指令集来进行数学运算!(虽然实质在Atom平台上反而拖累了性能),以下是劝退现场的伪代码。
// 此处是高度优化的向量化加法循环 Target = Source + Delta
while ( 1 )
{
// 加载Source和Delta数据到128位寄存器
v173 = _mm_shuffle_epi32(_mm_cvtsi32_si128(dword_54AD20), 0);
v74 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v70), 0);
// 使用SSE/AVX指令进行并行差分还原
// 这里的逻辑极其晦涩,不仅有加法,还有shuffle和乘法
*(__m128i *)(v171 - 16) = _mm_add_epi32(
_mm_mullo_epi32(
(__m128i)_mm_shuffle_ps(
si128,
(__m128)_mm_add_epi64(
_mm_load_si128((const __m128i *)&xmmword_4F09C0),
(__m128i)si128),
136),
v173),
v175
);
}其中出现的_mm_add_epi32、_mm_mullo_epi32和_mm_shuffle_ps直接证明了其差分运算是在寄存器层面进行的并行数学计算
那么就代表如果我要使用Python来写一个解包器,我不光光要去处理那个坑人的非标字典LZMA,还要处理复杂的SSE/AVX调用。
所以证明了,直接还原算法这条路走不通。
0x03 伪造运行环境欺骗RB_UA
既然逆向算法实在太难,为什么不直接利用官方提供的rb_ua程序呢?它自己肯定知道怎么解压,否则它怎么装?
现在只需要解决两个问题:
1.它通常运行在 Android 环境下,依赖特定的分区(/dev/block/mmcblk...)。
2.它在更新前会进行一系列严苛的环境检查。
逻辑一:环境检查的死穴(MISC分区)
rb_ua为了防止更新中途断电变砖,会在启动前向misc分区写入一个非标的Recovery Flag,所以必须给它一个可以写的地方才能通过验证。
// Force Misc Check
if ( !fstab->has_partition("misc") ) {
log_error("Cannot find partition 'misc'");
return ERROR;
}
write_recovery_flag(fstab->get("misc"), "UPDATING");对策:如果找不到misc它就罢工。那我们就给它造一个假的misc分区!
逻辑二:绕过哈希校验 (No Scout)
默认情况下,作为OTA方案,它会校验源分区的哈希。因为我们需要把把空文件变成新文件,哈希100是对不上的。(此时感谢该厂商提供的是“完整包”)
if ( operation_mode == "scout_update" ) {
// 校验哈希,不匹配则报错退出
if ( check_hash(target_file) != expected ) error();
}
else if ( operation_mode == "no_scout" ) {
// 隐藏的跳过哈希校验参数
log_info("Skipping hash check...");
vDirect_Engine_Run(source, delta, output);
}对策:使用隐藏参数 --update_operation no_scout,强制它闭嘴干活。
0x03 实战:搭建环境
我们在WSL环境下,通过文件映射来欺骗rb_ua。
1.伪造空白分区
# System分区给大点,4GB较好
dd if=/dev/zero of=./dummy_system.img bs=1M count=4096
# 内核通常64MB
dd if=/dev/zero of=./dummy_boot.img bs=1M count=64
# Android x86的bootloader分区,通常64MB
dd if=/dev/zero of=./dummy_bootloader.img bs=1M count=64
# 伪造misc分区,骗过环境检查
dd if=/dev/zero of=./dummy_misc.img bs=1M count=12. 伪造分区表 (fake_fstab)
/system emmc /mnt/d/Temp/PANA_RB/dummy_system.img
/boot emmc /mnt/d/Temp/PANA_RB/dummy_boot.img
/bootloader emmc /mnt/d/Temp/PANA_RB/dummy_bootloader.img
/misc emmc /mnt/d/Temp/PANA_RB/dummy_misc.img0x04 执行!
执行以下命令,启动rb_ua。注意单独传入的--update_operation no_scout参数,这是成功的关键。
# 清理其自动产生的结果文件,避免无法启动
rm -f ./result.txt
# 启动“更新”
./rb_ua \
-w ./work_temp \
-d ./UpdatePackage.mld \
--partitions_list ./fake_fstab.txt \
--update_flavor std \
--update_operation no_scout \
--result_file ./result.txt \
--no_ui \
-l debug:extract.log
日志中开始出现如下状态,则代表已经开始解压!
< 10 > RB emmc: Opened device .../dummy_misc.img...
< 10 > Redbend PL: Added 0 args to misc
...
< 10 > RB emmc: Opened device .../dummy_system.img...
< 00 > Redbend PL: Performing system partition update (op 1)
< 00 > Redbend PL: system partition update done, ret 0 <--- 成功解压了SYSTEM分区!此时再打开我们创建的dummy_system.img等文件,内部已经是正常的EXT4/FAT32分区数据了!
0x05 总结
逆向并不总是意味着要硬啃汇编和反汇编。有时候,理解程序的运行逻辑(如环境检查、模式选择)比理解它的计算逻辑(SIMD算法)来的更有效率。
通过构建一个符合程序预期的“楚门的世界”,我们成功让官方工具充当了我们的解包器,兵不血刃地拿下了完整固件。
Happy Hacking by Yuu!








