Android SO(动态链接库)UPX加固指南
前言
随着移动互联网的爆发性增长,人们对移动应用的需求变得越来越复杂,企业在带给用户众多便利和享受的同时,却容易忽视应用自身的安全性问题,一旦遭受攻击,就会给企业和用户的经济或声誉带来影响。本文主要是站在企业的角度,阐述如何通过给android SO(动态链接库)加壳来提升移动APP的安全性,减少SO被逆向反汇编分析的风险。
注:本文只做单方面的总结,如果对整体提升移动应用安全性有需求的人员,可参考作者另外一份文档:《移动应用安全开发指南v1.0(Android)》。
撰写本文的目的:
1、 为移动应用开发人员提供安全加固技术指导,作为发布时加固的实际依据。
2、 对实施过程做详细的记录和总结,为需要单独创建加固环境的人员提供具体细节,避免或少走弯路。
建议使用方法:
对于需要创建加固环境的人员,请阅读“创建加固环境”章节,对于只需要在加固环境下对SO加固的人员(比如开发人员),只需了解“加固步骤”章节即可。
创建加固环境(X64 Linux)
1、下载UPX和依赖组件的源码
UPX -3.92-src:https://www.pysol.org:4443/hg/upx.hg/archive/tip.tar.gz
注:v3.92为写本文档时的最新官方非正式发布版本(正式发布版本为v3.91)
下载入口如下:
LZMA4.43:http://nchc.dl.sourceforge.net/project/sevenzip/LZMA%20SDK/4.43/lzma443.tar.bz2
UCL1.03:http://www.oberhumer.com/opensource/ucl/download/ucl-1.03.tar.gz
2、删除UPX壳描述信息
编辑$(UPX_SRC_ROOT)/src/packer.cpp,删除该文件中定义的关于UPX壳的相关描述信息(详情请参考附录à相关问题总结5.5)。
3、编译
3.1、编译zlib:
tar zxvf zlib-1.2.3.tar.gz
cd zlib-1.2.3
make //编译生成libz.a
cp libz.a /usr/lib64/libz.a //拷贝到系统默认的动态链接库路径下。
3.2、编译UPX并执行
cd $(UPX_SRC_ROOT) //进入UPX源码根目录
CXX=g++ UPX_UCLDIR=/home/soft/ucl-1.03 UPX_LZMADIR=/home/soft/lzma-4.43 UPX_LZMA_VERSION=0x443 make all //编译UPX
说明:UPX_UCLDIR和UPX_LZMADIR的值分别为UCL和LZMA解压后的根路径,UPX_LZMA_VERSION环境变量则指定了LZMA的版本。
编译成功后可在$(UPX_SRC_ROOT)/src下查看到可执行文件upx.out,如下图所示:
执行效果如下:
加固步骤
1、配置NDK集成开发环境
参考附录《配置和使用NDK集成开发环境》的《配置步骤》章节。
2、修改native代码
2.1、在native代码中定义全局变量用于增加生成的二进制的体积,例如:
C:int const dummy_to_make_this_compressible[100000] = {1,2,3};
C++:extern "C" int const dummy_to_make_this_compressible[100000] = {1,2,3};
注意:如果编译出来的库本身足够大,则此步骤可省略。
2.2、在native代码中声明_init()函数,用于在编译时生成_init段,例如:
C:void _init(void){}
C++:extern "C" {void _init(void){}}
注意:C和C++代码定义或声明的方式是有所区别的,在C++中必须使用extern “C”关键 字进行修饰,被extern "C"修饰的变量和函数是按照C语言方式编译和连接的
2.3、在native代码中使用宏定义混淆函数名,用于增加静态反汇编分析难度,例如:
#define startSimpleWifi sSW
#define sendData sD
……
3、对SO库文件加壳
3.1、打开cygwin,进入Android工程目录,在NDK环境中编译native代码(详情可参考 附录《配置和使用NDK集成开发环境》的《编译native代码》章节),也可以通过CDT 自动编译(参考《配置和使用CDT编译环境》),编译通过后将在libs目录下生成SO 动态链接库文件。
3.2、将编译生成的SO库文件上传到加固服务器(本文将其和UPX执行文件放同一目录), 如下图所示:
3.3、对SO库进行加壳,常用命令:upx.out –o libhello-jniupx.so libhello-jni.so
4、验证
4.1、在eclipse的Android工程中使用加壳SO替换原有的SO,如图所示:
4.2、将使用加壳SO的Android程序安装到设备或模拟器中执行功能验证,若各项功能均正 常则表示加固成功。
加固前后效果对比
1、加固前反汇编:
2、加固后反汇编:
附录
1、术语表
术语 |
定义 |
UPX |
UPX是一个著名的压缩壳,主要功能是压缩可执行文件(比如exe,dll和elf等文件),有时候也可能被病毒用于免杀。 |
加壳 |
加壳的全称应该是可执行程序资源压缩,是保护文件的常用手段。加壳的程序经常想尽办法阻止外部程序或软件对加壳程序的反汇编分析或者动态分析,以达到它不可告人的目的,这种技术也常用来保护软件版权,防止被软件破解。 |
交叉编译 |
就是在一个平台上生成另一个平台上的可执行代码,比如在X86 Linux上编译出可在ARM Linux上执行的程序。 |
JNI |
JNI是Java Native Interface的缩写,中文为JAVA本地调用,它允许Java代码和其他语言(比如C/C++)写的代码进行交互。 |
反汇编 |
把目标代码转为汇编代码的过程,也可以说是把机器语言转换为汇编语言代码、低级转高级的意思,常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域。 |
2、配置和使用NDK集成开发环境
2.1、配置步骤:
2.1.1、http://www.cygwin.com/下载cygwin并双击安装。
2.1.2、选择从internet安装。
2.1.3、选择一种能连上网络的方式
2.1.4、建议选国内的镜像站点(速度快)
2.1.5、在search中输入”make”,选择Devel列表中的所有package进行安装,选择方法是点击package名,由keep变成install字样即可。注:本机已安装,故显示为reinstall。
2.1.6、继续下一步直至安装完毕。
2.1.7、运行cgywin bash,输入cygcheck -c cygwin命令,若显示Cygwin 的package信息则表 示运行正常。
2.1.8、分别运行gcc –version、g++ --version make –version和gdb –version来检查相关组件是 否运行正常。
2.2、编译native代码:
2.2.1、使用NDK编译一个程序,首先我们要找到我们cygwin的程序安装目录,找到一个 home\<你的用户名>\.bash_profile文件,如下图所示
2.2.2、在该文件末尾添加ndk=/cygdrive/<你的盘符>/<android ndk 目录> 例如: ndk=/cygdrive/f/android/adt-bundle-windows-x86-20131030/android-ndk-r9d
export ndk
注:"ndk"这个名字随便起,因为后面要经常使用,建议不要太长。如下图所示
2.2.3、之后重新打开cygwin,输入 cd $ndk,如果进入了android ndk 目录,证明环境变量 设置成功了
2.2.4、尝试使用NDK编译android NDK的样例程序hello-jni,路径为:
<android ndk 目 录>/samples/hello-jni,比如:
F:\android\adt-bundle-windows-x86-20131030\android-ndk-r9d\samples\hello-jni
2.2.5、进入工程根目录
2.2.6、执行$ndk/ndk-build命令,执行成功后它会自动生成一个libs目录,把编译生成的.so文件放里边,使用file命令可查看到文件为经过交叉编译的ARM Linux 动态链接库。
注:执行$ndk/ndk-build实际等于执行NDK目录下的ndk-build命令,如下图所示:
3、配置和使用CDT编译环境(非必须)
3.1、为eclipse安装CDT插件,也可以直接下载带CDT插件的eclipse(略)。
3.2、在eclipse中选择右键选中项目,选择Properties,在弹出的对话框中选择Builders。
3.3、点击对话框中的New新建一个编译器,在新弹出对话框中选择ProgramàOK。
3.4、对新建编译器进行配置,如下图:
参数解析如下:
Name:编译器的名字,可随便取。
Location: <你cygwin安装路径>\bin\bash.exe程序,即cygwin的bash程序的路径。
Working Directory: 你cygwin安装路径>\bin目录。
Arguments:给cygwin bash传递的参数,里面主要包含进入工程目录并使用NDK执行编译的命令。
3.5、接着切换到Refresh选项卡,给Refresh resources upon completion打上钩,如图:
3.6、最后切换到Build Options选项卡,勾选上最后三项,如下图所示:
3.7、点击Specify Resources按钮,选择资源目录,勾选你的项目目录即可,如下图所示:
3.8、保存配置后将回到Properties对话框,点右边的Up按钮,把它排到第一位,否则C代码的编译晚于Java代码的编译,会造成你的C代码要编译两次才能看到最新的修改,如图:
3.9、在eclipse中build Android工程,可以看到同时编译native代码并生成了SO库。
4、APK重打包流程
4.1、下载并安装APK改之理,下载地址:http://www.xiaomiren.net/
4.2、启动APK改之理,选择项目à打开APK 打开要重打包的APK程序。
4.3、打开成功后可看到原有的工程目录结构,打开libs目录可以看到APK需要载入的SO库。
4.4、删除要替换的SO,并把修改过的SO(比如经过加壳的SO)添加进来。
4.5、重新编译APK,即可安装到设备中运行并观察替换SO后的效果。
5、相关问题总结
5.1、编译UPX出现“cannot find -lz”错误。
分析:原因是链接器LD没有找到编译出来的zlib库libz.so或libz.a。
解决方法:将libz.so或libz.a拷贝到系统默认的动态链接库路径下,比如/usr/lib,/usr/lib64 等。
5.2、编译UPX出现“CantPackException: DT_TEXTREL found; re-compile with -fPIC”错误。
分析:这是早期NDK版本的BUG。
解决方案:使用NDK9或以上的版本
5.3、编译UPX出现“NotCompressibleException”错误。
分析:UPX对被加壳的二进制文件有最小限制,太小的文件将无法被加壳。
解决方案:在native代码中定义足够大的数据变量,使得编译出来的二进制文件容易达 到UPX的要求(参考《加固步骤》之《修改native代码》章节)。
5.4、编译UPX出现“UnknownExecutableFormatException”错误。
分析:被加壳的二进制文件必须存在init段,否则UPX将无法脱壳还原原始代码。
解决方案:在native代码中定义_init()方法,需要注意C和C++的区别(参考《加固步 骤》之《修改native代码》章节)。
备注:查看二进制文件是否存在init段的命令:readelf –dynamic xxx.so,如下图:
5.5、使用UPX加壳的SO,在eclipse中启动Android程序时出现”Fatal signal…”错误,如图:
分析:此错误是因为UPX解析某些特殊字符处理不当导致的,该BUG已经有人提交UPX 官方解决,但是当前官方正式发布的正式版本(V3.91)并没有fix该问题,而是在未正 式发布的V3.92才解决了该问题,因此本文档使用源代码版本为V3.92而非V3.91。
解决方案:下载使用V3.92源码,下载入口请参考《创建加固环境》章节。
5.6、为何删除UPX源码中的软件信息,以及如何定位查找这些信息。
分析:UPX对文件进行加壳时会把这些信息写入壳内,通过静态反汇编可查看到这些壳信息,进而寻找对应的脱壳机进行脱壳,使得攻击难度降低。
解决方案:在UPX源码中删除这些信息,并重新编译,步骤如下:
5.6.1、使用原始版本对文件进行加壳。
5.5.2、使用IDA反汇编加壳文件,在反汇编文件的上下文中查找UPX壳特征字符串, 如下图所示:
5.5.3、在UPX源码中查找这些特征字符串(建议使用Search and Replace),并一一删除, 如下图:
5.5.4、重新编译UPX(参考“创建加固环境”章节)。
5.7、在没有eclipse源码工程的情况下如何直接替换APK的SO并观察结果?
解决方案:参考《附录》之《APK重打包流程》,值得注意的是,如果Android程序中 内置了检查签名合法性的安全机制,使用该方法的前提是先破解该签名验证机制。
Android SO(动态链接库)UPX加固指南的更多相关文章
- 【文章内容来自《Android 应用程序开发权威指南》(第四版)】如何设计兼容的用户界面的一些建议(有删改)
最近一直在看的一本书是<Android 应用程序开发权威指南>(第四版),十分推荐.书中讲到了一些用户界面设计的规范,对于初学者我认为十分有必要,在这里码给大家,希望对我们都有用. 在我们 ...
- 《大话移动APP测试:Android与iOS应用测试指南》
<大话移动app测试:android与ios应用测试指南> 基本信息 作者: 陈晔 出版社:清华大学出版社 ISBN:9787302368793 上架时间:2014-7-7 出版日期:20 ...
- 推荐——Monkey《大话 app 测试——Android、iOS 应用测试指南》
<大话移动——Android与iOS应用测试指南> 京东可以预购啦!http://item.jd.com/11495028.html 当当网:http://product.dangdang ...
- 【腾讯开源】Android性能测试工具APT使用指南
[腾讯开源]Android性能测试工具APT使用指南 2014-04-23 09:58 CSDN CODE 作者 CSDN CODE 17 7833 腾讯 apt 安卓 性能测试 开源 我们近日对腾讯 ...
- Android开发之API应用指南
原文:http://android.eoe.cn/topic/android_sdk 编辑流程 这里主要是和Android技术相关的开发指南,很多都是来源于官方的API Guides( http:// ...
- 我的Android进阶之旅------>Android APP终极瘦身指南
首先声明,下面文字转载于: APK瘦身实践 http://www.jayfeng.com/2015/12/29/APK%E7%98%A6%E8%BA%AB%E5%AE%9E%E8%B7%B5/ APP ...
- Android Wear(手表)开发 - 学习指南
版权声明:欢迎自由转载-非商用-非衍生-保持署名.作者:Benhero,博客地址:http://www.cnblogs.com/benhero/ Android Wear开发 - 学习指南 http: ...
- 【开发必备】今天我们来谈谈Android NDK动态链接库(so文件)的一些见解
一.写在前面 直到现在,基本我写的每一个项目都会用到NDK动态链接库的知识,可见这个也的确十分常用.那么,今天,咱们就来谈谈它. 二.什么是ABI和so 1.发展 早起的Android系统几乎只支持A ...
- 一个神奇的控件——Android CoordinatorLayout与Behavior使用指南
CoordinatorLayout是support.design包中的控件,它可以说是Design库中最重要的控件. 本文通过模仿知乎介绍了自定义Behavior,通过模仿百度地图介绍了BottomS ...
随机推荐
- write-ups
https://github.com/MarioVilas/write-ups https://github.com/Deplorable-Mountaineer/Robot_Dynamite htt ...
- sicily 1046. Plane Spotting
1046. Plane Spotting Time Limit: 1sec Memory Limit:32MB Description Craig is fond of planes. Mak ...
- FineReport——表单设计
在单元格的数据设置这一选项中,有分组,列表,汇总三个选项.分组显示,即将相同的项合并,列表则将每一行的数据逐一的展示,不会合并相同的值,每一行的是完整的一条记录,而汇总则是将数字型数据进行汇总. 分组 ...
- javascript方法--apply()
今天琢磨了一下apply,以前对这个方法觉得比较懵,今天一琢磨确实觉得挺好玩的. 一开始把MDN的apply文档看了一遍,感觉不是很理解,而且有一些东西也是知道但是比较模糊,所以还是一步一步来,不懂查 ...
- jQuery实现,动态自动定位弹窗。JS分页,Ajax请求
工作中碰到一个问题,一个页面中碰到多个地方需要弹窗数据. 网上找了一圈,没有找到合适的,所以自己写了一个. 兼容IE7+,chrome.其它未测试. 需求:点击任意的输入框(也可其它元素,代码中有注释 ...
- linux命令(31):lsof命令
1.递归查看某个目录的文件信息: lsof test/test1 2.不使用+D选项,遍历查看某个目录的所有文件信息的方法 :lsof |grep 'test/test3' 3.列出某个用户打开的文 ...
- electron-builder中NSIS相关配置
electron-builder中NSIS相关配置 相比较于electron-packager打包,使用electron-builder打包使得包体积更小.在electron-builder中,对于N ...
- hdu多校第三场
Problem D. Euler Function 思路:打表找找规律. #include<bits/stdc++.h> #define LL long long #define fi f ...
- SPOJ - SUBST1 D - New Distinct Substrings
D - New Distinct Substrings 题目大意:求一个字符串中不同子串的个数. 裸的后缀数组 #include<bits/stdc++.h> #define LL lon ...
- Interllij IDEA常用快捷键
[常规] Ctrl+Shift + Enter,语句完成 “!”,否定完成,输入表达式时按 “!”键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更改的文件 Shift+Click,可以关 ...