[原创]android第一代加壳的验证和测试 | 宜武汇-ag真人国际厅网站

package com.apkunshell;

 

 

 

import android.app.application;

import android.app.instrumentation;

import android.content.context;

import android.content.pm.applicationinfo;

import android.content.pm.packagemanager;

import android.content.res.assetmanager;

import android.content.res.resources;

import android.os.bundle;

import android.util.arraymap;

import android.util.log;

 

import java.io.bufferedinputstream;

import java.io.bytearrayinputstream;

import java.io.bytearrayoutputstream;

import java.io.datainputstream;

import java.io.file;

import java.io.fileinputstream;

import java.io.fileoutputstream;

import java.io.ioexception;

import java.lang.ref.weakreference;

import java.lang.reflect.method;

import java.util.arraylist;

import java.util.iterator;

import java.util.zip.zipentry;

import java.util.zip.zipinputstream;

import dalvik.system.dexclassloader;

 

/**

 * =============================================================================

 * ag真人国际厅网站 copyright (c) 2017 yuxin all rights reserved.

 * packname com.jju.yuxin.reforceapk

 * created by yuxin.

 * created time 2017/6/18 0018 下午 5:03.

 * version   1.0;

 * describe :

 * history:

 * ==============================================================================

 */

 

//com.google.android.apps.plus

//com.adobe.flashplayer

//com.loader

//com.setup.loader

//applicaiton做为整个应用的上下文,会被系统第一时间调用,这也是应用开发者程序代码的第一执行点

public class myapplication extends application{

 

    private static string dexfilename = "update.apk";

 

    private static final string appkey = "application_class_name";

 

    private static string cryptkey = "fuck all the android crackers";

 

    public static string payload_odex = "my_payload_odex";

 

    public static string payload_lib = "my_payload_lib";

 

    private  static final string tag = myapplication.class.getsimplename();

 

    private string srcdexfilepath = "";

    private string odexpath = "";

    private string libpath = "";

 

    private static string gipstr = "";

    private static string gusernamestr = "";

 

    private context context = null;

 

    //以下是加载资源

    protected assetmanager massetmanager = null;

    protected resources mresources = null;

    protected resources.theme mtheme = null;

 

 

    //why run 2 times?

    @suppresswarnings("rawtypes")

    @override

    protected void attachbasecontext(context base) {

 

        super.attachbasecontext(base);

 

        //getapplicationcontext() 返回应用的上下文,生命周期是整个应用,应用摧毁它才摧毁

        //activity.this的context 返回当前activity的上下文,属于activity ,activity 摧毁他就摧毁

        //getbasecontext()  返回由构造函数指定或setbasecontext()设置的上下文

        //this.getapplicationcontext()取的是这个应 用程序的context,activity.this取的是这个activity的context,

        //这两者的生命周期是不同 的,前者的生命周期是整个应用,后者的生命周期只是它所在的activity。

        context = base;

 

        log.e(tag,"attachbasecontext");

 

        try {

//          /data/user/0/com.apkunshell/app_payload_odex

            file odexpathfile = this.getdir(payload_odex, mode_private);

//          /data/user/0/com.apkunshell/app_payload_libs

            file libspathfile = this.getdir(payload_lib, mode_private);

 

            //用于存放源apk释放出来的dex

            odexpath = odexpathfile.getabsolutepath();

            //用于存放源apk用到的so文件

            libpath = libspathfile.getabsolutepath();

            //用于存放解密后的apk

            srcdexfilepath = odexpathfile.getabsolutepath() "/" dexfilename;

 

//            string apppath = this.getfilesdir().getparent() "/";

//            inputstream is = this.getassets().open(apkfilename);

//            int size = is.available();

//            byte []buffer = new byte[size];

//            is.read(buffer);

//            outputstream os = new fileoutputstream(apppath apkfilename);

//            os.write(buffer);

 

            file srcdexfile = new file(srcdexfilepath);

            //第一次加载

            if (srcdexfile.exists() == false)

            {

                log.e(tag, "befirstloading");

 

                srcdexfile.createnewfile();

                //拿到dex文件

                byte[] dexdata = this.readdexfilefromapk();

                //取出源apk解密后放置在/payload.apk,及其so文件放置在payload_lib/

                this.splitpayloadfromdex(dexdata);

            }

 

            // 配置动态加载环境

            //反射获取主线程对象,并从中获取所有已加载的package信息,并中找到当前的loadapk对象的弱引用

            //// 配置动态加载环境 获取主线程对象 http://blog.csdn.net/myarrow/article/details/14223493

            object currentactivitythread = refinvoke.invokestaticmethod("android.app.activitythread",

                    "currentactivitythread",new class[] {}, new object[] {});

            arraymap mpackages = (arraymap) refinvoke.getfieldojbect("android.app.activitythread",

                    currentactivitythread,"mpackages");

            string packagename = this.getpackagename();

            weakreference wr = (weakreference) mpackages.get(packagename);

 

            ///创建被加壳apk的dexclassloader对象 加载apk内的类和本地代码(c/c 代码)

            //创建一个新的dexclassloader用于加载源apk,传入apk路径,dex释放路径,so路径,及父节点的dexclassloader使其遵循双亲委托模型

            classloader fathercl = (classloader) refinvoke.getfieldojbect("android.app.loadedapk", wr.get(), "mclassloader");

            dexclassloader dloader = new dexclassloader(srcdexfilepath, odexpath,libpath, fathercl);

 

            //getclassloader()等同于 (classloader) refinvoke.getfieldojbect(),但是为了替换掉父节点我们需要通过反射来获取并修改其值

 

            //将父节点dexclassloader替换

            ////把当前进程的dexclassloader 设置成了被加壳apk的dexclassloader

            refinvoke.setfieldojbect("android.app.loadedapk", "mclassloader",wr.get(), dloader);

 

            //object actobj = dloader.loadclass(loadclassname);

 

            //log.e(tag, "get class object:" actobj);

 

        } catch (exception e) {

            log.e(tag, "error:" log.getstacktracestring(e));

            e.printstacktrace();

        }

    }

 

 

    //java.lang.runtimeexception:

    //unable to create application com.loader.srelease: java.lang.nullpointerexception:

    //expected receiver of type android.content.contentprovider, but got null

    //at com.loader.srefinvoke.setfieldojbect(srefinvoke.java:178)

    //why run 2 times?

    @suppresswarnings("rawtypes")

    public void oncreate() {

        try {

            log.e(tag, "oncreate");

 

            log.e(tag,"application:" context

                    ",basecontext:" getbasecontext()

                    ",applicationcontext:" getapplicationcontext()

                    ",activity:" this);

 

            if(context == null){

                context = this;

                if(context == null){

                    context = utils.getcontext();

                }

            }

 

            utils.setvalue(context,"paramconfig.json","username",gusernamestr);

            utils.setvalue(context,"paramconfig.json","ip",gipstr);

 

            //加载源apk资源

            loadresources(srcdexfilepath);

 

            //获取配置在清单文件的源apk的application路径

            string appclassname = null;

            try {

                applicationinfo ai = this.getpackagemanager().getapplicationinfo(this.getpackagename(),packagemanager.get_meta_data);

                bundle bundle = ai.metadata;

                if (bundle != null && bundle.containskey(appkey)) {

                    appclassname = bundle.getstring(appkey);    //classname 是配置在xml文件中的

                }else {

                    log.e(tag, "not found application class name in bundle");

                    return;

                }

            } catch (exception e) {

                log.e(tag, "error:" log.getstacktracestring(e));

                e.printstacktrace();

                return;

            }

 

            //获取当前壳apk的applicationinfo

            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");

 

            //将loadedapk中的applicationinfo设置为null

            refinvoke.setfieldojbect("android.app.loadedapk", "mapplication",loadedapkinfo, null);

 

            //获取currentactivitythread中注册的application

            object oldapplication = refinvoke.getfieldojbect("android.app.activitythread",

                    currentactivitythread,"minitialapplication");

 

            //获取activitythread中所有已注册的application,并将当前壳apk的application从中移除

            @suppresswarnings("unchecked")

            arraylist mallapplications =

                    (arraylist) 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");

 

            //替换原来的application

            appinfo_in_loadedapk.classname = appclassname;

            appinfo_in_appbinddata.classname = appclassname;

 

            //注册application

            application app = (application) refinvoke.invokemethod("android.app.loadedapk", "makeapplication",

                    loadedapkinfo,new class[] { boolean.class, instrumentation.class },new object[] { false, null });

 

            //替换activitythread中的application

            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);

            }

 

            log.e(tag, "app:" app);

 

            app.oncreate();

        } catch (exception e) {

            e.printstacktrace();

        }

    }

 

 

    private void splitpayloadfromdex(byte[] shelldexdata) throws ioexception {

        //取被加壳apk的长度

        system.arraycopy(shelldexdata, sdlen - 4, bytedexlen, 0, 4);

 

        bytearrayinputstream bais = new bytearrayinputstream(bytedexlen);

        datainputstream dis = new datainputstream(bais);

        int readint = dis.readint();

        log.d(tag,"integer.tohexstring(readint):" integer.tohexstring(readint));

 

        //取出apk

        byte[] encryptdata = new byte[readint];

        system.arraycopy(shelldexdata, sdlen - 4 - readint, encryptdata, 0, readint);

 

        //对源程序apk进行解密

        byte[] flatdata = xorcrypt(encryptdata);

 

        int offset = 0;

        byte [] byteunamelen = new byte[4];

        system.arraycopy(flatdata, offset, byteunamelen, 0, 4);

        offset = 4;

 

        int unamelen = utils.bytestoint(byteunamelen);

        byte[] username = new byte[unamelen];

        system.arraycopy(flatdata , offset, username, 0, unamelen);

        offset = unamelen;

 

        gusernamestr = new string(username);

 

        system.arraycopy(flatdata, offset, byteiplen, 0, 4);

        offset = 4;

 

        int iplen = utils.bytestoint(byteiplen);

        byte[] ip = new byte[iplen];

        system.arraycopy(flatdata , offset, ip, 0, iplen);

        offset = iplen;

 

        gipstr = new string(ip);

 

        //写入源apk文件

        file file = new file(srcdexfilepath);

        try {

            fileoutputstream localfileoutputstream = new fileoutputstream(file);

            localfileoutputstream.write(flatdata,offset,readint - offset);

            localfileoutputstream.close();

        } catch (ioexception localioexception) {

            throw new runtimeexception(localioexception);

        }

 

        //分析源apk文件

        zipinputstream zis = new zipinputstream(new bufferedinputstream(new fileinputstream(file)));

        while (true) {

            zipentry ze = zis.getnextentry();

            if (ze == null) {

                break;

            }

 

            //依次取出被加壳apk用到的so文件,放到 libpath中(data/data/包名/payload_lib)

            string zfn = ze.getname();

            if (zfn.startswith("lib/") && zfn.endswith(".so")) {

                file sofile = new file(libpath zfn.substring(zfn.lastindexof('/')));

                sofile.createnewfile();

                fileoutputstream fos = new fileoutputstream(sofile);

                byte[] readbuf = new byte[0x4000];

                while (true) {

                    int readlen = zis.read(readbuf);

                    if (readlen == -1){

                        break;

                    }

                    fos.write(readbuf, 0, readlen);

                }

                fos.flush();

                fos.close();

            }

            zis.closeentry();

        }

        zis.close();

    }

 

 

    /**

     * 拿到自己apk文件中的dex文件

     * @return

     * @throws ioexception

     */

 

        bytearrayoutputstream dexbaos = new bytearrayoutputstream();

 

        //getapplicationinfo().sourcedir == /data/user/0/com.adobe.flashplayer/base.apk

        //bufferedinputstream会将该输入流数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从输入流中读取下一部分的数据

        //无其他用途

        //zipinputstream zis = new zipinputstream(new fileinputstream(this.getapplicationinfo().sourcedir));

 

        zipinputstream zis = new zipinputstream(new bufferedinputstream(new fileinputstream(this.getapplicationinfo().sourcedir)));

 

        while (true) {

            zipentry ze = zis.getnextentry();

            if (ze == null) {

                break;

            }

 

            //拿到dex文件

            if (ze.getname().equals("classes.dex")) {

                byte[] readbuf = new byte[0x10000];

                while (true) {

                    int readlen = zis.read(readbuf);

                    if (readlen == -1){

                        zis.closeentry();

                        break;

                    }

 

                    dexbaos.write(readbuf, 0, readlen);

                }

                zis.closeentry();

                break;

            }else{

                zis.closeentry();

            }

        }

 

        zis.close();

        return dexbaos.tobytearray();

    }

 

 

    private static byte[] xorcrypt(byte[] srcdata){

        byte[] key = cryptkey.getbytes();

        int keylen = cryptkey.length();

        for(int i = 0,j = 0; i ){

            srcdata[i] = (byte)(key[j] ^ srcdata[i]);

            j ;

            if(j >= keylen){

                j = 0;

            }

        }

        return srcdata;

    }

 

 

    protected void loadresources(string srcapkpath) {

        //创建一个assetmanager放置源apk的资源

        try {

            assetmanager assetmanager = assetmanager.class.newinstance();

            method addassetpath = assetmanager.getclass().getmethod("addassetpath", string.class);

            addassetpath.invoke(assetmanager, srcapkpath);

            massetmanager = assetmanager;

        } catch (exception e) {

            log.i(tag, "inject:loadresource error:" log.getstacktracestring(e));

            e.printstacktrace();

        }

        resources superres = super.getresources();

        superres.getdisplaymetrics();

        superres.getconfiguration();

        mresources = new resources(massetmanager, superres.getdisplaymetrics(),superres.getconfiguration());

        mtheme = mresources.newtheme();

        mtheme.setto(super.gettheme());

    }

 

    @override

    public assetmanager getassets() {

        return massetmanager == null ? super.getassets() : massetmanager;

    }

 

    @override

    public resources getresources() {

        return mresources == null ? super.getresources() : mresources;

    }

 

    @override

    public resources.theme gettheme() {

        return mtheme == null ? super.gettheme() : mtheme;

    }

 

原文链接:https://bbs.kanxue.com/thread-277240.htm

网络摘文,本文作者:15h,如若转载,请注明出处:https://www.15cov.cn/2023/08/27/原创android第一代加壳的验证和测试/

发表评论

邮箱地址不会被公开。 必填项已用*标注

网站地图