游戏SKU采集&出入库-Xposed Hook

738 阅读2分钟

说明

1.适用于Google游戏SKU数据采集、游戏批量出库、游戏批量入库!
2.阅读者需要了解Android Hook技术以及Xposed框架!
3.该文章不得转载,严禁用于任何商业途径,仅用于研究学习技术!

1.HOOK主类

public class MainXposed implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        String packageName = loadPackageParam.processName;
        if (!skipPackageName(packageName)) {
            XposedBridge.log("HOOK【" + packageName + "】主进程");
            //com.igg.android.lordsmobile王国纪元
            enter(loadPackageParam, packageName);
        }
    }

    private void enter(final XC_LoadPackage.LoadPackageParam loadPackageParam, String str) {
        if (str.equals("com.yalla.yallagames")) {
            XposedHelpers.findAndHookMethod("com.yalla.yallagames.FlyCheesApp", loadPackageParam.classLoader, "onCreate", new Object[]{new XC_MethodHook() {
                public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) {
                    MainXposed.this.enterApplication(loadPackageParam, methodHookParam, true);
                }
            }});
            return;
        }
        XposedHelpers.findAndHookMethod(Application.class, "onCreate", new Object[]{new XC_MethodHook() {
            public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
                super.beforeHookedMethod(methodHookParam);
                MainXposed.this.enterApplication(loadPackageParam, methodHookParam, false);
            }
        }});
    }

    public void enterApplication(XC_LoadPackage.LoadPackageParam loadPackageParam, XC_MethodHook.MethodHookParam methodHookParam, boolean z) {
        Application application = (Application) methodHookParam.thisObject;
        int hookStatus = 0;
        try {
            hookStatus = ProviderBridge.getHookStatus(application);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (hookStatus == 1) {
            new CollectionHook().hook(loadPackageParam);
        } else if (hookStatus == 2) {
            new ImportHook().hook(application, loadPackageParam);
            if (z) {
                methodHookParam.setResult((Object) null);
            }
        } else if (hookStatus == 3 || hookStatus == 4) {
            new ExportHook().hook(loadPackageParam);
        }
        XposedBridge.log("HOOK STATUS ->" + hookStatus);
    }

    private boolean skipPackageName(String str) {
        return str.startsWith("org.cn.google") || str.startsWith("com.android") || str.startsWith("com.google") || str.contains("launcher3") || str.startsWith("com.samsung") || TextUtils.equals(str, "com.topjohnwu.magisk") || str.startsWith("com.facebook") || TextUtils.equals(str, BuildConfig.APPLICATION_ID) || TextUtils.equals(str, "com.ap");
    }
}

2.游戏SKU采集

public class CollectionHook {

    public void hook(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        hookGoogleBinder(loadPackageParam);
        hookBuyPendingIntent();
    }

    public void hookGoogleBinder(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        XposedHelpers.findAndHookMethod("android.os.BinderProxy", loadPackageParam.classLoader, "transact", new Object[]{Integer.TYPE, Parcel.class, Parcel.class, Integer.TYPE, new XC_MethodHook() {
            public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws RemoteException{
                int intValue = ((Integer) methodHookParam.args[0]).intValue();
                if (intValue == 8 || intValue == 3) {
                    Parcel parcel = (Parcel) methodHookParam.args[1];
                    parcel.setDataPosition(4);
                    if (TextUtils.equals(parcel.readString(), "com.android.vending.billing.IInAppBillingService")) {
                        CollectionHooker.this.getSku((IBinder) methodHookParam.thisObject, parcel.readInt(), parcel.readString(), parcel.readString(), parcel.readString());
                    }
                }
            }
        }});
    }
    
    private static String gameName;
    private static String packageName;
    private static String skuJson;

    public void getSku(IBinder iBinder, int i, String str, String str2, String str3) throws RemoteException {
        String str4 = getSkuDetails(iBinder, i, str, str2, str3).getStringArrayList("DETAILS_LIST").get(0);
        XposedBridge.log("采集成功:" + str4);
        httpStoreSku(str4);
    }

    public void httpStoreSku(String str) {
        Application currentApplication = AndroidAppHelper.currentApplication();
        String packageName2 = currentApplication.getPackageName();
        String charSequence = currentApplication.getPackageManager().getApplicationLabel(currentApplication.getApplicationInfo()).toString();
        skuJson = str;
        gameName = charSequence;
        packageName = packageName2;
    }

    public void hookBuyPendingIntent() {
        XposedHelpers.findAndHookMethod(Bundle.class, "getParcelable", new Object[]{String.class, new XC_MethodHook() {
            public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
                super.beforeHookedMethod(methodHookParam);
                if (TextUtils.equals((String) methodHookParam.args[0], "BUY_INTENT")) {
                    Application currentApplication = AndroidAppHelper.currentApplication();
                    Intent intent = new Intent("org.cn.google.hook_collect.view.CollectActivity");
                    intent.putExtra("skuJson", CollectionHooker.skuJson);
                    intent.putExtra("packageName", CollectionHooker.packageName);
                    intent.putExtra("gameName", CollectionHooker.gameName);
                    methodHookParam.setResult(PendingIntent.getActivity(currentApplication, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
                }
            }
        }});
    }

    public Bundle getSkuDetails(IBinder iBinder, int i, String str, String str2, String str3) throws RemoteException {
        Bundle bundle = new Bundle();
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add(str2);
        bundle.putStringArrayList("ITEM_ID_LIST", arrayList);
        bundle.putString("playBillingLibraryVersion", "2.0.0");
        Parcel obtain = Parcel.obtain();
        Parcel obtain2 = Parcel.obtain();
        try {
            obtain.writeInterfaceToken("com.android.vending.billing.IInAppBillingService");
            obtain.writeInt(i);
            obtain.writeString(str);
            obtain.writeString(str3);
            obtain.writeInt(1);
            bundle.writeToParcel(obtain, 0);
            iBinder.transact(2, obtain, obtain2, 0);
            obtain2.readException();
            return obtain2.readInt() != 0 ? (Bundle) Bundle.CREATOR.createFromParcel(obtain2) : null;
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            obtain2.recycle();
            obtain.recycle();
        }
        return null;
    }
}

3.游戏入库

public class ImportHook {
    public void hook(Application application, XC_LoadPackage.LoadPackageParam loadPackageParam){
        hookActivityThreadInstrumentation(application,loadPackageParam.classLoader);
    }

    private void hookActivityThreadInstrumentation(Context context, ClassLoader classLoader) {
        try {
            Class<?> cls = Class.forName("android.app.ActivityThread");
            Field declaredField = cls.getDeclaredField("sCurrentActivityThread");
            declaredField.setAccessible(true);
            Object obj = declaredField.get(null);
            Field declaredField2 = cls.getDeclaredField("mInstrumentation");
            declaredField2.setAccessible(true);
            declaredField2.set(obj, new InstrumentationProxy(context, classLoader));
            XposedBridge.log("hookActivityThreadInstrumentation");
        } catch (Exception e) {
            e.printStackTrace();
//            ToastUtils.showShort("挂载失败");
            XposedBridge.log("挂载失败" + e.getMessage());
        }
    }
}

public class InstrumentationProxy extends Instrumentation {

    private ClassLoader classLoader;
    private Context mContext;

    public InstrumentationProxy(Context context, ClassLoader classLoader2) {
        this.mContext = context;
        this.classLoader = classLoader2.getParent();
    }

    @Override
    public Activity newActivity(ClassLoader classLoader2, String str, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        try {
            return super.newActivity(new PathClassLoader(new File(this.mContext.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0).sourceDir).getAbsolutePath(), this.classLoader), "org.cn.google.hook_import.view.SkuListActivity", intent);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return super.newActivity(classLoader2, str, intent);
    }
}

4.游戏出库

public class ExportHooker {
    public void hook(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        new OutBinderProxyHooker().enter(loadPackageParam);
        String packageName = loadPackageParam.packageName;
        if (packageName.equals("com.igg.android.lordsmobile")) {
            return;
        }
        if (packageName.equals("com.garena.game.fctw")) {
            //new GarenaFctw().hook(loadPackageParam);
        } else if (packageName.equals("com.more.dayzsurvival.gp")){
            //new SwlmHook().hook(loadPackageParam);
        } else if (!packageName.equals("com.nexon.nsc.maplem")) {
            if (packageName.equals("com.papegames.nn4.tw")) {
                //new SYNN().hook(loadPackageParam);
            }
            //new JsonHook().hook(loadPackageParam);
        } else {
            //new NexonMaplem().hook(loadPackageParam);
        }
    }
}

public class OutBinderProxyHooker {
    public static String developerPayload;
    public static String sku;

    public void hookConsumeAsync(int i, XC_MethodHook.MethodHookParam methodHookParam) {
        Parcel parcel = (Parcel) methodHookParam.args[2];
        XposedBridge.log("指针:"+ parcel.dataPosition() + "");
        parcel.readException();
        parcel.readInt();
        Bundle.CREATOR.createFromParcel(parcel);
        XposedBridge.log("指针:"+ parcel.dataPosition() + "");
        int dataPosition = parcel.dataPosition();
        parcel.writeNoException();
        if (i == 5) {
            parcel.writeInt(0);
            XposedBridge.log("修正消耗结果"+ "0");
        }
        if (i == 12) {
            parcel.writeInt(1);
            Bundle bundle = new Bundle();
            bundle.putInt("RESPONSE_CODE", 0);
            bundle.putString("DEBUG_MESSAGE", "success");
            bundle.writeToParcel(parcel, 0);
            XposedBridge.log("修正消耗结果"+ "12");
        }
        parcel.setDataPosition(dataPosition);
    }

    public void hookBuyIntent(Parcel parcel) {
        parcel.readInt();
        parcel.readString();
        sku = parcel.readString();
        parcel.readString();
        developerPayload = parcel.readString();
        XposedBridge.log("入库"+"档位id : " + sku);
        XposedBridge.log("入库"+ "developerPayload : " + developerPayload);
    }

    public void hookBinderProxy(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        XposedHelpers.findAndHookMethod("android.os.BinderProxy", loadPackageParam.classLoader, "transact", new Object[]{Integer.TYPE, Parcel.class, Parcel.class, Integer.TYPE, new XC_MethodHook() {
            public void afterHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
                super.afterHookedMethod(methodHookParam);
                int intValue = ((Integer) methodHookParam.args[0]).intValue();
                if (intValue == 8 || intValue == 3 || intValue == 5 || intValue == 12) {
                    Parcel parcel = (Parcel) methodHookParam.args[1];
                    int dataPosition = parcel.dataPosition();
                    parcel.setDataPosition(0);
                    parcel.readInt();
                    if (TextUtils.equals(parcel.readString(), "com.android.vending.billing.IInAppBillingService")) {
                        if (intValue == 8 || intValue == 3) {
                            hookBuyIntent(parcel);
                        }
                        if (intValue == 5 || intValue == 12) {
                            hookConsumeAsync(intValue, methodHookParam);
                        }
                    }
                    parcel.setDataPosition(dataPosition);
                }
            }
        }});
    }

    public void hookBuyPendingIntent() {
        XposedHelpers.findAndHookMethod(Bundle.class, "getParcelable", new Object[]{String.class, new XC_MethodHook() {
            public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
                super.beforeHookedMethod(methodHookParam);
                if (TextUtils.equals((String) methodHookParam.args[0], "BUY_INTENT")) {
                    Application currentApplication = AndroidAppHelper.currentApplication();
                    Intent intent = new Intent("org.cn.google.hook_export.view.PayActivity");
                    intent.putExtra("sku", sku);
                    intent.putExtra("packageName", currentApplication.getPackageName());
                    methodHookParam.setResult(PendingIntent.getActivity(currentApplication, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
                    XposedBridge.log("入库Intent"+"档位id : " + sku);
                }
            }
        }});
    }

    public void enter(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        hookBuyPendingIntent();
        hookBinderProxy(loadPackageParam);
    }
}