原创作者:王锐,多年 Linux 系统、龙芯平台移植与优化研发经验,Linux Contributor、Mozillian。

背景

某个设备配套的刷机程序是个 Linux recovery kernel,刷机过程会先从U盘加载刷机脚本,仅在签名校验通过后才执行脚本。本文记录了分析和移除签名校验的方法。

分析

刷机程序是一个 bzImage 文件,从启动的输出来看,内部包含了一个 initrd,在 initrd 中实现了读取U盘中的脚本和签名校验过程。

查看initrd内容

通过增加启动参数(cmdline)rdinit=/bin/sh,可以使 Kernel 启动后执行 /bin/sh,而不是默认的 /init 程序,有了命令行接口后,就可以查看 initrd 的内容。

# busybox find /
# cat /init

从 initrd 的内容来看,由 /init 调用 gpg2 对 U 盘中的刷机脚本执行签名校验,只有公钥集成在 initrd 中,没有私钥。

到这一步,我们已经清楚了签名校验的实现方法,并且也能使启动过程进入受控的命令行交互状态,其实已经可以手工操作跳过签名过程来刷机。

修改

每次手工操作的确太麻烦,那就来移除 initrd 中的签名校验过程吧。

从 bzImage 的结构来看,要想修改 initrd,先要从 bzImage 中提取出 vmlinux,再从 vmlinux 中提取出 initrd。

1. 提取 vmlinux

从 bzImage 中提取 vmlinux 比较简单,有现成的工具,位于 Linux 源代码中 scripts/extract-vmlinux

./scripts/extract-vmlinux bzImage > vmlinux

2. 提取 initrd

initrd 的格式可以是 cpio archive,也可以是 gzip、bzip2、lzma、xz 或 lzo 压缩的,先用 binwalk 扫描一遍。

binwalk vmlinuxDECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------0             0x0             ELF, 32-bit LSB executable, Intel 80386, version 1 (SYSV)3641536       0x3790C0        Linux kernel version "2.6.39 (ubuntu@ubuntu) (gcc version 4.9.3 20150311 (prerelease) (crosstool-NG 1.20.0) ) #24 SMP Fri Jun 7 14:32:37 CST 2019"3922304       0x3BD980        CRC32 polynomial table, little endian4318976       0x41E700        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/desc.h4321256       0x41EFE8        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/i387.h4322244       0x41F3C4        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/processor.h4323964       0x41FA7C        Unix path: /x86/kernel/cpu/perf_event_intel.c4324152       0x41FB38        Unix path: /x86/kernel/cpu/perf_event_intel_ds.c4325960       0x420248        Unix path: /x86/kernel/cpu/mcheck/mce.c4326820       0x4205A4        Unix path: /x86/kernel/cpu/mcheck/mce_intel.c4327124       0x4206D4        Unix path: /x86/kernel/cpu/mcheck/therm_throt.c4328480       0x420C20        Unix path: /x86/kernel/cpu/mtrr/generic.c4329752       0x421118        Unix path: /x86/kernel/cpu/mtrr/cleanup.c4329832       0x421168        Unix path: /x86/kernel/cpu/perfctr-watchdog.c4336148       0x422A14        Unix path: /x86/kernel/apic/apic_noop.c4336572       0x422BBC        Unix path: /x86/kernel/apic/io_apic.c4343276       0x4245EC        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/fixmap.h4347540       0x425694        Unix path: /x86/kernel/cpu/common.c4347663       0x42570F        Unix path: /x86/kernel/cpu/vmware.c4347911       0x425807        Unix path: /x86/kernel/cpu/intel.c4350475       0x42620B        Unix path: /x86/kernel/acpi/boot.c4352464       0x4269D0        Unix path: /x86/kernel/apic/apic.c4352799       0x426B1F        Unix path: /x86/kernel/apic/ipi.c4367224       0x42A378        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/mmu_context.h4374285       0x42BF0D        Unix path: /sys/kernel/debug/tracing/trace_clock4383716       0x42E3E4        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/pgalloc.h4384752       0x42E7F0        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/dma-mapping.h4513864       0x44E048        xz compressed data4514016       0x44E0E0        Unix path: /home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/syscall.h4533558       0x452D36        Unix path: /Buffer/String/Package/Ref/Ddb], found [%s] %p4612622       0x46620E        Unix path: /sys/kernel/debug/dri4614914       0x466B02        Unix path: /sys/kernel/debug/dri.4618302       0x46783E        Unix path: /sys/kernel/debug/dri/%s/%s4618366       0x46787E        Unix path: /sys/kernel/debug/dri/%s4618509       0x46790D        Unix path: /sys/kernel/debug/dri.4661219       0x471FE3        Unix path: /S70/S75/505V/F505/F707/F717/P84665828       0x4731E4        Unix path: /usr/include/asm/ioctls.h4678778       0x47647A        Copyright string: "Copyright(c) Pierre Ossman"4690408       0x4791E8        Unix path: /x86/oprofile/../../../drivers/oprofile/event_buffer.c5242204       0x4FFD5C        ELF, 32-bit LSB shared object, Intel 80386, version 1 (SYSV)5243884       0x5003EC        ELF, 32-bit LSB shared object, Intel 80386, version 1 (SYSV)

在 vmlinux 文件偏移 0x44E048 处,有一个疑似 xz 压缩文档,提取出来尝试解压。

if=vmlinux 

唯一的疑似压缩文档解压出错了,这个方法行不通,那就换另外一个方法吧。:)

2.1. 分析启动过程中的 initrd 加载

从 bzImage 中提取出的 vmlinux 是 strip 掉 symbols 的,不便于反汇编后定位函数,我们先提取该内核的 /proc/kallsyms,直接在 rdinit=/bin/sh 启动的命令行中 cat /proc/kallsyms 就可以了。

有了 symbols 后,首先我们要找 populate_rootfs 函数,从汇编代码中获得__initramfs_start 和 __initramfs_size。

c14d03b1 t populate_rootfsc14d0129 t unpack_to_rootfsc14d03b1:    55                      push   %ebpc14d03b2:    b8 6c 59 51 c1          mov    $0xc151596c,%eaxc14d03b7:    57                      push   %edic14d03b8:    56                      push   %esic14d03b9:    53                      push   %ebxc14d03ba:    8d 64 24 b8             lea    -0x48(%esp),%espc14d03be:    8b 15 70 6f 8e c1       mov    0xc18e6f70,%edxc14d03c4:    e8 60 fd ff ff          call   0xc14d0129

在 0xc14d03c4 处调用了0xc14d0129 这个函数,也就是 unpack_to_rootfs,传递了两个参数,%eax 就是 __initramfs_start,值是 0xc151596c,%edx 就是 __initramfs_size,%edx 的值是存储在地址 0xc18e6f70 处的。

有了 __initramfs_start 的程序地址后,只需要转换为 vmlinux 文件的偏移地址后,就可以提取出 initrd 的内容了。映射关系可以通过 readelf 获得。

readelf 

程序地址 0xc151596c 和 0xc18e6f70 都隶属于 .init.data section,文件偏移计算:

Offset = Addr - Section Base Addr + Section Base Offset

0xc151596c: 0xc151596c - 0xc14f3b40 + 0x4f4b40 = 0x51696c0xc18e6f70: 0xc18e6f70 - 0xc14f3b40 + 0x4f4b40 = 0x8e7f70
2.2. 提取 initrd

提取 initrd,首先需要知道 __initramfs_size,该值位于 vmlinux 文件的 0x8e7f70 偏移处,类型是 unsigned long。

08e7f70 
if=vmlinux 
2.3. 分析 initrd 格式

虽然提取出了 initrd,但不是已知的格式,内核支持的格式都有确定的 magic number:

static 
hexdump initrd

0000000 6fde 40fe 2ee2 5fbf 27e3 e8fe fb88 6a720000010 b649 904e 378a 49f4 057f 69b4 f9d9 4d430000020 7a8a fe5b 1ba5 2442 3ea5 365e 7945 fd490000030 9afb fca6 143c b30d eff8 a715 0982 424c...

既然这个内核能执行,说明它有一种未知的加载方法,那就看看它是怎么做的吧,我们需要找到 unpack_to_rootfs 函数。

c14d0129 t unpack_to_rootfsc1001410 T aes_key_schedule_128c10017c0 T aes_decrypt_128c14d0129:    55                      push   %ebpc14d012a:    b9 11 00 00 00          mov    $0x11,%ecxc14d012f:    89 d5                   mov    %edx,%ebpc14d0131:    57                      push   %edic14d0132:    56                      push   %esic14d0133:    be ce c6 41 c1          mov    $0xc141c6ce,%esic14d0138:    53                      push   %ebxc14d0139:    89 c3                   mov    %eax,%ebxc14d013b:    8d a4 24 30 ff ff ff    lea    -0xd0(%esp),%espc14d0142:    8d 7c 24 0f             lea    0xf(%esp),%edic14d0146:    8d 54 24 20             lea    0x20(%esp),%edxc14d014a:    8d 44 24 0f             lea    0xf(%esp),%eaxc14d014e:    f3 a4                   rep movsb %ds:(%esi),%es:(%edi)c14d0150:    e8 bb 12 b3 ff          call   0xc1001410 // aes_key_schedule_128c14d0155:    31 f6                   xor    %esi,%esic14d0157:    39 ee                   cmp    %ebp,%esic14d0159:    73 13                   jae    0xc14d016ec14d015b:    8d 14 33                lea    (%ebx,%esi,1),%edxc14d015e:    8d 44 24 20             lea    0x20(%esp),%eaxc14d0162:    89 d1                   mov    %edx,%ecxc14d0164:    83 c6 10                add    $0x10,%esic14d0167:    e8 54 16 b3 ff          call   0xc10017c0 // aes_decrypt_128c14d016c:    eb e9                   jmp    0xc14d0157c14d016e:    a1 7c 74 93 c1          mov    0xc193747c,%eax

果然是 unpack_to_rootfs 被修改了,里面调用了 aes_key_schedule_128 和 aes_decrypt_128 两个函数,加入了 AES128 解密过程,这说明我们提取出来的 initrd 是被加密的。
AES128 是对称加密,如果没有使用加硬件密钥不管,极有可能是硬编码在程序中的,试试提出它。从汇编代码中看,在栈上构建了一个 crypto 上下文,部分内容是从地址 0xc141c6ce 复制过来的,这会不会就是密钥呢?

041d6b2 

一个 16 字节的数据,与字符串混编在一起,极有可能是 128 位的密钥。

2.4. 解密 initrd
-128-ecb -

经常一些尝试,使用 AES-128-ECB 解密成功,还原出了 initrd.img,实际为 cpio 格式。

2.5. 修改 initrd
cd rootfs
2.6. 写回 initrd

从 unpack_to_rootfs 汇编代码可以看出,aes_decrypt 将明文写到了对应密文的相同内存空间,我们可以修改代码来跳过解密过程,这样就可以直接把 initrd-noverify.img 写回到 vmlinux 中,而不需要再加密来找麻烦了。

55                      

程序地址 0xc14d0155 处的代码修改为 jmp 0xc14d016e,这样就可以直接跳过 aes_decrypt 过程了。如果不懂 x86 的 instruction encoding 方法,有个简单的方法可以产生正确的 jmp 0xc14d016e 编码。

    .text    .globl _start_start:    jmp    0xc14d016egcc -m32 -o jmp -nostdlib -Wl,-Ttext=0xc14d0155 jmp.Sobjdump -d jmpc14d0155 <_start>:c14d0155:    e9 14 00 00 00          jmp    c14d016e <_start>

现在可以写回 initrd-noverify.img 了

if=initrd-noverify.img 

还需要编辑 vmlinux 文件 0x8e7f70 偏移处的 __initramfs_size,改为 initrd-noverify.img 的长度。

2.7. 写回 vmlinux

extract-vmlinux 脚本也是通过搜索 magic number 和尝试解压来提取 vmlinux 的,以此就可以获得 vmlinux 在 bzImage 中的偏移,参考写回 initrd 类似的方法,可以将 vmlinux 写回至 bzImage 原来的偏移位置。

不幸的是,修改后的 bzImage 无法启动,启动参数加入 earlyprintk=ttyS0,115200 后,可以看到 LZMA data is corrupt 的错误。

bzImage 的解压缩过程是从 input addr 读取压缩数据,解压后写到 output addr,而 output addr 与 input addr 之间的空间是不够完整存放解压后的数据的,之所以没有问题是因为这个 input addr 与 output addr 之间的差值是经过精心计算的,能够保证覆盖发生时,被覆盖的数据已完成解压。

正是因为这个原因,我们修改后的 vmlinux 由于内容变化,压缩比也发现了变化,已不能适应之前的比例计算出的差值,有两种方法解决这个问题:1. 将 output addr 向低地址方向移动。2. 将 input addr 向高地址方向移动。

从反汇编 bzImage 的代码来看,output addr 在之后还有校验,相对修改 input addr更为复杂。

objdump -D -b binary -mi386 bzImage...  5d91d8:    8d ab 00 10 bf ff       lea    -0x337000(%ebx),%ebp  5d91de:    55                      push   %ebp                // output address  5d91df:    68 f5 d1 4f 00          push   $0x5d5747           // input length  5d91e4:    8d 83 62 00 00 00       lea    0x62(%ebx),%eax     // input address  5d91ea:    50                      push   %eax  5d91eb:    8d 83 40 7b 5d 00       lea    0x5d7b40(%ebx),%eax // heap  5d91f1:    50                      push   %eax  5d91f2:    56                      push   %esi                // rmode  5d91f3:    e8 14 04 00 00          call   0x5d960c  5d91f8:    83 c4 14                add    $0x14,%esp  5d91fb:    31 db                   xor    %ebx,%ebx  5d91fd:    ff e5                   jmp    *%ebp...

本文转载自作者的博客,欢迎移步本文文末左下方点击 “”。

扫 码 关 注 我 们

再 + 好 友 tinylab

进 泰 晓 技 术 群

泰  晓  科  技


关注“泰晓科技”!点“在看”

GPS 校验和 代码_Linux recovery 移除签名校验相关推荐

  1. GPS 校验和 代码_浙江国产安全阀校验台批发价-欧迪美特ODMT

    金山区实验设备标定-世通检测校准中心那里可以校准仪器量具机构公司实验室,金山区在那里可以校准仪器认可*仪器校准金山区校验外校计量第三方认可权威机构附近单位在那?快速出具报告,计量所,金山区世通仪器校准 ...

  2. GPS 校验和 代码_PSPad editor(代码编辑器)v5.0.4.507绿色版

    PSPad editor 多功能文本编辑器,非常适用于编写程序代码,支持高亮html.VB.C++.SQL.Pascal.JS.PHP.ASP.Python等语言代码,具有十六进制与ASCII表等编辑 ...

  3. GPS 校验和 代码_YBS-YFQ-100K真空压力校验台气压校验压力台表压力变送器

    YBS-YFQ-100K真空压力校验台气压校验压力台表压力变送器 无线信号无法达到仪表室时,无线数据收发器就必须安装在无线温度传感器所在的隔室内.用户根据需求.须在定货单中注明以下事项,1)产品型号. ...

  4. GPS 校验和 代码_今日份∣学习(三菱-菱云系列)电梯故障代码表

    作为电梯人, 我们不仅要从培训中学习, 更要从实践中总结窍门和技巧, 这样,见的多了, 遇到问题才不会慌张,才能得心应手, 想要成为"电梯高手", 还需要我们下功夫,加油吧! 故障 ...

  5. GPS 校验和 代码_每天学习一点点~每天学习一点点~安全阀安全技术监察规程 安全阀校验与修理...

    点击蓝字获取更多精彩信息 安全阀校验与修理 D1 校验设备 D1.1 安全阀校验装置的组成 安全阀校验装置由校验台.气源和管路等组成(参见附件F). D1.2 校验气源 应当符合以下要求: (1)可配 ...

  6. GPS 校验和 代码_高精度GNSS服务每个人:安卓智能 手机提供GPS原始测量数据实现高精度定位...

    低成本GNSS芯片的发展推动了定位.导航和授时(PNT)设备的革命.GNSS定位技术曾用于军事行动和大地测量,随着启用GNSS功能的汽车导航设备和智能手机的发展,最终进入百万(甚至数十亿)用户的生活中 ...

  7. 安卓逆向_18 --- APK保护策略【Java代码混淆、资源混淆、签名校验】

    Java 代码混淆介绍:https://www.bilibili.com/video/BV1UE411A7rW?p=60 Android 反编译利器 jadx:GitHub上直接下载:https:// ...

  8. android签名校验代码,Android签名验证解析

    1.本文主要内容 知识回顾 签名验证解析 总结 本文介绍下Android在安装apk时,对签名的验证过程 2.知识回顾 在Android签名过程详解一文中,我已经详细说明签名的过程以及为什么要这么做, ...

  9. Android应用安全与防范之签名校验

    2019独角兽企业重金招聘Python工程师标准>>> Android黑产品里面有一个叫做二次打包,也称为重打包.通过反编译应用后,可以得到smali源码,往其中注入代码或者修改相应 ...

最新文章

  1. Linux 准确查找结构体定义位置
  2. 2017-2-19 C#基础 基本数据类型的转换,转义字符,常量
  3. Linux vim中使用计算器
  4. 2020年阿里云边缘计算和CDN的关键词
  5. 电脑测速软件_联通你我【宽带提速】让网速飞!超实用的宽带测速提速攻略来啦!...
  6. [转]简析 IOS 程序图标的设计
  7. mysql基础之数据库备份和恢复实操
  8. 论文笔记_S2D.22_2015-CVPR_利用深度特征回归和分层CRFs对单目图像进行深度和表面法线估计
  9. 宝马 android手机同步,BMW将发布无线安卓互联系统,CarPlay终于不再一家独大!
  10. 【图像去噪】基于matlab小波变换图像去噪(MSE和SNR)【含Matlab源码 2192期】
  11. 怎样用计算机算出54188,2010年考研计算机考生给教育部长的一封信(转自猫扑)...
  12. 学习和温习sqlserver
  13. 精美的手机WEB网页欣赏
  14. 学习记录 --【零基础CSS学习】03.ID选择器和类选择器
  15. 以“实景+科幻三维建模渲染”,助力“实景三维中国建设”
  16. spoolsv.exe占用cpu 100%的解决方法
  17. 35岁程序员,早到的中年危机
  18. ctypeh里的函数c语言,大满贯平台网址网址-官网首页
  19. 使用欧镭2D雷达ROS驱动
  20. uni-app 父组件无法获取到子组件传来的值,为undefined;父子组件传值undefined

热门文章

  1. Python面试题大全(二):python高级语法
  2. JavaScript 和 typeScript 中的 import、from
  3. 在SQL Server数据库之间进行数据导入导出
  4. java loadonstartup_java的web配置文件的“load-on-startup0/load-on-startup”一个问题
  5. leetcode No.2 两数相加
  6. 探索比特币源码5-私钥
  7. 软件工程课的分数系统,和打分方法
  8. 现代软件工程系列 学生和老师都不容易
  9. java算法的递归问题设计_java算法-递归算法思想
  10. ios 静态库合成_iOS : 静态库(.framework)合并