• 360安全无线攻防第二题


    题目要求
    -------------
    1.请修改本apk的包名,使得在同一手机上面可以重复安装并正确运行;
    2.请写个Application类,并在Manifest里面注册你的Application。同时要求使用该Application加载原包的Application;
    3.本题3分,以非重打包的方式达到同等效果的不计分。

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

    我参考了看雪上的文章,点击查看

    这篇文章记录做这道题的过程的一些细节

    第一问:

    首先是第一问,重打包。可以用Notepad++之类的工具把Manifest里的包名全部替换成com.qihoo.crack,smali里的改成com/qihoo/crack,并且把包名对应的smali目录名改掉。重点是so里也有包名。

    这个时候第一种方法是保留并删减com/qihoo/test,在smali里只留下这样的内容(对应的smali)就够了:

    package com.qihoo.test;
    
    import android.app.Activity;
    
    public class MainActivity extends Activity
    {
      static
      {
        System.loadLibrary("qihooTest");
      }
    
      public native void applicatioNameCheck();
    
      public native void packageNameCheck();
    }

    然后新建目录com/qihoo/crack,把com/qihoo/test里的东西复制过去,在MainActivity中把

    .super Landroid/app/Activity;

    改成:

    .super Lcom/qihoo/test/MainActivity;

    其实这就对应了把MainActivity extends Activity改成了MainActivity extends com.qihoo.test.MainActivity,如此一来就可以直接使用 com.qihoo.test.MainActivity中的函数,不用创建对象(之前我在继承test的MainActivity的同时又用了它的对象来调用它的函数,不知道是不是有影响。。)。

    注意要把com.qihoo.test.MainActivity中的方法改成public。

    注意,如果把继承改掉,com.qihoo.crack中所有继承自Activity类的方法都要在smali中改成继承这个MainActivity,不然肯定找不到这个方法啊。比如:

      invoke-direct {p0}, Landroid/app/Activity;-><init>()V
    
        .line 44
        const/4 v0, 0x0
    
        iput-object v0, p0, Lcom/qihoo/crack/MainActivity;->contentResolver:Landroid/content/ContentResolver;
    

      上面第一行就得把改成:

       invoke-direct {p0}, Lcom/qihoo/test/MainActivity;-><init>()V
    

      因为Landroid/app/Activity根本没有被继承。

      一开始我的思路是在Lcom/qihoo/crack/MainActivity中重写一个packageNameCheck函数,这个函数调用Lcom/qihoo/test/MainActivity中的packageNameCheck函数(这个方法好像是可以的,现在不想试了)。

      还有个细节:

    invoke-direct {p0}, Lcom/qihoo/crack/MainActivity;->packageNameCheck()V

    中的invoke-direct需要改成invoke-virtual。原因我在后面的链接里总结了:http://www.cnblogs.com/larrylawrence/p/3985464.html

    改smali常常是这样,一定要搞定每个细节,因为错了也没有Hint给你。

    OK,至此,第一问(重打包)用第一种方法已经搞定了。如果不会,可以从看雪的那个链接里下载作者rebuilt好的apk反编译一下就一目了然了。

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

    下面分析第二种方法搞定第一问。

    第二种方法就是原帖里的19楼的朋友提供的方法:

    通过自己创建一个与修改后的包名想对应.so文件,再在这个.so文件去调用原来的.so文件中的这两个native函数。

    好了,开始写c文件和Android.mk吧。首先在反编译出来的根目录下新建一个文件夹叫做jni,里面有:

    CallqihooTest.c:

     1 #include<dlfcn.h>
     2 #include <jni.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 void
     7 Java_com_qihoo_crack_MainActivity_packageNameCheck( JNIEnv* env,
     8                                                   jobject obj )
     9 {
    10 void*  filehandle = dlopen("/data/data/com.qihoo.crack/lib/libqihooTest.so", RTLD_LAZY );//dlopen打开动态链接库
    11   if(filehandle)
    12   {
    13     void (*packageNameCheck)(JNIEnv *,jobject);
    14     packageNameCheck = (void (*)(JNIEnv *,jobject)) dlsym(filehandle, "packageNameCheck"); //找到.so文件中的函数
    15     if(packageNameCheck)
    16     {
    17       packageNameCheck(env, obj); //传递参数调用
    18       return ;
    19     }
    20     else
    21     {
    22       // LOGI("packageNameCheck is null");
    23     }
    24   }
    25   
    26   // return (*env)->NewStringUTF(env,"hello guys");
    27 }
    28 
    29 void
    30 Java_com_qihoo_crack_MainActivity_applicatioNameCheck( JNIEnv* env,
    31                                                   jobject obj )
    32 {  
    33   void*  filehandle = dlopen("/data/data/com.qihoo.crack/lib/libqihooTest.so", RTLD_LAZY );
    34   if(filehandle)
    35   {
    36     void(*applicatioNameCheck)(JNIEnv *,jobject);
    37     applicatioNameCheck = (void (*)(JNIEnv *,jobject)) dlsym(filehandle, "applicatioNameCheck");
    38     if(applicatioNameCheck)
    39     {
    40         applicatioNameCheck(env, obj);
    41         return;
    42     }
    43     else
    44     {
    45         // LOGI("packageNameCheck is null");
    46     }
    47   }
    48  }

    这个c文件里面的两个函数返回值都是void(java和jni对void命名一致),要想清楚,函数只是调用另一个so里的方法,没必要返回jstring。

    说个题外话,静态注册JNI的函数名虽然长,但是命名规律看清楚之后完全不用借助javah啊,为什么那么多JNI hello world入门教程都要多此一举地大费周章地介绍javah呢(当时为了使用javah特意装了ubuntu折腾了半天,很多人说做技术需要「折腾」,我曾经也这么认为 但现在觉得人的精力是有限的,没必要做那些没什么意义的事情)。

    还有个坑的地方,这个applicatioNameCheck仔细看是application是少一个n的,不知道是出题人故意的还是怎么着,反正恶心到我了。

    Android.mk:

    LOCAL_PATH:= $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := qihooTest
    LOCAL_SRC_FILES := libqihooTest.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES:= CallqihooTest.c
    LOCAL_C_INCLUDES := /$(JNI_H_INCLUDE)
    LOCAL_SHARED_LIBRARIES := libutils
    LOCAL_PRELINK_MODULE := false
    LOCAL_LDLIBS := -L . -ldl -llog 
    LOCAL_MODULE := CallqihooTest
    include $(BUILD_SHARED_LIBRARY)

    还有一点,在MainActivity.smali里要把调用native方法里的:

     invoke-direct {p0}, Lcom/qihoo/crack/MainActivity;->packageNameCheck()V

    invoke-direct改成invoke-virtual。否则Logcat会提示NoSuchMethod Error。之前我分析的虚方法和直接方法的区别是「调用的方法在不在这个类里面」在这里好像不适用了,网上也找不到关于这两者的区别,思考一下,为什么加了一个so之后就要改成调用虚方法呢。如果说跟之前那个继承后在子类中调用父类中的方法的例子有什么共同点的话,那就是他们都是通过了一个「媒介」来调用这个方法;前者是经过了父类的媒介来调用so,后者是经过了CallqihooTest.so的媒介来调用qihooTest.so。

    还要把MainActivity里的load的lib改成Callqihootest。

    另外,还要把两个native方法改成public的,像这样:

    .method public native packageNameCheck()V

    否则会报错:java.lang.VerifyError。(为什么?)

    每次修改c之后的步骤是这样的,ndk-build,把libs中生成的so们复制到lib里,apktoo b,在dist里找到build好的apk用signapk签名,运行。挺繁琐的。不过发现LogCat是可以查看运行错误信息的(原先以为只有ADT中有项目才能看呢),还挺欣慰。

    这里提供一个第一问修改完成之后的apk。点击下载

    第二问:

    「请写个Application类,并在Manifest里面注册你的Application。同时要求使用该Application加载原包的Application;」

    做这题之前我甚至不知道application标签也可以对应一个类,之前开发中从没用过这个。

    So,what is an application

    What is Application
      Application和Activity,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)。

      android系统会为每个程序运行时创建一个Application类的对象仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享,数据缓存等操作。

     具体可以看这个链接(Click)里的那个ApplicationDemo的源代码,一目了然,从源码里可以看到application是怎么全局存储数据的,而且application的onCreate确实是先于launcher Activity的onCreate方法的(因为先在application里setValue才能在FirstActivity里getValue)。同时,Android4.0之后的任务切换界面把应用划去之后确实是结束进程的,因为划去之后再打开,application的set的Value又变成初始值了。

      接下来参考360博客的一篇文章:Android的Proxy/Delegate Application框架

      这篇文章里提到:

      ContentProvider:onCreate()调用优先于Application:onCreate()。

      在ContentProvider:onCreate()中,我们知道Application:onCreate()还没有运行,但已经可以使用getContext().getApplicationContext()函数获取Application对象,并访问其Context方法。显然,Android的API设计者不能允许此时获取的Application是“残废”的。结论是Application:attachBaseContext()必须要发生在ContentProvider:onCreate()之前,否则API将出现BUG;无论Android的系统版本如何变化,这一点也不能改变。

     Application与ContentProvider的初始化次序是这样的:

    Application:attachBaseContext()最早执行,然后是ContentProvider:onCreate(),
    然后是Application:onCreate()。

    声明,下面的代码转载自:http://www.cnblogs.com/wanyuanchun/p/3829918.html

    写一个同样包名的apk,

    新建一个类ProxyApplication.java:

      1 package com.qihoo.crack;
      2 
      3 import java.lang.reflect.Field;
      4 import java.lang.reflect.InvocationTargetException;
      5 import java.lang.reflect.Method;
      6 import java.util.ArrayList;
      7 
      8 import android.app.Application;
      9 import android.content.Context;
     10 import android.content.pm.ApplicationInfo;
     11 import android.content.pm.PackageManager;
     12 import android.content.pm.PackageManager.NameNotFoundException;
     13 import android.os.Bundle;
     14 import android.util.Log;
     15 
     16 public abstract class ProxyApplication extends Application {
     17 
     18     protected abstract void initProxyApplication();
     19 
     20     private static Context pContext = null; // 保存ProxyApp的mContext,后面有用
     21 
     22     private static String TAG = "proxy";
     23 
     24     @Override
     25     public void onCreate() {
     26 
     27         // TODO Auto-generated method stub
     28 
     29         super.onCreate();
     30 
     31         String className = "android.app.Application"; // 默认的Application名
     32         String key = "DELEGATE_APPLICATION_CLASS_NAME";
     33 
     34         try {
     35             /* (1)获取DelegateApplication的Class Name */
     36             ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
     37                     super.getPackageName(), PackageManager.GET_META_DATA);
     38             Bundle bundle = appInfo.metaData;
     39             if (bundle != null && bundle.containsKey(key)) {
     40                 className = bundle.getString(key);
     41                 if (className.startsWith(".")) {
     42                     className = super.getPackageName() + className;
     43                 }
     44             }
     45 
     46             /* (2) 加载DelegateApplication并生成对象 */
     47             Class delegateClass = Class.forName(className, true,
     48                     getClassLoader());
     49             Application delegate = (Application) delegateClass.newInstance();
     50 
     51             // 获取当前Application的applicationContext .全局
     52             Application proxyApplication = (Application) getApplicationContext();
     53             /* 使用反射一一替换proxyApplicationContext,这是本程序的重难点 */
     54             // 首先更改proxy的mbaseContext中的成员mOuterContext
     55 
     56             Class contextImplClass = Class.forName("android.app.ContextImpl");
     57 
     58             Field mOuterContext = contextImplClass
     59                     .getDeclaredField("mOuterContext");
     60 
     61             mOuterContext.setAccessible(true);
     62 
     63             mOuterContext.set(pContext, delegate);
     64 
     65             // 再获取context的mPackageInfo变量对象
     66             // ContextImp的分析可知,其方法的大多数操作都是直接调用其属性mPackageInfo(该属性类型为PackageInfo)的相关方法而来
     67 
     68             Field mPackageInfoField = contextImplClass
     69                     .getDeclaredField("mPackageInfo");
     70 
     71             mPackageInfoField.setAccessible(true);
     72 
     73             Object mPackageInfo = mPackageInfoField.get(pContext);
     74 
     75             Log.d(TAG, "mPackageInfo: " + mPackageInfo);
     76 
     77             // 修改mPackageInfo中的成员变量mApplication
     78 
     79             Class loadedApkClass = Class.forName("android.app.LoadedApk"); // mPackageInfo是android.app.LoadedApk类
     80 
     81             Field mApplication = loadedApkClass
     82                     .getDeclaredField("mApplication");
     83 
     84             mApplication.setAccessible(true);
     85 
     86             mApplication.set(mPackageInfo, delegate);
     87 
     88             // 然后再获取mPackageInfo中的成员对象mActivityThread
     89 
     90             Class activityThreadClass = Class
     91                     .forName("android.app.ActivityThread");
     92 
     93             Field mAcitivityThreadField = loadedApkClass
     94                     .getDeclaredField("mActivityThread");
     95 
     96             mAcitivityThreadField.setAccessible(true);
     97 
     98             Object mActivityThread = mAcitivityThreadField.get(mPackageInfo);
     99 
    100             // 设置mActivityThread对象中的mInitialApplication
    101 
    102             Field mInitialApplicationField = activityThreadClass
    103                     .getDeclaredField("mInitialApplication");
    104 
    105             mInitialApplicationField.setAccessible(true);
    106 
    107             mInitialApplicationField.set(mActivityThread, delegate);
    108 
    109             // 最后是mActivityThread对象中的mAllApplications,注意这个是List
    110 
    111             Field mAllApplicationsField = activityThreadClass
    112                     .getDeclaredField("mAllApplications");
    113 
    114             mAllApplicationsField.setAccessible(true);
    115 
    116             ArrayList<Application> al = (ArrayList<Application>) mAllApplicationsField
    117                     .get(mActivityThread);
    118 
    119             al.add(delegate);
    120 
    121             al.remove(proxyApplication);
    122 
    123             // 设置baseContext并调用onCreate
    124 
    125             Method attach = Application.class.getDeclaredMethod("attach",
    126                     Context.class);
    127 
    128             attach.setAccessible(true);
    129 
    130             attach.invoke(delegate, pContext);
    131 
    132             delegate.onCreate();
    133 
    134         } catch (NameNotFoundException e) {
    135 
    136             // TODO Auto-generated catch block
    137 
    138             e.printStackTrace();
    139 
    140         } catch (ClassNotFoundException e) {
    141 
    142             // TODO Auto-generated catch block
    143 
    144             e.printStackTrace();
    145 
    146         } catch (InstantiationException e) {
    147 
    148             // TODO Auto-generated catch block
    149 
    150             e.printStackTrace();
    151 
    152         } catch (IllegalAccessException e) {
    153 
    154             // TODO Auto-generated catch block
    155 
    156             e.printStackTrace();
    157 
    158         } catch (NoSuchFieldException e) {
    159 
    160             // TODO Auto-generated catch block
    161 
    162             e.printStackTrace();
    163 
    164         } catch (NoSuchMethodException e) {
    165 
    166             // TODO Auto-generated catch block
    167 
    168             e.printStackTrace();
    169 
    170         } catch (IllegalArgumentException e) {
    171 
    172             // TODO Auto-generated catch block
    173 
    174             e.printStackTrace();
    175 
    176         } catch (InvocationTargetException e) {
    177 
    178             // TODO Auto-generated catch block
    179 
    180             e.printStackTrace();
    181 
    182         }
    183 
    184     }
    185 
    186     @Override
    187     public String getPackageName() {
    188 
    189         // TODO Auto-generated method stub
    190 
    191         return "";
    192 
    193     }
    194 
    195     @Override//子类里覆写,父类(Application)引用指向子类对象,在父类里调用这个被覆写的函数。
    196     //attachBaseContext不在父类Application里,而是在Application的父类ContextWrapper里。
    197     protected void attachBaseContext(Context base) {
    198 
    199         // TODO Auto-generated method stub
    200 
    201         super.attachBaseContext(base);
    202 
    203         pContext = base;
    204 
    205         Log.d(TAG, "attachBaseContext");
    206 
    207         initProxyApplication();
    208 
    209     }
    210     
    211 
    212 }
    View Code

    再新建一个MyProxyApplication.java:

    package com.qihoo.crack;
    
    import android.util.Log;
    
    public class MyProxyApplication extends ProxyApplication {
    
        @Override
        protected void initProxyApplication() {
    
            // TODO Auto-generated method stub
    
            // 在这里替换surrounding,实现自定义的classloader等功能
    
            Log.d("proxy", "initProxyApplication");
    
        }
        //Application:attachBaseContext()最早执行,也就是说系统自动执行这个类中的attachBaseContext()方法(也就是父类中的),
        //而父类ProxyApplication中重写了Application中的attachBaseContext()方法。
        
        //Application与ContentProvider的初始化次序是这样的:
        //Application:attachBaseContext()最早执行,然后是ContentProvider:onCreate(),
        //然后是Application:onCreate()。
        //Application:onCreate()也执行父类中的onCreate();
        
    }

    AndroidMainifest.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.qihoo.crack"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="8"
            android:targetSdkVersion="21" />
    
        <application
            android:name="com.qihoo.crack.MyProxyApplication"
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <meta-data
                android:name="DELEGATE_APPLICATION_CLASS_NAME"
                android:value="com.qihoo.crack.StubApplication" >
            </meta-data>
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    这样的话,反编译一下,把ProxyApplication.smali和MyProxyApplication.smali拿到之前的文件夹里,再修改一下Manifest打包即可。

    最终的apk点击这里下载

    还有一点不是很清楚,这样做的话我点击「查看当前Application」的按钮,还是显示com.qihoo.crack.StubApplication,按照WanChouChou的说法,就是应该显示StubApplication;也就是说这样就是为了「骗过」系统检测让它以为没检测,其实Application已经换过了?

    试了一下,如果想让按钮提示别的,只要批量替换StubApplication的字符串为想要修改的字符串就行了。

    而如果把meta-data标签去掉了,Application name中仍然是MyProxyApplication,那就会显示系统默认的Android.app.Application,说明MyProxyApplication.java没有被识别成一个正规的Application文件(因为MyProxyApplication继承的ProxyApplication中,说了如果找不到key,就使用默认的Application)。

    这道题断断续续做了一个月了,到现在虽然还有些不懂,也算告一段落。好吧,到这里。十一结束,接下来论文要开题了。

    Oct 7th 2014

  • 相关阅读:
    linux下文件夹的创建、复制、剪切、重命名、清空和删除命令
    Linux 删除文件夹和创建文件的命令
    linux下拷贝整个目录
    星云大师:这十句话 我受用一生
    dex
    瘋耔java语言笔记
    VCC_VID_VTT等的含义
    一位数码管引脚
    android从应用到驱动之—camera(2)---cameraHAL的实现
    android从应用到驱动之—camera(1)---程序调用流程[转]
  • 原文地址:https://www.cnblogs.com/larrylawrence/p/3986313.html
Copyright © 2020-2023  润新知