蚂蚁森林自动收能量

    蚂蚁森林为支付宝内的一个小应用,一年前通过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.soJNICLibrary类就在该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/

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
蚂蚁森林是一个基于Uni-App框架开发的插件,它是一个完整的uni-app工程,可以在微信小程序中运行。蚂蚁森林插件的样式是模仿支付宝蚂蚁森林,其中所有的数据都是可配置的,可以根据不同的参数显示对应的动效。该插件可以用于为某种评比活动场景的参赛人员加油、拉票、助力等类似的场景。你可以在GitHub上找到蚂蚁森林插件的代码和在插件市场上下载插件。 运行蚂蚁森林插件需要使用Uni-App框架,其语法类似于Vue,模板类似于微信小程序,动画效果主要使用CSS3实现,辅以JS。你可以根据自己的需求和使用场景修改相应的配置参数和逻辑,比如水滴值、树的类型、大小、树的成长动画、云走动的动画、浇水动画、树的进度、选手信息等等。代码中有详细的注释,可以根据自己的业务需求进行相应的修改。 如果你想实现类似微信运动步数功能的蚂蚁森林插件,可以使用微信小程序的API,比如通过wx.login获取code并请求获取session_key,然后使用wx.getWeRunData获取微信运动步数等信息。根据这些信息,可以在蚂蚁森林插件中实现相应的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Uni-App 蚂蚁森林](https://blog.csdn.net/muguli2008/article/details/127569469)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [微信小程序获取微信运动步数的实例代码](https://download.csdn.net/download/weixin_38708223/13489416)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值