Menu
Woocommerce Menu

零售业如何用Hadoop开启大数据之门,实现无清单启动Activity

0 Comment


// Permission: CN-2082-2

有许多销售点工具可以聚合销售信息并将其存储在大数据集中。然而,零售商难以用常规工具从PoS中挖掘大数据,即使它就存储在SQL数据库中。Hadoop使零售商更容易从客户数据库访问信息,此数据可以转换为其他格式,并与其他文件中的数据集合并。New
Horizons CLC的John Soto声称Hadoop是零售业主要的改变者。

大功告成~喜欢的大佬,现在就可以下载demo了.

又是历时3天,无清单注册,启动Activity也完成。结果是美好的,然而,过程,是纠结的,但是也是值得的.完成了3篇博文,自己对Hook技术也有了更深层次的认识:所谓hook,套路是简单的,就是1.找到hook点(比如上面,3次hook,一次是系统的AMS,一次是
ActivityThread的mH,还有一次是,ActivityThread的sPackageManager)2.用合适的方式创建代理对象,通常
要hook一个系统类,就用继承的方式,重写某方法。hook一个系统接口的实现类,那就用JDK的Proxy动态代理3.最后用代理对象,反射set,替换被hook的对象.
套路并不难,掌握好反射,以及代理模式,就行了.真正难的是哪里?

**
源码的阅读能力,还有写出兼容性Hook核心代码的能力!**androidSDK
有很多版本迭代,现在最新的是SDK 28,我们要保证我们的hook代码能够兼容所有的系统版本,就需要大量阅读源码,确保万无一失,比如上面,如果不是在SDK 28-android9.0的模拟器上运行发现报异常,我根本就不会去做最后一次的
hook.

欧了,hook从入门,到实践,3篇博文,全都通俗易懂,喜欢的大佬,点个赞,就是对我最大的鼓励,以后还有干货,也会用这种方式,呈现给大家.

#env “planet_engine”

在过去几年,全球零售商一直试图利用大数据创造价值。由于其大数据分析基础架构的限制,许多工作被一再推迟。Hadoop为这些零售商打开了新的大门,它可以解决他们在过去几年在大数据领域面临的许多问题和挑战。

一份大礼:
2019-02-27 18:20:12.287 28253-28253/study.hank.com.activityhookdemo E/AndroidRuntime: FATAL EXCEPTION: main Process: study.hank.com.activityhookdemo, PID: 28253 java.lang.RuntimeException: Unable to start activity ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity} at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity} at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222) at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155) at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:61) at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72) at study.hank.com.activityhookdemo.methodA.Main2Activity.onCreate(Main2Activity.java:14) at android.app.Activity.performCreate(Activity.java:7136) at android.app.Activity.performCreate(Activity.java:7127) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity} at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:435) at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:240) at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:219) at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155) at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:61) at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72) at study.hank.com.activityhookdemo.methodA.Main2Activity.onCreate(Main2Activity.java:14) at android.app.Activity.performCreate(Activity.java:7136) at android.app.Activity.performCreate(Activity.java:7127) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

提取关键信息:

Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity} at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222)

居然找不到包?问题出在:NavUtils.getParentActivityName

还是被谷歌摆了一道,查原因啊,进去NavUtils.getParentActivityName()去看看:

图片 1image.png

看来就是这里报的错,继续:

图片 2image.png找到可疑点:图片 3image.png可能就是这里抛出的异常,继续:图片 4image.png然而,它是一个接口,那么就找它的实现类(注意,如果这个接口涉及到隐藏@hide的类,你用ctrl+T是不能找到的,不过也有办法,回到NavUtil.java):图片 5image.png哦,原来pm对象是来自context,既然提到了context这个抽象类,它的很多抽象方法的实现都在ContextImpl,手动进入ContextImpl:找这个方法:图片 6image.png这个pm对象原来是来自ActivityThread,然后进行了一次封装,最后返回出去的是一个ApplicationPackageManager对象.那就进入主线程咯.图片 7image.png看看IPackageManager的内容:它是AIDL动态生成的接口,用androidStudio是看不到接口内容的,只能到源码官网,查到的接口如下:

interface IPackageManager { ... ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);}

Ok,看到IBinder,就知道应该无法继续往下追查了,已经跨进程了.前面提到了,从主线程拿到的pm,被封装成了ApplicationPackageManager,那么,进入它里面去找:getActivityInfo方法:

图片 8image.png原来异常是这里抛出的,当mPm.getActivityInfo为空的时候,才会抛出.OK,就查到这里,得出结论:源码,其实对Activity的合法性进行了两次检测,一次是在AMS,一次是在这里的PMS,前面的AMS,我们用一个已有的Activity伪装了一下,通过了验证,那么这里的PMS,我们也可以采用同样的方式.注:上图的参数ComponentName className,其实,他就是!IntentComponentName成员:图片 9image.png懂了吧··这里对intent又进行了一次检查,检查的就是这个ComponentName.

接下来用同样的方式对PMS的检测进行hook,让它不再报异常.此次hook的参照的源码是:

图片 10image.png

hook核心代码如下(对sPackageManager进行代理替换,让代理类检查的永远是合法的Activity):

private static void hookPMAfter28(Context context) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { String pmName = Util.getPMName; String hostClzName = Util.getHostClzName(context, pmName); Class<?> forName = Class.forName("android.app.ActivityThread");//PM居然是来自ActivityThread Field field = forName.getDeclaredField("sCurrentActivityThread"); field.setAccessible; Object activityThread = field.get; Method getPackageManager = activityThread.getClass().getDeclaredMethod("getPackageManager"); Object iPackageManager = getPackageManager.invoke(activityThread); String packageName = Util.getPMName; PMSInvocationHandler handler = new PMSInvocationHandler(iPackageManager, packageName, hostClzName); Class<?> iPackageManagerIntercept = Class.forName("android.content.pm.IPackageManager"); Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{iPackageManagerIntercept}, handler); // 获取 sPackageManager 属性 Field iPackageManagerField = activityThread.getClass().getDeclaredField("sPackageManager"); iPackageManagerField.setAccessible; iPackageManagerField.set(activityThread, proxy); } static class PMSInvocationHandler implements InvocationHandler { private Object base; private String packageName; private String hostClzName; public PMSInvocationHandler(Object base, String packageName, String hostClzName) { this.packageName = packageName; this.base = base; this.hostClzName = hostClzName; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("getActivityInfo")) { ComponentName componentName = new ComponentName(packageName, hostClzName); return method.invoke(base, componentName, PackageManager.GET_META_DATA, 0);//破费,一定是这样 } return method.invoke(base, args); } }

}

2、与不同的数据格式兼容。零售商以许多不同的格式存储数据。内部财务数据通常存储在.csv文件中。零售商一直在努力进行审计,因为他们无法比较结构化和非结构化数据集的数据。Hadoop可以提取多种格式的数据,进行分析并以更具凝聚力的形式呈现,它使大数据分析专家能够从多个来源的数据集之间寻找相关性。

结果,脸一黑:报错!***

intinit(){

在此我向大家推荐一个大数据开发交流圈:

3.hook核心代码

还记得我们的整体思路么?

1.在AMS的hook函数中,将
真实的Intent中的信息,替换成manifest中已有的Activity信息.
骗过系统的检测机制。2.虽然骗过了系统的检测机制,但是这么一来,每一次的跳转,都会跳到"假"Activity,这肯定不是我们想要的效果,那么就必须,在真正的跳转时机之前,将
真实的Activity信息,还原回去, 跳到原本该去的Activity.

说通俗一点就是,第一,伪造一个Intent,骗过Activity Manifest检测。第二,真正要跳转之前,把原始的Intent还原回去.开始撸代码,大量反射代码即将到来,注释应该很详尽了,特别注意看反射代码要对照源代码来看,不然很容易走神

伪造intent,骗过Activity Manifest检测

这里,请对照:ActivityManager.java 的 4125-4137行

图片 11hook对照源代码.pnghook核心代码如下

 /** * 这里对AMS进行hook * * @param context */ private static void hookAMS(Context context) { try { Class<?> ActivityManagerClz; final Object IActivityManagerObj;//这个就是AMS实例 Method getServiceMethod; Field IActivityManagerSingletonField; if (ifSdkOverIncluding26 {//26,27,28的ams获取方式是通过ActivityManager.getService() ActivityManagerClz = Class.forName("android.app.ActivityManager"); getServiceMethod = ActivityManagerClz.getDeclaredMethod("getService"); IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");//单例类成员的名字也不一样 } else {//25往下,是ActivityManagerNative.getDefault() ActivityManagerClz = Class.forName("android.app.ActivityManagerNative"); getServiceMethod = ActivityManagerClz.getDeclaredMethod("getDefault"); IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("gDefault");//单例类成员的名字也不一样 } IActivityManagerObj = getServiceMethod.invoke;//OK,已经取得这个系统自己的AMS实例 // 2.现在创建我们的AMS实例 // 由于IActivityManager是一个接口,那么其实我们可以使用Proxy类来进行代理对象的创建 // 结果被摆了一道,IActivityManager这玩意居然还是个AIDL,动态生成的类,编译器还不认识这个类,怎么办?反射咯 Class<?> IActivityManagerClz = Class.forName("android.app.IActivityManager"); // 构建代理类需要两个东西用于创建伪装的Intent String packageName = Util.getPMName; String clz = Util.getHostClzName(context, packageName); Object proxyIActivityManager = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{IActivityManagerClz}, new ProxyInvocation(IActivityManagerObj, packageName, clz)); //3.拿到AMS实例,然后用代理的AMS换掉真正的AMS,代理的AMS则是用 假的Intent骗过了 activity manifest检测. //偷梁换柱 IActivityManagerSingletonField.setAccessible; Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get; Class<?> SingletonClz = Class.forName("android.util.Singleton");//反射创建一个Singleton的class Field mInstanceField = SingletonClz.getDeclaredField("mInstance"); mInstanceField.setAccessible; mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager); } catch (Exception e) { e.printStackTrace(); } } private static final String ORI_INTENT_TAG = "origin_intent"; /** * 把InvocationHandler的实现类提取出来,因为这里包含了核心技术逻辑,最好独立,方便维护 */ private static class ProxyInvocation implements InvocationHandler { Object amsObj; String packageName;//这两个String是用来构建Intent的ComponentName的 String clz; public ProxyInvocation(Object amsInstance, String packageName, String clz) { this.amsObj = amsInstance; this.packageName = packageName; this.clz = clz; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //proxy是创建出来的代理类,method是接口中的方法,args是接口执行时的实参 if (method.getName().equals("startActivity")) { Log.d("GlobalActivityHook", "全局hook 到了 startActivity"); Intent currentRealIntent = null;//侦测到startActivity动作之后,把intent存到这里 int intentIndex = -1; //遍历参数,找到Intent for (int i = 0; i < args.length; i++) { Object temp = args[i]; if (temp instanceof Intent) { currentRealIntent =  temp;//这是原始的Intent,存起来,后面用得着 intentIndex = i; break; } } //构造自己的Intent,这是为了绕过manifest检测(这个Intent是伪造的!只是为了让通过manifest检测) Intent proxyIntent = new Intent(); ComponentName componentName = new ComponentName(packageName, clz);//用ComponentName重新创建一个intent proxyIntent.setComponent(componentName); proxyIntent.putExtra(ORI_INTENT_TAG, currentRealIntent);//将真正的proxy作为参数,存放到extras中,后面会拿出来还原 args[intentIndex] = proxyIntent;//替换掉intent //哟,已经成功绕过了manifest清单检测. 那么,我不能老让它跳到 伪装的Activity啊,我要给他还原回去,那么,去哪里还原呢? //继续看源码。 } return method.invoke(amsObj, args); } }
真正要跳转之前,把原始的Intent还原回去

PS: 这里hook mh的手段,并不是针对
mh本身做代理,而是对mh的mCallback成员.因为:

public class Handler {...public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback; } else { if (mCallback != null) { if (mCallback.handleMessage { return; } } handleMessage; } }}

handlerdispatchMessage逻辑,是
先执行mCallbackhandlerMessage,然后根据它的返回值决定要不要执行handler本身的handlerMessage函数.我们的目的是还原Intent,并不需要对ActivityThread原本的mH做出逻辑修改,所以,hook mCallback,加入还原Intent的逻辑,即可.

这次hook,对照的源码是(源码太长了,我就直接截取了ActivityThread里面一些关键的代码):

图片 12image.png

下面是Hook Mh的完整代码:

 //下面进行ActivityThread的mH的hook,这是针对SDK28做的hook private static void hookActivityThread_mH_After28() { try { //确定hook点,ActivityThread类的mh // 先拿到ActivityThread Class<?> ActivityThreadClz = Class.forName("android.app.ActivityThread"); Field field = ActivityThreadClz.getDeclaredField("sCurrentActivityThread"); field.setAccessible; Object ActivityThreadObj = field.get;//OK,拿到主线程实例 //现在拿mH Field mHField = ActivityThreadClz.getDeclaredField; mHField.setAccessible; Handler mHObj =  mHField.get(ActivityThreadObj);//ok,当前的mH拿到了 //再拿它的mCallback成员 Field mCallbackField = Handler.class.getDeclaredField("mCallback"); mCallbackField.setAccessible; //2.现在,造一个代理mH, // 他就是一个简单的Handler子类 ProxyHandlerCallback proxyMHCallback = new ProxyHandlerCallback();//错,不需要重写全部mH,只需要对mH的callback进行重新定义 //3.替换 //将Handler的mCallback成员,替换成创建出来的代理HandlerCallback mCallbackField.set(mHObj, proxyMHCallback); } catch (Exception e) { e.printStackTrace(); } } private static class ProxyHandlerCallback implements Handler.Callback { private int EXECUTE_TRANSACTION = 159;//这个值,是android.app.ActivityThread的内部类H 中定义的常量EXECUTE_TRANSACTION @Override public boolean handleMessage(Message msg) { boolean result = false;//返回值,请看Handler的源码,dispatchMessage就会懂了 //Handler的dispatchMessage有3个callback优先级,首先是msg自带的callback,其次是Handler的成员mCallback,最后才是Handler类自身的handlerMessage方法, //它成员mCallback.handleMessage的返回值为true,则不会继续往下执行 Handler.handlerMessage //我们这里只是要hook,插入逻辑,所以必须返回false,让Handler原本的handlerMessage能够执行. if (msg.what == EXECUTE_TRANSACTION) {//这是跳转的时候,要对intent进行还原 try { //先把相关@hide的类都建好 Class<?> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction"); Class<?> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem"); Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");//ClientTransaction的成员 mActivityCallbacksField.setAccessible; //类型判定,好习惯 if (!ClientTransactionClz.isInstance return true; Object mActivityCallbacksObj = mActivityCallbacksField.get;//根据源码,在这个分支里面,msg.obj就是 ClientTransaction类型,所以,直接用 //拿到了ClientTransaction的List<ClientTransactionItem> mActivityCallbacks; List list =  mActivityCallbacksObj; if (list.size return true; Object LaunchActivityItemObj = list.get;//所以这里直接就拿到第一个就好了 if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) return true; //这里必须判定 LaunchActivityItemClz, // 因为 最初的ActivityResultItem传进去之后都被转化成了这LaunchActivityItemClz的实例 Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent"); mIntentField.setAccessible; Intent mIntent =  mIntentField.get(LaunchActivityItemObj); Intent oriIntent =  mIntent.getExtras().get(ORI_INTENT_TAG); //那么现在有了最原始的intent,应该怎么处理呢? Log.d; mIntentField.set(LaunchActivityItemObj, oriIntent); return result; } catch (Exception e) { e.printStackTrace(); } } return result; } }

PS:这里有个坑(请看上面
if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) return true;,
我为什么要加这个判断?因为,我通过debug,发现,从mH里面的msg.what得到的ClientTransaction,它有这么一个成员List<ClientTransactionItem> mActivityCallbacks;
注意看,从list里面拿到的ClientTransactionItem
的实际类型是:LaunchActivityItem.)之前我索引源码的时候,追查Intent的去向,只知道它最后被封装成了一个ClientTransaction

图片 13image.png图片 14image.png

图片 15image.png但是,最后我从mH
switch case EXECUTE_TRANSACTION分支,去debug(因为无法继续往下查源码)的时候,

发现 原本塞进去的ActivityResultItemlist,居然变成了LaunchActivityItemlist,而我居然查了半天,查不到是在源码何处发生的变化.

而 LaunchActivityItem 和 ActivityResultItem
他们两个都是ClientTransaction的子类

public class LaunchActivityItem extends ClientTransactionItem public class ActivityResultItem extends ClientTransactionItem

emmmm…也是很尴尬。=_ =!

不过,最后能够确定,从mH
switch case EXECUTE_TRANSACTION分支得到的transaction,就是包含了Intent的包装对象,所以只需要解析这个对象,就可以拿到intent,进行还原.

OK,大功告成,安装好 android 9.0 SDK
28的模拟器,启动起来,运行程序,看看能不能无清单跳转:

在电影中,“春节十二响”程序是工程师李一一编写出来的,在现实中,也有程序员编写出了《流浪地球》“春节十二响”的C语言源码,并上传到了GitHub中

Hadoop可以让零售商预测分析挑战

前面两篇Hook博文,写了两个demo,一个是hook入门,一个是略微复杂的Activity启动流程的hook。

那么玩点更高端的吧, 正常开发中,所有Activity都要在
AndroidManifest,xml中进行注册,才可以正常跳转,通过hook,可以绕过系统对activity注册的检测,即使不注册,也可以正常跳转。

感谢大神的博文
27进行hook开发,我则是使用SDK 28,所以我的最终Demo中,copy了大佬博文里面的Activity mHhook核心代码,和我自己的SDK 28Activity mHhook`核心代码进行了版本兼容设计.

1.整体思路
2.源码索引
3.hook核心代码
4. 最终效果

// File: twelve_biubiu.c

Sears控股公司分部副总裁Aashish
Chandra表示,Hadoop已经帮助公司降低了运营成本,提高了销售额。Chandra说,以前的大数据提取工具缺乏他们所需要的功能。

*手把手讲解系列文章,是我写给各位看官,也是写给我自己的。**文章可能过分详细,但是这是为了帮助到尽量多的人,毕竟工作5,6年,不能老吸血,也到了回馈开源的时候.这个系列的文章:1、用通俗易懂的讲解方式,讲解一门技术的实用价值2、详细书写源码的追踪,源码截图,绘制类的结构图,尽量详细地解释原理的探索过程3、提供Github

可运行的Demo工程,但是我所提供代码,更多是提供思路,抛砖引玉,请酌情cv4、集合整理原理探索过程中的一些坑,或者demo的运行过程中的注意事项5、用gif图,最直观地展示demo运行效果*

如果觉得细节太细,直接跳过看结论即可。本人能力有限,如若发现描述不当之处,欢迎留言批评指正。

目前,这段源码只在GitHub仅获得了 46
个Star,14个Fork(GitHub地址:

Hadoop消除了零售商在利用大数据方面的一些障碍。这里有一些该技术带来的好处:

2.源码索引

下图大致画出了:从 Activity.startActivity动作开始,到最终
跳转动作的最终执行者 全过程.

图片 16方案1.png

下面开始看源码,从Activity.startActivity开始:

图片 17image.png图片 18image.png图片 19image.png图片 20image.png

这里开始分支:if(mParent==null),但是两个分支最终执行如下:

图片 21true分支图片 22false分支

很显然,两者都是同样的调用过程:

Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity;mMainThread.sendActivityResult;

第一句,execStartActivity
对一些参数的合法性校验,如果不合法,那就会直接抛出异常,比如之前的

图片 23image.png第二句,sendActivityResult才是真正的跳转动作执行者

先进入第一句Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity看看,既然是合法性校验,且看他是如何校验的。

这是InstrumentationexecStartActivity方法

图片 24image.png结论:它是通过AMS去校验的,AMS
startActivity会返回一个int数值,随后,checkStartActivityResult方法会根据这个int值,抛出响应的异常,或者什么都不做.图片 25image.png

再进入第二句mMainThread.sendActivityResult看真正的跳转动作是如何执行的:ps:这里其实有个诀窍,既然我们的终极目标是要骗过系统的Activity
Intent检测,那么,跟着Intent这个变量,就不会偏离方向.

图片 26image.png

既然intent被封装到了ClientTransaction,交给了mAppThread,那么继续:

图片 27image.png前方有坑,请注意:androidStudio里并不能直接跳转,所以要手动,找到下图中的方法,这个ClientTransactionHandlerActivityThread的父类.图片 28image.png上图中,调用了sendMessage(int,Object),在ActivityThread中找到这个方法的实现:图片 29image.png找它的最终实现:图片 30image.png找到另一个关键点:mH
,图片 31image.pngH类的定义:(太长了,我就不完整截图了,留下关键的信息)

final H mH = new H();class H extends Handler { ... public static final int EXECUTE_TRANSACTION = 159; String codeToString { if (DEBUG_MESSAGES) { switch  { case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION"; case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; } } return Integer.toString; } public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString); switch  { case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; mTransactionExecutor.execute(transaction); if (isSystem { // Client transactions inside system process are recycled on the client side // instead of ClientLifecycleManager to avoid being cleared before this // message is handled. transaction.recycle(); } // TODO(lifecycler): Recycle locally scheduled transactions. break; ... } Object obj = msg.obj; if (obj instanceof SomeArgs) { ( obj).recycle(); } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString); } }

很明显,他就是一个Handler的普通子类,定义了主线程ActivityThread中可能发生的各种事件。PS:
这里,我留下了case EXECUTE_TRANSACTION:分支,是因为,之前ClientTransactionHandler
抽象类里面,sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);,就是用的这个
EXECUTE_TRANSACTION常量。
终于找到了startActivity的最终执行代码!

final ClientTransaction transaction = (ClientTransaction) msg.obj;mTransactionExecutor.execute(transaction);

Ok,就到这里了.(事实上,我本来还想往下追查,Intent被封装到ClientTransaction之后,又被得到了什么样的处理,最后发现居然查到了一个源码中都不存在的类,我表示看不懂了,就到这里吧,不影响我们hook)

for( inti :range( 0, 12, 1)) {

Hadoop:跨多门编程语言的大数据解决方案

4.最终效果

ok,见证奇迹的时候到了,准备好SDK28 -android 9.0虚拟机,运行demo:

图片 32无清单注册启动Activity上图中有3个Activity跳转,但是我们看demo的清单文件,只有一个launchActivity图片 33image.png

sleep;

在此我向大家推荐一个大数据技术交流圈: 658558542
突破技术瓶颈,提升思维能力 (☛点击即可加入群聊)

1.整体思路

在之前Activity启动流程的hook的Demo里,我进行了Activity流程的hook,最终采用的方案,是HookAMS,实现了全局的startActivity动作的劫持.
现在就从这个AMS的hook为起点,来实现无清单启动Activity.

Activity启动流程的hook的Demo里,最后实现的效果是,每次跳转Activity,都能看到这个日志:

图片 34image.png

那么,我们既然侦测到了startActivity这个方法的调用,那么自然就可以拿到里面的实参,比如,IntentIntent是跳转意图,从哪里来,跳到哪里去的信息,都包含在Intent里面.而,manifest Activity的检测,也是要根据Intent里面的信息来的.

所以,要骗过系统,要假装我们跳的Activity是已经注册过的,那么只需要将Intent里面的信息换成
已经在manifest中注册的某个Activity就可以了(这里可能就有人像抬杠了,你怎么知道manifest里面一定有注册Activity....如果一个Activity都没有,你的app是怎么启动的呢,至少得有一个LauncherActivity吧 - -!).

确定思路:1.在AMS的hook函数中,将
真实的Intent中的信息,替换成manifest中已有的Activity信息.
骗过系统的检测机制。2.虽然骗过了系统的检测机制,但是这么一来,每一次的跳转,都会跳到"假"Activity,这肯定不是我们想要的效果,那么就必须,在真正的跳转时机之前,将
真实的Activity信息,还原回去, 跳到原本该去的Activity.

对应的核心代码,其实也就两句话:

图片 35image.png

高智商技术宅李一一几乎电脑不离手,一旦进入运算模式,在键盘上飞速敲打的双手和专注的表情是不是觉得特别像平常敲代码的你

“Hadoop平台旨在解决大量数据(可能是复杂的和结构化的,并且不能很好地融入表中的数据)的混合问题。它适用于深度和计算量大的分析,例如聚类和定位…在在线零售中,如果想为客户提供更好的搜索答案,以提高用户的购买欲望,Hadoop可以很好地解决这一问题。

学到老活到老,路漫漫其修远兮。与众君共勉 !

// Dept: PE-362, UG

结语

提示:本文所有源码索引图,都基于SDK 28 -android9.0系统.

intmain(){

“大型零售商永远不可能利用其传统的大数据基础设施进行这种分析。存储如此多的历史数据是十分昂贵的,并且数据类型复杂,并且需要相当多的准备以允许它与PoS事务组合。Hadoop解决了这两个问题,并且可以运行比旧系统更复杂的分析。”

// Origin: TI-352132

销售点数据在零售业中起着非常重要的作用。公司依靠销售点大数据来预测未来销售,管理库存和项目人员需求。

if(unix_time() < make_unix_time( 2082, 1, 28, 23, 59, 60-10))
returnERR_ENGIN_ENV;

1、Staples使用Hadoop分析大数据和预测未来的销售,这有助于他们更有效地分配资源给人员和库存。
据报道,自使用Hadoop以来,Staples的促销成本降低了25%。

标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图