9、substrate和xposed两个HOOK框架综合-同时支持的插件框架

2014/04/08 android 共 27436 字,约 79 分钟

插件一开始用的资源布局写的,也就是通过R引用,代码如下:

package com.netease.ga.plug;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by sing on 2014/12/10.
 * desc:
 */
public class PlugMain {
    private static final String TAG = "PlugMain";
    public static String TITLE = "NISHook框架插件(1.0)";
    public static String mSoPath;
    private static PlugMain me = null;
    private Activity mActivity;
    private float mDp;
    private RelativeLayout.LayoutParams mRlparams;
    private ViewGroup mRootView;
    private PopupWindow popupWindow;
    static private EditText edt_log;

    static {
        mSoPath = null;
    }

    public PlugMain(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) {
            me = new PlugMain(paramActivity);
            me.show();
            logMsg("init run, target Activity: \n" + paramActivity);
            logMsg("插件已加载");
            Toast.makeText(paramActivity.getApplicationContext(), "插件已加载", Toast.LENGTH_LONG).show();
        }
    }

    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();
    }

    private void initNativeFunC() {
        if (mSoPath != null) {
            logMsg("load so: \n" + mSoPath);
            System.load(mSoPath);
        }
//        if (mSoPath != null)
//            xxUtility.init(this.mActivity, mSoPath);
    }

    private void initView() {
        Context context = PlugMain.this.mRootView.getContext();
        View contentView = View.inflate(context, R.layout.popup_main, null);//getApplicationContext()

        LinearLayout localLinearLayout = new LinearLayout(this.mActivity);
        localLinearLayout.setOrientation(LinearLayout.VERTICAL);
        localLinearLayout.setBackgroundResource(R.drawable.rounded_corners_view);
        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(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        localLayoutParams.addRule(11);
        localLayoutParams.addRule(15);
        LinearLayout.LayoutParams localLayoutParams1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        TextView localTextView1 = new TextView(this.mActivity);
        localTextView1.setText("查看");
        localTextView1.setTextSize(26.0F);
        localTextView1.setTextColor(Color.WHITE);
        localLinearLayout.addView(localTextView1, localLayoutParams1);
        this.mRootView.addView(localLinearLayout, localLayoutParams);
        this.mRlparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        this.mRlparams.addRule(11);
        this.mRlparams.addRule(15);

        /////////////////////////////////////////////////////////////////////////
        //创建PopupWindow
        //将布局文件转成view,该view用于显示PopupWindow中的内容
        contentView.setBackgroundResource(R.drawable.rounded_corners_view);
        edt_log = (EditText) contentView.findViewById(R.id.edt_log);

        //创建PopupWindow窗体时必须要指定窗体的大小,否则不会显示在界面上。参数一:窗体中用于显示内容的viewContent,参数二、三:表示PopupWindow窗体的宽和高
        popupWindow = new PopupWindow(contentView, DensityUtil.dip2px(context, 300), ViewGroup.LayoutParams.WRAP_CONTENT, true);
        // 注意:一定要给popwindow设置背景图片或背景资源,如果不设置背景资源 , 动画、 焦点的处理 都会产生问题。
        popupWindow.setBackgroundDrawable(PlugMain.this.mActivity.getResources().getDrawable(R.drawable.rounded_corners_pop));
        /////////////////////////////////////////////////////////////////////////


        localTextView1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View paramView) {
                dismissPopupWindow();
                //参数一:PopupWindow挂载在那个View上,参数二:设置PopupWindow显示的重心位置
                //参数三:PopupWindow在View上X轴的偏移量,参数四:PopupWindow在View上Y轴的偏移量。X、Y轴的偏移量是相对于当前Activity所在的窗体,参照点为(0,0)
                popupWindow.showAtLocation(mRootView, Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0);
            }
        });
    }

    /**
     * 设置log信息
     *
     * @param s
     */
    public static void logMsg(String s) {
        if (edt_log != null) {
            String strText = edt_log.getText().toString();
            edt_log.setText(strText + s + "\n");
        }
    }

    private void dismissPopupWindow() {
        if (popupWindow != null && popupWindow.isShowing()) {
            popupWindow.dismiss();
            //popupWindow = null;
        }
    }
}

资源布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_popup_container"
    android:background="@android:color/darker_gray"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:text="NISHook插件"
        android:textSize="20sp"
        android:textColor="@android:color/white"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <EditText
        android:id="@+id/edt_log"
        android:textColor="@android:color/black"
        android:background="@android:color/white"
        android:layout_margin="2dp"
        android:editable="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:hint="信息输出栏..."
        android:inputType="textMultiLine"
        android:gravity="left|top"
        android:minLines="10"
        android:scrollbars="vertical"
        android:layout_alignParentBottom="true"/>
</LinearLayout>

由于插件被加载后R实际上是找不到的,完美的解决方案可以参考:

Android apk动态加载机制的研究(二):资源加载和activity生命周期管理

对应的git代码:https://github.com/singwhatiwanna/dynamic-load-apk

但是感觉蛮复杂的,我们的插件界面又比较简单,所以沿用叉叉助手插件的方式动态创建布局。

写的时候动态创建的控件属性参考popup_main.xml进行设置。

后来写的时候由于一开始HOOK的是启动类的Activity,这个Activity有时候作为欢迎界面,所以生存周期比较短,当时考虑显示一个桌面悬浮窗口,

于是有了下面的代码:

package com.netease.ga.plug;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.GradientDrawable;
import android.text.InputType;
import android.text.method.ScrollingMovementMethod;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by sing on 2014/12/10.
 * desc:
 */
public class PlugMain {
    private static final String TAG = "PlugMain";
    public static String TITLE = "NISHook框架插件(1.0)";
    public static String mSoPath;
    private static PlugMain me = null;
    //private Activity mActivity;
    private Context mContext;
    private WindowManager mWindowManager;
    private float mDp;
    private RelativeLayout.LayoutParams mRlparams;
    private ViewGroup mRootView;
    private PopupWindow popupWindow;
    static private EditText edt_log;

    static {
        mSoPath = null;
    }

    public PlugMain(Activity paramActivity) {
        mContext = paramActivity.getApplicationContext();
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics localDisplayMetrics = new DisplayMetrics();
        mWindowManager.getDefaultDisplay().getMetrics(localDisplayMetrics);
        this.mDp = localDisplayMetrics.density;
    }

    public static void init(Activity paramActivity, String strSoPath) {
        mSoPath = strSoPath;
        if (me == null) {
            me = new PlugMain(paramActivity);
            me.show();
            logMsg("init run, target Activity: \n" + paramActivity);
            me.initNativeFunC();
            logMsg("插件已加载");
            Toast.makeText(paramActivity.getApplicationContext(), "插件已加载", Toast.LENGTH_LONG).show();
        }
    }

    private void show() {
        this.mRootView = new RelativeLayout(mContext);
        WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
        wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;  // 设置window type
        wmParams.format = PixelFormat.RGBA_8888;                // 设置图片格式,效果为背景透明

        wmParams.gravity = Gravity.CENTER;
        // 以屏幕左上角为原点,设置x、y初始值
        wmParams.x = 0;
        wmParams.y = 0;

        // 设置悬浮窗口长宽数据
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;;
        wmParams.height =WindowManager.LayoutParams.WRAP_CONTENT;;

        mWindowManager.addView(this.mRootView, wmParams);
        initView();
    }

    private void initNativeFunC() {
        if (mSoPath != null) {
            logMsg("load so: \n" + mSoPath);
            System.load(mSoPath);
        }else{
            logMsg("未指定要加载的so文件\n");
        }
//        if (mSoPath != null)
//            xxUtility.init(this.mActivity, mSoPath);
    }

    private void initView() {
        Context context = PlugMain.this.mRootView.getContext();
        GradientDrawable gradientDrawable;

        gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(0xff606060);
        gradientDrawable.setStroke(DensityUtil.dip2px(context, 3), 0xffff808);
        gradientDrawable.setCornerRadius(DensityUtil.dip2px(context, 10));

        LinearLayout localLinearLayout = new LinearLayout(mContext);
        localLinearLayout.setOrientation(LinearLayout.VERTICAL);
        localLinearLayout.setBackgroundDrawable(gradientDrawable);
        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(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        localLayoutParams.addRule(11);
        localLayoutParams.addRule(15);
        LinearLayout.LayoutParams localLayoutParams1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        TextView localTextView1 = new TextView(mContext);
        localTextView1.setText("查看");
        localTextView1.setTextSize(26.0F);
        localTextView1.setTextColor(Color.WHITE);
        localLinearLayout.addView(localTextView1, localLayoutParams1);
        TextView localTextView2 = new TextView(mContext);
        localTextView2.setText("退出");
        localTextView2.setTextSize(26.0F);
        localTextView2.setTextColor(Color.RED);
        localLinearLayout.addView(localTextView2, localLayoutParams1);
        this.mRootView.addView(localLinearLayout, localLayoutParams);
        this.mRlparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        this.mRlparams.addRule(11);
        this.mRlparams.addRule(15);

        /////////////////////////////////////////////////////////////////////////
        //创建PopupWindow
        LinearLayout.LayoutParams layoutParamsPopView = new LinearLayout.LayoutParams(DensityUtil.dip2px(context, 300), DensityUtil.dip2px(context, 250));
        LinearLayout contentView = new LinearLayout(mContext);
        contentView.setOrientation(LinearLayout.VERTICAL);
        contentView.setLayoutParams(layoutParamsPopView);

        gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(0xff606060);
        gradientDrawable.setStroke(DensityUtil.dip2px(context, 3), 0xffff808);
        gradientDrawable.setCornerRadius(DensityUtil.dip2px(context, 6));
        contentView.setBackgroundDrawable(gradientDrawable);
        contentView.setPadding(DensityUtil.dip2px(context, 4), DensityUtil.dip2px(context, 4), DensityUtil.dip2px(context, 4), DensityUtil.dip2px(context, 4));

        //创建一个标题
        LinearLayout.LayoutParams layoutParamsTitle = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        layoutParamsTitle.gravity = Gravity.CENTER;
        TextView tv_Title = new TextView(mContext);
        tv_Title.setText("NISHook插件");
        tv_Title.setTextSize(22.0f);
        tv_Title.setTextColor(Color.WHITE);
        tv_Title.setPadding(1, 1, 1, 1);
        contentView.addView(tv_Title, layoutParamsTitle);

        //创建一个信息输出栏
        LinearLayout.LayoutParams layoutParamsLog = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        edt_log = new EditText(mContext);
        edt_log.setHint("信息输出栏...");
        edt_log.setTextColor(Color.BLACK);
        edt_log.setBackgroundColor(Color.WHITE);
        edt_log.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE);
        edt_log.setSingleLine(false);  //改变默认的单行模式
        edt_log.setMinLines(10);
        edt_log.setMaxLines(15);
        edt_log.setVerticalScrollBarEnabled(true);
        edt_log.setMovementMethod(ScrollingMovementMethod.getInstance());
        edt_log.setGravity(Gravity.LEFT | Gravity.TOP);
        contentView.addView(edt_log, layoutParamsLog);

        //创建PopupWindow窗体时必须要指定窗体的大小,否则不会显示在界面上。参数一:窗体中用于显示内容的viewContent,参数二、三:表示PopupWindow窗体的宽和高
        popupWindow = new PopupWindow(contentView, DensityUtil.dip2px(context, 300), ViewGroup.LayoutParams.WRAP_CONTENT, true);
        // 注意:一定要给popwindow设置背景图片或背景资源,如果不设置背景资源 , 动画、 焦点的处理 都会产生问题。

        gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(0xffffffff);
        gradientDrawable.setStroke(DensityUtil.dip2px(context, 3), 0xffff808);
        gradientDrawable.setCornerRadius(DensityUtil.dip2px(context, 6));
        popupWindow.setBackgroundDrawable(gradientDrawable);
        popupWindow.getBackground().setAlpha(153);
        /////////////////////////////////////////////////////////////////////////

        localTextView1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View paramView) {
                dismissPopupWindow();
                //参数一:PopupWindow挂载在那个View上,参数二:设置PopupWindow显示的重心位置
                //参数三:PopupWindow在View上X轴的偏移量,参数四:PopupWindow在View上Y轴的偏移量。X、Y轴的偏移量是相对于当前Activity所在的窗体,参照点为(0,0)
                popupWindow.showAtLocation(mRootView, Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0);
            }
        });
        localTextView2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dismissPopupWindow();
                mWindowManager.removeView(mRootView);
            }
        });
    }

    /**
     * 设置log信息
     *
     * @param s
     */
    public static void logMsg(String s) {
        if (edt_log != null) {
            String strText = edt_log.getText().toString();
            edt_log.setText(strText + s + "\n");
        }
    }

    private void dismissPopupWindow() {
        if (popupWindow != null && popupWindow.isShowing()) {
            popupWindow.dismiss();
            //popupWindow = null;
        }
    }
}

但是显示悬浮窗口需要一个权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

当插件被加载到目标进程中时,是需要宿主拥有这个权限才行,只能通过先静态修改目标APK包添加上这个权限,然后安装重复以上步骤。

如果目标程序有防二次打包的话,这个方案就不是很好,而且来回操作也挺麻烦,最后无奈只好老老实实按照叉叉助手的插件模式来写,

就是动态创建的方式:

package com.netease.ga.plug;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.text.InputType;
import android.text.method.ScrollingMovementMethod;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by sing on 2014/12/10.
 * desc:
 */
public class PlugMain {
    private static final String TAG = "PlugMain";
    public static String TITLE = "NISHook框架插件(1.0)";
    public static String mSoPath;
    private static PlugMain me = null;
    private Activity mActivity;
    private float mDp;
    private RelativeLayout.LayoutParams mRlparams;
    private ViewGroup mRootView;
    private PopupWindow popupWindow;
    static private EditText edt_log;

    static {
        mSoPath = null;
    }

    public PlugMain(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 strSoPath) {
        mSoPath = strSoPath;
        if (me == null) {
            me = new PlugMain(paramActivity);
            me.show();
            logMsg("init run, target Activity: \n" + paramActivity);
            me.initNativeFunC();
            logMsg("插件已加载");
            Toast.makeText(paramActivity.getApplicationContext(), "插件已加载", Toast.LENGTH_LONG).show();
        }
    }

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

    private void initNativeFunC() {
        if (mSoPath != null) {
            logMsg("load so: \n" + mSoPath);
            System.load(mSoPath);
        } else {
            logMsg("未指定要加载的so文件\n");
        }
//        if (mSoPath != null)
//            xxUtility.init(this.mActivity, mSoPath);
    }

    private void initView() {
        Context context = PlugMain.this.mRootView.getContext();
        GradientDrawable gradientDrawable;

        gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(0xff606060);
        gradientDrawable.setStroke(DensityUtil.dip2px(context, 3), 0xffff808);
        gradientDrawable.setCornerRadius(DensityUtil.dip2px(context, 10));

        LinearLayout localLinearLayout = new LinearLayout(mActivity);
        localLinearLayout.setOrientation(LinearLayout.VERTICAL);
        localLinearLayout.setBackgroundDrawable(gradientDrawable);
        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(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        localLayoutParams.addRule(11);
        localLayoutParams.addRule(15);
        LinearLayout.LayoutParams localLayoutParams1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        TextView localTextView1 = new TextView(mActivity);
        localTextView1.setText("查看");
        localTextView1.setTextSize(26.0F);
        localTextView1.setTextColor(Color.WHITE);
        localLinearLayout.addView(localTextView1, localLayoutParams1);
        TextView localTextView2 = new TextView(mActivity);
        localTextView2.setText("退出");
        localTextView2.setTextSize(26.0F);
        localTextView2.setTextColor(Color.RED);
        localLinearLayout.addView(localTextView2, localLayoutParams1);
        this.mRootView.addView(localLinearLayout, localLayoutParams);
        this.mRlparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        this.mRlparams.addRule(11);
        this.mRlparams.addRule(15);

        /////////////////////////////////////////////////////////////////////////
        //创建PopupWindow
        LinearLayout.LayoutParams layoutParamsPopView = new LinearLayout.LayoutParams(DensityUtil.dip2px(context, 300), DensityUtil.dip2px(context, 250));
        LinearLayout contentView = new LinearLayout(mActivity);
        contentView.setOrientation(LinearLayout.VERTICAL);
        contentView.setLayoutParams(layoutParamsPopView);

        gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(0xff606060);
        gradientDrawable.setStroke(DensityUtil.dip2px(context, 3), 0xffff808);
        gradientDrawable.setCornerRadius(DensityUtil.dip2px(context, 6));
        contentView.setBackgroundDrawable(gradientDrawable);
        contentView.setPadding(DensityUtil.dip2px(context, 4), DensityUtil.dip2px(context, 4), DensityUtil.dip2px(context, 4), DensityUtil.dip2px(context, 4));

        //创建一个标题
        LinearLayout.LayoutParams layoutParamsTitle = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        layoutParamsTitle.gravity = Gravity.CENTER;
        TextView tv_Title = new TextView(mActivity);
        tv_Title.setText("NISHook插件");
        tv_Title.setTextSize(22.0f);
        tv_Title.setTextColor(Color.WHITE);
        tv_Title.setPadding(1, 1, 1, 1);
        contentView.addView(tv_Title, layoutParamsTitle);

        //创建一个信息输出栏
        LinearLayout.LayoutParams layoutParamsLog = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        edt_log = new EditText(mActivity);
        edt_log.setHint("信息输出栏...");
        edt_log.setTextColor(Color.BLACK);
        edt_log.setBackgroundColor(Color.WHITE);
        edt_log.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE);
        edt_log.setSingleLine(false);  //改变默认的单行模式
        edt_log.setMinLines(10);
        edt_log.setMaxLines(15);
        edt_log.setVerticalScrollBarEnabled(true);
        edt_log.setMovementMethod(ScrollingMovementMethod.getInstance());
        edt_log.setGravity(Gravity.LEFT | Gravity.TOP);
        contentView.addView(edt_log, layoutParamsLog);

        //创建PopupWindow窗体时必须要指定窗体的大小,否则不会显示在界面上。参数一:窗体中用于显示内容的viewContent,参数二、三:表示PopupWindow窗体的宽和高
        popupWindow = new PopupWindow(contentView, DensityUtil.dip2px(context, 300), ViewGroup.LayoutParams.WRAP_CONTENT, true);
        // 注意:一定要给popwindow设置背景图片或背景资源,如果不设置背景资源 , 动画、 焦点的处理 都会产生问题。

        gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(0xffffffff);
        gradientDrawable.setStroke(DensityUtil.dip2px(context, 3), 0xffff808);
        gradientDrawable.setCornerRadius(DensityUtil.dip2px(context, 6));
        popupWindow.setBackgroundDrawable(gradientDrawable);
        popupWindow.getBackground().setAlpha(153);
        /////////////////////////////////////////////////////////////////////////

        localTextView1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View paramView) {
                dismissPopupWindow();
                //参数一:PopupWindow挂载在那个View上,参数二:设置PopupWindow显示的重心位置
                //参数三:PopupWindow在View上X轴的偏移量,参数四:PopupWindow在View上Y轴的偏移量。X、Y轴的偏移量是相对于当前Activity所在的窗体,参照点为(0,0)
                popupWindow.showAtLocation(mRootView, Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0);
            }
        });
        localTextView2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dismissPopupWindow();
                mRootView.setVisibility(View.INVISIBLE);
            }
        });
    }

    /**
     * 设置log信息
     *
     * @param s
     */
    public static void logMsg(String s) {
        if (edt_log != null) {
            String strText = edt_log.getText().toString();
            edt_log.setText(strText + s + "\n");
        }
    }

    private void dismissPopupWindow() {
        if (popupWindow != null && popupWindow.isShowing()) {
            popupWindow.dismiss();
            //popupWindow = null;
        }
    }
}

运行预览:

img

HOOK框架界面部分:

package com.netease.ga.view;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.netease.ga.Injector;
import com.netease.ga.R;
import com.netease.ga.utils.util;

import java.util.List;

public class MainActivity extends Activity {
    public static final String TAG = "MainActivity";

    private native void hookApplication(Context context);

    static {
    }

    private boolean bret;
    private SharedPreferences sp;
    private EditText edt_packageName;
    private Button btn_browse;
    private TextView tv_activity;
    private EditText edt_activity;
    private Button btn_startApp;
    private Button btn_inject;
    private static EditText edt_log;

    private String strTargetActivity;

    public MainActivity() {
        super();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edt_packageName = (EditText) findViewById(R.id.edt_package);
        btn_browse = (Button) findViewById(R.id.btn_browse);
        tv_activity = (TextView) findViewById(R.id.tv_activity);
        edt_activity = (EditText) findViewById(R.id.edt_activity);
        btn_startApp = (Button) findViewById(R.id.btn_startApp);
        btn_inject = (Button) findViewById(R.id.btn_inject);
        edt_log = (EditText) findViewById(R.id.edt_log);
        edt_log.setFocusableInTouchMode(false);

        sp = getSharedPreferences("config", Context.MODE_PRIVATE);
        //SharedPreferences.Editor editor = sp.edit();
        String packageName = sp.getString("hookpackage", "com.netease.anep");
        strTargetActivity = sp.getString("launcherActivity", "");

        edt_packageName.setText(packageName);
        edt_activity.setText(strTargetActivity);
        tv_activity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String topActivityClassName = null;
                ActivityManager am = (ActivityManager) MainActivity.this.getSystemService(Activity.ACTIVITY_SERVICE);
                List<ActivityManager.RunningTaskInfo> mRunningTasks = am.getRunningTasks(10);
                for (ActivityManager.RunningTaskInfo runningTaskInfo : mRunningTasks) {
                    ComponentName cn = runningTaskInfo.topActivity;
                    String activityName = cn.getClassName();
                    if (activityName.indexOf("com.netease.ga.view") == -1 && activityName.indexOf("com.android.") == -1) {
                        activityName = activityName.replace('.', '/');
                        topActivityClassName = activityName;
                    }
                }
                if (topActivityClassName != null) {
                    strTargetActivity = topActivityClassName;
                    logMsg("launcher:" + strTargetActivity);
                    edt_activity.setText(strTargetActivity);
                    SharedPreferences.Editor editor = sp.edit();
                    editor.putString("launcherActivity", strTargetActivity);
                    editor.commit();
                }
            }
        });

        //显示到信息框中
        logMsg("HOOK目标信息:");
        logMsg("package: " + packageName);
        logMsg("launcher: " + strTargetActivity + "\n");
        if (strTargetActivity.isEmpty()) {
            logMsg("[×]尚未选择目标,请先选择目标!");
        }

        //浏览app列表
        btn_browse.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, MyGamesActivity.class);
                MainActivity.this.startActivityForResult(intent, 0);
            }
        });

        //注入
        btn_inject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //把hook的配置信息转换成易读取的文件,逐行写入到:/data/data/com.netease.ga/app_plugin/config.cfg
                //保证在hook前把最新设置的hook信息配置到文件中去
                MainActivity.this.setHookConfigFile();
                Injector.injectSoToParent(MainActivity.this);
            }
        });

        //运行指定的app
        btn_startApp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //启动的时候先配置好Hook信息
                MainActivity.this.setHookConfigFile();
                util.runApp(MainActivity.this, edt_packageName.getText().toString());
            }
        });
    }

    //把hook的配置信息转换成易读取的文件,逐行写入到:/data/data/com.netease.ga/app_plugin/config.cfg
    private void setHookConfigFile() {
        //把插件从资源目录下复制到app_plugin目录下
        String plugPath = Injector.getPluginPath(edt_packageName.getText().toString(), this);
        bret = Injector.copyAssetsFileToDir("plug.apk", plugPath, this);
        bret = bret && Injector.setFileAttr755(plugPath + "/plug.apk");
        logMsg(bret ? "[√]插件APK部署成功" : "[×]插件APK部署失败");
        bret = Injector.copyAssetsFileToDir("libplug.so", plugPath, this);
        bret = bret && Injector.setFileAttr755(plugPath + "/libplug.so");
        logMsg(bret ? "[√]部署插件SO成功" : "[×]部署插件SO失败");

        //plugPath = "/data/data/com.netease.anep/app_plugin";
        bret = Injector.setHookConfigFile(this, edt_packageName.getText().toString(), strTargetActivity,
                "com.netease.ga.plug.PlugMain", plugPath + "/plug.apk", plugPath + "/libplug.so");
        bret = bret && Injector.setFileAttr755(plugPath + "/config.cfg");
        logMsg(bret ? "[√]HOOK配置文件部署成功" : "[×]HOOK配置文件部署失败");
        logMsg("");
    }

    /**
     * 设置log信息
     *
     * @param s
     */
    public static void logMsg(String s) {
        String strText = edt_log.getText().toString();
        edt_log.setText(strText + s + "\n");
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (resultCode) {
            case RESULT_OK:
                String packageName = data.getExtras().getString("package");
                String mainActivityClassName = null;
                String topActivityClassName = null;
                edt_packageName.setText(packageName);

                //获取包名对应的主activity
                PackageManager localPackageManager = this.getPackageManager();
                try {
                    PackageInfo localPackageInfo = localPackageManager.getPackageInfo(packageName, 0);
                    Intent localIntent1 = new Intent("android.intent.action.MAIN", null);
                    localIntent1.addCategory("android.intent.category.LAUNCHER");
                    localIntent1.setPackage(localPackageInfo.packageName);
                    List localList = localPackageManager.queryIntentActivities(localIntent1, 0);
                    if (localList.iterator().hasNext()) {
                        ResolveInfo localResolveInfo = (ResolveInfo) localList.iterator().next();
                        mainActivityClassName = localResolveInfo.activityInfo.name;
                        mainActivityClassName = mainActivityClassName.replace('.', '/');
                        //完美点可以动态检查一下该类是否有onCreate方法实现,此处默认它是有的
                        //...
                    }
                } catch (PackageManager.NameNotFoundException localNameNotFoundException) {
                    localNameNotFoundException.printStackTrace();
                }

                ActivityManager am = (ActivityManager) MainActivity.this.getSystemService(Activity.ACTIVITY_SERVICE);
                List<ActivityManager.RunningTaskInfo> mRunningTasks = am.getRunningTasks(10);
                for (ActivityManager.RunningTaskInfo runningTaskInfo : mRunningTasks) {
                    ComponentName cn = runningTaskInfo.topActivity;
                    String activityName = cn.getClassName();
                    if (activityName.indexOf(packageName) != -1) {
                        activityName = activityName.replace('.', '/');
                        topActivityClassName = activityName;
                    }
                }

                if (topActivityClassName == null) {
                    //说明目标程序当前没有运行,则默认使用启动类
                    strTargetActivity = mainActivityClassName;
                    logMsg("[乄]所选择的目标程序当前没有运行,默认使用LAUNCHER属性的Activity作为HOOK目标! 如果该Activity的生存周期很短,意味着插件也会很快消失。");
                    logMsg("靠谱的做法是先启动一次目标程序,进入到生存周期较长的页面,然后再点击“浏览”按钮,本程序会自动识别有效的Activity。");
                } else {
                    //说明是从正在运行的程序列表中找到的目标类,最有效!
                    strTargetActivity = topActivityClassName;
                    logMsg("[√]目标程序已经运行,将自动识别HOOK目标的Activity! 识别成功后请杀死目标进程,然后点击“启动”按钮。");
                }

                logMsg("launcher:" + strTargetActivity);
                edt_activity.setText(strTargetActivity);
                SharedPreferences.Editor editor = sp.edit();
                editor.putString("hookpackage", packageName);
                editor.putString("launcherActivity", strTargetActivity);
                editor.commit();
                break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

运行预览:

img

注入后重新运行目标程序,效果:

img

文档信息

Search

    Table of Contents