吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11916|回复: 41
收起左侧

[Android 原创] 记一次反Xposed的处理

  [复制链接]
一夜梦惊人 发表于 2019-8-17 21:43
前言:最近两天在搞一个App,由于某些问题我就不会公布了。在这App中看到一个有意思的反xposed的处理,觉得有意思就公布出来了。
正文:这个App混淆如同一坨屎,所有的string都需要经过解密,而且class name和method name是尽量一样就一样,上个截图表示意思意思。
yymjr_2019-08-17_20-33-45.png
经过本人发现,string有两种解密算法,不在同一个类。两种解密算法不在同一个类,一个类居然用apktool反编译出来后没有。。。初步猜想是类似于热修复那样,运行时动态加载到class loader中,只不过不是本文重点,所以就不深究。而另一个则是直接一大片的计算。
yymjr_2019-08-17_20-40-47.png
为了偷懒我就是直接用xposed查看一下。hook后运行没有超过5s就退出,查看logcat直接是一大片的错误。经过分析,是解密算法解析出现问题,不能正常解析从而导致的出错。在查看方法的途中,注意到一个关键的地方。
[Java] 纯文本查看 复制代码
objArr[7] = Class.forName(String.valueOf(cArr, 0, 16)).getMethod(String.valueOf(cArr, 16, 13), null);
        objArr[8] = Class.forName(String.valueOf(cArr, 0, 16)).getMethod(String.valueOf(cArr, 29, 13), null);
        objArr[9] = Class.forName(String.valueOf(cArr, 42, 16)).getMethod(String.valueOf(cArr, 58, 11), null);
        objArr[10] = Class.forName(String.valueOf(cArr, 69, 27)).getMethod(String.valueOf(cArr, 96, 12), null);
        objArr[11] = Class.forName(String.valueOf(cArr, 69, 27)).getMethod(String.valueOf(cArr, 108, 13), null);
        objArr[12] = Integer.valueOf(oix(objArr));

反射获取一些东西,还运用到了这个oix方法。
[Java] 纯文本查看 复制代码
private static final int oix(Object[] objArr) {
        Object[] objArr2 = (Object[]) objArr[8].invoke(objArr[7].invoke(null, null), null);
        int length = objArr2.length;
        for (int i = 1; i < length; i++) {
            StringBuilder sb = new StringBuilder();
            sb.append(objArr[10].invoke(objArr2[i], null));
            sb.append(objArr[11].invoke(objArr2[i], null));
            if (sb.toString().hashCode() == 180000400) {
                return i;
            }
        }
        return 0;
    }

好的,看到这里就可以知道,基本上是xposed导致某些地方变化从而解析失败。
[Java] 纯文本查看 复制代码
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        for (int i=0;i<stackTraceElements.length;i++){
            StringBuilder stringBuilder = new StringBuilder("");
            stringBuilder.append(stackTraceElements[i].getClassName());
            stringBuilder.append(stackTraceElements[i].getMethodName());
            if (stringBuilder.toString().hashCode() == 180000400)
                return  i;
        }
        return void;

面对这种,可以直接Hook这个getStackTrace方法,把有关于这个xposed的全部去掉就可以,我也就不贴代码了。
[Java] 纯文本查看 复制代码
}
        Object[] objArr = (Object[]) ((Method) Ec[8]).invoke(((Method) Ec[7]).invoke(null, null), null);
        StringBuilder sb = new StringBuilder();
        sb.append(((Method) Ec[10]).invoke(objArr[((Integer) Ec[12]).intValue()], null));
        int hashCode = sb.append(((Method) Ec[11]).invoke(objArr[((Integer) Ec[12]).intValue()], null)).toString().hashCode();

说句实话,我个人觉得比较新奇,所以就发出来了。
最后附加一个小知识点:xposed的find and hook method方法需要拿到class,但是由于加固或者其他一些原因导致class not found,这是常有的问题。给一点通用的办法,非本人原创,是virjar所写在ucrack中的(真心推荐UCrack,一款在逆反的方面的好帮手)。
下面贴下代码:
[Asm] 纯文本查看 复制代码
package com.yymjr.android.xposedpay;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Process;
import android.util.Log;

import com.google.common.collect.Maps;
import com.virjar.xposed_extention.ClassLoadMonitor;
import com.virjar.xposed_extention.ReflectUtil;
import com.virjar.xposed_extention.SharedObject;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentMap;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import de.robv.android.xposed.callbacks.XCallback;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class MainHook implements IXposedHookLoadPackage, IXposedHookZygoteInit {
    private final static String TAG = "XposedPay-MainHook";
    private Context context;

    private volatile boolean hooked = false;

    private volatile boolean attached = false;

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        if (!lpparam.packageName.equals("com.eg.android.AlipayGphone")) return;
        if (!lpparam.isFirstApplication) {
            return;
        }
        if (hooked) {
            return;
        }
        hooked = true;
        findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook(XCallback.PRIORITY_HIGHEST * 2) {

            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                if (attached) {
                    return;
                }
                attached = true;
                context = (Context) param.args[0];
                hotLoadPlugin(lpparam.classLoader, context, lpparam);
                process();
            }
        });
    }

    @SuppressLint("PrivateApi")
    private void hotLoadPlugin(ClassLoader ownerClassLoader, Context context, XC_LoadPackage.LoadPackageParam lpparam) {

        if (hansInstantRun(MainHook.class.getClassLoader())) {
            Log.e("yymjr", "  Cannot load module, please disable \"Instant Run\" in Android Studio.");
            return;
        }

        ClassLoader hotClassLoader = replaceClassloader(context, lpparam);
        if (hansInstantRun(hotClassLoader)) {
            Log.e("yymjr", "  Cannot load module, please disable \"Instant Run\" in Android Studio.");
            return;
        }

        SharedObject.context = context;
        SharedObject.loadPackageParam = lpparam;

    }

    private void process(){
        Log.d(TAG, "Hooking Application Class");
        ClassLoadMonitor.addClassLoadMonitor("android.app.Application", new ClassLoadMonitor.OnClassLoader() {
            @Override
            public void onClassLoad(Class<?> clazz) {
                findAndHookMethod(clazz, "attach",new XC_MethodHook() {
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        super.afterHookedMethod(param);
                    }
                });
            }
        });
    }

    private boolean hansInstantRun(ClassLoader classLoader) {
        Class<?> aClass = ReflectUtil.findClassIfExists(INSTANT_RUN_CLASS, classLoader);
        if (aClass != null) {
            return true;
        }
        aClass = ReflectUtil.findClassIfExists(INSTANT_RUN_CLASS_2, classLoader);
        return aClass != null;
    }

    private static ClassLoader replaceClassloader(Context context, XC_LoadPackage.LoadPackageParam lpparam) {
        ClassLoader classLoader = MainHook.class.getClassLoader();
        if (!(classLoader instanceof PathClassLoader)) {
            XposedBridge.log("classloader is not PathClassLoader: " + classLoader.toString());
            return classLoader;
        }

        //find real apk location by package name
        PackageManager packageManager = context.getPackageManager();
        if (packageManager == null) {
            XposedBridge.log("can not find packageManager");
            return classLoader;
        }

        PackageInfo packageInfo = null;
        try {
            packageInfo = packageManager.getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA);
        } catch (PackageManager.NameNotFoundException e) {
            //ignore
        }
        if (packageInfo == null) {
            XposedBridge.log("can not find plugin install location for plugin: " + BuildConfig.APPLICATION_ID);
            return classLoader;
        }

        return createClassLoader(classLoader.getParent(), packageInfo, context);
    }

    private static final String INSTANT_RUN_CLASS = "com.android.tools.fd.runtime.BootstrapApplication";
    private static final String INSTANT_RUN_CLASS_2 = "com.android.tools.ir.server.InstantRunContentProvider";
    private static ConcurrentMap<String, BaseDexClassLoader> classLoaderCache = Maps.newConcurrentMap();

    private static BaseDexClassLoader createClassLoader(ClassLoader parent, PackageInfo packageInfo, Context context) {
        if (classLoaderCache.containsKey(packageInfo.applicationInfo.sourceDir)) {
            return classLoaderCache.get(packageInfo.applicationInfo.sourceDir);
        }
        synchronized (MainHook.class) {
            if (classLoaderCache.containsKey(packageInfo.applicationInfo.sourceDir)) {
                return classLoaderCache.get(packageInfo.applicationInfo.sourceDir);
            }
            XposedBridge.log("create a new classloader for plugin with new apk path: " + packageInfo.applicationInfo.sourceDir);
            BaseDexClassLoader hotClassLoader = null;
            File ucrackOptimizeDir = new File(context.getFilesDir(), "ucrackDex");
            try {
                FileUtils.forceMkdir(ucrackOptimizeDir);
                hotClassLoader = new DexClassLoader(packageInfo.applicationInfo.sourceDir
                        , ucrackOptimizeDir.getAbsolutePath(),
                        packageInfo.applicationInfo.nativeLibraryDir,
                        parent);
            } catch (IOException e) {
                e.printStackTrace();
                hotClassLoader = new PathClassLoader(packageInfo.applicationInfo.sourceDir, parent);
            }
            classLoaderCache.putIfAbsent(packageInfo.applicationInfo.sourceDir, hotClassLoader);
            return hotClassLoader;
        }
    }

    private static final int DEBUG_ENABLE_DEBUGGER = 0x1;

    private XC_MethodHook debugAppsHook = new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param)
                throws Throwable {
            XposedBridge.log("-- beforeHookedMethod :" + param.args[1]);
            int id = 5;
            int flags = (Integer) param.args[id];
            if ((flags & DEBUG_ENABLE_DEBUGGER) == 0) {
                flags |= DEBUG_ENABLE_DEBUGGER;
            }
            param.args[id] = flags;
        }
    };

    @Override
    public void initZygote(StartupParam startupParam) throws Throwable {
        XposedBridge.hookAllMethods(Process.class, "start", debugAppsHook);
    }

}

最后面是让所有APP都是可调试状态,不需要的可以删除掉。
[Asm] 纯文本查看 复制代码
    implementation 'com.virjar:hermes-api:1.0.9'

记得在build.gradle中添上上面这个才可以。

最后打个广告
想要学习一些其他的xposed东西可以看看我另一篇文章:https://www.52pojie.cn/thread-873013-1-1.html没有咯虽然少,也就将就着看吧。

免费评分

参与人数 19威望 +2 吾爱币 +24 热心值 +15 收起 理由
小酒馆ovo + 1 热心回复!
womiha + 1 + 1 用心讨论,共获提升!
lianziheqing + 1 + 1 谢谢@Thanks!
fcguo800 + 1 + 1 谢谢@Thanks!
茎待佳阴 + 1 + 1 大片的代码看得头昏脑涨,膜拜大佬
$喂人民服雾 + 1 大佬不在四群开车正经发文章 赶紧来学习
icode2019 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
CrazyNut + 2 + 1 用心讨论,共获提升!
qtfreet00 + 9 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
qaz003 + 1 + 1 谢谢@Thanks!
菜鸟也想飞 + 1 + 1 谢谢@Thanks!
白茅蓝 + 1 + 1 谢谢@Thanks!
z279487447 + 1 我很赞同!
笙若 + 1 + 1 谢谢@Thanks!
tanghengvip + 1 + 1 经验挺丰富啊
nbwzlyd + 1 用心讨论,共获提升!
looooooc + 1 表哥,求能绕过app双向验证、绕过root检测的插件
Liu0827 + 1 + 1 热心回复!
甘愿堕落 + 1 + 1 膜拜大佬

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 一夜梦惊人 发表于 2019-11-12 11:52
老中医w 发表于 2019-11-12 00:08
有一点没有太明白:
「基本上是xposed导致某些地方变化从而解析失败」,具体是什么地方呢?
看代码感觉像 ...

反射获取了栈堆,然后如果hook了这个方法就会导致栈堆中有xposed的存在,导致参数不对从而解密失败。
 楼主| 一夜梦惊人 发表于 2019-8-18 10:41
looooooc 发表于 2019-8-18 08:48
表哥,求能绕过app双向验证、绕过root检测的插件

我这里是没有的,因为root和xposed有太多办法来检测,比如什么包名,栈推,内存中加载的so,一些特有的文件等等,我都是根据app的检测来现写的,除非特别常用。
a250720 发表于 2019-8-17 22:10
KARMA07007 发表于 2019-8-17 22:12
第一次看到-普通教程-代码-整齐规范的。
 楼主| 一夜梦惊人 发表于 2019-8-17 22:20
LGLG 发表于 2019-8-17 22:12
第一次看到-普通教程-代码-整齐规范的。

那大佬,给个热心值呗!!!
JiaGela233 发表于 2019-8-17 22:23
666,正好需要
多幸运遇见baby 发表于 2019-8-17 23:17
欢迎分析讨论交流,吾爱破解论坛有你更精彩!
kltdhc 发表于 2019-8-17 23:22
woc 这么厉害的吗、、、
Ericky 发表于 2019-8-17 23:28
无Bin无真相
otgserver 发表于 2019-8-17 23:53
这个文章好
ljn666 发表于 2019-8-18 04:40
好文章必须要顶上
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 18:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表