Android(安卓)apk快速定位、灰色按钮克星--DroidSword
本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/80994434
在进行Android应用程序的逆向分析时,经常需要对Android应用程序的按钮事件、Activity界面等类的代码进行定位分析,传统的代码定位方法就是进行按钮或者Activity界面等显示的 字符串信息 进行全局的搜索,然后找他们的id或者类进行代码的定位,比较繁琐,这里介绍一个基于Xposed Hook实现的Android apk快速定位,灰色按钮克星工具DroidSword,当然了亦可以使用我前面的博客中提到的《Xposed框架Hook Android应用的所有类方法打印Log日志》和《查找和定位Android应用的按钮点击事件的代码位置基于Xposed Hook实现》进行Android应用程序的需要分析的代码的定位。
DroidSword工具的功能介绍:
1.快速定位Activity,以及点击View的信息
2.点击悬浮窗口获取Fragment
3.灰色按钮克星
4.文字修改神器
DroidSword工具的github地址:https://github.com/githubwing/DroidSword
DroidSword工具作者的学习博客:http://androidwing.net
DroidSword工具是基于Xposed Hook实现的,但是作者githubwing是使用Kotlin语言实现的,对于Kotlin语言不熟悉,但是对于DroidSword工具的实现思路还是能看明白,下面简要的分析一下。
1.类IHooker是作者编写的xposed hook的接口类,代码如下:
2.类net.androidwing.droidsword.Init是DroidSword工具xposed hook的入口类:
3.类ViewClickedHooker主要用于实现Hook类android.view.View的方法onTouchEvent,获取到View类的名称和View类的id以及View的事件监听类对象的类名称;Hook类android.view.View的方法dispatchTouchEvent,获取到的View类的名称、View类的id、View的事件监听类对象的类名称并在设备的界面上显示出来。
源码文件路径:/frameworks/base/core/java/android/view/View.java
一般View组件情况下,Hook类android.view.View的类方法onTouchEvent函数,View组件通过获取实例对象View中的成员变量mListenerInfo->mOnClickListener所属的类名称,得到响应View按钮单击事件的监听响应类OnClickListener的信息。
/** * Implement this method to handle touch screen motion events. * * If this method is used to detect click actions, it is recommended that * the actions be performed by implementing and calling * {@link #performClick()}. This will ensure consistent system behavior, * including: *
* - obeying click sound preferences *
- dispatching OnClickListener calls *
- handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when * accessibility features are enabled *
* * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // take focus if we don't have it already and we should in // touch mode. boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (prepressed) { // The button is being released before we actually // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. setPressed(true); } if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: mHasPerformedLongPress = false; if (performButtonActionOnTouchDown(event)) { break; } // Walk up the hierarchy to determine if we're inside a scrolling container. boolean isInScrollingContainer = isInScrollingContainer(); // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if (isInScrollingContainer) { mPrivateFlags |= PFLAG_PREPRESSED; if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true); checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); setPressed(false); } } break; } return true; } return false; }
对于AdapterView类型的View组件,通过Hook类android.view.View的方法dispatchTouchEvent,AdapterView组件获取实例对象View中的成员变量mOnItemClickListener的类(事件响应类)的类名称,得到监听和响应用户单击事件的处理类OnItemClickListener的信息。
/** * Pass the touch screen motion event down to the target view, or this * view if it is the target. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; }
4.类ActivityHooker主要用于实现Hook类android.app.Activity的方法onResume,获取类方法onResume所属类Activity的实例对象的类名称并显示出来。
/** * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or * {@link #onPause}, for your activity to start interacting with the user. * This is a good place to begin animations, open exclusive-access devices * (such as the camera), etc. * * Keep in mind that onResume is not the best indicator that your activity * is visible to the user; a system window such as the keyguard may be in * front. Use {@link #onWindowFocusChanged} to know for certain that your * activity is visible to the user (for example, to resume a game). * *
Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.
* * @see #onRestoreInstanceState * @see #onRestart * @see #onPostResume * @see #onPause */ protected void onResume() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); getApplication().dispatchActivityResumed(this); mCalled = true; }
5.类FragmentHooker用于实现Hook类"android.support.v4.app.Fragment"的方法onResume和方法setUserVisibleHint,获取类Fragment的类名称并进行显示。
源码文件路径:/frameworks/support/v4/java/android/support/v4/app/Fragment.java
/** * Called when the fragment is visible to the user and actively running. * This is generally * tied to {@link Activity#onResume() Activity.onResume} of the containing * Activity's lifecycle. */ public void onResume() { mCalled = true; }
/** * Set a hint to the system about whether this fragment's UI is currently visible * to the user. This hint defaults to true and is persistent across fragment instance * state save and restore. * * An app may set this to false to indicate that the fragment's UI is * scrolled out of visibility or is otherwise not directly visible to the user. * This may be used by the system to prioritize operations such as fragment lifecycle updates * or loader ordering behavior.
* * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default), * false if it is not. */ public void setUserVisibleHint(boolean isVisibleToUser) { if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) { mFragmentManager.performPendingDeferredStart(this); } mUserVisibleHint = isVisibleToUser; mDeferStart = !isVisibleToUser; }
有作者gtict112将DroidSword工具的功能用java代码进行了实现并添加了新的功能构建成工程xposedhook,xposedhook工程的github地址:https://github.com/gtict112/xposedhook,后面有时间我再看下将这部分代码集成到我自己的Xposed模块中。
DroidSword工具的类ViewClickedHooker的代码:
package net.androidwing.droidsword.hookerimport android.app.AlertDialogimport android.app.AndroidAppHelperimport android.app.Dialogimport android.content.DialogInterfaceimport android.view.MotionEventimport android.view.Viewimport android.widget.*import de.robv.android.xposed.XC_MethodHookimport de.robv.android.xposed.XposedHelpersimport de.robv.android.xposed.callbacks.XC_LoadPackageimport net.androidwing.droidsword.func.TextViewChangerimport net.androidwing.droidsword.func.ViewEnablerimport net.androidwing.droidsword.utils.LogUtils/** * Created on 28/10/2017. */class ViewClickedHooker : IHooker { override fun hook(lp: XC_LoadPackage.LoadPackageParam) { // Hook类android.view.View的方法onTouchEvent // public boolean onTouchEvent(MotionEvent event) XposedHelpers.findAndHookMethod(View::class.java, "onTouchEvent", MotionEvent::class.java, object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) // 获取类方法onTouchEvent所在的实例对象View val view = param?.thisObject as View // 获取类方法onTouchEvent的传入参数MotionEvent实例对象 val event = param.args!![0] as MotionEvent // 对用户点击屏幕的事件进行判断 if (event.action == MotionEvent.ACTION_UP) { // 获取实例对象View中的成员变量mListenerInfo->mOnClickListener所属的类名称 val listener = XposedHelpers.getObjectField( XposedHelpers.getObjectField(view, "mListenerInfo"), "mOnClickListener").javaClass.name // 显示获取到的View类的名称、View类的id、View的事件监听类对象的类名称 ActivityHooker.setActionInfoToMenu("", "${view.javaClass.name} ${view.id} \nListener: $listener") antiDisable(view) } } }) // Hook类android.view.View的方法dispatchTouchEvent // public boolean dispatchTouchEvent(MotionEvent event) XposedHelpers.findAndHookMethod(View::class.java, "dispatchTouchEvent", MotionEvent::class.java, object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) // 获取类方法dispatchTouchEvent所在类View的实例 val view = param?.thisObject as View // 获取类方法onTouchEvent的传入参数MotionEvent实例对象 val event = param.args!![0] as MotionEvent // 进行用户点击屏幕的事件类型的判断 if (event.action == MotionEvent.ACTION_DOWN) { // 进行View类型的判断(AdapterView) if (view is AdapterView<*>) { // 获取实例对象View中的成员变量mOnItemClickListener的类(事件响应类)的类名称 val listener = XposedHelpers.getObjectField(view,"mOnItemClickListener").javaClass.name // 显示获取到的View类的名称、View类的id、View的事件监听类对象的类名称 ActivityHooker.setActionInfoToMenu("", "${view.javaClass.name} ${view.id} \nListener: $listener") } } } }) // 文字修改功能的实现 XposedHelpers.findAndHookMethod(View::class.java, "onTouchEvent", MotionEvent::class.java, object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) val targetView = param?.thisObject as View if (true) { // ?? showChangeTextDialog(targetView, param) } } }) } private fun antiDisable(view: View) { //TODO 默认开启待添加配置文件 if (false) { ViewEnabler.antiDisable(view) } } /** * 文本修改神器功能 */ private fun showChangeTextDialog(targetView: View, param: XC_MethodHook.MethodHookParam) { //TODO 默认开启待添加配置文件 val event = param.args!![0] as MotionEvent if (false) { TextViewChanger.showChangeDialog(targetView, event) } }
DroidSword工具的类ActivityHooker的代码:
package net.androidwing.droidsword.hookerimport android.app.Activityimport android.app.AndroidAppHelperimport android.app.Fragmentimport android.content.Contextimport android.graphics.Colorimport android.os.Buildimport android.os.Bundleimport android.support.v7.widget.AppCompatImageHelperimport android.text.TextUtilsimport android.view.LayoutInflaterimport android.view.ViewGroupimport android.widget.FrameLayoutimport android.widget.TextViewimport android.widget.Toastimport de.robv.android.xposed.XC_MethodHookimport de.robv.android.xposed.XposedHelpersimport de.robv.android.xposed.callbacks.XC_LoadPackageimport net.androidwing.droidsword.utils.LogUtilsimport java.util.ArrayList/** * Created on 30/10/2017. */class ActivityHooker : IHooker { // /frameworks/base/core/java/android/app/Activity.java override fun hook(lp: XC_LoadPackage.LoadPackageParam) { // Hook类android.app.Activity的方法onResume // protected void onResume() XposedHelpers.findAndHookMethod(Activity::class.java, "onResume", object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) // 获取类方法onResume所属类Activity的实例对象 val activity = param?.thisObject as Activity // 显示类对象实例Activity的类名称 addTextView(activity) // Hook类Fragment的类方法,获取类Fragment实例对象的类名称 FragmentHooker().hookFragment(param) } }) } // 显示类对象实例Activity的类名称 private fun addTextView(activity: Activity) { // 获取类对象实例Activity的类名称 val className = activity.javaClass.name.toString() // 构建TextView实例对象 if (sTextView == null) { genTextView(activity) } if (sTextView?.parent != null) { val parent = sTextView?.parent if (parent is ViewGroup) { parent.removeView(sTextView) } } (activity.window.decorView as FrameLayout).addView(sTextView) // 显示类对象实例Activity的类名称 setActionInfoToMenu(className, "") sTextView?.bringToFront() } // 创建TextView的实例对象 private fun genTextView(activity: Activity) { sTextView = TextView(activity) with(sTextView!!) { textSize = 8f y = 48 * 2f setBackgroundColor(Color.parseColor("#cc888888")) setTextColor(Color.WHITE) layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT) } } companion object { var sTextView: TextView? = null private var sActivityName = "" private var sViewName = "" fun setActionInfoToMenu(activityName: String, viewName: String) { sTextView?.text = getActionInfo(activityName, viewName) } public var sFragmentName = "" private fun getActionInfo(activityName: String, viewName: String): CharSequence? { if (activityName.isEmpty().not()) { sActivityName = activityName } if (viewName.isEmpty().not()) { sViewName = viewName } val pid = android.os.Process.myPid() return "Activity: $sActivityName \nPid: $pid \nClick: $sViewName \nFragment:$sFragmentName" } }
DroidSword工具的类FragmentHooker的代码:
package net.androidwing.droidsword.hookerimport de.robv.android.xposed.XC_MethodHookimport de.robv.android.xposed.XposedHelpersimport de.robv.android.xposed.callbacks.XC_LoadPackageimport net.androidwing.droidsword.utils.LogUtils/** * Created on 30/10/2017. */class FragmentHooker : IHooker { override fun hook(lp: XC_LoadPackage.LoadPackageParam) { } // /frameworks/support/v4/java/android/support/v4/app/Fragment.java fun hookFragment(param: XC_MethodHook.MethodHookParam?) { // Hook类"android.support.v4.app.Fragment"的方法onResume // public void onResume() XposedHelpers.findAndHookMethod( param?.thisObject?.javaClass?.classLoader?.loadClass("android.support.v4.app.Fragment"), "onResume", object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) // 获取类Fragment的类名称 ActivityHooker.sFragmentName = (param?.thisObject?.javaClass?.name!!) // 进行类Fragment的类名称显示的设置 ActivityHooker.setActionInfoToMenu("","") } override fun beforeHookedMethod(param: MethodHookParam?) { super.beforeHookedMethod(param) } }) // Hook类"android.support.v4.app.Fragment"的方法setUserVisibleHint // public void setUserVisibleHint(boolean isVisibleToUser) XposedHelpers.findAndHookMethod( param?.thisObject?.javaClass?.classLoader?.loadClass("android.support.v4.app.Fragment"), "setUserVisibleHint", Boolean::class.java, object : XC_MethodHook() { override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) if (param?.args!![0] == true) { LogUtils.e("fragment showing:") LogUtils.e("fragment ${param?.thisObject?.javaClass?.name}") // 获取类Fragment的类名称 ActivityHooker.sFragmentName = (param?.thisObject?.javaClass?.name!!) // 进行类Fragment的类名称显示的设置 ActivityHooker.setActionInfoToMenu("","") } } override fun beforeHookedMethod(param: MethodHookParam?) { super.beforeHookedMethod(param) } }) } }
更多相关文章
- Android的编译过程 & Android(安卓)dex 方法限制的一些总结
- JDK与adb/android环境变量配置完整教程
- android监听系统联系人修改
- android 解决getColor()方法过时
- android 移动View的方法汇
- Android(安卓)ContentProvider
- Android(安卓)TextView预渲染研究
- android aidl 使用实例
- Android(安卓)4.0 捕获Home键