保护技术开发03 - 添加Application

一般情况下,一个正常的APP都会有一个Application类,少有APP没有这个,那么我们加壳的时候就要处理这个Application

我们前面直接加上了自己的ProtectApplication,因为没有Application,所以直接就调用了入口的MainActivity,如果待加壳的应用存在Application,我们在加壳的时候,就需要先保存这个Application,然后加载完自己的ProtectApplication后,恢复应用自身的Application,再去调用入口的MainActivity

依旧使用前一篇文章的SourceAPK工程,我们稍微进行一下修改,给添加一个MyApplication,在这个Application里定义两个字符串,一个计数器,两个Activity互相跳转的时候获取Application的数据进行显示

MyApplication.java

package com.wnagzihxa1n.sourceapk;

import android.app.Application;

public class MyApplication extends Application {
    private int count = 1;
    private String main_activity = "I am MainActivity";
    private String second_activity = "I am SecondActivity";

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public String getMain_activity() {
        return main_activity;
    }

    public String getSecond_activity() {
        return second_activity;
    }
}

MainActivity.java

package com.wnagzihxa1n.sourceapk;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

    private TextView textView;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.view1_textView);
        button = (Button) findViewById(R.id.view1_button);

        MyApplication myApplication = (MyApplication)getApplicationContext();
        textView.setText(myApplication.getMain_activity() + "\nCount : " + myApplication.getCount());

        myApplication.setCount(myApplication.getCount() + 1);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, SecondActivity.class);
                startActivity(intent);
                MainActivity.this.finish();
            }
        });
    }
}

SecondActivity.java

package com.wnagzihxa1n.sourceapk;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class SecondActivity extends Activity {

    private TextView textView;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        textView = (TextView) findViewById(R.id.view2_textView);
        button = (Button) findViewById(R.id.view2_button);

        MyApplication myApplication = (MyApplication)getApplicationContext();
        textView.setText(myApplication.getSecond_activity() + "\nCount : " + myApplication.getCount());

        myApplication.setCount(myApplication.getCount() + 1);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(SecondActivity.this, MainActivity.class);
                startActivity(intent);
                SecondActivity.this.finish();
            }
        });
    }
}

运行,字符串和下面的数字均从MyApplication获取

IMAGE

点击按钮,修改数字并跳转

IMAGE

调试着要是没啥问题,就编译签名,命名为SourceAPK.apk

删掉所有代码,添加壳代码,因为我们添加了Application,所以除了动态加载APK之外,还需要加载MyApplication,所以代码中比原来的多了一段

public void onCreate() {
    String appClassName = null;
    try {
        ApplicationInfo ai = this.getPackageManager().getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA);
        Bundle bundle = ai.metaData;
        if (bundle != null && bundle.containsKey("APPLICATION_CLASS_NAME")) {
            appClassName = bundle.getString("APPLICATION_CLASS_NAME");
        } else {
            return;
        }
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread", new Class[]{}, new Object[]{});
    Object mBoundApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mBoundApplication");
    Object loadedApkInfo = RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "info");
    RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication", loadedApkInfo, null);
    Object oldApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mInitialApplication");
    ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mAllApplications");
    mAllApplications.remove(oldApplication);
    ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke.getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplicationInfo");
    ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "appInfo");
    appinfo_In_LoadedApk.className = appClassName;
    appinfo_In_AppBindData.className = appClassName;
    Application app = (Application) RefInvoke.invokeMethod("android.app.LoadedApk", "makeApplication", loadedApkInfo, new Class[]{boolean.class, Instrumentation.class}, new Object[]{false, null});
    RefInvoke.setFieldOjbect("android.app.ActivityThread", "mInitialApplication", currentActivityThread, app);
    ArrayMap mProviderMap = (ArrayMap) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mProviderMap");
    Iterator it = mProviderMap.values().iterator();
    while (it.hasNext()) {
        Object providerClientRecord = it.next();
        Object localProvider = RefInvoke.getFieldOjbect("android.app.ActivityThread$ProviderClientRecord", providerClientRecord, "mLocalProvider");
        RefInvoke.setFieldOjbect("android.content.ContentProvider", "mContext", localProvider, app);
    }
    app.onCreate();
}

大概的作用就是替换源APK的Application对象,同时在AndroidManifest.xml里添加

 <meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.wnagzihxa1n.sourceapk.MyApplication"/>

这样我们就可以找到待加壳程序的入口Application

再把SourceAPK.apk重命名为a放到assets,跑起来

IMAGE

IMAGE

点击按钮也能正常获取数据,跳转页面没啥大问题