记一次反Xposed的处理
前言:最近两天在搞一个App,由于某些问题我就不会公布了。在这App中看到一个有意思的反xposed的处理,觉得有意思就公布出来了。正文:这个App混淆如同一坨屎,所有的string都需要经过解密,而且class name和method name是尽量一样就一样,上个截图表示意思意思。
经过本人发现,string有两种解密算法,不在同一个类。两种解密算法不在同一个类,一个类居然用apktool反编译出来后没有。。。{:301_1002:}初步猜想是类似于热修复那样,运行时动态加载到class loader中,只不过不是本文重点,所以就不深究。而另一个则是直接一大片的计算。
{:301_1004:}为了偷懒我就是直接用xposed查看一下。hook后运行没有超过5s就退出,查看logcat直接是一大片的错误。经过分析,是解密算法解析出现问题,不能正常解析从而导致的出错。在查看方法的途中,注意到一个关键的地方。
objArr = Class.forName(String.valueOf(cArr, 0, 16)).getMethod(String.valueOf(cArr, 16, 13), null);
objArr = Class.forName(String.valueOf(cArr, 0, 16)).getMethod(String.valueOf(cArr, 29, 13), null);
objArr = Class.forName(String.valueOf(cArr, 42, 16)).getMethod(String.valueOf(cArr, 58, 11), null);
objArr = Class.forName(String.valueOf(cArr, 69, 27)).getMethod(String.valueOf(cArr, 96, 12), null);
objArr = Class.forName(String.valueOf(cArr, 69, 27)).getMethod(String.valueOf(cArr, 108, 13), null);
objArr = Integer.valueOf(oix(objArr));
反射获取一些东西,还运用到了这个oix方法。
private static final int oix(Object[] objArr) {
Object[] objArr2 = (Object[]) objArr.invoke(objArr.invoke(null, null), null);
int length = objArr2.length;
for (int i = 1; i < length; i++) {
StringBuilder sb = new StringBuilder();
sb.append(objArr.invoke(objArr2, null));
sb.append(objArr.invoke(objArr2, null));
if (sb.toString().hashCode() == 180000400) {
return i;
}
}
return 0;
}
好的,看到这里就可以知道,基本上是xposed导致某些地方变化从而解析失败。
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (int i=0;i<stackTraceElements.length;i++){
StringBuilder stringBuilder = new StringBuilder("");
stringBuilder.append(stackTraceElements.getClassName());
stringBuilder.append(stackTraceElements.getMethodName());
if (stringBuilder.toString().hashCode() == 180000400)
returni;
}
return void;
面对这种,可以直接Hook这个getStackTrace方法,把有关于这个xposed的全部去掉就可以,我也就不贴代码了。
}
Object[] objArr = (Object[]) ((Method) Ec).invoke(((Method) Ec).invoke(null, null), null);
StringBuilder sb = new StringBuilder();
sb.append(((Method) Ec).invoke(objArr[((Integer) Ec).intValue()], null));
int hashCode = sb.append(((Method) Ec).invoke(objArr[((Integer) Ec).intValue()], null)).toString().hashCode();
说句实话,我个人觉得比较新奇,所以就发出来了。
最后附加一个小知识点:xposed的find and hook method方法需要拿到class,但是由于加固或者其他一些原因导致class not found,这是常有的问题。给一点通用的办法,非本人原创,是virjar所写在ucrack中的(真心推荐UCrack,一款在逆反的方面的好帮手)。
下面贴下代码:
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;
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);
int id = 5;
int flags = (Integer) param.args;
if ((flags & DEBUG_ENABLE_DEBUGGER) == 0) {
flags |= DEBUG_ENABLE_DEBUGGER;
}
param.args = flags;
}
};
@Override
public void initZygote(StartupParam startupParam) throws Throwable {
XposedBridge.hookAllMethods(Process.class, "start", debugAppsHook);
}
}
最后面是让所有APP都是可调试状态,不需要的可以删除掉。
implementation 'com.virjar:hermes-api:1.0.9'
记得在build.gradle中添上上面这个才可以。
最后打个广告
想要学习一些其他的xposed东西可以看看我另一篇文章:https://www.52pojie.cn/thread-873013-1-1.html没有咯{:301_1001:}虽然少,也就将就着看吧。 老中医w 发表于 2019-11-12 00:08
有一点没有太明白:
「基本上是xposed导致某些地方变化从而解析失败」,具体是什么地方呢?
看代码感觉像 ...
反射获取了栈堆,然后如果hook了这个方法就会导致栈堆中有xposed的存在,导致参数不对从而解密失败。 looooooc 发表于 2019-8-18 08:48
表哥,求能绕过app双向验证、绕过root检测的插件
我这里是没有的,因为root和xposed有太多办法来检测,比如什么包名,栈推,内存中加载的so,一些特有的文件等等,我都是根据app的检测来现写的,除非特别常用。 可以可以 学习了 第一次看到-普通教程-代码-整齐规范的。 LGLG 发表于 2019-8-17 22:12
第一次看到-普通教程-代码-整齐规范的。
{:301_1009:}那大佬,给个热心值呗!!! 666,正好需要
欢迎分析讨论交流,吾爱破解论坛有你更精彩! woc 这么厉害的吗、、、 无Bin无真相 这个文章好 好文章必须要顶上