GPS 校验和 代码_Linux recovery 移除签名校验
原創作者:王銳,多年 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?>?vmlinux2. 提取 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?endian
4318976???????0x41E700????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/desc.h
4321256???????0x41EFE8????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/i387.h
4322244???????0x41F3C4????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/processor.h
4323964???????0x41FA7C????????Unix?path:?/x86/kernel/cpu/perf_event_intel.c
4324152???????0x41FB38????????Unix?path:?/x86/kernel/cpu/perf_event_intel_ds.c
4325960???????0x420248????????Unix?path:?/x86/kernel/cpu/mcheck/mce.c
4326820???????0x4205A4????????Unix?path:?/x86/kernel/cpu/mcheck/mce_intel.c
4327124???????0x4206D4????????Unix?path:?/x86/kernel/cpu/mcheck/therm_throt.c
4328480???????0x420C20????????Unix?path:?/x86/kernel/cpu/mtrr/generic.c
4329752???????0x421118????????Unix?path:?/x86/kernel/cpu/mtrr/cleanup.c
4329832???????0x421168????????Unix?path:?/x86/kernel/cpu/perfctr-watchdog.c
4336148???????0x422A14????????Unix?path:?/x86/kernel/apic/apic_noop.c
4336572???????0x422BBC????????Unix?path:?/x86/kernel/apic/io_apic.c
4343276???????0x4245EC????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/fixmap.h
4347540???????0x425694????????Unix?path:?/x86/kernel/cpu/common.c
4347663???????0x42570F????????Unix?path:?/x86/kernel/cpu/vmware.c
4347911???????0x425807????????Unix?path:?/x86/kernel/cpu/intel.c
4350475???????0x42620B????????Unix?path:?/x86/kernel/acpi/boot.c
4352464???????0x4269D0????????Unix?path:?/x86/kernel/apic/apic.c
4352799???????0x426B1F????????Unix?path:?/x86/kernel/apic/ipi.c
4367224???????0x42A378????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/mmu_context.h
4374285???????0x42BF0D????????Unix?path:?/sys/kernel/debug/tracing/trace_clock
4383716???????0x42E3E4????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/pgalloc.h
4384752???????0x42E7F0????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/dma-mapping.h
4513864???????0x44E048????????xz?compressed?data
4514016???????0x44E0E0????????Unix?path:?/home/ubuntu/ce5300/barcelona_kernel/arch/x86/include/asm/syscall.h
4533558???????0x452D36????????Unix?path:?/Buffer/String/Package/Ref/Ddb],?found?[%s]?%p
4612622???????0x46620E????????Unix?path:?/sys/kernel/debug/dri
4614914???????0x466B02????????Unix?path:?/sys/kernel/debug/dri.
4618302???????0x46783E????????Unix?path:?/sys/kernel/debug/dri/%s/%s
4618366???????0x46787E????????Unix?path:?/sys/kernel/debug/dri/%s
4618509???????0x46790D????????Unix?path:?/sys/kernel/debug/dri.
4661219???????0x471FE3????????Unix?path:?/S70/S75/505V/F505/F707/F717/P8
4665828???????0x4731E4????????Unix?path:?/usr/include/asm/ioctls.h
4678778???????0x47647A????????Copyright?string:?"Copyright(c)?Pierre?Ossman"
4690408???????0x4791E8????????Unix?path:?/x86/oprofile/../../../drivers/oprofile/event_buffer.c
5242204???????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_rootfs
c14d03b1:????55??????????????????????push???%ebp
c14d03b2:????b8?6c?59?51?c1??????????mov????$0xc151596c,%eax
c14d03b7:????57??????????????????????push???%edi
c14d03b8:????56??????????????????????push???%esi
c14d03b9:????53??????????????????????push???%ebx
c14d03ba:????8d?64?24?b8?????????????lea????-0x48(%esp),%esp
c14d03be:????8b?15?70?6f?8e?c1???????mov????0xc18e6f70,%edx
c14d03c4:????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?Offset0xc151596c:?0xc151596c?-?0xc14f3b40?+?0x4f4b40?=?0x51696c
0xc18e6f70:?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?initrd0000000?6fde?40fe?2ee2?5fbf?27e3?e8fe?fb88?6a72
0000010?b649?904e?378a?49f4?057f?69b4?f9d9?4d43
0000020?7a8a?fe5b?1ba5?2442?3ea5?365e?7945?fd49
0000030?9afb?fca6?143c?b30d?eff8?a715?0982?424c
...
既然這個內核能執行,說明它有一種未知的加載方法,那就看看它是怎么做的吧,我們需要找到 unpack_to_rootfs 函數。
c14d0129?t?unpack_to_rootfsc1001410?T?aes_key_schedule_128
c10017c0?T?aes_decrypt_128
c14d0129:????55??????????????????????push???%ebp
c14d012a:????b9?11?00?00?00??????????mov????$0x11,%ecx
c14d012f:????89?d5???????????????????mov????%edx,%ebp
c14d0131:????57??????????????????????push???%edi
c14d0132:????56??????????????????????push???%esi
c14d0133:????be?ce?c6?41?c1??????????mov????$0xc141c6ce,%esi
c14d0138:????53??????????????????????push???%ebx
c14d0139:????89?c3???????????????????mov????%eax,%ebx
c14d013b:????8d?a4?24?30?ff?ff?ff????lea????-0xd0(%esp),%esp
c14d0142:????8d?7c?24?0f?????????????lea????0xf(%esp),%edi
c14d0146:????8d?54?24?20?????????????lea????0x20(%esp),%edx
c14d014a:????8d?44?24?0f?????????????lea????0xf(%esp),%eax
c14d014e:????f3?a4???????????????????rep?movsb?%ds:(%esi),%es:(%edi)
c14d0150:????e8?bb?12?b3?ff??????????call???0xc1001410?//?aes_key_schedule_128
c14d0155:????31?f6???????????????????xor????%esi,%esi
c14d0157:????39?ee???????????????????cmp????%ebp,%esi
c14d0159:????73?13???????????????????jae????0xc14d016e
c14d015b:????8d?14?33????????????????lea????(%ebx,%esi,1),%edx
c14d015e:????8d?44?24?20?????????????lea????0x20(%esp),%eax
c14d0162:????89?d1???????????????????mov????%edx,%ecx
c14d0164:????83?c6?10????????????????add????$0x10,%esi
c14d0167:????e8?54?16?b3?ff??????????call???0xc10017c0?//?aes_decrypt_128
c14d016c:????eb?e9???????????????????jmp????0xc14d0157
c14d016e:????a1?7c?74?93?c1??????????mov????0xc193747c,%eax
果然是 unpack_to_rootfs 被修改了,里面調用了 aes_key_schedule_128 和 aes_decrypt_128 兩個函數,加入了 AES128 解密過程,這說明我們提取出來的 initrd 是被加密的。
AES128 是對稱加密,如果沒有使用加硬件密鑰不管,極有可能是硬編碼在程序中的,試試提出它。從匯編代碼中看,在棧上構建了一個 crypto 上下文,部分內容是從地址 0xc141c6ce 復制過來的,這會不會就是密鑰呢?
一個 16 字節的數據,與字符串混編在一起,極有可能是 128 位的密鑰。
2.4. 解密 initrd
-128-ecb?-經常一些嘗試,使用 AES-128-ECB 解密成功,還原出了 initrd.img,實際為 cpio 格式。
2.5. 修改 initrd
cd?rootfs2.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????0xc14d016e
gcc?-m32?-o?jmp?-nostdlib?-Wl,-Ttext=0xc14d0155?jmp.S
objdump?-d?jmp
c14d0155?<_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 移除签名校验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: update 千万数据_mysql学习(
- 下一篇: 用随机梯度下降来优化人生