如何解压assets目录下的压缩包解压 文件损坏文件

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
public class UnzipAssets {
* 解压Assets中的文件
* @param context上下文对象
* @param assetName压缩包文件名
* @param outputDirectory输出目录
* @throws IOException
public static void unZip(Context context, String assetName,
String outputDirectory) throws IOException {
//创建解压目标目录
File file = new File(outputDirectory);
//如果目标目录不存在,则创建
if (!file.exists()) {
file.mkdirs();
InputStream inputStream = null;
//打开压缩文件
inputStream = context.getAssets().open(assetName);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
//读取一个进入点
ZipEntry zipEntry = zipInputStream.getNextEntry();
//使用1Mbuffer
byte[] buffer = new byte[1024 * 1024];
//解压时字节计数
int count = 0;
//如果进入点为空说明已经遍历完所有压缩包中文件和目录
while (zipEntry != null) {
//如果是一个目录
if (zipEntry.isDirectory()) {
//String name = zipEntry.getName();
//name = name.substring(0, name.length() - 1);
file = new File(outputDirectory + File.separator + zipEntry.getName());
file.mkdir();
//如果是文件
file = new File(outputDirectory + File.separator
+ zipEntry.getName());
//创建该文件
file.createNewFile();
FileOutputStream fileOutputStream = new FileOutputStream(file);
while ((count = zipInputStream.read(buffer)) & 0) {
fileOutputStream.write(buffer, 0, count);
fileOutputStream.close();
//定位到下一个文件入口
zipEntry = zipInputStream.getNextEntry();
zipInputStream.close();
} 解决了一个小问题,hoho。。。还要继续啊,还有很多是和river那个不同的,要加油看明白然后写出自己的呀。。。。
阅读(1213)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'Android 将assets中的压缩文件解压到SD卡',
blogAbstract:'要做单本阅读器,用epub格式,因为是直接将epub放在assets文件中的,要先解压,学习river的
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}查看: 2913|回复: 0
最后登录在线时间65535 小时威望5238 金钱3919466 注册时间阅读权限100帖子精华0积分5900398UID37778
帮朋友转帖也很幸苦gif 有亮点
发米&&版主把我帐号 又封了,哈哈(转帖请注明)
发米的&&别BB你妈原创你妈首发 换个图片就美化了,没脸没皮的
教程放这个吧
本帖不涉及任何插件,就是动手修改替换
本人的软硬件环境是IP 6 IOS 8.1 给出的修改样本压缩包中都有路径说明的
动手能力差的就不要弄了,我只是给你们方法,弄坏了不怪我
不喜欢用插件改,就想直接替换系统文件DIY, 爬了几小时洋文,整理一下头绪,教程如下
但是文件太多,很累搞不清哪个图片对应哪个功能,谁研究一下;
附有我修改的样本是测试效果的 IP6 8.1
不同目录下的.CAR文件都叫Assets.car,不同软硬件 别随意换,搞清楚在换
ThemeEngine.app.zip
(248.68 KB, 下载次数: 6)
前天18:24 上传
点击文件名下载附件
1)最好修改4-5个资源后做一次保存,这个软件还不稳定;(资源修改后请重新检查一遍你修改过的对象)
2)OS X Yosemite(软件作者的环境如此,低于10.10 无法运行)
3)Photoshop
4)如果使用导出PS功能,在导入到原文件会有2象素的误差,不知道是不是我PS版本的问题(导出导入功能会损坏.PNG透明,尽量用拖拽替换)(在我的机器上是可以使用拖拽替换的,有人不能使用,我不知道原因)
6)关于颜色 IOS 7+使用了masking 这种方式,所以 Springboard 上是无法得到你想定义的颜色的,或者你有更好的办法;
7)IP6 使用了@2 但是有些地方你还是要修改@3
8)常见Assets.car 资源路径
Springboard - /System/Library/CoreServices/SpringBoard.app
Telephone - /System/Library/PrivateFrameworks/TelephonyUI.framework
UIKit - /System/Library/Frameworks/UIKit.framework/Artwork.bundle
3.png (45.1 KB, 下载次数: 0)
前天18:27 上传
4.png (111.7 KB, 下载次数: 0)
前天18:27 上传
1.gif (3.16 MB, 下载次数: 0)
前天18:26 上传
2.gif (1.88 MB, 下载次数: 0)
前天18:26 上传
5.png (125.38 KB, 下载次数: 0)
前天18:31 上传
6.png (121.13 KB, 下载次数: 0)
前天18:31 上传
8.png (203.41 KB, 下载次数: 0)
前天18:31 上传
呼吸灯(其实就是滑块)继续弄cydia可以卸载不少插件
删掉滑动解锁文字
替换目录下同名文件 /System/Library/Frameworks/UIKit.framework/Artwork.bundle
(G)IP6-IOS8.1-UIKit-Assets.zip
(5.88 MB, 下载次数: 6)
前天18:33 上传
点击文件名下载附件
11.png (238.88 KB, 下载次数: 0)
前天18:33 上传
(F)IP6-IOS8.1-UIKit-Assets.zip
(5.89 MB, 下载次数: 2)
前天18:34 上传
点击文件名下载附件
12.png (239.58 KB, 下载次数: 0)
前天18:34 上传
IP6-IOS8.1-USpringboard-Assets.zip
(635.28 KB, 下载次数: 4)
前天18:36 上传
点击文件名下载附件
13.png (162.95 KB, 下载次数: 0)
前天18:36 上传
(B)IP6-IOS8.1-USpringboard-Assets.zip
(636.09 KB, 下载次数: 3)
前天18:38 上传
点击文件名下载附件
14.png (292.43 KB, 下载次数: 0)
前天18:38 上传
状态栏修改后记得删相应图片缓存(*代表所有) 可以把下面这条
写进我给的cache.sh 如果你会用就知道有多方便了
/var/mobile/Library/Caches/com.apple.UIStatusBar/*
IP6-IOS8.1-UIKit-Assets.zip
(5.89 MB, 下载次数: 6)
前天18:41 上传
点击文件名下载附件
15.png (78.03 KB, 下载次数: 0)
前天18:40 上传
(L)IP6-IOS8.1-UIKit-Assets.zip
(5.89 MB, 下载次数: 2)
前天18:42 上传
点击文件名下载附件
16.PNG (197.33 KB, 下载次数: 0)
前天18:42 上传
17.PNG (18.94 KB, 下载次数: 0)
前天18:42 上传
信息对话框
ChatKit-Assets.zip
(108.99 KB, 下载次数: 14)
前天18:43 上传
点击文件名下载附件
18.PNG (255.83 KB, 下载次数: 0)
前天18:42 上传
删缓存 注销 你可以编辑把你想做的行为加进去,Filza可以直接打开运行这个脚本,主要方便经常修改系统UI,经常要删各种缓存的人
cache.sh cache.sh.zip (277 Bytes, 下载次数: 38)
cache.sh.zip
(277 Bytes, 下载次数: 3)
前天18:44 上传
点击文件名下载附件
自己用的; 请自己赋权限777;不放心自己文本打开看代码,目录随便 只要你记住路径就行,TERMINAL 运行或者 FILZA 打开
==============================================================================
============================================================================================================================================================
下面讲.artwork文件的编译,工具包我已经修复了,可以对支持IO 8.*;我改出来的样本支持苹果手机 6
列举两个.artwork
19.png (24.63 KB, 下载次数: 0)
前天18:45 上传
1)配置python2.7, Yosemite 系统自带,找到该目录赋予它777 权限,这个目录将做我的工作目录
20.gif (873.63 KB, 下载次数: 0)
前天18:47 上传
iOS-artwork-master.zip
(231.45 KB, 下载次数: 2)
前天18:48 上传
点击文件名下载附件
(231.45 KB, 下载次数: 2) 把解压后文件夹内的所有文件拷贝到python2.7目录下
Python成像库.pkg.zip
(703.02 KB, 下载次数: 2)
前天18:49 上传
点击文件名下载附件
(703.02 KB, 下载次数: 2)
4)提取你的.artwork文件到python2.7目录下
21.gif (4.14 MB, 下载次数: 0)
前天18:50 上传
22.gif (3.15 MB, 下载次数: 0)
前天18:50 上传
我改好的两个.artwork
dataclassIconCache@2x.artwork.zip
(73.81 KB, 下载次数: 10)
前天18:51 上传
点击文件名下载附件
iconCache@2x.artwork.zip
(190.15 KB, 下载次数: 8)
前天18:51 上传
点击文件名下载附件
23.PNG (186.73 KB, 下载次数: 0)
前天18:52 上传
抢个沙发坐坐,感谢楼主分享
厉害&很需要这个&&不过得去借一个电脑了………屌丝没有苹果电脑………回楼主不够xiha于昨天 18:54发表的: 帮朋友转帖也很幸苦gif 有亮点......
话说文件对应的话,楼主可以参考下iOS5的时候的封包教程和手动替换的教程,有几个好帖子说的很清楚
对不够xiha于 18:54:01在楼主发表的人气:+5;
帮朋友转帖也很幸苦gif 有亮点
发米&&版主把我帐号 又封了,哈哈(转帖请注明)
发米的&&别BB你妈原创你妈首发 换个图片就美化 ...精品文章^_^
对不够xiha于 18:54:01在楼主发表的人气:+5;
帮朋友转帖也很幸苦gif 有亮点
发米&&版主把我帐号 又封了,哈哈(转帖请注明)
发米的&&别BB你妈原创你妈首发 换个图片就美化 ...好贴
为什么你可以拖进去替换,我们都不能,而且像素惨不忍睹
对不够xiha于 18:54:01在楼主发表的内容评分:人气:+2;
帮朋友转帖也很幸苦gif 有亮点
发米&&版主把我帐号 又封了,哈哈(转帖请注明)
发米的&&别BB你妈原创你妈首发 换个图片就美化了,没脸没皮的
教程放这个吧
本帖不涉及任何插件,就是动手修改替换
本人的软硬件环境是I……美化技术贴
我不会弄,也是请别朋友给弄的,自己换的不好看啊
支持下 喜欢手动美化&如果有win就方便了
Powered byandroid zip解压缩-android100学习网
android zip解压缩
如果目录中含有中文名称, 要用substr = new String(substr.getBytes(&8859_1&), &GB2312&);这样的语句转换,否则为乱码01/**02* 解压缩功能.03* 将ZIP_FILENAME文件解压到ZIP_DIR目录下.04* @throws E......
如果目录中含有中文名称, 要用substr = new String(substr.getBytes(&8859_1&), &GB2312&);这样的语句转换,否则为乱码&
&&&&&&&&* 解压缩功能.
&&&&&&&&* 将ZIP_FILENAME文件解压到ZIP_DIR目录下.
&&&&&&&&* @throws Exception
&&&&&&&&*/
&&&&&&&&public&int&upZipFile(File zipFile, String folderPath)throws&ZipException,IOException {
&&&&&&&&//public static void upZipFile() throws Exception{
&&&&&&&&&&&&&&&&ZipFile zfile=new&ZipFile(zipFile);
&&&&&&&&&&&&&&&&Enumeration zList=zfile.entries();
&&&&&&&&&&&&&&&&ZipEntry ze=
&&&&&&&&&&&&&&&&byte[] buf=new&byte[1024];
&&&&&&&&&&&&&&&&while(zList.hasMoreElements()){
&&&&&&&&&&&&&&&&&&&&&&&&ze=(ZipEntry)zList.nextElement();
&&&&&&&&&&&&&&&&&&&&&&&&if(ze.isDirectory()){
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Log.d(&upZipFile&,&&ze.getName() = &+ze.getName());
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&String dirstr = folderPath + ze.getName();
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//dirstr.trim();
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&dirstr =&new&String(dirstr.getBytes(&8859_1&),&&GB2312&);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Log.d(&upZipFile&,&&str = &+dirstr);
&&&&&&&&&&&&&&&&&&&&File f=new&File(dirstr);
&&&&&&&&&&&&&&&&&&&&f.mkdir();
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&Log.d(&upZipFile&,&&ze.getName() = &+ze.getName());
&&&&&&&&&&&&&&&&&&&&&&&&OutputStream os=new&BufferedOutputStream(newFileOutputStream(getRealFileName(folderPath, ze.getName())));
&&&&&&&&&&&&&&&&&&&&&&&&InputStream is=new&BufferedInputStream(zfile.getInputStream(ze));
&&&&&&&&&&&&&&&&&&&&&&&&int&readLen=0;
&&&&&&&&&&&&&&&&&&&&&&&&while&((readLen=is.read(buf,&0,&1024))!=-1) {
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&os.write(buf,&0, readLen);
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&is.close();
&&&&&&&&&&&&&&&&&&&&&&&&os.close();
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&zfile.close();
&&&&&&&&&&&&&&&&return&0;
&&&&&&&&* 给定根目录,返回一个相对路径所对应的实际文件名.
&&&&&&&&* @param baseDir 指定根目录
&&&&&&&&* @param absFileName 相对路径名,来自于ZipEntry中的name
&&&&&&&&* @return java.io.File 实际的文件
&&&&&&&&*/
&&&&&&&&public&static&File getRealFileName(String baseDir, String absFileName){
&&&&&&&&&&&&&&&&String[] dirs=absFileName.split(&/&);
&&&&&&&&&&&&&&&&File ret=new&File(baseDir);
&&&&&&&&&&&&&&&&String substr =&
&&&&&&&&&&&&&&&&if(dirs.length&1){
&&&&&&&&&&&&&&&&&&&&&&&&for&(int&i =&0; i & dirs.length-1;i++) {
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&substr = dirs[i];
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&try&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//substr.trim();
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&substr =&new&String(substr.getBytes(&8859_1&),&GB2312&);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&catch&(UnsupportedEncodingException e) {
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ret=new&File(ret, substr);
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&Log.d(&upZipFile&,&&1ret = &+ret);
&&&&&&&&&&&&&&&&&&&&&&&&if(!ret.exists())
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ret.mkdirs();
&&&&&&&&&&&&&&&&&&&&&&&&substr = dirs[dirs.length-1];
&&&&&&&&&&&&&&&&&&&&&&&&try&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//substr.trim();
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&substr =&new&String(substr.getBytes(&8859_1&),&&GB2312&);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Log.d(&upZipFile&,&&substr = &+substr);
&&&&&&&&&&&&&&&&&&&&&&&&}&catch&(UnsupportedEncodingException e) {
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&ret=new&File(ret, substr);
&&&&&&&&&&&&&&&&&&&&&&&&Log.d(&upZipFile&,&&2ret = &+ret);
&&&&&&&&&&&&&&&&&&&&&&&&return&
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&return&打造安全无毒、好玩的单机游戏下载基地
当前位置:
→ 太空殖民地Unity Assets解包工具 v1.2汉化版
太空殖民地Unity Assets解包工具 v1.2汉化版
软件大小:195.0 KB
软件语言:中文
软件类型:游戏工具
授权方式:免费软件
更新时间:
下载次数:1786
软件标签:
运行环境:WinXP, Win7, WinAll
同类人气软件
太空殖民地Unity Assets解包工具是玩家提供给《太空殖民地》一款游戏工具。玩家使用这款工具可以轻松对游戏的资源包级进行解包或者导入,轻松实现管理,很实用的一款工具。  Assets解包工具使用方法:  1.解压文件  2.运行UnityAssetsExplorer.exe  3.点击打开Assets,选择游戏目录下的Assets即可
太空殖民地Unity Assets解包工具 v1.2汉化版
类型: 模拟经营
大小: 611.5 MB
语言: 英文
热门游戏工具
5.6 MB/英文/1.8
6.0 MB/中文/1.8
12.4 MB/中文/1.8
1.4 MB/中文/1.8
98.0 KB/中文/1.8
489.0 KB/中文/1.8
1.5 MB/英文/1.8
补丁排名榜
单机游戏排行榜
1 1.8类型: 射击游戏语言: 中文大小: 1.9 GB
2 1.8类型: 策略游戏语言: 繁体中文大小: 5.0 GB
3 1.8类型: 休闲益智语言: 中文大小: 137.3 MB
4 1.8类型: 即时战略语言: 中文大小: 195.4 MB
5 1.8类型: PSP游戏语言: 中文大小: 82.8 MB
6 1.8类型: 动作冒险语言: 中文大小: 337.4 MB
7 1.8类型: 模拟经营语言: 中文大小: 34.3 MB
8 1.8类型: 休闲街机语言: 英文大小: 2.5 MB
9 1.8类型: 射击游戏语言: 英文大小: 45.4 MB
10 1.8类型: 其他游戏语言: 中文大小: 95.7 MB
1 1.8类型: 策略游戏语言: 繁体中文大小: 5.0 GB
2 1.8类型: 射击游戏语言: 中文大小: 1.9 GB
3 1.8类型: 射击游戏语言: 英文大小: 103.4 MB
4 1.8类型: 模拟经营语言: 中文大小: 34.3 MB
5 1.8类型: 动作冒险语言: 英文大小: 2.5 GB
6 1.8类型: PSP游戏语言: 中文大小: 82.8 MB
7 1.8类型: 休闲益智语言: 中文大小: 137.3 MB
8 1.8类型: 赛车游戏语言: 中文大小: 439.6 MB
9 1.8类型: 即时战略语言: 中文大小: 1.1 GB
10 1.8类型: 射击游戏语言: 中文大小: 941.1 MB
1 1.8类型: 模拟经营语言: 中文大小: 34.3 MB
2 1.8类型: 休闲益智语言: 中文大小: 59.7 MB
3 1.8类型: PSP游戏语言: 中文大小: 82.8 MB
4 1.8类型: 赛车游戏语言: 中文大小: 439.6 MB
5 1.8类型: 即时战略语言: 中文大小: 1.1 GB
6 1.8类型: 其他游戏语言: 日文大小: 1.0 GB
7 1.8类型: 其他游戏语言: 日文大小: 1.7 GB
8 1.8类型: 动作冒险语言: 中文大小: 1.0 GB
9 1.8类型: 休闲益智语言: 中文大小: 137.3 MB
10 1.8类型: 动作冒险语言: 中文大小: 1.2 GB
◎ 因为单机游戏文件比较大,当游网强烈推荐使用迅雷或QQ旋风下载,下载前建议查看配置要求、游戏说明和网友评论。
◎ 如果游戏无法正常运行,运行的时候出现缺少dll、内存不能读、配置不正确等,请查看,或者直接下载游戏常用运行库安装包。
◎ 提供太空殖民地Unity Assets解包工具 v1.2汉化版,保证安全无毒,可能部分杀毒软件存在误报,请大家放心下载,如果怕有危害电脑的,请误下载。
超多下载基地 当游网()
越当越快乐
版权所有 浙ICP备号【原创】APK自我保护方法 - 看雪安全论坛
本站声明:看雪论坛文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者信息及本声明!
注册日期: Jul 2011
现金: 201 Kx
获感谢文章数:8获会员感谢数:46
【原创】APK自我保护方法
标 题: 【原创】APK自我保护方法 作 者: MindMac
时 间: ,21:41:15 链 接: /showthread.php?t=183116
APK&的自我保护
由于&Android&应用程序中的大部分代码使用&Java&语言编写,而&Java&语言又比较容易进
行逆向,所以&Android&应用程序的自我保护具有一定的意义。本文总结了&Android&中可以使
用的一些&APK&自我保护的技术,大部分都经过实际的代码测试。
Dex&文件结构
classes.dex&文件是&Android&系统运行于&Dalvik&Virtual&Machine&上的可执行文件,也是
Android&应用程序的核心所在,所以我们首先来看下&DEX&文件的结构,这样能够更好的理解
后续的分析,需要更加详细的信息,可以参考&Google&关于&Dex&的技术文档。
从&Java&源文件(当然&Android&也支持&JNI&的调用方式)到生成&Dex&文件的基本映射关系
如图&1&所示,Java&源文件通过&Java&编译器生成&class&文件,再通过&dx&工具转换为&classes.dex
文件。Dex&文件从整体上来看是个索引的结构,类名、方法名、字段名等信息都存储在常量
池中,这样能够充分减少存储空间,一个&Dex&文件的基本结构如图&2&所示,相关结构声明定
义在&DexFile.h&中,在&AOSP&中的路径为/dalvik/libdex/DexFile.h。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85913&d=')}}"
图&1&Java&源文件生成&Dex&文件的映射关系
header:&Dex&文件头,包含&magic&字段、adler32&校验值、SHA-1&哈希值、string_ids&的个数
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85930&d=')}}"
图&2&Dex&文件基本结构
以及偏移地址等。Dex&文件头结构固定,占用&0x70&个字节,定义如下所示。
1.&struct&DexHeader&{
2.&u1&magic[8];&/*&includes&version&number&*/
3.&u4&&/*&adler32&checksum&*/
4.&u1&signature[kSHA1DigestLen];&/*&SHA-1&hash&*/
5.&u4&fileS&/*&length&of&entire&file&*/
6.&u4&headerS&/*&offset&to&start&of&next&section&*/
7.&u4&endianT
8.&u4&linkS
9.&u4&linkO
10.&u4&mapO
11.&u4&stringIdsS
12.&u4&stringIdsO
13.&u4&typeIdsS
14.&u4&typeIdsO
15.&u4&protoIdsS
16.&u4&protoIdsO
17.&u4&fieldIdsS
18.&u4&fieldIdsO
19.&u4&methodIdsS
20.&u4&methodIdsO
21.&u4&classDefsS
22.&u4&classDefsO
23.&u4&dataS
24.&u4&dataO
DexStringId:&定义了字符串数据的偏移,&stringDataOff&指向字符串数据;
1.&struct&DexStringId&{
2.&u4&stringDataO&/*&file&offset&to&string_data_item&*/
DexTypeId:&表示应用程序代码中使用到的具体类型,如整型、字符串等,在&Dalvik&字节&
码中表示为&I、Ljava/lang/S,descriptorIdx&指向&DexStringId&列表的索引;
1.&struct&DexTypeId&{
2.&u4&descriptorI&/*&index&into&stringIds&list&for&type&descriptor&*/
DexProtoId:表示方法声明的结构体,shortyIdx&是方法声明字符串,格式为返回值类型
后紧跟参数列表类型,如方法声明为&VI,表示返回值为&V(空,无返回值),参数为&I
(整型),所有的引用类型用&L&表示;returnTypeIdx&指向&DexTypeId&列表的索引,表示返
回值类型;parametersOff&指向&DexTypeList&的偏移,表示参数列表类型;
1.&struct&DexProtoId&{
2.&u2&classI&/*&index&into&typeIds&list&for&defining&class&*/
3.&u2&typeI&/*&index&into&typeIds&for&field&type&*/
4.&u4&nameI&/*&index&into&stringIds&for&field&name&*/
DexFieldId:&表示代码中的字段,classIdx&指向&DexTypeId&列表索引,表示字段所属的类;
typeIdx&表示字段类型,nameIdx&指向&DexStringId&列表索引,表示字段名;
1.&struct&DexFieldId&{
2.&u2&classI&/*&index&into&typeIds&list&for&defining&class&*/
3.&u2&typeI&/*&index&into&typeIds&for&field&type&*/
4.&u4&nameI&/*&index&into&stringIds&for&field&name&*/
DexMethodId:&表示代码中使用的方法,&classIdx&表示方法所属的类,&protoIdx&指向
DexProtoId&列表索引,表示方法原型,nameIdx&表示方法名;
1.&struct&DexMethodId&{
2.&u2&classI&/*&index&into&typeIds&list&for&defining&class&*/
3.&u2&protoI&/*&index&into&protoIds&for&method&prototype&*/
4.&u4&nameI&/*&index&into&stringIds&for&method&name&*/
DexClassDef:&该结构相对要复杂一些,定义了代码中的使用的类,以及相关的代码指令。
1.&struct&DexClassDef&{
2.&u4&classI&/*&index&into&typeIds&for&this&class&*/
3.&u4&accessF
4.&u4&superclassI&/*&index&into&typeIds&for&superclass&*/
5.&u4&interfacesO&/*&file&offset&to&DexTypeList&*/
6.&u4&sourceFileI&/*&index&into&stringIds&for&source&file&name&*/
7.&u4&annotationsO&/*&file&offset&to&annotations_directory_item&*/
8.&u4&classDataO&/*&file&offset&to&class_data_item&*/
9.&u4&staticValuesO&/*&file&offset&to&DexEncodedArray&*/
classIdx&指向&DexTypeId&列表索引,表示该类的类型;accessFlags&是类的访问标志,如
public,private,static&等;superclassIdx&表示父类的类型;interfacesOff&指向一个&DexTypeList
的偏移值,因为&Java&中可以实现多个接口,这里使用列表也就不难理解了;sourceFileIdx
指向&DexStringIdx&列表的索引,表示类所在的源文件名称;annotationsOff&指向注解目录
结构;classDataOff&指向&DexClassData&结构,表示类的数据部分;staticValuesOff&表示类
中的静态数据。
DexClassData&结构体定义在&DexClass.h&文件中,路径为/dalvik/libdex/DexClass.h,声明如
下,header&中包含静态字段个数,实例字段个数,直接方法(通过类直接访问的方法)
个数,虚方法(通过类实例访问的方法)个数;
1.&struct&DexClassData&{
2.&DexClassDataHeader&
3.&DexField*&staticF
4.&DexField*&instanceF
5.&DexMethod*&directM
6.&DexMethod*&virtualM
DexField&表示字段的类型和访问标志,&fieldIdx&指向&DexFieldId;
1.&struct&DexField&{
2.&u4&fieldI&/*&index&to&a&field_id_item&*/
3.&u4&accessF
DexMethod&结构描述了方法的原型、名称、访问标志以及代码指令的偏移地址,methodIdx
指向&DexMethodId&索引,需要注意的是在&Google&的&Dex&文件文档中对此的定义:
index&into&the&method_ids&list&for&the&identity&of&this&method&(includes&the&name&and&descriptor),
represented&as&a&difference&from&the&index&of&previous&element&in&the&list.&The&index&of&the&first
element&in&a&list&is&represented&directly.
注意红色字体部分,表示的是在&Dex&文件中,methodIdx&是相对于前一个&DexMethod&中
的&methodIdx&的增量,例如如果一个类中有两个&directMethods,第一个&directMethod&的
methodIdx&值为&0x13,表示指向索引为&0x13&的&methodIdx,那么第二个&directMethod&的
methodIdx&的值是相对于前一个值的增量,例如&0x01,表示指向索引为&0x14&的&methodIdx;
accessFlags&为方法的访问标志,codeOff&表示指令代码的偏移地址;
1.&struct&DexMethod&{
2.&u4&methodI&/*&index&to&a&method_id_item&*/
3.&u4&accessF
4.&u4&codeO&/*&file&offset&to&a&code_item&*/
DexCode&的结构体声明如下。
1.&struct&DexCode&{
2.&u2&registersS
3.&u2&insS
4.&u2&outsS
5.&u2&triesS
6.&u4&debugInfoO&/*&file&offset&to&debug&info&stream&*/
7.&u4&insnsS&/*&size&of&the&insns&array,&in&u2&units&*/
8.&u2&insns[1];
9.&/*&followed&by&optional&u2&padding&*/
10.&/*&followed&by&try_item[triesSize]&*/
11.&/*&followed&by&uleb128&handlersSize&*/
12.&/*&followed&by&catch_handler_item[handlersSize]&*/
需要注意的是,在&DexClass.h&中,所有的&u4&类型,实际上是&uleb128&类型。每个&uleb128
类型是&leb128&的无符号类型,每个&leb128&类型的数据包含&1-5&个字节,表示一个&32bit
的数值。每个字节只有&7&位有效,最高一位用来表示是否需要使用到下一个字节,比如
如果第一个字节最高位为&1,表示还需要使用到第&2&个字节,如果第二个字节的最高位
为&1,表示会使用到第&3&个字节,以此类推,最多&5&个字节。对于一个&2&个字节的&leb128
类型数据,其结构如图&3&所示。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85931&d=')}}"
图&3&两字节的&leb128&类型数据格式
Dex&中方法的隐藏
此部分内容可参考&Playing&Hide&and&Seek&with&Dalvik&Executables。
前文分析了&Dex&文件的结构,根据&Dex&的文件结构,可以实现对&Dex&中特定方法的隐藏,
这样在使用&baksamli&或者&apktool&工具对&classes.dex&文件进行反汇编时,无法发现隐藏的方
法,不过会有特定的现象发生,其实也是比较容易检测出来的。
在&Dex&文件格式分析中关于&method&的结构体是&DexMethod,如果将&methodIdx&的值指向
另一个&method,同时修改相应的代码偏移量&codeOff(accessFlags&一般不需要修改),修改
后续相应的&methodIdx,则可以实现特定方法的隐藏。对&Dex&文件修改后需要重新计算&Dex
文件的&SHA1&值以及校验值,用来更新&Dex&文件。
隐藏方法的步骤如下:
修改&Dex&文件中需要隐藏方法的&DexMethod&结构体,如图&4&所示,图中隐藏了方法
B。具体包括:&将&DexMethod&的&methodIdx&值设为&0x0,相当于将原先的方法指向了前一个方
访问标志符&accessFlags&一般不需要修改,在&Dex&文件格式里,directMethods
和&virtualMethods&是分开的;
将&codeOffset&设置为前一个方法的代码偏移地址。
更新需隐藏方法的下一个方法的&methodIdx,可以使用公式:
next_method_idx=original_next_method_idx&+&original_method_idx
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85932&d=')}}"
/>重新计算&Dex&的&SHA1&哈希值和&Adler&校验值,并用以更新&DexHeader,可以使用
DexFixer&修复&classes.dex&文件;
重新打包生成&APK&文件:将&APK&解压缩,提取其中出&META-INF&文件夹之外的所有文件;
压缩成&Zip&格式文件;
使用&jarsigner&或者其他工具对生成的&Zip&文件签名,后缀名修改成.apk。
隐藏的方法仍然需要在程序中进行调用,调用隐藏方法的步骤如下:
使用反射调用&android.content.res.AssetManager.openNonAsset&方法打开当前应用程
&序&的&classes.dex&文&件&,&将&数&据&保&存&到&内&存&中&;&还&可&以&通&过&调&用
Context.getPackageCodePath()来获得当前应用程序对应的&apk&文件的路径,利用此
路&径&构&造&ZipFile&对&象&,&进&而&获&取&classes.dex&的&ZipEntry&,&利&用&ZipFile&的
getInputStream(ZipEntry)方法获取&classes.dex&的数据流,核心代码如下所示;
1.&String&apkPath&=&this.getPackageCodePath();
2.&ZipFile&apkfile&=&new&ZipFile(apkPath);
3.&ZipEntry&dexentry&=&zipfile.getEntry(&classes.dex&);
4.&InputStream&dexstream&=&zipfile.getInputStream(dexentry);
修复&Dex&文件,将之前隐藏方法的&DexMethod&结构体恢复;
将修复后的&Dex&数据使用类加载器重新加载;
搜索被隐藏的方法;
调用被隐藏的方法。
需要注意的是,方法在&Dex&文件中是按方法名的字典序排序的,所以需要隐藏的方法如
果是该类中所有方法排序第一个的话,那么&methodIdx&值是个绝对值,如果要隐藏的话就不
是很方便,所以建议可以写个无用的方法,其方法名排序为第一个,让需要隐藏的方法重新
指向该方法。
使用修改&methodIdx&的方法,让其指向另一个&DexMethodId&的结构体,如果使用&baksmali
进行反汇编,则会发现在一个类中有两个完全相同的函数。
那有没有更加隐蔽的手段来隐藏一个方法了?考虑到在&DexClassData&结构体中的
DexClassDataHeader&头部,其中&directMethodsSize&和&virtualMethodsSize&分别表示直接方法个
数和虚方法个数,因此如果希望隐藏某个方法,可以通过将相应的&directMethodsSize&或
virtualMethodsSize&减&1,同时将表示该需要隐藏方法的&DexMethod&结构体中的数据全部修改
为&0,这样就可以将该方法隐藏起来,使用&baksmali&反汇编时,不会显示出该方法的反汇编
代码,具体可以参考&Hashdays&2012&Android&Chanllenge。
当然,上述这两种隐藏方法,都没能隐藏掉&DexMethodId&结构体,这个结构体中包含了
方法所属的类名、原型声明以及方法名,所以可以通过对比&DexMethodId&的个数和&DexMethod
结构体的个数来判断是否存在方法隐藏的问题。
Dex&完整性校验
classes.dex&在&Android&系统上基本负责完成所有的逻辑业务,因此很多针对&Android&应用
程序的篡改都是针对&classes.dex&文件的。在&APK&的自我保护上,也可以考虑对&classes.dex
文件进行完整性校验,简单的可以通过&CRC&校验完成,也可以检查&Hash&值。由于只是检查
classes.dex,所以可以将&CRC&值存储在&string&资源文件中,当然也可以放在自己的服务器上,
通过运行时从服务器获取校验值。基本步骤如下:
&首先在代码中完成校验值比对的逻辑,此部分代码后续不能再改变,否则&CRC&值
会发生变化;
从生成的&APK&文件中提取出&classes.dex&文件,计算其&CRC&值,其他&hash&值类似;
将计算出的值放入&strings.xml&文件中。
核心代码如下:
1.&String&apkPath&=&this.getPackageCodePath();
2.&Long&dexCrc&=&Long.parseLong(this.getString(R.string.dex_crc));
4.&ZipFile&zipfile&=&new&ZipFile(apkPath);
5.&ZipEntry&dexentry&=&zipfile.getEntry(&classes.dex&);
6.&if(dexentry.getCrc()&!=&dexCrc){
7.&System.out.println(&Dex&has&been&*modified!&);
9.&System.out.println(&Dex&hasn't&been&modified!&);
11.&}&catch&(IOException&e)&{
12.&//&TODO&Auto-generated&catch&block
13.&e.printStackTrace();
但是上述的保护方式容易被暴力破解,&完整性检查最终还是通过返回&true/false&来控制
后续代码逻辑的走向,如果攻击者直接修改代码逻辑,完整性检查始终返回&true,那这种方
法就无效了,所以类似文件完整性校验需要配合一些其他方法,或者有其他更为巧妙的方式
APK&完整性校验
虽然&Android&程序的主要逻辑通过&classes.dex&文件执行,但是其他文件也会影响到整个
程序的逻辑走向,以上述&Dex&文件校验为例,如果程序依赖&strings.xml&文件中的某些值,则
修改这些值就会影响程序的运行,所以进一步可以整个&APK&文件进行完整性校验。但是如
果对整个&APK&文件进行完整性校验,由于在开发&Android&应用程序时,无法知道完整&APK&文
件的&Hash&值,所以这个&Hash&值的存储无法像&Dex&完整性校验那样放在&strings.xml&文件中,
所以可以考虑将值放在服务器端。核心代码如下:
1.&MessageDigest&msgDigest&=&
3.&msgDigest&=&MessageDigest.getInstance(&MD5&)
4.&byte[]&bytes&=&new&byte[8192];
5.&int&byteC
6.&FileInputStream&fis&=&
7.&fis&=&new&FileInputStream(new&File(apkPath));
8.&while&((byteCount&=&fis.read(bytes))&&&0)
9.&msgDigest.update(bytes,&0,&byteCount);
10.&BigInteger&bi&=&new&BigInteger(1,&msgDigest.digest());
11.&String&md5&=&bi.toString(16);
12.&fis.close();
14.&从服务器获取存储的&Hash&值,并进行比较
16.&}&catch&(Exception&e)&{
17.&e.printStackTrace();
Android&应用程序开发主要使用&Java&语言,Java&中可以使用反射技术来更加灵活地控制
程序的运行,为&Java&运行时的行为提供了强大的支持。Java&反射机制允许运行中的&Java&程
序对自身进行检查,并能直接操作程序的内部属性或方法,可动态生成类实例、变更属性内
容以及调用方法。关于&Java&反射更详细内容可以参考&Java&programming&dynamics,&Part&2:
Introducing&reflection。
在&Android&中使用反射技术来动态调用方法,可以增加对应用程序进行静态分析的难度。
以下代码是使用&Java&反射的一个简单例子,需要使用反射调用的方法存在于&Reflection&类中。
1.&public&class&Reflection&{
2.&public&void&methodA(){
3.&System.out.println(&Invoke&methodA&);
5.&public&void&methodB(){
6.&System.out.println(&Invoke&methodB&);
以下代码完成对&Reflection&类中方法的直接调用和反射调用。
1.&protected&void&onCreate(Bundle&savedInstanceState)&{
3.&Reflection&reflection&=&new&Reflection();
4.&reflection.methodA();
5.&reflection.methodB();
7.&Class[]&consTypes&=&new&Class[]{};
8.&Class&reflectionCls&=&
9.&String&className&=&&com.example.reflection.Reflection&;
10.&String&methodName&=&&methodA&;
12.&reflectionCls&=&Class.forName(className);
13.&Constructor&cons&=&reflectionCls.getConstructor(consTypes);
14.&Reflection&reflectionIns&=&(Reflection)&cons.newInstance(new&Object[]{});
15.&Method&method&=&reflectionCls.getDeclaredMethod(methodName,&new&Class[]{});
16.&method.invoke(reflectionIns,&new&Object[]{});
17.&}&catch&(Exception&e)&{
18.&//&TODO&Auto-generated&catch&block
19.&e.printStackTrace();
当然以上&Java&反射的例子过于简单,使用&dex2jar&反编译后,用&jd-gui&打开,还是能够很容
易的识别出需要调用的方法,如图&5&所示。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85933&d=')}}"
图&5&使用&dex2jar+jd-gui&反编译结果
所以需要进一步采取措施增加静态分析的难度。反射调用需要获取调用的类名和方法名,而
上述代码将需要调用的类名或方法硬编码在代码中,一方面违背了&Java&反射使用的场景,
Java&反射主要是为了提供程序的运行时动态行为的控制,另一方面并没有增加了静态分析
可以根据程序运行过程中的实时状态来调用相应的方法,从而进一步提高静态分析的难
度。一个可能的应用场景是:根据当前应用程序的状态,从网络服务器获取需要进行反射调
用的方法以及参数信息。例如对于上述例子,类名和方法名都可以从网络获取。这样做的好
处是使得仅仅通过静态分析无法获知程序运行过程中实际调用的方法,也会增加自动化分析
的难度。也可以使用反射加密的方式,将类名、方法名做加密处理,在实际调用时再进行解
密。当然以上两种处理方式可能对性能有较大影响(本身&Java&反射对性能就有一定影响),
不应该频繁使用,而且必须申请网络连接的权限(不过现在凡是个&Android&应用程序,不申
请个网络连接权限都不好意思说自己是个&Android&应用)同时还得需要接入网络。
Android&系统提供了&DexClassLoader&来支持在程序运行过程中动态加载包含&classes.dex
的.jar&或者.apk&文件,如果再结合&Java&反射技术,可以实现执行非应用程序部分的代码。利
用动态加载技术,可以提供逆向分析的难度,在一定程度上可以保护&APK&自身的业务逻辑
防止被破解。
DexClassLoader&的构造函数原型如下:
1.&public&DexClassLoader&(String&dexPath,&String&optimizedDirectory,&String
libraryPath,&ClassLoader&parent)
其中,dexPath&为包含&dex&文件的.apk&或者.jar&路径,optimizedDirectory&是优化后的&dex&文件
的路径,libraryPath&表示&Native&库的路径,parent&是父类加载器。通过&DexClassLoader&实例
化对象,调用&loadClass&加载需要调用的类,获得&Class&对象后,就可以进一步使用&Java&反
射技术来调用相应的方法。如下:
1.&DexClassLoader&classLoader&=&new&DexClassLoader(apkPath,&dexPath,&null,
getClassLoader());
3.&Class&?&&mLoadClass&=
classLoader.loadClass(&com.example.dexclassloaderslave.DexSlave&);
4.&Constructor&?&&constructor&=&mLoadClass.getConstructor(new&Class[]&{});
5.&Object&dexSlave&=&constructor.newInstance(new&Object[]&{});
6.&Method&sayHello&=&mLoadClass.getDeclaredMethod(&sayHello&,&new&Class[]{}&);
7.&sayHello.setAccessible(true);
8.&sayHello.invoke(dexSlave,&new&Object[]{});
9.&}&catch&(Exception&e)
11.&e.printStackTrace();
上述代码实现调用&com.example.dexclassloaderslave.DexSlave&类中的&sayHello&方法。
对于需要通过&DexClassLoader&被调用的.apk&或者.jar&文件的分发,可以将其放入&Android
项目的&assets&或者&res&目录下,也可以将其放在服务器端,在实际需要调用时通过网络获取
文件。为了提高逆向的难度,可以对被调用的.apk&或者.jar&文件采取以下措施进行进一步的
&进行完整性校验,防止文件被篡改;
进行加密处理,在调用加载前进行解密;
对需要调用的函数相关信息使用通过网络获取的方式,而不是硬编码在代码中,可
以真正实现动态调用,提高静态分析的难度;
对于使用网络服务器分发的方式,注意对网络服务器地址的保护,不要以字符串硬
编码的方式写在代码中,对下载请求也需要使用&cookie&等辅助识别的技术。
除了使用&DexClassLoader&类实现动态加载外,还可以使用&dalvik.system.DexFile&类实现
Dex&文件的加载,但是&DexFile&类提供的构造方法在实例化过程中需要在/data/davik-cache&目
录下生成相应的&Dex&文件,而/data/davik-cache&目录对于一般应用程序是没有写权限的,所
以在程序中无法实例化&DexFile&对象,也就无法调用&DexFile.loadClass&方法。所以需要通过反
射调用&DexFile&类的&openDex&方法,具体可以参考该代码中&invokeHidden&函数。
字符串处理
Android&应用程序开发中难免会使用到字符串,如服务器的地址等一些敏感信息,对于
这些字符串如果使用硬编码的方式,容易通过静态分析获取,甚至可以使用自动化分析工具
批量提取。例如若在&Java&源代码中定义一个字符串如下:
1.&String&str&=&&I&am&a&string!&;
则在反编译的.smali&代码中对应的代码如下(寄存器可能会有区别):
1.&const-string&v0,&&I&am&a&string!&
对于自动化分析工具,只需要扫描到&const-string&关键字就可以提取到字符串值。因此应该
尽量避免在源代码中定义字符串常量,比较简单的做法可以使用&StringBuilder&类通过&append
方法来构造需要的字符串,或者使用数组的方式来存储字符串。使用&StringBuilder&构造字符
串反编译后的代码如下,使用这种方式可以增加自动化分析的难度,如果想要完整提取一个
字符串,如果仅仅采用静态分析方法就必须要进行相应的词法语法解析了。
1.&.line&26
2.&.local&v10,&strBuilder:Ljava/lang/StringB
3.&const-string&v11,&&I&
4.&invoke-virtual&{v10,&v11},
&Ljava/lang/StringB-&append(Ljava/lang/S)Ljava/lang/StringB
5.&.line&27
6.&const-string&v11,&&am&
7.&invoke-virtual&{v10,&v11},
Ljava/lang/StringB-&append(Ljava/lang/S)Ljava/lang/StringB
8.&.line&28
9.&const-string&v11,&&a&
10.&invoke-virtual&{v10,&v11},
Ljava/lang/StringB-&append(Ljava/lang/S)Ljava/lang/StringB
11.&.line&29
12.&const-string&v11,&&String&
13.&invoke-virtual&{v10,&v11},
Ljava/lang/StringB-&append(Ljava/lang/S)Ljava/lang/StringB
14.&.line&30
15.&invoke-virtual&{v10},&Ljava/lang/StringB-&toString()Ljava/lang/S
另外也可以对字符串进行加密处理,很多恶意代码就采用了此种方法,例如一些具有
bot&功能的恶意代码会将&C&C&服务器地址以及命令进行加密处理,运行时再进行解密。
为了增加逆向分析的难度,可以将原有代码在&smali&格式上进行乱序处理同时又不会影
响程序的正常运行。乱序的基本原理如图&6&所示,将指令重新布局,并给每块指令赋予一个
label,在函数开头处使用&goto&跳到原先的第一条指令处,然后第一条指令处理完,再跳到
第二条指令,以此类推。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85934&d=')}}"
图&6&代码乱序的基本原理
以两个整数相加为例,Java&代码如下所示:
1.&public&void&test(){
2.&int&a&=&1;
3.&int&b&=&2;
4.&int&c&=&a&+&b;
5.&System.out.println(c);
反编译后的&smali&代码如下所示
1.&.method&public&test()V
2.&.locals&4
4.&.prologue
5.&.line&24
6.&const/4&v0,&0x1
8.&.line&25
9.&.local&v0,&a:I
10.&const/4&v1,&0x2
12.&.line&26
13.&.local&v1,&b:I
14.&add-int&v2,&v0,&v1
16.&.line&27
17.&.local&v2,&c:I
18.&sget-object&v3,&Ljava/lang/S-&out:Ljava/io/PrintS
20.&invoke-virtual&{v3,&v2},&Ljava/io/PrintS-&println(I)V
22.&.line&28
23.&return-void
我们可以根据上述提到的代码乱序原理,将&test&这个函数乱序成如下代码所示(删除了.line):
1.&.method&public&test()V
2.&.locals&4
4.&.local&v2,&c:I
5.&goto&:lab1
7.&sget-object&v3,&Ljava/lang/S-&out:Ljava/io/PrintS
8.&invoke-virtual&{v3,&v2},&Ljava/io/PrintS-&println(I)V
9.&goto&:end
11.&.local&v1,&b:I
13.&add-int&v2,&v0,&v1
14.&goto&:lab3
16.&.local&v0,&a:I
18.&const/4&v0,&0x1
19.&const/4&v1,&0x2
20.&goto&:lab2
23.&return-void
24.&.end&method
最后使用&apktool&重新打包发布。进行代码乱序可以在一定程度上增加逆向分析的难度,例
如可以使用&dex2jar+jd-GUI&工具来分析上述乱序前后的代码。乱序前代码如图&7&所示:
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85935&d=')}}"
图&7&使用&jd-GUI&分析乱序前代码
乱序后的代码如图&8&所示。从乱序前后的代码可以看出,使用代码乱序技术能够在一定程
度上增加逆向分析的难度,当然这是因为&dex2jar&工具在进行代码解析时的问题,如果能够
针对性的处理这种代码乱序的情况,那么这种反编译的情况应该会有所好转。
关于代码乱序的技术,可以参考&ADAM:An&automatic&and&extensible&platform&to&stress&test&android&anti-virus&systems&、&DroidChameleon:Evaluating&&Android&Anti-malware&against&Transformation&Attacks。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85936&d=')}}"
图&8&使用&jd-GUI&分析乱序后代码
模拟器检测
般在分析&APK&的过程中会借助于&Android&模拟器,比如分析网络行为,动态调试等。
因此从&APK&自我保护的角度出发,可以增加对&APK&当前运行环境的检测,判断是否运行在
模拟器中,如果运行在模拟器中可以选择退出整个应用程序的执行或者跳到其他分支。模拟
器检测的手段有很多,下面逐一分析。
1.属性检测
Android&属性系统类似于&Windows&的注册表机制,所有的进程可以共享系统设置值。关
于&Android&属&性&系&统&的&详&细&原&理&,&可&以&参&考&我&对&于&Android&属&性&系&统&的&分&析&文&章
/bbs/showthread.php?t=182901。一些属性值在&Android&模拟器和真机上
是不同的,例如对于&Nexus4&和&SDK&为&4.1.2&的模拟器来说,Build.BRAND&和&Build.DEVICE&属
性值分别如图&9&和图&10&所示。根据这些属性值在真实机器和模拟器上的差别可以比较容易
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85937&d=')}}"
图&9&Nexus4&的&BRAND&和&DEVICE&值
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85938&d=')}}"
图&10&Android&模拟器的&BRAND&和&DEVICE&值
检测&Android&应用程序是否运行在模拟器中。不过对于这种检测方式,绕过也是比较容易的,
我现在想到的有&3&种方式绕过:在源码中修改相应的属性值,重新编译生成内核等镜像文件,再使用这些重新生成
的镜像文件加载模拟器(对于&BRAND&属性值可以修改/build/target/product/generic.mk
文件中的&PRODUCT_BRAND&值,重新编译过程没测试,是不是只需要修改这个值就
能搞定不一定正确,可以参考下&build.prop&生成过程分析);
修改&boot.img&文件;
使用&Xposed&框架,可以&hook&SystemProperties.get&函数,在&before&函数中检查需要&
获取的属性,根据情况修改对应的值,然后返回;
另外还可以通过检测&IMEI,IMSI&等值来判断是否是模拟器,在模拟器中,这两个值默认
分别是&000&和&000。通过以下代码可以获取&IMSI&值:
1.&TelephonyManager&manager&=&(TelephonyManager)getSystemService(TELEPHONY_SERVICE);
2.&String&imsi&=&manager.getSubscriberId();
TELEPHONY_SERVICE&需要申请&android.permission.READ_PHONE_STATE&权限。同样我们可以有
相应的绕过方式,一个相对简单的方法是直接修改&Android&SDK&下/tools/emulator-arm.exe&文
件(Windows&版本)。使用&010Editor&打开&emulator-arm.exe&文件,搜索&CIMI,如图&11,”CIMI.”
后面的&15&位数字值是&IMSI,”CGSN.”后面的&15&位数字为&IMEI,修改这两个值(确保没有运行
模拟器),然后保存。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85939&d=')}}"
图&11&修改&IMSI&和&IMEI
修改后再运行模拟器,此时查看&IMSI&值如所示,IMEI&值如所示,可见可以成功修改这两个
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85940&d=')}}"
图&12&修改后的&IMSI&值
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85941&d=')}}"
图&13&修改后的&IMEI&值
还存在其他的修改方式,可以参考&Hide&the&Emulator以及&Android&emulator&patch&for&configurable
IMEI,&IMSI&and&SIM&card&serial&number。
当然还可以检查一些其他的值,如电池的电池状态、电池电量,Secure.ANDROID_ID,
DeviceId,手机号码等。
2.虚拟机文件检测
相&对&于&真&实&设&备&,&Android&模&拟&器&中&存&在&一&些&特&殊&的&文&件&或&者&目&录&,&如
/system/bin/qemu-props,该可执行文件可以用来在模拟器中设置系统属性。另外还有
/system/lib/libc_malloc_debug_qemu.so&文件以及/sys/qemu_trace&目录。我们可以通过检测这些
特殊文件或者目录是否存在来判断&Android&应用程序是否运行在模拟器中,关键代码如下:
1.&private&static&String[]&known_files&=&{
2.&&/system/lib/libc_malloc_debug_qemu.so&,
3.&&/sys/qemu_trace&,
4.&&/system/bin/qemu-props&
7.&public&static&boolean&hasQEmuFiles()&{
8.&for(String&pipe&:&known_files)&{
9.&File&qemu_file&=&new&File(pipe);
10.&if&(qemu_file.exists())
11.&return&
13.&return&
更完整的代码可以参考&Tim&Strazzere&的&Github&中&anti-emulator,该项目中还列举了其他一些
模拟器检测的方法,如检测&socket&文件/dec/socket/qemud。
3.基于&Cache&行为的模拟器检测方法
BlueBox&关于&Android&模拟器检测的方法
/corporate-blog/android-emulator-detection/
4.基于代码指令执行的模拟器检测方法
DexLabs&关于&Android&模拟器检测的方法
http://dexlabs.org/blog/btdetect
5.其他方法
其他一些检测方法,可以参考如下文献:DISSECTING&THE&ANDROID&BOUNCER
逃离安卓动态检测
Guns&and&Smoke&to&Defeat&Mobile&Malware
DEX&EDUCATION&201&ANTI-EMULATION
INSECURE&MAGZINE&34&–&Introduction&to&Android&malware&analysis
APK&伪加密
APK&实际上是&Zip&压缩文件,但是&Android&系统在解析&APK&文件时,和传统的解压缩软
件在解析&Zip&文件时还是有所差异的,利用这种差异可以实现给&APK&文件加密的功能。Zip
文件格式可以参考&MasterKey&漏洞分析的一篇文章。在&Central&Directory&部分的&File&Header&头
文件中,有一个&2&字节长的名为&General&purpose&bit&flags&的字段,这个字段中每一位的作用
可以参考&Zip&文件格式规范的&4.4.4&部分,其中如果第&0&位置&1,则表示&Zip&文件的该&Central
Directory&是加密的,如果使用传统的解压缩软件打开这个&Zip&文件,在解压该部分&Central
Directory&文件时,是需要输入密码的,如图&14&所示。但是&Android&系统在解析&Zip&文件时并
没有使用这一位,也就是说这一位是否置位对&APK&文件在&Android&系统的运行没有任何影响。
一般在逆向&APK&文件时,会首先使用&apktool&来完成资源文件的解析,dex&文件的反汇编工
作,但如果将&Zip&文件中&Central&Directory&的&General&purpose&bit&flags&第&0&位置&1&的话,
apktool(version:1.5.2)将无法完成正常的解析工作,如图&15&所示,但是又不会影响到&APK&在
Android&系统上的正常运行,如图&16&所示。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85942&d=')}}"
图&14&传统解压缩软件需要输入密码进行解压缩
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85943&d=')}}"
图&15&apktool&解析伪加密的&APK&文件失败
对&APK&文件进行伪加密可以使用这个脚本,在&Python&的&zipfile&模块中,ZipInfo&类中记
录了&Zip&文件中相应的&Central&Directory&的相关信息,包括&General&purpose&bit&flags,在&ZipInfo
类中属性为&flag_bits,因此上述脚本中将需加密的&APK&文件的每个&ZipInfo&的&flag_bits&和&1&做
或操作,实现在&General&purpose&bit&flags&的第&0&位置&1.
而需要去除这些伪加密的标志的话,可以使用这个脚本。相关内容可以参考&BlueBox&之
前提出的一个&Android&Security&Analysis&Chanllenge.。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85944&d=')}}"
图&16&伪加密的&APK&可以正常运行
1.&unsigned&int&gpbf&=&get2LE(lfhBuf&+&kLFHGPBFlags);
2.&if&((gpbf&&&kGPFUnsupportedMask)&!=&0)&{
3.&ALOGW(&Invalid&General&Purpose&Bit&Flag:&%d&,&gpbf);
4.&return&
Manifest&Cheating
AndroidManifest.xml&是&Android&应用程序的配置文件,包含了包名、应用程序名称、申请
的权限信息以及组件信息等。在&Android&应用程序开发,生成&APK&时,aapt&会负责完成资源
的打包,打包会将文本格式的&XML&资源文件编译成二进制格式的&XML&资源文件。将文本格
式的&XML&文件转换成二进制格式,一方面通过字符串资源池的统一管理,减少文件体积;
另一方面二进制格式的&XML&文件解析速度也会更快。在&Android&开发过程中,生成的&R.java
文件中包含了相应的资源类型、名称以及对应的&id&值。资源&id&是&32bit&的整型值,格式
为:0xPPTTNNNN。其中&PP&表示使用该资源的包,TT&代表该资源的类型,而&NNNN&是该类型
中资源的名称。对于应用程序资源,PP&值固定为&7f,而对于被引用的系统资源包,其&PP
值为&01。TT&和&NNNN&一般是&aapt&按照资源出现的顺序生成的。更多分析可以参考罗升阳的
Android&应用程序资源的编译和打包过程分析。
Manifest&Cheating&的基本原理是,在&AndroidManifest&的&application&节点中插入一个未知
id(如&0x0),名称为&name&的属性,其值可以是一个从未定义实现的&Java&类文件名。而对
AndroidManifest&的修改需要在二进制格式下进行,这样才能不会破坏之前&aapt&对资源文件的
处理。由于是未知的资源&id,在应用程序运行过程中,Android&会忽略此属性。但是在使用
apktool&进行重打包时,首先会将&AndroidManifest.xml&转换为明文,进而会包含名称为&name
的属性,而相应的&id&信息会丢失,apktool&重打包会重新进行资源打包处理,由于该&name
属性值是一个未实现的&Java&类,重打包后的应用程序在运行过程中,由于&application&节点
中定义的类是先于所有其他组件运行的,若系统找不到对应的类,会出现运行时错误,Dalvik
虚拟机会直接关闭。另外,也可以实现&name&属性值对应的&Java&类,若此类被调用,则表明
被重打包了,可以采取进一步的措施。这样就可以起到保护自身&APK&的作用,防止被重打
包。但是这种方法也很容易被绕过,只需要在经过&apktool&解码的&AndroidManifest&文件中,
去掉在&application&节点中添加的&name&属性即可。整个过程如下:将&APK&解压缩,提取其中的&AndroidManifest.xml&文件;
使用&axml&工具,修改二进制的&AndroidManifest.xml&文件,在&application&节点下插入
id&未知(如&0x0),名为&name&的属性(值可以任意,只要不对应到项目中的类文件名
即可,如&some.class);
将除&META-INF&文件夹之外的文件压缩成&zip&文件,签名后生成.apk&文件。
若是攻击者使用&apktool&重打包,运行重打包后的文件会出现如下运行时错误:
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85945&d=')}}"
图&17&使用&Manifest&Cheating&重打包后&APK&文件运行时错误
调试器检测
在对&APK&逆向分析时,往往会采取动态调试技术,可以使用&netbeans+apktool&对反汇编
生成的&smali&代码进行动态调试。为了防止&APK&被动态调试,可以检测是否有调试器连接。
Android&系统在&android.os.Debug&类中提供了&isDebuggerConnected()方法,用于检测是否有调
试器连接。可以在&Application&类中调用&isDebuggerConnected()方法,判断是否有调试器连接,
如果有,直接退出程序。
除了&isDebuggerConnected&方法,还可以通过在&AndroidManifest&文件的&application&节点中
加入&android:debuggable=”false”使得程序不可被调试,这样如果希望调试代码,则需要修改
该值为&true,因此可以在代码中检查这个属性的值,判断程序是否被修改过,代码如下:
1.&if(getApplicationInfo().flags&&=&ApplicationInfo.FLAG_DEBUGGABLE&!=&0){
2.&System.out.println(&Debug&);
3.&android.os.Process.killProcess(android.os.Process.myPid());
使用&Java&编写的代码很容易被反编译,因此可以使用代码混淆的方法增加反编译代码
阅读的难度。ProGuard&是一款免费的&Java&代码混淆工具,提供了文件压缩、优化、混淆和
审核功能。在&Eclipse+ADT&开发环境下,每个&Android&应用程序项目目录下会默认生成
project.properties&和&proguard-project.txt&文件。如果需要使用&ProGuard&进行压缩以及混淆,首
先需要在&project.properties&文件中去掉对如下语句的注释:
1.&proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
ProGuard&的相关配置信息需要在&proguard-project.txt&文件中声明,在其中可以设置需要混淆
和保留的类或方法。由于在某些情况下,ProGuard&会错误地认为某些代码没有被使用,如在
只在&AndroidManifest&文件中引用的类,从&JNI&中调用的方法等。对于这些情况,需要在
proguard-project.txt&文件中添加-keep&命令,用来保留类或方法。关于&ProGuard&更加详细的配
置项可以参考&ProGuard&Manual。
除了使用&ProGuard&对&Android&代码进行混淆外,还可以使用&DexGuard。DexGuard&是特别
针对&Android&的一款代码优化混淆的收费软件,提供代码优化混淆、字符串加密、类加密、
Assets&资源加密、隐藏对敏感&API&的调用、篡改检测以及移除&Log&代码。DexGuard&的进一步
分析可以参考&JEB&上的相关&blog,可以在这里总结一下:
1.字符串加密
经过&DexGuard&加固过的&APK,对字符串的访问会通过调用一个解密函数来完成加密字
符串的解密。如图&18&所示,红框中的字节数组是加密后的字符串,在&onCreate&函数中,调
用了解密函数进行解密。字符解密函数如图&19&所示,对其进行处理后如图&20&所示,加密算
法也很简单,基本思路是:当前字符由前一个字符加上加密字符数组中的字符,再减去常量
8&形成,当字符长度达到给定的长度时,会最终构成字符串并返回。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85953&d=')}}"
图&18&DexGuard&字符串加密
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85954&d=')}}"
图&19&字符串解密函数
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85955&d=')}}"
图&20&处理后的解密函数
2.assets&加密
APK&文件的&assets&目录下包含了应用程序需要使用到的资源文件,DexGuard&提供了对
assets&资源文件的加密功能。对于一个经过保护的&asset&资源文件,例如&1.png&文件,使用
十六进制查看器查看该文件,如图&21&所示。从图中可见,加密后的&png&文件,缺失了相应
的文件头。解密则是首先通过反射调用&AssetManager.open&函数,同时对该函数的反射调用
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85956&d=')}}"
图&21&经过加密的&png&文件
又使用了加密处理,最后通过&Cipher&类完成&png&文件的解密。相关解密处理如所示。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85957&d=')}}"
图&22&asset&解密处理
关于&ProGuard&和&DexGuard&还可以参考&ProGuard&and&DexGuard,其中除了介绍了&ProGuard
和&DexGuard,还提供了一些&APK&加固处理的方法。
关于代码混淆,还可以参考&Android:Game&of&Obfuscation。
Android&软件的开发主要使用&Java&语言,但是&Android&也提供了对本地语言&C、C++的支
持。借助&JNI,可以在&Java&类中使用&C&语言库中的特定函数,或在&C&语言程序中使用&Java
类库。一般来说,如果代码中对处理速度有较高要求或者为了更好地控制硬件,抑或者为了
复用既有的&C/C++代码,都可以考虑通过&JNI&来实现对&Native&代码的调用。
由于逆向&Native&程序的汇编代码要比逆向&Java&汇编代码困难,因此可以考虑在关键代
码部位使用&Native&代码,如注册验证,加解密操作等。一个可能的借助&Native&代码保护&APK
的方法是:将核心业务逻辑代码放入加密的.jar&或者.apk&文件中,在需要调用时使用&Native
代码进行解密,同时完成对解密后文件的完整性校验,不过不管是.jar&还是.apk&文件,解密
后都会留在物理存储上,为了避免这种情况,可以使用反射技术直接调用
dalvik.system.DexFile.openDex()方法,该方法接受&classes.dex&文件字节流返回&DexFile&对象。
关于&Native&代码的编写,可以参考&Google&官方文档的&Android&NDK。
逆向工具对抗
在逆向分析&Android&应用程序时,一般会使用&apktool,baksmali/smali,dex2jar,androguard,
jdGUI&以及&IDA&Pro&等。因此可以考虑使得这些工具在反编译&APK&时出错来保护&APK,这些工
具大部分都是开源的,可以通过阅读其源代码,分析其在解析&APK、dex&等文件存在的缺陷,
在开发&Android&应用程序时加以利用。可以参考&Tim&Strazzere&的&Dex&Education:Practicing&Safe
Dex,相应的&Demo,看雪上的中文翻译,不过其中的很多技巧已经失效了。DexLabs&的&Dalvik
Bytecode&Obfuscation&on&Android&介绍了垃圾字节码插入的技术。
使用&apktool&进行重打包时,对于后缀为&png&的文件,会按照&png&格式的文件进行打包
处理,因此如果在项目开发时,有意将一个非&png&格式文件的文件名改为后缀为&png&的文件,
则使用&apktool&进行重打包时会出错。可以利用这种方法来对抗重打包。可以试试对这个文
件使用&apktool&进行重打包,会报很多错误,但是这种&appt&导致的错误,很多都是由于第一
个错误一起的,如图&23&所示。从第一个错误描述中可知,res/drawable-hdpi/station.png&不是
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85958&d=')}}"
图&23&apktool&重打包错误
一个&PNG&格式的文件,使用&file&命令,可以发现实际上是一个&Windows&icon&文件,如图&24
所示。将这个文件后缀修改成.icon&就可以重新打包了。
screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('/attachment.php?s=a2a56bab8c90d1f603bbbaf&attachmentid=85959&d=')}}"
图&24&station.png&的真实文件类型
以上&APK&自我保护的技术并不能做到完全的保护作用,只是提高了逆向分析的难度,
在实际运用中应该根据情况多种技术结合使用。这些技术其实很多来源于&Android&恶意代码,
所以可以关注&Android&恶意代码中使用的一些技术来应用到自己开发的&Android&应用程序中。
注:本帖由看雪论坛志愿者PEstone&重新将pdf整理排版,若和原文有出入,以原作者附件为准&
总结的一些关于APK自我保护的方法,当然还有很多其他的技巧,无法一一列举,现在可以使用的一些服务包括:
2.爱加密&:
3.APKProtect&:
4.Shield4J&:
5.DexGuard&:
欢迎补充!*转载请注明来自看雪论坛@
上传的附件
(1.37 MB, 2633 次下载)
被 PEstone 最后编辑
共 26 位会员感谢 MindMac 发表的文章:
&(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &(), &()
注册日期: Sep 2007
现金: 142 Kx
获感谢文章数:2获会员感谢数:3
, 21:58:35
最初由 MindMac发布
总结的一些关于APK自我保护的方法,当然还有很多其他的技巧,无法一一列举,现在可以使用的一些服务包括:
1.梆梆&:/&(&/&)
2.爱加密&:htt...
注册日期: Nov 2011
现金: 232 Kx
致谢数: 10
获感谢文章数:3获会员感谢数:7
, 22:11:00
注册日期: Dec 2013
现金: 101 Kx
获感谢文章数:4获会员感谢数:9
, 22:21:09
注册日期: Nov 2011
现金: 7 Kx
获感谢文章数:0获会员感谢数:0
, 23:24:35
其实还有个方向,就是防注入,比如卸载被注入的so,ptrace自己等等
注册日期: Oct 2005
现金: 157 Kx
致谢数: 11
获感谢文章数:5获会员感谢数:5
, 12:22:28
支持加收藏加下载!
多谢楼主分享!
『Android安全』版主
注册日期: Feb 2010
现金: 1231 Kx
获感谢文章数:6获会员感谢数:48
, 14:38:49
最近给MindMac加精华加得手都酸了……
注册日期: Jul 2011
现金: 201 Kx
获感谢文章数:8获会员感谢数:46
, 18:10:30
最初由 Claud发布
最近给MindMac加精华加得手都酸了……...
你确定不是因为其他事情手酸?
你是版主,我在积极支持你的工作:P
注册日期: Oct 2009
现金: 161 Kx
获感谢文章数:8获会员感谢数:11
, 18:15:47
是哪只手酸,这个要搞清楚,嘿嘿!!学习,学习,多谢分享!
『Android安全』版主
注册日期: Feb 2010
现金: 1231 Kx
获感谢文章数:6获会员感谢数:48
, 19:20:34
最初由 MindMac发布
你确定不是因为其他事情手酸?
你是版主,我在积极支持你的工作:P
我错了,您加油……
注册日期: Aug 2013
现金: 100 Kx
致谢数: 13
获感谢文章数:3获会员感谢数:9
, 20:14:45
收藏备用,版主右手都酸了
注册日期: Oct 2008
现金: 228 Kx
获感谢文章数:1获会员感谢数:1
, 20:29:10
『临时会员版』版主
注册日期: Jul 2011
现金: 1012 Kx
致谢数: 23
获感谢文章数:12获会员感谢数:78
, 01:27:44
好东西great
注册日期: Nov 2008
现金: 208 Kx
获感谢文章数:1获会员感谢数:1
, 08:12:26
收藏稍后慢慢看
注册日期: Nov 2011
现金: 97 Kx
致谢数: 52
获感谢文章数:1获会员感谢数:2
, 09:03:38
您不可以发表主题
您不可以回复帖子
您不可以上传附件
您不可以编辑自己的帖子
论坛论坛启用
用户控制面板
会员在线状态
CrackMe攻击篇,分析文章提交区
『看雪众测/众包』
『Android 安全』
『Android 开发』
『iOS安全』
『求助问答』
『经典问答』
『资料导航』
『软件调试逆向』
『密码学』
『编程技术』
『C32Asm』
『MDebug』
『安全工具开发』
『加壳与脱壳』
『CrackMe&ReverseMe』
『资源下载』
『WEB安全』
『漏洞分析』
『外文翻译』
『招聘专区』
『职业生涯』
『15PB培训』
『麦洛克菲培训』
『茶余饭后』
『安全资讯』
『论坛活动』
6)PEDIY Crackme竞赛2009
7)看雪十周年专版
8)腾讯公司2010软件安全竞赛
9)2011 Exploit Me竞赛
『图书项目版』
《加密与解密(第三版)》
《C++反汇编与逆向分析技术揭秘》
《Android软件安全与逆向分析》
『论坛版务』
所有时间均为北京时间, 现在的时间是 .
&&& 看雪学院()
| 提供带宽资源
|&微信公众帐号:}

我要回帖

更多关于 不解压查看压缩包文件 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信