转载请注明出处
前段时间玩《吃货大食堂》,不知从哪个应用市场,下了个第三方修改的,进入的时候会有广告页,还会在后台不停的pull广告数据发到通知栏,觉得蛮有意思,大概的做法应该是反编译了原app,然后加入了自己的代码。寻找这方面的资料,找到本《Android软件安全与逆向分析》,记录一下读书笔记。不得不说这本书确实很有货,见过的不多的国内作者的好书之一。
ps,虽然说本文的内容对于玩jvm和逆向的高手来说算不上什么,但是
本文涉及到的所有内容均来自互联网或者书籍。禁止用于其他任何商业,非商业/不正当用途,本文仅供学习交流,其他任何用途所产生的利益和责任与作者无关,谢谢!
1.准备
android sdk,java是必须的。另准备apktool和jd-gui。见这篇blog。
将apktool所在的目录加到环境变量
2.先来一个简单的,修改跳转逻辑。
先写个sample,第一个activity界面上一个输入框,一个按钮,输入“zuoshu”时点击按钮会跳转到第二个activity.输入其他内容则finish().
将编译出来的apk放到d:\hack\Inject.apk,命令行下
cd d:\hack
apktool d -f Inject.apk
会生成d:\hack\Inject目录
java中跳转的逻辑部分是如下写的,MainActivity.java
Button go = (Button) findViewById(R.id.go); go.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String str = text.getText().toString(); if (str.equals("zuoshu")) { goSecond(); } else { MainActivity.this.finish(); } } });
看到Inject目录下有MainActivity.smali和MainActivity$1.smali两个文件,后面一个才是匿名OnClickListener的代码。onClick部分如下
# virtual methods .method public onClick(Landroid/view/View;)V .locals 2 .parameter "v" .prologue .line 24 iget-object v1, p0, Lcom/example/injectactivtytest/MainActivity$1;->val$text:Landroid/widget/EditText; invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable; move-result-object v1 invoke-interface {v1}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v0 .line 25 .local v0, str:Ljava/lang/String; const-string v1, "zuoshu" invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z move-result v1 #hack here if-eqz v1, :cond_0 .line 26 iget-object v1, p0, Lcom/example/injectactivtytest/MainActivity$1;->this$0:Lcom/example/injectactivtytest/MainActivity; #calls: Lcom/example/injectactivtytest/MainActivity;->goSecond()V invoke-static {v1}, Lcom/example/injectactivtytest/MainActivity;->access$0(Lcom/example/injectactivtytest/MainActivity;)V .line 30 :goto_0 return-void .line 28 :cond_0 iget-object v1, p0, Lcom/example/injectactivtytest/MainActivity$1;->this$0:Lcom/example/injectactivtytest/MainActivity; invoke-virtual {v1}, Lcom/example/injectactivtytest/MainActivity;->finish()V goto :goto_0 .end method
smali是dalvik字节码文件,详细的说明在android源码的dalvik\vm\mterp\c目录下
在25行用#hach here标出的地方为跳转判断。"if-eqz v1, :cond_0"意思是如果v1为0,则跳转到":cond_0"处。改为"if-nez v1, :cond_0",不为0则跳转。代码的改动就完了,接下来编译回apk。
先准备个签名文件sign.keystore放到Inject目录下,参考这里.
cd d:\hack\Inject
apktool b
在会生成一个目录dist,里面有个InjectTest.apk
jarsigner -verbose -keystore aeo_android.keystore -signedjar dist\InjectTest_signed.apk dist\InjectTest.apk
完了安装dist\InjectTest.apk即可。输入“zuoshu”的时候不会跳转,输入其他内容时会跳转。
还可以做些其他修改,比如修改原来是跳转到SecondActivity,可以修改为跳转到ThirdActivity之类的。
3.来点高级的,把自己的代码注入到目标程序
思路就是先写个Service,在目标程序启动的时候,在oncreate里加入startservice的代码。为什么不是activity?service是后台运行的。
建立一个Target project,内容是显示helloworld。要做的事情有三个
1>写一个HackService。另建一个Hack project。Service代码如下
public class HackService extends Service { @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Toast.makeText(this, "this is a hack app", Toast.LENGTH_SHORT).show(); } @Override public IBinder onBind(Intent intent) { return null; } }
启动service的话,会显示"this is a hack app".
xml文件配置
<service android:name="com.example.servicetest.HackService" > <intent-filter> <action android:name="com.oneguy.startservice" /> </intent-filter> </service>
另外在MainActivity加入如下启动代码,目的是为了获得启动Service的字节码。
Intent i = new Intent("com.oneguy.startservice"); startService(i);
2>将启动Service的字节码加到Target.apk反编译后的字节码内
a.反编译Hack.apk放在Hack目录,反编译Target.apk放在Target目录。
b.将Hack\smali目录下的HackService.smali拷贝到Target\smali目录下,这是Service的字节码。
c.打开Hack\smali\MainActivity.smali,拷贝启动Service的相关代码,到Target\smali\MainActivity.smali中,大致内容如下
#inject start .line 16 new-instance v0, Landroid/content/Intent; const-string v1, "com.oneguy.startservice" invoke-direct {v0, v1}, Landroid/content/Intent;-><init>(Ljava/lang/String;)V .line 17 .local v0, i:Landroid/content/Intent; invoke-virtual {p0, v0}, Lcom/oneguy/hack/MainActivity;->startService(Landroid/content/Intent;)Landroid/content/ComponentName; #inject end
d.将Hack\AndroidManifest.xml中Service相关的声明拷贝到Target\AndroidManifest.xml中。
3>编译修改后的代码。重新签名即可。
找了个app练手,费了点劲,大致效果如下
4.防御
有万无一失的防御方式么?只能说有办法可以加大hack的难度,一山更有一山高。
作为开发者,大概有如下几个方法
1>代码混淆。这个sdk里面自带工具,能大大提高hack的难度。
2>程序md5自检。玩了一款国内的app,看到了类似代码,没细读,应该是启动时检查程序自身apk的md5,修改过的apk和未修改的apk md5是不一样的。这个检测确实有用,但是,直接将md5这段检测的逻辑hack掉也可以绕过。
3>使用c++或者c#写代码,程序逻辑不在字节码内。这个对于大多数app来说有点不靠谱了。但是像cocos2d的游戏,或者Unity的游戏,基本上hack不了的。
作为普通用户怎么办?
1>安装官方app。修改过的app签名会和以前不一样,安装的时候会提示用户。并且一定看清楚程序所需要的权限。
2>不要轻易root。没收root权限,是给手机的最后一道防御。我认为厂商们鼓吹发烧,个性,并且以"我家的手机能root,我们不做限制“为卖点是极不合适的。刷了root后,有新病毒来的时候,不要过多的指望安全厂商。好在现在病毒传播的途径是通过各个app市场向客户端传播,没有客户端之间的传播,如果像以前pc上邮件,pc,局域网都能传播的话,刷root等于是放病毒进来。
不久前金山公司推出了手机毒霸,并声称手机毒霸具有了清除广告的功能,顿时在业内掀起轩然大波。国内众多靠广告生存的移动开发者们对金山的这一功能进行强烈的谴责,但另一方面,大多数用户都觉得这个功能挺不错的,毕竟国内市场上的应用里的广告真的实在太多了。 当然口水战不是本文关注的重点,只是对手机毒霸的去广告功能产生的极大的兴趣。这东西到底是怎么实现的呢?在好奇心的驱使下,@安卓安全小分队通过一些技术手段分析了一下手机毒霸(v1.6.0)去广告功能的实现原理,在此与大家一起分享。如有错误的地方,敬请不吝赐教!
1. 手机毒霸去广告功能简介
如图所示,当用户打开某款带有广告的应用后,广告在屏幕底部显示了出来,而此时在屏幕的左下角,出现的手机毒霸的一个小窗口。当用户点击这个
小窗口里的叉叉,广告就会被去除。那这个毒霸的小窗口是手机毒霸放出来的一个悬浮窗口吗?是与不是,只需要看一下当前屏幕上View的层级关系即可。请出SDK自带工具hierarchyviewer后,可以发现这个小窗口不是悬浮的,它就是在广告应用的界面之中。到这里读者应该也猜到了,广告应用被手机毒霸注入了。这也是手机毒霸需要申请root权限才能去广告的原因。
2.手机毒霸把什么注进了广告应用进程注入的通常做法是使用ptrace()来使目标进程加载一个预先准备好的.so,然后执行.so里的函数。那广告应用被注入时,加载了哪个.so呢?其实通过访问/proc节点就可以知道了。运行adb shell su 0 cat /proc/xxx/maps(其中xxx是广告应用的pid)。
51914000-51915000 r--s 00014000 b3:14 97609/data/data/com.ijinshan.duba/app_jar/ksremote.jar
5b31f000-5b32c000 rwxp 00000000 b3:14 97610/data/data/com.ijinshan.duba/app_ctrl/libksrootclient.so
5b353000-5b369000 r--p 00000000 b3:14 97612/data/data/com.ijinshan.duba/app_jar/ksremote.dex
5b369000-5b36b000 r--p 00016000 b3:14 97612/data/data/com.ijinshan.duba/app_jar/ksremote.dex
5b36b000-5b388000 r--p 00018000 b3:14 97612/data/data/com.ijinshan.duba/app_jar/ksremote.dex
可以看到广告应用被注入后,加载了3个文件: ksremote.jarksremote.dex 和libksrootclient.so。其中ksremote.jar 包含了两个文件:ksremote.dex 和 adclose.png。这个adclose.png就是刚才提到的关闭广告的小窗口图标
了。顺便提一下,手机毒霸不仅注入普通应用进程,同时也注入了zygote,system_server以及com.android.systemui三个系统进程,但注入之后所做的事情跟注入普通应用进程是不一样的。
3.注入后做了些什么想要实现Java层的注入,通常的做法是首先注入.so,然后通过.so加载.jar/.dex文件。libksrootclient.so 很显然担起了这个重任,并且跳到ksremote.dex中KsRemoteCtrl类的SetAppHook()中去执行。在SetAppHook()里主要做了一下几件事情。
a) 加载图片资源:adclose.png
b) 反射得到ActivityThread类的currentActivityThread()获得当前ActivityThread对象
c) 加载/data/data/com.ijinshan.duba/app_ctrl/libksrootclient.so,并调用native方法StartSocketHook()。这里是在Native层对网络操作函数加Hook。
d) 反射获得当前ActivityThread对象的mInitialApplication成员(Application 类型),从而获得当前包名(Context.getPackageName())
e) 如果发现自己不是"com.ijinshan.duba",就开始更新广告规则f) 替换当前ActivityThread中的mH(Handler类型)的mCallback,用金山自定义的一个callback对象来包裹过原callback并且替换原callback,从而起到hook作用。拦截的消息有:RESUME_ACTIVITY,PAUSE_ACTIVITY,RECEIVER,ACTIVITYBIND_APPLICATION在广告应用中某个Activity的onResume()触发前,注入的代码会先遍历当前Activity里的所有View,一旦找到广告View,就会调用addView()将添加到广告View的附近。同样在Activity的onPause()触发前,会做一些清理工作。
4.广告是怎么“去除”的当用户点击时,注入的代码主要做了如下几件事情:
a)将目标广告view设成View.GONE,从而隐藏。
b)将 设成View.GONE。总的来说,手机毒霸的去广告的功能的用户体验还是不错的。无需修改应用就能去除应用中的广告。而且目前市场上还未曾出现过第二款能去嵌入式广告的安全软件。但同时手机毒霸也存在着一些不足和可以改进的空间:
1)需要root,毕竟并不是所有的用户都会root自己的手机,而且root以后手机的风险将更大。
2)仅能去除部分类型的广告。有些广告手机毒霸目前还检测不出来, 都没能显示。
3)去除的广告有可能复发。因为有些广告插件会反复将广告View设成VISIBLE,而手机毒霸只是去除第一次的显示。
4)手机毒霸的去广告功能其实只是一个UI操作,广告SDK如果还在后台继续下载广告内容的话,依然会占用流量,也会占用一定的CPU。另外,补充一点,手机毒霸的开发人员还真是大大滴狡猾,把好几个重要的.so和.jar文件都隐藏成了.mp3文件,然后打包进了apk。不过还是被我们发现了。
@安卓安全小分队
更多内容请关注 http://blog.sina.com.cn/u/3194858670
我测试的系统为4.1.2其他未试过
教程开始:
1.装RE文件管理器,打开
2. 先备份一下SYSTEM/CSC下的feature.xml吧
3.1然后把SYSTEM/CSC下的feature.xml复制到手机内存也行SD卡也行
用文本编辑器打开
搜索<!-- Camera -->
然后另起一行添加下面代码
<CscFeature_Camera_ShutterSoundMenu>true</CscFeature_Camera_ShutterSoundMenu>
保存
管理器修改权限rw-r-r
替换
重启手机
3.2还有一种直接用re管理器编辑文本慢慢
搜索<!-- Camera -->
然后另起一行添加下面代码
<CscFeature_Camera_ShutterSoundMenu>true</CscFeature_Camera_ShutterSoundMenu>
保存重启手机