前言:最近两天在搞一个App,由于某些问题我就不会公布了。在这App中看到一个有意思的反xposed的处理,觉得有意思就公布出来了。
正文:这个App混淆如同一坨屎,所有的string都需要经过解密,而且class name和method name是尽量一样就一样,上个截图表示意思意思。
经过本人发现,string有两种解密算法,不在同一个类。两种解密算法不在同一个类,一个类居然用apktool反编译出来后没有。。。初步猜想是类似于热修复那样,运行时动态加载到class loader中,只不过不是本文重点,所以就不深究。而另一个则是直接一大片的计算。
为了偷懒我就是直接用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没有咯虽然少,也就将就着看吧。 |