之前注册玩过一段时间的社交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());
}
}
三、后续
后续有时间再把整个源码看一遍。。毕竟还是代码量不小。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。