Android逆向分析之Cydia

最近开始使用Cydia Substrate框架进行逆向分析。

Cydia是什么东东我就不多说了,自行百度,听说它可以越狱什么的,修改手机配置什么的,但这里只是通过一个例子,介绍如何使用Cydia Substrate框架进行hook Android java代码。

相比Xposed框架,Cydia是有一定的优势的,根据我目前使用的情况,暂时有如下两点:

一是,Cydia能hook除了第一个smali文件夹外的第二第三个smali文件夹(如下图)。

这话怎说?APKTool反编译一个APK时,有时会分割成好几个smali文件,Xposed只能hook第一个smali文件夹下的smali文件,有点坑,但Cydia却都可以hook。


二是,Cydia能hook本地方法,而Xposed不可以



基于以上两个优点,个人认为Cydia比Xposed好用,Xposed局限性较大,起码目前是这么认为的~

但有个技巧的方法只能在Xposed上用,就是通过刻意抛异常,根据异常日志找方法调用路径,这个在逆向分析常用的技巧无法再Cydia上用,因为Cydia抛异常后貌似不会以日志的形式打印出来,反正我试了很多次都不行。。。


开始说说如何使用吧

-------------------------------------------------------

1.  首先,下载com.saurik.substrate.apk,然后安装,手机必须得root过。这个是Cydia Substrate框架,需要另写模块加载进去。


安装之后点击 “Link  Substrate  Files” 即可。


2.  确定需要hook的对象APK中的方法,编写模块代码。

这里,我将hook一个apk里被混淆过的方法,并说明注意点。


因为混淆过,所以类名和方法名都被改成a,b,c等

该方法所在类:com.dianping.h.f.a.o

方法名:b, 

参数:com.dianping.nvnetwork.u 类

返回值 :com.dianping.nvnetwork.u 类


好,开始写模块,先在Android Studio里创建一个工程,该工程不需要Activity,在工程创建一个java文件,该文件就是模块代码,然后下载Cydia Substrate SDK,解压后将其放到libs文件夹下,并将jar包“Add as library”。



接下来,打开AndroidManifest,配置好一个meta-data和一个permission即可:

[html]  view plain  copy
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="com.samuelzhan.substratehook">  
  3.   
  4.     <application>  
  5.         <meta-data android:name="com.saurik.substrate.main"  
  6.             android:value=".Module"/>  
  7.     </application>  
  8.   
  9.     <uses-permission android:name="cydia.permission.SUBSTRATE"/>  
  10.   
  11. </manifest>  

然后,编写模块代码:

[java]  view plain  copy
  1. public class Module {  
  2.   
  3.     static void initialize(){  
  4.   
  5.         MS.hookClassLoad("com.dianping.h.f.a.o",  
  6.                 new MS.ClassLoadHook() {  
  7.                     public void classLoaded(Class<?> resources) {  
  8.                         Method method;  
  9.                         try {  
  10. //                       不能使用本进程的ClassLoader,必须使用hook对象进程的ClassLoader  
  11. //                            Class<?> u=ClassLoader.getSystemClassLoader().loadClass("com.dianping.nvnetwork.u");  
  12. //                            Class<?> u = Class.forName("com.dianping.nvnetwork.u").getClass();  
  13.                             Class<?> u= resources.getClassLoader().loadClass("com.dianping.nvnetwork.u");  
  14.   
  15. //                       getMethod只能获取public方法,getDeclareMethod能获取所有方法  
  16. //                            method = resources.getMethod("b", u);  
  17.                             method = resources.getDeclaredMethod("b", u);  
  18.                               
  19.   
  20.                         } catch (Exception e) {  
  21.                             Log.d("zz""exception: "+e.toString());  
  22.                             method = null;  
  23.                         }  
  24.                         if (method != null) {  
  25.                             final MS.MethodPointer old = new MS.MethodPointer();  
  26.                             MS.hookMethod(resources, method, new MS.MethodHook() {  
  27.                                 @Override  
  28.                                 public Object invoked(Object o, Object... objects) throws Throwable {  
  29.                                     // before hook  
  30.                                     Log.d("zz""hook 成功");  
  31.   
  32.                                     Object result = old.invoke(o, objects);  
  33.   
  34.                                     // after hook  
  35.   
  36.                                     return result;  
  37.                                 }  
  38.                             }, old);  
  39.                         }  
  40.                     }  
  41.                 });  
  42.     }  


经过一个下午的摸索,我这里提出三点注意的地方:

第一,模块和hook对象的进程是两个进程,所以使用ClassLoader加载 u 类时要从resources那里getClassLoader,这是获取hook对象的ClassLoader,不然你加载不了 u 类而抛异常ClassNotFound(当然,如果只是使用java原生的类,即不是自定义的类的话,那么直接  .class后缀就可以获取到 class<?>了)。

第二,获取该类下的方法时,使用getDeclaredMethod可以获取所有方法,包括public,private,protected,而getMethod只能获取public

因为我hook的那个函数是protected的,但由于笔者之前一直用getMethod而导致没法hook到这方法。。。

[java]  view plain  copy
  1. Method method = resources.getDeclaredMethod("b", u);  
第三, 在方法被hook时,回调invoked方法,在此方法内有一句

Object result = old.invoke(o, objects);
该行代码的上面和下面分别对应Xposed的beforeMethodHook和afterMethodHook两个回调方法,如下图:
 
 


3.  模块写好后,导出项目,然后安装到手机上,手机若安装了Cydia SubStrate,则会在通知栏提醒软重启。

重启手机后,我们运行我们的目标APK即可以被Cydia Substrate 模块识别到,并打印出相关日志。

------------------------------------------------

到此为止,Cydia Substrate的基本使用已到此为止。

附加内容(可以忽略):

我上面提到的参数和返回值都是 u类实例,我之所以要hook这个 u类,是因为该 u 类内有个变量 InputStream,混淆后以 g 字符命名。

为了看到这个InputStream的内容,我必须通过反射找到该类的字段g,所以模块的写法会有所变化,但变化不大,增加了Field变量的获取,直接代码:

[java]  view plain  copy
  1. public class Module {  
  2.   
  3.     static void initialize(){  
  4.   
  5.         MS.hookClassLoad("com.dianping.h.f.a.o",  
  6.                 new MS.ClassLoadHook() {  
  7.                     public void classLoaded(final Class<?> resources) {  
  8.                         try {  
  9.                             Class<?> u= resources.getClassLoader().loadClass("com.dianping.nvnetwork.u");  
  10.                             final Method method = resources.getDeclaredMethod("b", u);  
  11.                             final Field field=u.getDeclaredField("g");  
  12.   
  13.                             final MS.MethodPointer old = new MS.MethodPointer();  
  14.                             MS.hookMethod(resources, method, new MS.MethodHook() {  
  15.                                 @Override  
  16.                                 public Object invoked(Object o, Object... objects) throws Throwable{  
  17.                                         // before hook  
  18.                                         InputStream is1=(InputStream)field.get(objects[0]);  
  19.                                         String before=byte2HexString(inputStream2Byte(is1));  
  20.                                         Log.d("zz""Before HexString:"+before);  
  21.                                         is1.reset();  
  22.   
  23.                                         Object result = old.invoke(o, objects);  
  24.   
  25.                                         // after hook  
  26.                                         InputStream is2=(InputStream)field.get(result);  
  27.                                         String after=byte2HexString(inputStream2Byte(is2));  
  28.                                         Log.d("zz""After HexString:"+after);  
  29.                                         is2.reset();  
  30.                                         return result;  
  31.                                 }  
  32.                             }, old);  
  33.   
  34.                         } catch (Exception e) {  
  35.                             Log.d("zz""exception: "+e.toString());  
  36.                         }  
  37.                     }  
  38.                 });  
  39.     }  
  40.   
  41.     public static byte[] inputStream2Byte(InputStream is) throws IOException {  
  42.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  43.         byte[] buffer = new byte[1024];  
  44.         int n = 0;  
  45.         while ((n = is.read(buffer)) != -1) {  
  46.             baos.write(buffer, 0, n);  
  47.         }  
  48.         return baos.toByteArray();  
  49.     }  
  50.   
  51.     public static byte[] hexString2Byte(String hex) {  
  52.         int len = (hex.length() / 2);  
  53.         hex=hex.toUpperCase();  
  54.         byte[] result = new byte[len];  
  55.         char[] achar = hex.toCharArray();  
  56.         for (int i = 0; i < len; i++) {  
  57.             int pos = i * 2;  
  58.             result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));  
  59.         }  
  60.         return result;  
  61.     }  
运行结果:


好了,Cydia的介绍到此为止,其实还有其它功能的,比如hook C/C++的方法,这个日后再继续研究。

猜你喜欢

转载自blog.csdn.net/u012278016/article/details/80706590
今日推荐