常見的Android SO加殼(加密)思路

   常見的Android SO加殼(加密)思路

    1.1 破壞Elf Header

    將Elf32_Ehdr 中的e_shoff, e_shnum, e_shstrndx, e_shentsize字段處理,變爲無效值。由於在鏈接過程中,這些字段是無用的,所以可以隨意修改,這會導致ida打不開這個so文件。


    1.2 刪除Section Header

    同樣的道理,在鏈接過程中,Section Header是沒有用到的,可以隨意刪除,這會導致ida打不開這個so文件。


    1.3 有源碼加密Section或者函數

    一是對section加殼,一個是對函數加殼。參考Android逆向之旅---基於對so中的section加密技術實現so加固Android逆向之旅---基於對so中的函數加密技術實現so加固


    1.4 無源碼加密Section或者函數

    將解密函數放在另一個so中,只需保證解密函數在被加密函數執行前執行即可。和其他so一樣,解密so的加載也放在類的初始化static{}中。我們可以在解密so加載過程中執行解密函數,就能保證被加密函數執行前解密。執行時機可以選在linker執行.init_array時,也可以選在OnLoad函數中。當然,解密so一定要放在被解密so後加載。否則,搜索進程空間找不到被解密的so。

    詳細介紹和代碼:無源碼加解密實現 && NDK Native Hook 


    1.5 自定義loader來加載SO,即從內存加載SO

    我們可以DIY SO,然後使用我們自定義的loader來加載,破解難度又加大了。詳解的介紹參考SO文件格式及linker機制學習總結(1)SO文件格式及linker機制學習總結(2)


    1.6 在原so外面加一層殼,如下圖

    

     packed so相當於把loader的代碼插入到原so的init_array或者jni_onload處,然後重新打包成packed so,加載這個so,首先執行init_array或者jni_onload,在這裏完成對原so的解密,從內存加載,並形成soinfo結構,然後替換原packed so的soinfo結構。


    1.7 llvm源碼級混淆

    

    Clang ( 發音爲 /klæŋ/) 是 LLVM 的一個編譯器前端,它目前支持 C, C++, Objective-C 以及 objective-c++ 等編程語言。Clang 對源程序進行詞法分析和語義分析,並將分析結果轉換爲 Abstract Syntax Tree ( 抽象語法樹 ) ,最後使用 LLVM 作爲後端代碼的生成器。

    在android llvm源碼級混淆比較成熟的是Safengine。

    如果想自己研究llvm源碼級混淆,可以參考Android LLVM-Obfuscator C/C++ 混淆編譯的深入研究,通過修改NDK的編譯工具,來實現編譯混淆。


    1.8 花指令

    通過在C語言中,內嵌arm彙編的方式,可以加入arm花指令,迷惑IDA。

 

    1.9 so vmp保護

    寫一個arm虛擬機虛擬執行so中被保護的代碼,在手機上效率是一個問題。 

對應的脫殼思路

    1.1 針對破壞Elf Header和刪除Section Header

    參考ELF section修復的一些思考

    裏面有個知識點,需要說明下:

    從DT_PLTGOT可以得到__global_offset_table的偏移位置。由got表的結構知道,__global_offset_table前是rel.dyn重定位結構,之後爲rel.plt重定位結構,都與rel一一對應。我們還是以libPLTUtils.so爲例,下載地址:http://download.csdn.net/detail/jltxgcy/9602803

    arm-Linux-androideabi-readelf -d ~/Public/libPLTUtils.so:

[plain] view plain copy
  1. Dynamic section at offset 0x2e8c contains 32 entries:  
  2.   Tag        Type                         Name/Value  
  3.  0x00000003 (PLTGOT)                     0x3fd4  
  4.  0x00000002 (PLTRELSZ)                   64 (bytes)  
  5.  0x00000017 (JMPREL)                     0xc74  
  6.  0x00000014 (PLTREL)                     REL  
  7.  0x00000011 (REL)                        0xc24  
  8.  0x00000012 (RELSZ)                      80 (bytes)  
  9.  0x00000013 (RELENT)                     8 (bytes)  
  10.  0x6ffffffa (RELCOUNT)                   7  
  11.  0x00000006 (SYMTAB)                     0x18c  
  12.  0x0000000b (SYMENT)                     16 (bytes)  
  13.  0x00000005 (STRTAB)                     0x51c  
  14.  0x0000000a (STRSZ)                      1239 (bytes)  
  15.  0x00000004 (HASH)                       0x9f4  
  16.  0x00000001 (NEEDED)                     Shared library: [liblog.so]  
  17.  0x00000001 (NEEDED)                     Shared library: [libdl.so]  
  18.  0x00000001 (NEEDED)                     Shared library: [libstdc++.so]  
  19.  0x00000001 (NEEDED)                     Shared library: [libm.so]  
  20.  0x00000001 (NEEDED)                     Shared library: [libc.so]  
  21.  0x0000000e (SONAME)                     Library soname: [libPLTUtils.so]  
  22.  0x0000001a (FINI_ARRAY)                 0x3e80  
  23.  0x0000001c (FINI_ARRAYSZ)               8 (bytes)  
  24.  0x00000019 (INIT_ARRAY)                 0x3e88  
  25.  0x0000001b (INIT_ARRAYSZ)               4 (bytes)  
  26.  0x00000010 (SYMBOLIC)                   0x0  
  27.  0x0000001e (FLAGS)                      SYMBOLIC BIND_NOW  
  28.  0x6ffffffb (FLAGS_1)                    Flags: NOW  
  29.  0x6ffffff0 (VERSYM)                     0xb74  
  30.  0x6ffffffc (VERDEF)                     0xbe8  
  31.  0x6ffffffd (VERDEFNUM)                  1  
  32.  0x6ffffffe (VERNEED)                    0xc04  
  33.  0x6fffffff (VERNEEDNUM)                 1  
  34.  0x00000000 (NULL)                       0x0  
    我們看到PLTGOT的value爲0x3fb4。

    然後arm-linux-androideabi-readelf -r ~/Public/libPLTUtils.so:

[plain] view plain copy
  1. Relocation section '.rel.dyn' at offset 0xc24 contains 10 entries:  
  2.  Offset     Info    Type            Sym.Value  Sym. Name  
  3. 00003e80  00000017 R_ARM_RELATIVE     
  4. 00003fb4  00000017 R_ARM_RELATIVE     
  5. 00003fb8  00000017 R_ARM_RELATIVE     
  6. 00003fbc  00000017 R_ARM_RELATIVE     
  7. 00003fc0  00000017 R_ARM_RELATIVE     
  8. 00003fc8  00000017 R_ARM_RELATIVE     
  9. 00003fcc  00000017 R_ARM_RELATIVE     
  10. 00004004  00000402 R_ARM_ABS32       00000000   puts  
  11. 00003fc4  00000915 R_ARM_GLOB_DAT    00000000   __gnu_Unwind_Find_exid  
  12. 00003fd0  00001f15 R_ARM_GLOB_DAT    00000000   __cxa_call_unexpected  
  13.   
  14. Relocation section '.rel.plt' at offset 0xc74 contains 8 entries:  
  15.  Offset     Info    Type            Sym.Value  Sym. Name  
  16. 00003fe0  00000216 R_ARM_JUMP_SLOT   00000000   __cxa_atexit  
  17. 00003fe4  00000116 R_ARM_JUMP_SLOT   00000000   __cxa_finalize  
  18. 00003fe8  00000416 R_ARM_JUMP_SLOT   00000000   puts  
  19. 00003fec  00000916 R_ARM_JUMP_SLOT   00000000   __gnu_Unwind_Find_exid  
  20. 00003ff0  00000f16 R_ARM_JUMP_SLOT   00000000   abort  
  21. 00003ff4  00001116 R_ARM_JUMP_SLOT   00000000   memcpy  
  22. 00003ff8  00001c16 R_ARM_JUMP_SLOT   00000000   __cxa_begin_cleanup  
  23. 00003ffc  00001d16 R_ARM_JUMP_SLOT   00000000   __cxa_type_match  
    我們可以看到0x00003fb4恰好是其分界線。

    在ida中,是這樣的。

[plain] view plain copy
  1. .got:00003FB4 ; ===========================================================================  
  2. .got:00003FB4  
  3. .got:00003FB4 ; Segment type: Pure data  
  4. .got:00003FB4                 AREA .got, DATA  
  5. .got:00003FB4                 ; ORG 0x3FB4  
  6. .got:00003FB4 global_fun_ptr  DCD global_fun          ; DATA XREF: Java_com_example_ndkplt_PLTUtils_pltTest+16o  
  7. .got:00003FB4                                         ; Java_com_example_ndkplt_PLTUtils_pltTest+18r ...  
  8. .got:00003FB8 __aeabi_unwind_cpp_pr0_ptr DCD __aeabi_unwind_cpp_pr0 ; DATA XREF: sub_E54+1Cr  
  9. .got:00003FB8                                         ; .text:off_E98o  
  10. .got:00003FBC __aeabi_unwind_cpp_pr1_ptr DCD __aeabi_unwind_cpp_pr1 ; DATA XREF: sub_E54+28r  
  11. .got:00003FBC                                         ; .text:off_E9Co  
  12. .got:00003FC0 __aeabi_unwind_cpp_pr2_ptr DCD __aeabi_unwind_cpp_pr2 ; DATA XREF: sub_E54+34r  
  13. .got:00003FC0                                         ; .text:off_EA0o  
  14. .got:00003FC4 __gnu_Unwind_Find_exidx_ptr DCD __imp___gnu_Unwind_Find_exidx  
  15. .got:00003FC4                                         ; DATA XREF: sub_EA4+8r  
  16. .got:00003FC4                                         ; .text:off_F9Co  
  17. .got:00003FC8 off_3FC8        DCD aCallTheMethod      ; DATA XREF: sub_EA4+48r  
  18. .got:00003FC8                                         ; .text:off_FA0o  
  19. .got:00003FC8                                         ; "call the method"  
  20. .got:00003FCC off_3FCC        DCD 0x2304              ; DATA XREF: sub_EA4+4Cr  
  21. .got:00003FCC                                         ; .text:off_FA4o  
  22. .got:00003FD0 __cxa_call_unexpected_ptr DCD __cxa_call_unexpected ; DATA XREF: sub_150C+3A8r  
  23. .got:00003FD0                                         ; .text:off_18F8o  
  24. .got:00003FD4 _GLOBAL_OFFSET_TABLE_ DCD 0             ; DATA XREF: .plt:00000CBCo  
  25. .got:00003FD4                                         ; .plt:off_CC4o  
  26. .got:00003FD8                 DCD 0  
  27. .got:00003FDC                 DCD 0  
  28. .got:00003FE0 __cxa_atexit_ptr DCD __imp___cxa_atexit ; DATA XREF: __cxa_atexit+8r  
  29. .got:00003FE4 __cxa_finalize_ptr DCD __imp___cxa_finalize ; DATA XREF: __cxa_finalize+8r  
  30. .got:00003FE8 puts_ptr        DCD __imp_puts          ; DATA XREF: puts+8r  
  31. .got:00003FEC __gnu_Unwind_Find_exidx_ptr_0 DCD __imp___gnu_Unwind_Find_exidx  
  32. .got:00003FEC                                         ; DATA XREF: __gnu_Unwind_Find_exidx+8r  
  33. .got:00003FF0 abort_ptr       DCD __imp_abort         ; DATA XREF: abort+8r  
  34. .got:00003FF4 memcpy_ptr      DCD __imp_memcpy        ; DATA XREF: memcpy+8r  
  35. .got:00003FF8 __cxa_begin_cleanup_ptr DCD __imp___cxa_begin_cleanup  
  36. .got:00003FF8                                         ; DATA XREF: __cxa_begin_cleanup+8r  
  37. .got:00003FFC __cxa_type_match_ptr DCD __imp___cxa_type_match  
  38. .got:00003FFC                                         ; DATA XREF: __cxa_type_match+8r  
  39. .got:00003FFC ; .got          ends  
    我們在0x00003fb4地址前,看到了:

[plain] view plain copy
  1. 00004004  00000402 R_ARM_ABS32       00000000   puts  
  2. 00003fc4  00000915 R_ARM_GLOB_DAT    00000000   __gnu_Unwind_Find_exid  
  3. 00003fd0  00001f15 R_ARM_GLOB_DAT    00000000   __cxa_call_unexpected  
    在這個地址後面,看到了:
[plain] view plain copy
  1. 00003fe0  00000216 R_ARM_JUMP_SLOT   00000000   __cxa_atexit  
  2. 00003fe4  00000116 R_ARM_JUMP_SLOT   00000000   __cxa_finalize  
  3. 00003fe8  00000416 R_ARM_JUMP_SLOT   00000000   puts  
  4. 00003fec  00000916 R_ARM_JUMP_SLOT   00000000   __gnu_Unwind_Find_exid  
  5. 00003ff0  00000f16 R_ARM_JUMP_SLOT   00000000   abort  
  6. 00003ff4  00001116 R_ARM_JUMP_SLOT   00000000   memcpy  
  7. 00003ff8  00001c16 R_ARM_JUMP_SLOT   00000000   __cxa_begin_cleanup  
  8. 00003ffc  00001d16 R_ARM_JUMP_SLOT   00000000   __cxa_type_match  


    1.2 針對有源碼加密Section或者函數
    使用dlopen來加載so,返回一個soinfo結構體如下:
[plain] view plain copy
  1. struct soinfo {  
  2.     const char name[SOINFO_NAME_LEN]; Elf32_Phdr *phdr; //Elf32_Phdr 實際內存地址 int phnum;  
  3.     unsigned entry;  
  4.     unsigned base; //SO 起始  
  5.     unsigned size; //內存對齊後佔用大小  
  6.     int unused; // DO NOT USE, maintained for compatibility. unsigned *dynamic; //.dynamic 實際內存地址  
  7.     unsigned wrprotect_start; //mprotect 調用 unsigned wrprotect_end;  
  8.     soinfo *next; //下一個 soinfo unsigned flags;  
  9.     const char *strtab; //.strtab 實際內存地址 Elf32_Sym *symtab; //. symtab 實際內存地址  
  10.     //hash 起始位置:bucket – 2 * sizeof(int) unsigned nbucket; //size = nbucket * sizeof(int) unsigned nchain; //size = nchain * sizeof(int) unsigned *bucket;  
  11.     unsigned *chain;  
  12.     unsigned *plt_got; //對應.dynamic: DT_PLTGOT Elf32_Rel *plt_rel; //函數重定位表  
  13.     unsigned plt_rel_count;  
  14.     Elf32_Rel *rel; //符號重定位表 unsigned rel_count;  
  15.     ....  
  16. };  

    得到這個結構體時,已經執行了init_array,已經實現瞭解密。剩下的工作就是如何恢復原so了,這部分參考ELF section修復的一些思考和從零打造簡單的SODUMP工具


    1.3 針對無源碼加密Section或者函數和自定義loader來加載SO,即從內存加載SO

    和針對有源碼加密Section或者函數類似,但不像原來那樣,只要在ndk開發中調用dlopen即可。從soinfo結構體恢復so文件的時機,要選擇在Android源碼中,具體時機,如果日後有需要,再做研究。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章