對Soul 安卓App的一次 api請求 抓取記錄

以前註冊玩過一段時間的社交app--soul,發現其沒有網頁版也沒有桌面版,app裏也沒有相關的數據導出功能,做爲一個老用戶,不少平常發佈的瞬間很想導出來,做爲記念,因此就想看看能不能腳本抓取個人數據,纔有了下面的記錄:java

一.對soul抓包

分析Soul App的數據請求,須要用到工具對app應用進行抓包json

0 - 工具:Fillder(直接下載安裝)
1 - 設置手機和電腦鏈接的wifi代理,具體步驟能夠參考《使用fiddler對手機APP進行抓包》
2 - 打開Fillder,對soul 的數據請求api進行跟蹤,抓到下面的請求:
圖片描述api

1. 請求Headerapp

其中有幾個主要的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:時間戳
其餘的幾個如上圖所示,基本不會變化dom

2.響應body工具

如圖所示,若是請求成功,返回的是一個json數據
失敗的話就是:{"code":9000003,"message":"您傳遞的信息有誤,請仔細檢查後重試","data":null,"success":false}ui

以上基本的抓包,能夠知道如今須要的是api-sign的生成方法是怎樣的,而後根據本身的ID和token組合header進行爬取的請求僞造。但官方沒有公開API調用文檔,因此只有下載soul app的apk文件,進行反編譯查看源碼spa

2、反編譯apk查找api簽名規則

1.反編譯工具.net

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());
    }
  }

3、後續

後續有時間再把整個源碼看一遍。。畢竟仍是代碼量不小。。

相關文章
相關標籤/搜索