Android SO 加壳(加密)与脱壳思路

2023-05-16

   0x01 常见的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中被保护的代码,在手机上效率是一个问题。    

 

    0x02 对应的脱壳思路

    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:

 

Dynamic section at offset 0x2e8c contains 32 entries:
  Tag        Type                         Name/Value
 0x00000003 (PLTGOT)                     0x3fd4
 0x00000002 (PLTRELSZ)                   64 (bytes)
 0x00000017 (JMPREL)                     0xc74
 0x00000014 (PLTREL)                     REL
 0x00000011 (REL)                        0xc24
 0x00000012 (RELSZ)                      80 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   7
 0x00000006 (SYMTAB)                     0x18c
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0x51c
 0x0000000a (STRSZ)                      1239 (bytes)
 0x00000004 (HASH)                       0x9f4
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x0000000e (SONAME)                     Library soname: [libPLTUtils.so]
 0x0000001a (FINI_ARRAY)                 0x3e80
 0x0000001c (FINI_ARRAYSZ)               8 (bytes)
 0x00000019 (INIT_ARRAY)                 0x3e88
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x00000010 (SYMBOLIC)                   0x0
 0x0000001e (FLAGS)                      SYMBOLIC BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x6ffffff0 (VERSYM)                     0xb74
 0x6ffffffc (VERDEF)                     0xbe8
 0x6ffffffd (VERDEFNUM)                  1
 0x6ffffffe (VERNEED)                    0xc04
 0x6fffffff (VERNEEDNUM)                 1
 0x00000000 (NULL)                       0x0

    我们看到PLTGOT的value为0x3fb4。

 

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

 

Relocation section '.rel.dyn' at offset 0xc24 contains 10 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00003e80  00000017 R_ARM_RELATIVE   
00003fb4  00000017 R_ARM_RELATIVE   
00003fb8  00000017 R_ARM_RELATIVE   
00003fbc  00000017 R_ARM_RELATIVE   
00003fc0  00000017 R_ARM_RELATIVE   
00003fc8  00000017 R_ARM_RELATIVE   
00003fcc  00000017 R_ARM_RELATIVE   
00004004  00000402 R_ARM_ABS32       00000000   puts
00003fc4  00000915 R_ARM_GLOB_DAT    00000000   __gnu_Unwind_Find_exid
00003fd0  00001f15 R_ARM_GLOB_DAT    00000000   __cxa_call_unexpected

Relocation section '.rel.plt' at offset 0xc74 contains 8 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00003fe0  00000216 R_ARM_JUMP_SLOT   00000000   __cxa_atexit
00003fe4  00000116 R_ARM_JUMP_SLOT   00000000   __cxa_finalize
00003fe8  00000416 R_ARM_JUMP_SLOT   00000000   puts
00003fec  00000916 R_ARM_JUMP_SLOT   00000000   __gnu_Unwind_Find_exid
00003ff0  00000f16 R_ARM_JUMP_SLOT   00000000   abort
00003ff4  00001116 R_ARM_JUMP_SLOT   00000000   memcpy
00003ff8  00001c16 R_ARM_JUMP_SLOT   00000000   __cxa_begin_cleanup
00003ffc  00001d16 R_ARM_JUMP_SLOT   00000000   __cxa_type_match

    我们可以看到0x00003fb4恰好是其分界线。

 

    在ida中,是这样的。

.got:00003FB4 ; ===========================================================================
.got:00003FB4
.got:00003FB4 ; Segment type: Pure data
.got:00003FB4                 AREA .got, DATA
.got:00003FB4                 ; ORG 0x3FB4
.got:00003FB4 global_fun_ptr  DCD global_fun          ; DATA XREF: Java_com_example_ndkplt_PLTUtils_pltTest+16o
.got:00003FB4                                         ; Java_com_example_ndkplt_PLTUtils_pltTest+18r ...
.got:00003FB8 __aeabi_unwind_cpp_pr0_ptr DCD __aeabi_unwind_cpp_pr0 ; DATA XREF: sub_E54+1Cr
.got:00003FB8                                         ; .text:off_E98o
.got:00003FBC __aeabi_unwind_cpp_pr1_ptr DCD __aeabi_unwind_cpp_pr1 ; DATA XREF: sub_E54+28r
.got:00003FBC                                         ; .text:off_E9Co
.got:00003FC0 __aeabi_unwind_cpp_pr2_ptr DCD __aeabi_unwind_cpp_pr2 ; DATA XREF: sub_E54+34r
.got:00003FC0                                         ; .text:off_EA0o
.got:00003FC4 __gnu_Unwind_Find_exidx_ptr DCD __imp___gnu_Unwind_Find_exidx
.got:00003FC4                                         ; DATA XREF: sub_EA4+8r
.got:00003FC4                                         ; .text:off_F9Co
.got:00003FC8 off_3FC8        DCD aCallTheMethod      ; DATA XREF: sub_EA4+48r
.got:00003FC8                                         ; .text:off_FA0o
.got:00003FC8                                         ; "call the method"
.got:00003FCC off_3FCC        DCD 0x2304              ; DATA XREF: sub_EA4+4Cr
.got:00003FCC                                         ; .text:off_FA4o
.got:00003FD0 __cxa_call_unexpected_ptr DCD __cxa_call_unexpected ; DATA XREF: sub_150C+3A8r
.got:00003FD0                                         ; .text:off_18F8o
.got:00003FD4 _GLOBAL_OFFSET_TABLE_ DCD 0             ; DATA XREF: .plt:00000CBCo
.got:00003FD4                                         ; .plt:off_CC4o
.got:00003FD8                 DCD 0
.got:00003FDC                 DCD 0
.got:00003FE0 __cxa_atexit_ptr DCD __imp___cxa_atexit ; DATA XREF: __cxa_atexit+8r
.got:00003FE4 __cxa_finalize_ptr DCD __imp___cxa_finalize ; DATA XREF: __cxa_finalize+8r
.got:00003FE8 puts_ptr        DCD __imp_puts          ; DATA XREF: puts+8r
.got:00003FEC __gnu_Unwind_Find_exidx_ptr_0 DCD __imp___gnu_Unwind_Find_exidx
.got:00003FEC                                         ; DATA XREF: __gnu_Unwind_Find_exidx+8r
.got:00003FF0 abort_ptr       DCD __imp_abort         ; DATA XREF: abort+8r
.got:00003FF4 memcpy_ptr      DCD __imp_memcpy        ; DATA XREF: memcpy+8r
.got:00003FF8 __cxa_begin_cleanup_ptr DCD __imp___cxa_begin_cleanup
.got:00003FF8                                         ; DATA XREF: __cxa_begin_cleanup+8r
.got:00003FFC __cxa_type_match_ptr DCD __imp___cxa_type_match
.got:00003FFC                                         ; DATA XREF: __cxa_type_match+8r
.got:00003FFC ; .got          ends

    我们在0x00003fb4地址前,看到了:

 

 

00004004  00000402 R_ARM_ABS32       00000000   puts
00003fc4  00000915 R_ARM_GLOB_DAT    00000000   __gnu_Unwind_Find_exid
00003fd0  00001f15 R_ARM_GLOB_DAT    00000000   __cxa_call_unexpected

    在这个地址后面,看到了:

00003fe0  00000216 R_ARM_JUMP_SLOT   00000000   __cxa_atexit
00003fe4  00000116 R_ARM_JUMP_SLOT   00000000   __cxa_finalize
00003fe8  00000416 R_ARM_JUMP_SLOT   00000000   puts
00003fec  00000916 R_ARM_JUMP_SLOT   00000000   __gnu_Unwind_Find_exid
00003ff0  00000f16 R_ARM_JUMP_SLOT   00000000   abort
00003ff4  00001116 R_ARM_JUMP_SLOT   00000000   memcpy
00003ff8  00001c16 R_ARM_JUMP_SLOT   00000000   __cxa_begin_cleanup
00003ffc  00001d16 R_ARM_JUMP_SLOT   00000000   __cxa_type_match

 

 

    1.2 针对有源码加密Section或者函数
    使用dlopen来加载so,返回一个soinfo结构体如下:

struct soinfo {
    const char name[SOINFO_NAME_LEN]; Elf32_Phdr *phdr; //Elf32_Phdr 实际内存地址 int phnum;
    unsigned entry;
    unsigned base; //SO 起始
    unsigned size; //内存对齐后占用大小
    int unused; // DO NOT USE, maintained for compatibility. unsigned *dynamic; //.dynamic 实际内存地址
    unsigned wrprotect_start; //mprotect 调用 unsigned wrprotect_end;
    soinfo *next; //下一个 soinfo unsigned flags;
    const char *strtab; //.strtab 实际内存地址 Elf32_Sym *symtab; //. symtab 实际内存地址
    //hash 起始位置:bucket – 2 * sizeof(int) unsigned nbucket; //size = nbucket * sizeof(int) unsigned nchain; //size = nchain * sizeof(int) unsigned *bucket;
    unsigned *chain;
    unsigned *plt_got; //对应.dynamic: DT_PLTGOT Elf32_Rel *plt_rel; //函数重定位表
    unsigned plt_rel_count;
    Elf32_Rel *rel; //符号重定位表 unsigned rel_count;
    ....
};

    得到这个结构体时,已经执行了init_array,已经实现了解密。剩下的工作就是如何恢复原so了,这部分参考ELF section修复的一些思考和从零打造简单的SODUMP工具。

 

    1.3 针对无源码加密Section或者函数和自定义loader来加载SO,即从内存加载SO

    和针对有源码加密Section或者函数类似,但不像原来那样,只要在ndk开发中调用dlopen即可。从soinfo结构体恢复so文件的时机,要选择在Android源码中,具体时机,如果日后有需要,再做研究。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android SO 加壳(加密)与脱壳思路 的相关文章

  • 设置 CollapsingToolbarLayout 的起始高度

    我希望能够在 CollapsingToolbarLayout 内的 ImageView 上滚动 那么这怎么可能 以及如何设置该图像视图的起始高度 我的 ImageView 高度是 280p 在活动开始时我想显示 200p 然后我可以向下滚动
  • 移动网站 Facebook 使用 Facebook App 登录以获取登录详细信息

    使用网络浏览器 例如Android 上的 Chrome 如果移动网站要求用户登录 Facebook 而用户没有登录 则即使手机可能通过 Facebook 本机应用程序登录 Facebook 浏览器也会要求用户提供登录详细信息 有没有什么方法
  • 如何使用 ArrayAdapter

    ArrayList
  • 从当前位置出发的移动网络行车路线

    我正在构建一个网站的移动版本 试图通过一键式链接来启动 Google 地图 并提供从用户当前位置到企业的行车路线 我让它在 iPhone 上运行良好 但在 Android 上测试时 它会查看 Current 20Location 并尝试查找
  • 检查从 arrayadapter 获取的复选框

    我有标题清单 CheckBox 我想控制默认检查哪一个 所以我试图获得正确的视图并检查它 但由于某种原因它不起作用 知道为什么吗 form checkbox item xml
  • 像 Google Play 商店一样在垂直 RecyclerView 中动态不同图像水平 RecyclerView

    我一直在关注这个教程 http android pratap blogspot co za 2015 12 horizo ntal recyclerview in vertical html http android pratap blog
  • overridePendingTransition 显示第二个活动的速度太快

    我有 2 个活动 我想在两个活动之间创建一个动画过渡 以便两个活动的视图向上滑动 就好像第二个活动正在向上推动第一个活动一样 在我的第一个活动中我使用 Intent iSecondActivity new Intent FirstActiv
  • Android Studio,工具提示消失得这么快

    我有以下问题 我想从这个工具提示中复制错误文本 但是一旦我将鼠标悬停在它上面 它就消失得如此之快 这让我发疯 我有以下 android studio 版本 我有以下设置 谢谢您的帮助 如果有人遇到这个问题 这与logcat刷新的方式有关 每
  • 在路径上找不到类:DexPathList?

    当我在 android studio 中使用 USB 在真实设备中测试时 我的应用程序工作正常 但是当我将 apk 发送到另一台设备并在那里安装时 它无法工作 应用程序崩溃了 我没有找到任何正确的方法来解决问题 错误如下 FATAL EXC
  • Gradle 构建无法解析生成的类

    我刚刚将 Android Studio 从 1 5 更新到 2 2 1 Gradle 也是如此 我有一个项目 它使用注释处理器框架 由我制作 来执行类似 Dagger 的依赖注入 这在更新之前工作得很好 但现在 Gradle 无法解析生成的
  • Android:TelephonyManager 类

    我不明白为什么 API 文档中这么写TelephonyManager类是public 但是当我尝试创建一个实例时 它说它不是公共类 并且无法从包中访问 我看到它也说使用Context getSystemService Context TEL
  • 尝试使用 Facebook ID 获取 Facebook 图像时获取空位图

    这是我正在使用的代码 String imageURL Bitmap bitmap null imageURL http graph facebook com fbID picture type try bitmap BitmapFactor
  • Facebook 好友请求 - 失踪好友

    我请求从我正在开发的 Android 应用程序中获取用户好友 从 Facebook Api V2 0 开始 我知道我应该只获取已经通过我的应用程序登录的用户好友 但是 尽管我知道用户的某些朋友已通过我的应用程序登录 但在请求该用户的朋友时
  • Firestore OncompleteListener [重复]

    这个问题在这里已经有答案了 我想看看这段代码的执行有什么错误 当我编译它时 它只返回 log 1 3 2 的值 并且我希望 log2 在 3 之前 Log d 1 antes de validar DocumentReference doc
  • 如何使 Edittext 大小保持不变?安卓

    我知道使 Edittext 左侧的文本 消失 以保持单行的属性 singleLine true 但我的问题是 当我在显示视图之前填充编辑文本时 在这种情况下 我的编辑文本都超出了屏幕 有任何想法吗 谢谢 这是填充空的 Edittext 时得
  • androidx.navigation.fragment.NavHostFragment 无法从 xml 文件访问

    我正在尝试使用带有底部导航视图的 androidx 导航 因此 当我在 xml 文件中放置带有 android name androidx navigation fragment NavHostFragment 的片段时 它会给我一个错误
  • Android Lollipop:将应用程序小部件添加到主屏幕时启动器崩溃

    添加小部件时 启动器在 Android Lollipop 上崩溃 并显示以下消息 在以前的 Android 版本上运行良好 编辑 这只发生在横向方向 12 16 12 35 10 208 E AndroidRuntime 960 java
  • 未捕获的引用错误:cordova 未定义

    这是我的 HelloPlugin js 文件 var HelloPlugin callNativeFunction function success fail resultType return cordova exec success f
  • android 填充包含片段的布局

    问题是什么 我如何膨胀包含片段的布局 我不知道错误消息的含义 请帮我 谢谢 错误信息 09 01 18 44 58 698 E AndroidRuntime 20617 Caused by java lang IllegalArgument
  • Volley 在第一次调用方法时返回 null

    我正在尝试使用 volley 从服务器检索数据 但是当我第一次调用此方法时 我收到服务器的响应 但该方法返回 null 如果我第二次调用它 我会得到最后的响应 public String retrieveDataFromServer Str

随机推荐