对Soul 安卓App的一次 api请求 抓取记录

Java基础

浏览数:101

2018-12-11

之前注册玩过一段时间的社交app–soul,发现其没有网页版也没有桌面版,app里也没有相关的数据导出功能,作为一个老用户,很多日常发布的瞬间很想导出来,作为纪念,所以就想看看能不能脚本抓取我的数据,才有了下面的记录:

一.对soul抓包

分析Soul App的数据请求,需要用到工具对app应用进行抓包

0 – 工具:Fillder(直接下载安装)
1 – 设置手机和电脑连接的wifi代理,具体步骤可以参考《使用fiddler对手机APP进行抓包》
2 – 打开Fillder,对soul 的数据请求api进行跟踪,抓到下面的请求:
图片描述

1. 请求Header

其中有几个主要的header :
api-sign : 请求签名(由一个 字符串,UUID(替换’-‘为”),时间戳 三个参数通过类c的
public static String a(aa paramaa, String paramString, long paramLong)
方法生成)
requeust-nonce:由UUID.randomUUID().toString().replaceAll("-", "")生成, 生成api-sign签名的参数之一,String类型的UUID
X-Auth-UserId:用户的ID(固定不变)
X-Auth-Token:用户身份认证Token(固定不变)
app-time:时间戳
其他的几个如上图所示,基本不会变化

2.响应body

如图所示,如果请求成功,返回的是一个json数据
失败的话就是:{"code":9000003,"message":"您传递的信息有误,请仔细检查后重试","data":null,"success":false}

以上基本的抓包,可以知道现在需要的是api-sign的生成方法是怎样的,然后根据自己的ID和token组合header进行爬取的请求伪造。但官方没有公开API调用文档,所以只有下载soul app的apk文件,进行反编译查看源码

二、反编译apk查找api签名规则

1.反编译工具

0 – dex2jar(直接下载)
1 – jd-GUI(直接下载,是一个可执行的jar文件)
2 – soul 安卓版的apk

2.使用步骤

0 – 使用系统解压软件直接对apk文件解压,获得文件夹目录,其中包含了classes.dex的类似文件,dex文件是Android虚拟机上面可以执行的文件,jar文件其实就是java的class文件,解压后如下图:
图片描述

1 – 下载dex2jar后解压,将后缀为.dex的classes文件拷贝到dex2jar的目录下,使用DOS的cmd进入到该目录,使用命令:

d2j-dex2jar  classes.dex

对dex文件进行转换为jar文件,该文件包含了soul App的java源码:
图片描述

2 – 使用jd-GUI查看jar文件的java源码:
直接将classes2-dex2jar.jar 文件拖入到JD-GUI界面中,查找关键词”api-sign”便可找到生成请求header的类及方法:
图片描述

生成api-sign的方法:

  public static String a(aa paramaa, String paramString, long paramLong)
  {
    localStringBuilder = new StringBuilder();
    localStringBuilder.append(paramaa.a().a().getPath());
    HashMap localHashMap = new HashMap();
    Object localObject1 = new ArrayList();
    int i = 0;
    try
    {
      Object localObject2;
      while (i < ((s)paramaa.d()).a())
      {
        localObject2 = (s)paramaa.d();
        String str2 = ((s)localObject2).a(i);
        localHashMap.put(str2, ((s)localObject2).c(i));
        ((List)localObject1).add(str2);
        i += 1;
      }
      localObject1 = (String[])((List)localObject1).toArray(new String[0]);
      Arrays.sort((Object[])localObject1, String.CASE_INSENSITIVE_ORDER);
      if (localHashMap.size() != 0)
      {
        int j = localObject1.length;
        i = 0;
        while (i < j)
        {
          localObject2 = localObject1[i];
          if (!bw.a((CharSequence)localHashMap.get(localObject2))) {
            localStringBuilder.append((String)localObject2).append(URLDecoder.decode((String)localHashMap.get(localObject2), "Utf-8"));
          }
          i += 1;
        }
      }
      String str1;
      return f.b(localStringBuilder.toString()).toUpperCase();
    }
    catch (Exception localException1)
    {
      paramaa = paramaa.a();
      if (paramaa.q() > 0)
      {
        i = 0;
        for (;;)
        {
          if (i < paramaa.q())
          {
            str1 = paramaa.a(i);
            localObject1 = paramaa.b(i);
            if ((!bw.a(str1)) && (!bw.a((CharSequence)localObject1))) {}
            try
            {
              localObject2 = URLDecoder.decode(((String)localObject1).replaceAll("%(?![0-9a-fA-F]{2})", "%25"), "utf-8");
              localStringBuilder.append(str1).append((String)localObject2);
              i += 1;
            }
            catch (Exception localException3)
            {
              for (;;)
              {
                try
                {
                  localStringBuilder.append(str1).append((String)localObject1);
                }
                catch (Exception localException2) {}
              }
            }
          }
        }
      }
      localStringBuilder.append(UTDevice.getUtdid(SoulApp.b()));
      localStringBuilder.append("10000003");
      localStringBuilder.append(SoulApp.b().a().getAuthKey());
      localStringBuilder.append(a(paramLong));
      localStringBuilder.append(paramString);
      localStringBuilder.append("3.0.15".replaceAll("\\.", ""));
      j.a("genSign = :" + localStringBuilder.toString());
    }
  }

三、后续

后续有时间再把整个源码看一遍。。毕竟还是代码量不小。。

原文地址:https://segmentfault.com/a/1190000016442145