蚂蚁森林自动收能量 您所在的位置:网站首页 蚂蚁森林偷能量出表情 蚂蚁森林自动收能量

蚂蚁森林自动收能量

2023-11-27 05:42| 来源: 网络整理| 查看: 265

    蚂蚁森林为支付宝内的一个小应用,一年前通过xposed hook的方式实现了静默式的自动偷取好友能量,具体的分析过程此处略去,可见尼古拉斯_赵四的帖子:Android支付宝蚂蚁森林能量自动收取插件开发原理解析。xposed拥有十分强大的功能,但是也有一定的缺点。首先很难24小时运行,由于支付宝不可能一直在前台,而手机会在特定情况下清理或者冻结掉后台应用。每次都得运行支付宝才行,这是一个很头疼的问题。最近趁着周围的人又在疯狂偷能量,于是想着能不能通过爬虫的方式来实现偷能量呢?经过几天的分析,终于实现了功能,对其中的一些坑以及难点进行一下记录。

    首先通过前面的分析已经知道,蚂蚁森林的数据发送调用了

 

com.alipay.mobile.nebulaappproxy.api.rpc.H5RpcUtil.rpcCall(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/alibaba/fastjson/JSONObject;Ljava/lang/String;ZLcom/alipay/mobile/h5container/api/H5Page;ILjava/lang/String;ZILjava/lang/String;)Lcom/alipay/mobile/nebulaappproxy/api/rpc/H5Response;

通过xposed hook可以得到rpcCall的参数内容并且自己构造这部分参数来实现自动偷能量。跟踪rpcCall函数有一定难度,于是继续采用DDMS跟踪堆栈以及日志来分析。对H5Log以及LogCatUtil两个日志类进行hook,手机上操作好友列表时进行跟踪方法调用。在方法调用中发现了:

com.alipay.mobile.common.transport.http.HttpWorker.call()Lcom/alipay/mobile/common/transport/Response; com.alipay.mobile.common.transport.http.HttpWorker.executeHttpClientRequest(Lorg/apache/http/HttpHost;Lorg/apache/http/HttpRequest;Lorg/apache/http/protocol/HttpContext;)Lorg/apache/http/HttpResponse;

这两个可能相关的函数,同时纵览一遍日志,也发现了可能相关的信息(对关键信息进行了删除):

HttpWorker:printHeaderLog. : visibleflag:1, miniwua:{"w":"HHnB_..."}, x-mgs-productversion:8f5..., AppId:Android-container, Version:2, Did:WSv..., Operation-Type:alipay.mobileaix.fetchAppList, Ts:MtfX9ar, Content-Type:application/protobuf, Sign:647..., signType:0, clientVersion:10.1.75.7000, Cookie:ALIPAYJSESSIONID=RZ4...; zone=RZ4..., Accept-Language:zh-Hans, Accept-Encoding:gzip, Connection:Keep-Alive, Retryable2:0,

在附近也发现了url,body,cookie等信息,猜想可能就是关键点:

url:https://mobilegw.alipay.com/mgw.htm body:[{"ct":"android","av":"5","startPoint":"1","pageSize":20}] cookie:ALIPAYJSESSIONID=RZ4...; zone=RZ4... header: visibleflag: 1 miniwua: {"w":"..."} x-mgs-productversion: 8f5... AppId: Android-container Version: 2 Did: WSvmO..., Operation-Type: alipay.antmember.forest.h5.queryEnergyRanking, Ts: MtfX9ar, Content-Type: application/protobuf, Sign: 647..., signType: 0, clientVersion: 10.1.75.7000 Accept-Language: zh-Hans Connection: Keep-Alive Retryable2: 0

自己写一遍发送,发现成功,说明蚂蚁森林数据确实采用http协议并且数据包可以重放。 其中body时在前面制作hook插件时已经知道,是主要发送的数据。cookie也不必多说。对协议头部分进行分析,其中miniwua经过字符串查找,找到了定位函数com.alipay.rdssecuritysdk.v2.face.RDSClient.getMiniWuaData,对该函数进行跟踪调用,发现生成主要与设备有关,因此可以固定。x-mgs-productversion应该为产品版本,直接固定。Did由httpworker.call中分析

httpUrlRequest.setHeader(new BasicHeader("Did", DeviceInfoUtil.getDeviceId()));

因此为设备ID,可固定。唯一难点在于sign函数。在httpworker.call中可以看到

SignData signData = getSignData();

继续跟踪到达此处(删掉部分无用代码):

public SignResult signature(SignRequest signRequest) { try { SecurityGuardManager instance = SecurityGuardManager.getInstance(EnvUtil.getContext()); ISecureSignatureComponent secureSignatureComp = instance.getSecureSignatureComp(); SignResult signResult = new SignResult(); Object hashMap = new HashMap(); hashMap.put("INPUT", signRequest.content); SecurityGuardParamContext securityGuardParamContext = new SecurityGuardParamContext(); securityGuardParamContext.paramMap = hashMap; securityGuardParamContext.appKey = signRequest.appkey; if (signRequest.isSignTypeMD5()) { securityGuardParamContext.requestType = 4; } signResult.sign = secureSignatureComp.signRequest(securityGuardParamContext, ""); signResult.setSuccess(true); LoggerFactory.getTraceLogger().warn("SecurityManager", "[doSignature] Get security signed string: " + signResult.sign + ", requestType: " + securityGuardParamContext.requestType + ", appKey: " + securityGuardParamContext.appKey); return signResult; } }

看到

signResult.sign = secureSignatureComp.signRequest(securityGuardParamContext, "");

最后跟踪落在了此处:

@InterfacePluginInfo(pluginName = "main") public interface ISecureSignatureComponent extends IComponent { String getSafeCookie(String str, String str2, String str3); String signRequest(SecurityGuardParamContext securityGuardParamContext, String str); }

emmmmm遇到了这种插件就很难受,但是可以继续在DDMS中跟踪堆栈调用,在后续的堆栈调用了发现了关键函数:

com.alibaba.wireless.security.open.securesignature.a.signRequest(Lcom/alibaba/wireless/security/open/SecurityGuardParamContext;Ljava/lang/String;)Ljava/lang/String;

最终找到了:

com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;

在apk反编译中并没有找到com.taobao.wireless.security.adapter.JNICLibrary类。实际上,支付宝apk解压,在lib目录下发现libsgmain.so,该文件为jar文件,解压得到插件dex以及lib目录下的核心so库:libsgmainso-6.4.11174435.so,JNICLibrary类就在该dex中,doCommandNative为native函数。尝试分析该so库,难度太大,放弃。但是既然sign签名涉及到这儿,还是得想办法搞定。想到最近很多人研究unicorn,看雪上有一篇帖子介绍了基于unicorn开发的模拟器,可以模拟执行ARM指令,可以基于此项目进行直接调用so库的加密函数。调用函数得知道参数,尝试hook参数。既然该dex为动态加载的,那么不能直接hook,得hook类加载器ClassLoader的loadClass函数:

XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); if (param.hasThrowable()) return; Class cl = (Class)param.getResult(); String clsname = cl.getName(); ...... } });

这样写对于别的app动态加载dex是没有问题的,但是对于支付宝,只要执行到Class cl = (Class)param.getResult();这一句就会报错,jni error,本地引用溢出。这儿卡了我很久,后来才想起来,不如打印一下loadClass的参数,看看到底是加载什么类才会报错,经过分析得知,当加载到java.lang.String类时会报错,因此首先应该检查参数,过滤掉java.lang.String才hook正常并且得到调用参数。

doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object

表明参数1为整数,参数2为一个Object数组。

signature_0:10401 signature_1[0]:Operation-Type=alipay.antmember.forest.h5.queryEnergyRanking&Request-Data=Cgx...&Ts=MtfZKyp signature_1[1]:rpc-sdk-online signature_1[2]:0 signature_1[3]:null

其中,Request-Data为base64编码过的body,Ts为:

private static String get64Time(){ long ts = System.currentTimeMillis(); return c10to64(ts); } private static final String c10to64(long j) { char[] a = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '/'}; int pow = (int) Math.pow(2.0d, 6.0d); char[] cArr = new char[pow]; int i = pow; do { i--; cArr[i] = a[(int) (63 & j)]; j >>>= 6; } while (j != 0); return new String(cArr, i, pow - i); }

接下来尝试使用unidbg模拟执行so库中的doCommandNative函数。根据demo,我们可以让模拟器加载指定的apk文件并且加载apk内部相应的so文件。在测试中发现,如果直接加载支付宝apk,由于libsgmainso-6.4.11174435.so在插件内部,因此模拟器加载不到这个so,没办法调用。如果直接加载libsgmain.so,模拟器可以加载到该jar文件内部的so库,但是在调用过程中会出现某个类出错,经过检查,该类并不是在插件内,而是在支付宝apk中出现。为了解决这个问题,可以手动将libsgmainso-6.4.11174435.so放入支付宝apk/lib目录下,然后加载支付宝apk,测试加载so库,发现测试正常。 给出关键性的代码:

//创建模拟器 public void initSign() throws IOException{ emulator = createARMEmulator(); emulator.getSyscallHandler().addIOResolver(this); final Memory memory = emulator.getMemory(); memory.setLibraryResolver(createLibraryResolver()); memory.setCallInitFunction(); vm = emulator.createDalvikVM(APK_FILE); vm.setJni(this); DalvikModule dm = vm.loadLibrary("sgmainso-6.4.11174435", false); dm.callJNI_OnLoad(emulator); Native = vm.resolveClass("com.taobao.wireless.security.adapter.JNICLibrary".replace(".", "/")); } //获取签名值 public String getSign(String str) { Number ret = Native.callStaticJniMethod(emulator, "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;", 10401, new ArrayObject(new ArrayObject(new StringObject(vm, str)), new StringObject(vm, "rpc-sdk-online"), DvmInteger.valueOf(vm, 0), new StringObject(vm, ""))); long hash = ret.intValue() & 0xffffffffL; DvmObject dvmObject = vm.getObject(hash); vm.deleteLocalRefs(); return dvmObject.toString(); }

基于以上分析过程,便可以开发出蚂蚁森林爬虫程序,不再依赖手机以及支付宝。让你的支付宝随时随地都在偷能量!

回顾一下分析过程,找到协议的发送信息以及定位sign并不是难点,单纯的是一个体力活。之所以浪费了比较多的时间,首先在hook动态加载的类时,由于没有对loadClass加载的类做过滤导致报错,百思不得其解,随后采用frida进行hook也是报错。最后突然开窍尝试过滤才搞定。其次,吹爆unidbg,支付宝的libsgmain做的确实到位,直接破解很难搞定,但是利用该项目,我们可以不去分析加密的具体实现细节,只要将其当成一个黑盒进行直接调用即可。对于动态加载插件中的so库,可以手动放在apk的lib目录中进行加载。通过本次的分析,对android、frida以及unidbg有了更加深入的了解。但是也有问题没有解决,当参数为object[]{String[] str, String str1, int i, String str2}时,frida如何输出该参数的所有值?!需要进一步看看frida的接口深入分析一下frida才行。

github项目地址:https://github.com/WithHades/forest/



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有