说明
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);
}
}