3、叉叉助手逆向分析(下)

2014/04/08 android 共 24220 字,约 70 分钟

①继续分析插件,上面分析到插件的存放目录在“/data/data/com.xxAssistant/app_plugin/”下,ls查看目录列表:

ls
103
111
117
120

每个插件有一个插件id,进入每个插件id的目录下查看文件组织形式。

/data/data/com.xxAssistant/app_plugin/120 # ls

com.yodo1tier1.ido360.SkiSafari.xxplist
libxxHuaxue.so
xxHuaXue.apk

滑雪大冒险的插件id是120,里面有三个文件,分别pull出来,其中com.yodo1tier1.ido360.SkiSafari.xxplist

--game-name:com.yodo1tier1.ido360.SkiSafari
--apk-path:/data/data/com.xxAssistant/app_plugin/120/xxHuaXue.apk
--ui-name:com.xxAssistant.UI.xxMain
--activity-name:com/example/werk_lock/MainActivity
--so-path:/data/data/com.xxAssistant/app_plugin/120/libxxHuaxue.so

放开那三国分析:

adb pull /data/data/com.xxAssistant/app_plugin/117/com.babeltime.fknsango_360.xxplist e:\com.babeltime.fknsango_360.xxplist
adb pull /data/data/com.xxAssistant/app_plugin/117/com.babeltime.fknsango_91.xxplist e:\com.babeltime.fknsango_91.xxplist
adb pull /data/data/com.xxAssistant/app_plugin/117/com.babeltime.fknsango_uc.xxplist e:\com.babeltime.fknsango_uc.xxplist
adb pull /data/data/com.xxAssistant/app_plugin/117/com.babletimes.fknsango.wdj.xxplist e:\com.babletimes.fknsango.wdj.xxplist
adb pull /data/data/com.xxAssistant/app_plugin/117/libxxfknsg.so e:\libxxfknsg.so
adb pull /data/data/com.xxAssistant/app_plugin/117/xxFknsg.apk e:\xxFknsg.apk

其中com.babeltime.fknsango_360.xxplist

--game-name:com.babeltime.fknsango_360
--apk-path:/data/data/com.xxAssistant/app_plugin/117/xxFknsg.apk
--ui-name:com/xxAssistant/FknsgUI/xxMain
--activity-name:com/babeltimes/main/MainActivity
--so-path:/data/data/com.xxAssistant/app_plugin/117/libxxfknsg.so

我们将手机里的apk提取出来:com.babeltime.fknsango_360-1.apk,分析xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="3"
    android:versionName="1.1.8"
    android:installLocation="0"
    package="com.babeltime.fknsango_360"
    >
    <supports-screens
        android:anyDensity="true"
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:resizeable="true"
        >
    </supports-screens>
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17"
        >
    </uses-sdk>
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE"
        >
    </uses-permission>
……
    <uses-permission
        android:name="android.permission.VIBRATE"
        >
    </uses-permission>
    <application
        android:theme="@7F060001"
        android:label="@7F050000"
        android:icon="@7F020000"
        android:allowBackup="true"
        >
        <activity
            android:theme="@android:01030007"
            android:label="@7F050000"
            android:name="com.babeltimes.main.MainActivity"
            android:screenOrientation="1"
            android:configChanges="0x00000080"
            >
            <intent-filter
                >
                <action
                    android:name="android.intent.action.MAIN"
                    >
                </action>
                <category
                    android:name="android.intent.category.LAUNCHER"
                    >
                </category>
            </intent-filter>
        </activity>
        <activity
            android:theme="@android:01030007"
            android:label="@7F050000"
            android:name="com.babeltimes.main.CrashHandler"
            android:screenOrientation="1"
            android:configChanges="0x00000080"
            >
            <intent-filter
                >
                <category
                    android:name="android.intent.category.LAUNCHER"
                    >
                </category>
            </intent-filter>
        </activity>
        <activity
            android:theme="@android:01030010"
            android:name="com.qihoopay.insdk.activity.ContainerActivity"
            android:configChanges="0x400006E4"
            >
        </activity>
        <activity
            android:theme="@android:01030010"
            android:name="com.qihoopp.qcoinpay.QcoinActivity"
            android:configChanges="0x400006E4"
            android:windowSoftInputMode="0x00000013"
            >
        </activity>
        <meta-data
            android:name="QHOPENSDK_APPID"
            android:value="200983446"
            >
        </meta-data>
       ……
        <meta-data
            android:name="QHOPENSDK_CHANNEL"
            android:value="default"
            >
        </meta-data>
        <activity
            android:theme="@7F060015"
            android:name="cn.paypalm.pppayment.InitialAct"
            android:screenOrientation="3"
            android:configChanges="0x000004A0"
            android:windowSoftInputMode="0x00000003"
            >
        </activity>
       ……
        <activity
            android:theme="@7F060015"
            android:name="cn.paypalm.pppayment.BankcardAgreement"
            android:screenOrientation="3"
            android:configChanges="0x000004A0"
            android:windowSoftInputMode="0x00000003"
            >
        </activity>
    </application>
</manifest>

可以看出:

game-name为游戏包名,

apk-path为插件全路径

ui-name为插件界面

activity-name为游戏启动activity

so-path为插件so库全路径

再分析插件xxFknsg.apk:

<?xml version="1.0" encoding="utf-8"?><manifest android:versionCode="1" android:versionName="1.0" package="com.xxAssistant.FknsgUI"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:debuggable="true" android:allowBackup="true">
        <activity android:label="@string/app_name" android:name="com.xxAssistant.FknsgUI.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Application是没有name标签的,activity:com.xxAssistant.FknsgUI.MainActivity:

package com.xxAssistant.FknsgUI;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;

public class MainActivity extends Activity
{
  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130903040);
    xxMain.init(this, null);
  }

  public boolean onCreateOptionsMenu(Menu paramMenu)
  {
    getMenuInflater().inflate(2131165184, paramMenu);
    return true;
  }
}

插件的MainActivity只是调用了一个xxMain.init,查看xxMain.class:

package com.xxAssistant.FknsgUI;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import com.xxAssistant.FknsgUI.bg.ShapeBg;

public class xxMain
{
  public static String TITLE = "叉叉放开那三国助手 1.0.0";
  public static String mSoPath = null;
  private static xxMain me = null;
  private xxAbout mAboutView;
  private Activity mActivity;
  private float mDp;
  private RelativeLayout.LayoutParams mRlparams;
  private ViewGroup mRootView;
  private xxSettingView mSettingView;

  public xxMain(Activity paramActivity)
  {
    this.mActivity = paramActivity;
    DisplayMetrics localDisplayMetrics = new DisplayMetrics();
    this.mActivity.getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
    this.mDp = localDisplayMetrics.density;
  }

  public static void init(Activity paramActivity, String paramString)
  {
    mSoPath = paramString;
    if (me == null);
    for (xxMain localxxMain = new xxMain(paramActivity); ; localxxMain = me)
    {
      me = localxxMain;
      me.show();
      return;
    }
  }

  private void initNativeFunC()
  {
    if (mSoPath != null)
      xxUtility.init(this.mActivity, mSoPath);
  }

  private void initView()
  {
    this.mSettingView = new xxSettingView(this.mActivity, this.mRootView);
    this.mAboutView = new xxAbout(this.mActivity, this.mRootView);
    this.mAboutView.setXXVersionString(TITLE);
    LinearLayout localLinearLayout = new LinearLayout(this.mActivity);
    localLinearLayout.setOrientation(1);
    ShapeBg localShapeBg = new ShapeBg(this.mActivity);
    localShapeBg.setColor(-16777216);
    localShapeBg.setCornerRadii(new float[] { 4.0F, 0.0F, 0.0F, 4.0F });
    localLinearLayout.setBackgroundDrawable(localShapeBg);
    localLinearLayout.getBackground().setAlpha(153);
    localLinearLayout.setPadding((int)(4.0F * this.mDp), (int)(4.0F * this.mDp), (int)(4.0F * this.mDp), (int)(4.0F * this.mDp));
    RelativeLayout.LayoutParams localLayoutParams = new RelativeLayout.LayoutParams(-2, -2);
    localLayoutParams.addRule(11);
    localLayoutParams.addRule(15);
    LinearLayout.LayoutParams localLayoutParams1 = new LinearLayout.LayoutParams(-2, -2);
    TextView localTextView1 = new TextView(this.mActivity);
    localTextView1.setText("设置");
    localTextView1.setTextSize(16.0F);
    localTextView1.setTextColor(-1);
    LinearLayout.LayoutParams localLayoutParams2 = new LinearLayout.LayoutParams(-2, -2);
    TextView localTextView2 = new TextView(this.mActivity);
    localTextView2.setText("叉叉");
    localTextView2.setTextSize(16.0F);
    localTextView2.setTextColor(-16711936);
    localLayoutParams2.topMargin = ((int)(5.0F * this.mDp));
    localLinearLayout.addView(localTextView1, localLayoutParams1);
    localLinearLayout.addView(localTextView2, localLayoutParams2);
    this.mRootView.addView(localLinearLayout, localLayoutParams);
    this.mRlparams = new RelativeLayout.LayoutParams(-1, -1);
    this.mRlparams.addRule(11);
    this.mRlparams.addRule(15);
    localTextView1.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        xxMain.this.mRootView.removeView(xxMain.this.mAboutView);
        xxMain.this.mRootView.removeView(xxMain.this.mSettingView);
        xxMain.this.mRootView.addView(xxMain.this.mSettingView, xxMain.this.mRlparams);
      }
    });
    localTextView2.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        xxMain.this.mRootView.removeView(xxMain.this.mAboutView);
        xxMain.this.mRootView.removeView(xxMain.this.mSettingView);
        xxMain.this.mRootView.addView(xxMain.this.mAboutView, xxMain.this.mRlparams);
      }
    });
  }

  private void show()
  {
    this.mRootView = new RelativeLayout(this.mActivity);
    FrameLayout.LayoutParams localLayoutParams = new FrameLayout.LayoutParams(-1, -1);
    this.mActivity.addContentView(this.mRootView, localLayoutParams);
    initNativeFunC();
    initView();
  }
}

img

package com.xxAssistant.FknsgUI;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.xxAssistant.FknsgUI.bg.ShapeBg;

public class xxSettingView extends RelativeLayout
{
  private static int MAX;
  private static int mSpeed = 0;
  private Activity mActivity;
  private float mDp;
  private ViewGroup mParent;
  private LinearLayout mRealSettingView;
  private RelativeLayout mSettingView;

  static
  {
    MAX = 100;
  }

  public xxSettingView(Activity paramActivity, ViewGroup paramViewGroup)
  {
    super(paramActivity);
    this.mActivity = paramActivity;
    this.mParent = paramViewGroup;
    this.mSettingView = this;
    DisplayMetrics localDisplayMetrics = new DisplayMetrics();
    this.mActivity.getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
    this.mDp = localDisplayMetrics.density;
    initView();
  }

  private void initView()
  {
    this.mSettingView.setGravity(17);
    this.mSettingView.setBackgroundColor(-16777216);
    this.mSettingView.getBackground().setAlpha(80);
    this.mRealSettingView = new LinearLayout(this.mActivity);
    this.mRealSettingView.setOrientation(1);
    this.mRealSettingView.setGravity(1);
    int i = (int)(16.0F * this.mDp);
    this.mRealSettingView.setPadding(i, i, i, i);
    ShapeBg localShapeBg1 = new ShapeBg(this.mActivity);
    localShapeBg1.setCornerRadius(8.0F);
    localShapeBg1.setColor(-16777216);
    localShapeBg1.setStroke(1, -1);
    this.mRealSettingView.setBackgroundDrawable(localShapeBg1);
    this.mRealSettingView.getBackground().setAlpha(153);
    RelativeLayout.LayoutParams localLayoutParams = new RelativeLayout.LayoutParams((int)(300.0F * this.mDp), -2);
    this.mSettingView.addView(this.mRealSettingView, localLayoutParams);
    TextView localTextView1 = new TextView(this.mActivity);
    localTextView1.setText("设置");
    localTextView1.setTextSize(22.0F);
    localTextView1.setTextColor(-1);
    localTextView1.setGravity(1);
    this.mRealSettingView.addView(localTextView1);
    ScrollView localScrollView = new ScrollView(this.mActivity);
    localScrollView.setScrollBarStyle(0);
    LinearLayout localLinearLayout1 = new LinearLayout(this.mActivity);
    localLinearLayout1.setOrientation(1);
    LinearLayout localLinearLayout2 = new LinearLayout(this.mActivity);
    LinearLayout localLinearLayout3 = new LinearLayout(this.mActivity);
    ShapeBg localShapeBg2 = new ShapeBg(this.mActivity);
    localShapeBg2.setCornerRadius(10.0F);
    localShapeBg2.setColor(-1);
    localLinearLayout2.setBackgroundDrawable(localShapeBg2);
    localLinearLayout3.setBackgroundDrawable(localShapeBg2);
    (int)(6.0F * this.mDp);
    LinearLayout.LayoutParams localLayoutParams1 = new LinearLayout.LayoutParams(-1, -2);
    localLayoutParams1.topMargin = (int)(8.0F * this.mDp);
    this.mRealSettingView.addView(localScrollView, localLayoutParams1);
    localScrollView.addView(localLinearLayout1);
    TextView localTextView2 = new TextView(this.mActivity);
    localTextView2.setText("游戏加速:0 倍");
    localTextView2.setTextColor(-1);
    localTextView2.setTextSize(17.0F);
    localTextView2.setGravity(14);
    SeekBar localSeekBar = new SeekBar(this.mActivity);
    localSeekBar.setMinimumHeight(2 * (int)this.mDp);
    localLinearLayout1.addView(localTextView2, localLayoutParams1);
    localLinearLayout1.addView(localSeekBar, localLayoutParams1);
    localSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(localTextView2)
    {
      public void onProgressChanged(SeekBar paramSeekBar, int paramInt, boolean paramBoolean)
      {
        xxSettingView.mSpeed = paramInt * (xxSettingView.MAX / 100);
        this.val$tittle.setText("游戏加速:" + xxSettingView.mSpeed + " 倍");
        if (xxSettingView.mSpeed == 0);
        for (int i = 1000; ; i = 1000 * xxSettingView.mSpeed)
        {
          xxUtility.setTimeScale(i);
          return;
        }
      }

      public void onStartTrackingTouch(SeekBar paramSeekBar)
      {
      }

      public void onStopTrackingTouch(SeekBar paramSeekBar)
      {
      }
    });
    this.mSettingView.setOnTouchListener(new View.OnTouchListener()
    {
      public boolean onTouch(View paramView, MotionEvent paramMotionEvent)
      {
        float f1 = paramMotionEvent.getX();
        float f2 = paramMotionEvent.getY();
        float f3 = xxSettingView.this.mRealSettingView.getLeft();
        float f4 = xxSettingView.this.mRealSettingView.getTop();
        float f5 = xxSettingView.this.mRealSettingView.getBottom();
        float f6 = xxSettingView.this.mRealSettingView.getRight();
        switch (paramMotionEvent.getAction())
        {
        default:
          return false;
        case 0:
        }
        if ((f1 < f3) || (f1 > f6));
        while (true)
        {
          xxSettingView.this.mParent.removeView(xxSettingView.this.mSettingView);
          return false;
          if (f2 < f4)
            continue;
          if (f2 <= f5)
            break;
        }
      }
    });
  }
}

img

package com.xxAssistant.FknsgUI;

import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import com.xxAssistant.FknsgUI.bg.ShapeBg;

public class xxAbout extends RelativeLayout
{
  private RelativeLayout mAbout;
  private Activity mActivity;
  private float mDp;
  private ViewGroup mParent;
  private LinearLayout mRealAbout;
  private TextView mTvContent;
  private TextView mTvTitle;

  public xxAbout(Activity paramActivity, ViewGroup paramViewGroup)
  {
    super(paramActivity);
    this.mActivity = paramActivity;
    this.mParent = paramViewGroup;
    this.mAbout = this;
    DisplayMetrics localDisplayMetrics = new DisplayMetrics();
    this.mActivity.getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
    this.mDp = localDisplayMetrics.density;
    initView();
  }

  private void initView()
  {
    this.mAbout.setGravity(17);
    this.mAbout.setBackgroundColor(-16777216);
    this.mAbout.getBackground().setAlpha(0);
    this.mRealAbout = new LinearLayout(this.mActivity);
    this.mRealAbout.setOrientation(1);
    this.mRealAbout.setGravity(1);
    ShapeBg localShapeBg1 = new ShapeBg(this.mActivity);
    localShapeBg1.setCornerRadius(8.0F);
    localShapeBg1.setColor(-16777216);
    localShapeBg1.setStroke(1, -1);
    this.mRealAbout.setBackgroundDrawable(localShapeBg1);
    this.mRealAbout.getBackground().setAlpha(153);
    int i = (int)(16.0F * this.mDp);
    this.mRealAbout.setPadding(i, i, i, i);
    RelativeLayout.LayoutParams localLayoutParams = new RelativeLayout.LayoutParams((int)(300.0F * this.mDp), (int)(150.0F * this.mDp));
    this.mAbout.addView(this.mRealAbout, localLayoutParams);
    this.mTvTitle = new TextView(this.mActivity);
    this.mTvTitle.setTextSize(22.0F);
    this.mTvTitle.setText("关于");
    this.mTvTitle.setTextColor(-1);
    this.mTvTitle.setGravity(17);
    this.mRealAbout.addView(this.mTvTitle);
    this.mTvContent = new TextView(this.mActivity);
    ShapeBg localShapeBg2 = new ShapeBg(this.mActivity);
    localShapeBg2.setCornerRadius(6.0F);
    localShapeBg2.setColor(-1);
    this.mTvContent.setBackgroundDrawable(localShapeBg2);
    this.mTvContent.setTextColor(-16777216);
    this.mTvContent.setTextSize(18.0F);
    int j = (int)(4.0F * this.mDp);
    this.mTvContent.setPadding(j, j, j, j);
    this.mTvContent.setGravity(3);
    LinearLayout.LayoutParams localLayoutParams1 = new LinearLayout.LayoutParams(-1, -2);
    localLayoutParams1.topMargin = (int)(20.0F * this.mDp);
    this.mRealAbout.addView(this.mTvContent, localLayoutParams1);
    TextView localTextView = new TextView(this.mActivity);
    localTextView.setText("隐藏界面");
    localTextView.setTextColor(-16711936);
    LinearLayout.LayoutParams localLayoutParams2 = new LinearLayout.LayoutParams(-2, -2);
    localLayoutParams2.topMargin = (int)(20.0F * this.mDp);
    localLayoutParams2.gravity = 5;
    this.mRealAbout.addView(localTextView, localLayoutParams2);
    localTextView.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramView)
      {
        AlertDialog.Builder localBuilder = new AlertDialog.Builder(xxAbout.this.mActivity);
        localBuilder.setMessage("警告:界面隐藏后,只能在下次游戏启动时再次出现,请确认是否执行该操作。");
        localBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener()
        {
          public void onClick(DialogInterface paramDialogInterface, int paramInt)
          {
            xxAbout.this.mParent.removeAllViews();
          }
        });
        localBuilder.setNegativeButton("取消", null);
        localBuilder.show();
      }
    });
    this.mAbout.setOnTouchListener(new View.OnTouchListener()
    {
      public boolean onTouch(View paramView, MotionEvent paramMotionEvent)
      {
        float f1 = paramMotionEvent.getX();
        float f2 = paramMotionEvent.getY();
        float f3 = xxAbout.this.mRealAbout.getLeft();
        float f4 = xxAbout.this.mRealAbout.getTop();
        float f5 = xxAbout.this.mRealAbout.getBottom();
        float f6 = xxAbout.this.mRealAbout.getRight();
        switch (paramMotionEvent.getAction())
        {
        default:
          return false;
        case 0:
        }
        if ((f1 < f3) || (f1 > f6));
        while (true)
        {
          xxAbout.this.mParent.removeView(xxAbout.this.mAbout);
          return false;
          if (f2 < f4)
            continue;
          if (f2 <= f5)
            break;
        }
      }
    });
  }

  public void setXXVersionString(String paramString)
  {
    this.mTvContent.setText(paramString);
  }
}

img

img

其中xxUtility.setTimeScale在xxUtility中:

package com.xxAssistant.FknsgUI;

import android.app.Activity;

public class xxUtility
{
  public static Activity mActivity;
  private static boolean mIsInitOk = false;

  private static native void doSetTimeScale(int paramInt);

  public static void init(Activity paramActivity, String paramString)
  {
    mActivity = paramActivity;
    if (paramString != null)
    {
      System.load(paramString);
      mIsInitOk = xxdohook();
    }
  }

  public static void setTimeScale(int paramInt)
  {
    if (mIsInitOk)
      doSetTimeScale(paramInt);
  }

  private static native boolean xxdohook();
}

加载对应的插件目录下的libxxfknsg.so:

img

对gettimeofday和clock_gettime做一些hook,然后通过xxUtility::setTimeScale加速设置加速倍数。

再分析“天天星连萌”的插件:

package com.xxAssistant.UI;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class UniversalUI extends RelativeLayout
{
  private static String mSoPath;
  private static UniversalUI me = null;
  private Activity mActivity;
  private Context mContext;
  private boolean mShow;

  private UniversalUI(Activity paramActivity)
  {
    super(paramActivity);
    this.mContext = paramActivity;
    this.mActivity = paramActivity;
    this.mShow = false;
  }

  public static void init(Activity paramActivity, String paramString)
  {
    if (me == null);
    for (UniversalUI localUniversalUI = new UniversalUI(paramActivity); ; localUniversalUI = me)
    {
      me = localUniversalUI;
      mSoPath = paramString;
      me.show();
      return;
    }
  }

  private void show()
  {
    if (!this.mShow)
    {
      this.mShow = true;
      Log.d("native", "叉叉辅助已成功装载");
      Toast.makeText(this.mContext.getApplicationContext(), "叉叉辅助已成功装载1111111111111111111111111111111111", 1).show();
      System.load(mSoPath);
      xxdohook();
    }
  }

  private native void xxdohook();
}

可以看出都有一个静态的init函数,插桩修改打印参数:

.class public Lcom/xxAssistant/UI/UniversalUI;
.super Landroid/widget/RelativeLayout;
.source "UniversalUI.java"


# static fields
.field private static mSoPath:Ljava/lang/String;

.field private static me:Lcom/xxAssistant/UI/UniversalUI;


# instance fields
.field private mActivity:Landroid/app/Activity;

.field private mContext:Landroid/content/Context;

.field private mShow:Z


# direct methods
.method static constructor <clinit>()V
    .locals 1

    .prologue
    .line 12
    const/4 v0, 0x0

    sput-object v0, Lcom/xxAssistant/UI/UniversalUI;->me:Lcom/xxAssistant/UI/UniversalUI;

    .line 17
    return-void
.end method

.method private constructor <init>(Landroid/app/Activity;)V
    .locals 1
    .parameter "activity"

    .prologue
    .line 20
    invoke-direct {p0, p1}, Landroid/widget/RelativeLayout;-><init>(Landroid/content/Context;)V

    .line 22
    iput-object p1, p0, Lcom/xxAssistant/UI/UniversalUI;->mContext:Landroid/content/Context;

    .line 23
    iput-object p1, p0, Lcom/xxAssistant/UI/UniversalUI;->mActivity:Landroid/app/Activity;

    .line 24
    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/xxAssistant/UI/UniversalUI;->mShow:Z
    
    #debug by sing
    const-string v0, "MODBYSING"
    invoke-virtual {p1}, Ljava/lang/Object;->toString()Ljava/lang/String;
    move-result-object v1
    invoke-static {v0, v1}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
    #debug by sing

    .line 25
    return-void
.end method

.method public static init(Landroid/app/Activity;Ljava/lang/String;)V
    .locals 1
    .parameter "activity"
    .parameter "soPath"

    .prologue
    .line 29
    sget-object v0, Lcom/xxAssistant/UI/UniversalUI;->me:Lcom/xxAssistant/UI/UniversalUI;

    if-nez v0, :cond_0

    new-instance v0, Lcom/xxAssistant/UI/UniversalUI;
    


    invoke-direct {v0, p0}, Lcom/xxAssistant/UI/UniversalUI;-><init>(Landroid/app/Activity;)V

    :goto_0
    sput-object v0, Lcom/xxAssistant/UI/UniversalUI;->me:Lcom/xxAssistant/UI/UniversalUI;

    .line 30
    sput-object p1, Lcom/xxAssistant/UI/UniversalUI;->mSoPath:Ljava/lang/String;

    .line 31
    sget-object v0, Lcom/xxAssistant/UI/UniversalUI;->me:Lcom/xxAssistant/UI/UniversalUI;

    invoke-direct {v0}, Lcom/xxAssistant/UI/UniversalUI;->show()V

    .line 32
    return-void

    .line 29
    :cond_0
    sget-object v0, Lcom/xxAssistant/UI/UniversalUI;->me:Lcom/xxAssistant/UI/UniversalUI;

    goto :goto_0
.end method

.method private show()V
    .locals 3

    .prologue
    const/4 v2, 0x1

    .line 35
    iget-boolean v0, p0, Lcom/xxAssistant/UI/UniversalUI;->mShow:Z

    if-nez v0, :cond_0

    .line 36
    iput-boolean v2, p0, Lcom/xxAssistant/UI/UniversalUI;->mShow:Z

    .line 38
    const-string v0, "native"

    const-string v1, "\u53c9\u53c9\u8f85\u52a9\u5df2\u6210\u529f\u88c5\u8f7d"

    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    .line 39
    iget-object v0, p0, Lcom/xxAssistant/UI/UniversalUI;->mContext:Landroid/content/Context;

    invoke-virtual {v0}, Landroid/content/Context;->getApplicationContext()Landroid/content/Context;

    move-result-object v0

    const-string v1, "\u53c9\u53c9\u8f85\u52a9\u5df2\u6210\u529f\u88c5\u8f7d1111111111111111MODBYSING"

    invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v0

    invoke-virtual {v0}, Landroid/widget/Toast;->show()V

    .line 40
    sget-object v0, Lcom/xxAssistant/UI/UniversalUI;->mSoPath:Ljava/lang/String;
    
    

    #debug by sing
    const-string v1, "MODBYSING"
    invoke-static {v1,v0} ,Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
    #debug by sing

    
    invoke-static {v0}, Ljava/lang/System;->load(Ljava/lang/String;)V

    .line 41
    invoke-direct {p0}, Lcom/xxAssistant/UI/UniversalUI;->xxdohook()V

    .line 43
    :cond_0
    return-void
.end method

.method private native xxdohook()V
.end method

img

img

此插件实现的效果是任意消除,捕获的log信息:

img

04-09 15:46:23.770: V/MODBYSING(13478): com.tencent.lian.MiniGame@41c711a8 
04-09 15:46:23.800: V/MODBYSING(13478): /data/data/com.xxAssistant/app_plugin/105/libxxlianmeng_mm.so 

实际运行中MainActivity::onCreate并没有执行(单独直接运行APK会执行,仅仅方便开发使用,具体启动游戏时插件的运行逻辑中不会执行到MainActivity::onCreate),证实Activity确实是游戏的主Activity。

再看插件so文件导出的xxdohook函数,Java_com_xxAssistant_UI_UniversalUI_xxdohook:

img

函数内部调用了do_hook:

img

调用了MSHookFunction(libSubstrate.so提供的函数)来hook原游戏libGameApp.so中的函数。

img

下面把插件APK直接安装运行,这个是滑雪大冒险的插件运行效果图:

img

img

img

可以看出和启动游戏后的插件显示效果类似,只不过单独运行时只是一个hello world的界面。

综合以上分析猜测:

所有插件按照一定格式编写,需要有一个.xxplist配置文件,一个可以安装运行的APK(可以直接安装运行是为了方便插件的开发),如果有做底层hook操作的还会有一个so文件。

.xxplist配置文件的内容格式如下:

game-name为游戏包名,

apk-path为插件全路径

ui-name为插件界面

activity-name为游戏启动activity

so-path为插件so库全路径

叉叉助手游戏列表中保存有插件id,点击“启动游戏”时按照item对应的插件id加载创建目录对应的插件apk,启动规则是按照插件目录下的.xxplist配置文件来的。

叉叉助手在运行时会做一次注入操作,在叉叉助手里启动游戏时,将相应的插件配置文件重定向到一个plist.xx的公共配置文件,由于做了注入,处于底层的监控层监控到游戏启动时判断当前的activity的包名是否和配置文件中的包名吻合,如果吻合说明当前游戏和当前插件匹配,则通过配置文件读取插件apk并动态加载,并调用一个静态的init函数,传递参数为:参数1是游戏的activity启动实例,参数2为配置文件中的插件so库文件路径。init函数完成插件的界面配置,显示为游戏界面上的悬浮窗口,加载so库并调用so的导出函数进行hook操作。

待研究内容

1、libsubstrate的hook框架。

2、验证:注入后如何监控游戏的启动并加载插件。

文档信息

Search

    Table of Contents