Zaycev.net音楽サービスのAndroidクライアントを逆にして、外出先でapiを実装する

厳密に言えば、この記事は一気に逆転することに起因します。







あなたはすべてzaycev.netのようなサービスに精通しています。 誰もが少なくとも一度はウェブインターフェースまたはモバイルアプリケーションを介して彼から音楽をダウンロードしたと仮定して、私は間違っていません。







サービス担当者の要求に応じて、出版物の定数を置き換えました。必要に応じて要求できます。







まだ興味があるなら、猫へようこそ。







パート1 デブリーフィング



かつて、私の良き友人の1人から、公式のAndroidクライアントがどのように機能するかを尋ねられました。 クライアントをダウンロードした後、Jadx(Dex to Javaの逆コンパイラー)で実験をアップロードしました。 記事の最後にあるすべてのリンク。







最初に目を引くのは、難読化の存在です。









まあ、それは重要ではありません、私たちは突破します、結局は初めてではありません。 簡単な検査により、必要な機能がパッケージに集中していることがわかりました。







パッケージfree.zaycev.net.api;


元の認証コード:
public synchronized String b() { String str; if (Looper.myLooper() == Looper.getMainLooper()) { throw new IllegalThreadStateException("Method must run in not main thread!"); } else if (ae.b(ZaycevApp.ay())) { String str2 = ""; str2 = ""; str2 = ""; try { str = (String) new JSONObject(ga("https://api.zaycev.net/external/hello", false)).get("token"); if (ZaycevApp.W().equals("4pda")) { str2 = str + "kmskoNdkYHDnl3ol3"; aa(); } else { str2 = str + "kmskoNdkYHDnl3ol3"; } ha("ZAuth", "token - " + str2); str2 = a(str2); str = new JSONObject(ga(String.format("https://api.zaycev.net/external/auth?code=%s&hash=%s", new Object[]{str, str2}), false)).getString("token"); if (!ae.b((CharSequence) str)) { ZaycevApp.ae(str); } } catch (Exception e) { } str = ""; } else { str = ZaycevApp.ay(); } return str; } private String a(String str) { try { MessageDigest instance = MessageDigest.getInstance("MD5"); instance.update(str.getBytes()); byte[] digest = instance.digest(); StringBuffer stringBuffer = new StringBuffer(); for (byte b : digest) { String toHexString = Integer.toHexString(b & 255); while (toHexString.length() < 2) { toHexString = "0" + toHexString; } stringBuffer.append(toHexString); } return stringBuffer.toString(); } catch (Exception e) { ha((Object) this, e); return ""; } }
      
      





コードからわかるように、サービスリクエストの順序は次のとおりです。







挨拶、Helloトークンの受信:







https://api.zaycev.net/external/hello


サーバーがjsonオブジェクトで応答するもの:







 { "token":"I-fte8MSfXjw8bYFQkcq629iB6uLb5thZSoj3rGvlCPG4ZJzpgbFPylrtLDpw7L_qQ2EBeuBIMvA7BUWkwilS8IWUg3CWGwj8SCmdIU5I8M" }
      
      





ハッシュ計算:







ハッシュ= md5(helloToken + "kmskoNdkYHDnl3ol3")


今後、プログラムに接続された定数(kmskoNdkYHDnl3ol3)はバージョンごとに変わると言いますが、現時点では3つの異なる定数に遭遇しています。







android: "63kQw2LlpV3jv"、 "kmskoNdkYHDnl3ol3"

ios: "d7DVdaELf"


認証、アクセストークンの取得:







https://api.zaycev.net/external/auth?code=%s&hash=%s


サーバーがjsonオブジェクトで応答するもの:







 { "token":"wnfQgLZoLErwL6g_axTTTUkCcobXGLMRZS75Zozr3oC05kWNfd07Bngjpg2VRY2GgXYPaCPqSGarqki6YU278ZO6XJP4RLdNqZMqHFwv-25iH8M_R6rSna2CmnP5OuwgTuUundxiTWqI2Am5rHA2gbU8kbB9Ya0gRJ1mHhq_MpksW3R49Fm4VBDd6vYnNUWykibWmxzxvhRBhJ2dmiKJkw" }
      
      



" { "token":"wnfQgLZoLErwL6g_axTTTUkCcobXGLMRZS75Zozr3oC05kWNfd07Bngjpg2VRY2GgXYPaCPqSGarqki6YU278ZO6XJP4RLdNqZMqHFwv-25iH8M_R6rSna2CmnP5OuwgTuUundxiTWqI2Am5rHA2gbU8kbB9Ya0gRJ1mHhq_MpksW3R49Fm4VBDd6vYnNUWykibWmxzxvhRBhJ2dmiKJkw" }





パフォーマンスを確認します。







 curl -X "GET" "https://api.zaycev.net/external/track/1310964?access_token=wnfQgLZoLErwL6g_axTTTUkCcobXGLMRZS75Zozr3oC05kWNfd07Bngjpg2VRY2GgXYPaCPqSGarqki6YU278ZO6XJP4RLdNqZMqHFwv-25iH8M_R6rSna2CmnP5OuwgTuUundxiTWqI2Am5rHA2gbU8kbB9Ya0gRJ1mHhq_MpksW3R49Fm4VBDd6vYnNUWykibWmxzxvhRBhJ2dmiKJkw"
      
      





JSONレスポンス:







 { "track": { "name": "Sharp Dressed Man", "bitrate": 128, "duration": 258, "size": 4.08, "created": 1333340577000, "userId": 2750888, "userName": "zver19", "artistId": 272997, "artistName": "ZZTop", "lyrics": {}, "lyricAuthor": [], "musicAuthor": [], "rightPossessors": [ { "url": "http://zaycev.net/legal/reriby", "name": "nETB", "pictureUrl": "http://cdnimg.zaycev.net/rp/logo/29/2954-35447.png" } ], "artistImageUrlSquare100": "http://cdnimg.zaycev.net/artist/2729/272997-52076.jpg", "artistImageUrlSquare250": "http://cdnimg.zaycev.net/artist/2729/272997-86370.jpg", "artistImageUrlTop917": null }, "rating": 0.0, "rbtUrl": "" }
      
      





認証トークン-一時的で、約1日間有効です。その後、再度リクエストする必要があります。







これらの簡単な手順を実行した後、サービスサーバーへのリクエストを完了するために必要なAuthトークンを受け取りました。 プログラムで使用されるクエリの検索を開始する時間。







https://api.zaycev.net 」でテキスト検索すると、すべてのリクエストのリストが返されました。







APIリクエストのリスト:







" https://api.zaycev.net/external/hello "

" https://api.zaycev.net/external/auth?code=%s&hash=%s "

" https://api.zaycev.net/external/search?query=%s&page=%s&type=%s&sort=%s&style=%s&access_token=%s "

" https://api.zaycev.net/external/autocomplete?access_token=%s&code%s "

" https://api.zaycev.net/external/top?page=%s&access_token=%s "

" https://api.zaycev.net/external/musicset/list?page=%s&access_token=%s "

" https://api.zaycev.net/external/musicset/detail?id=%s&access_token=%s "

" https://api.zaycev.net/external/genre?genre=%s&page=%s&access_token=%s "

" https://api.zaycev.net/external/artist/%d?access_token=%s "

" https://api.zaycev.net/external/track/%d?access_token=%s "

" https://api.zaycev.net/external/options?access_token=%s "

" https://api.zaycev.net/external/track/%d/download/?access_token=%s&encoded_identifier=%s "

" https://api.zaycev.net/external/track/%s/play?access_token=%s&encoded_identifier=%s "

" https://api.zaycev.net/external/bugs?access_token=%s "

" https://api.zaycev.net/external/feedback?email=%s&clientInfo=%s&text=%s&access_token=%s "


パート2 コードがあります



それで、私たちは研究の最終段階に来ました。今では、獲得した知識をコードに移さなければなりません。 示されているように、記事のタイトルであるGo言語を使用します。すべてのコードを提供するわけではありません。記事の最後にあるリンクで見つけることができます。







APIリンク定数を宣言する
 const ( apiURL string = "https://api.zaycev.net/external" helloURL string = apiURL + "/hello" authURL string = apiURL + "/auth?" topURL string = apiURL + "/top?" artistURL string = apiURL + "/artist/%d?" musicSetListURL string = apiURL + "/musicset/list?" musicSetDetileURL string = apiURL + "/musicset/detail?" genreURL string = apiURL + "/genre?" trackURL string = apiURL + "/track/%d?" autoCompleteURL string = apiURL + "/autocomplete?" searchURL string = apiURL + "/search?" optionsURL string = apiURL + "/options?" playURL string = apiURL + "/track/%d/play?" downloadURL string = apiURL + "/track/%d/download/?" )
      
      





実装のために、TOPトラックのリクエストなどのリクエストの1つを選択し、JSONオブジェクトを記述します。







ZTop構造体
 type ZTop struct { Page int `json:"page"` PagesCount int `json:"pagesCount"` Tracks []struct { Active bool `json:"active"` ArtistID int `json:"artistId"` ArtistImageURLSquare100 string `json:"artistImageUrlSquare100"` ArtistImageURLSquare250 string `json:"artistImageUrlSquare250"` ArtistImageURLTop917 string `json:"artistImageUrlTop917"` ArtistName string `json:"artistName"` Bitrate int `json:"bitrate"` Block bool `json:"block"` Count int `json:"count"` Date int64 `json:"date"` Duration string `json:"duration"` HasRingBackTone bool `json:"hasRingBackTone"` ID int `json:"id"` LastStamp int `json:"lastStamp"` Phantom bool `json:"phantom"` Size float64 `json:"size"` Track string `json:"track"` UserID int `json:"userId"` } `json:"tracks"` }
      
      





API固有のエラー:







 type ClientError struct { msg string } func (self ClientError) Error() string { return self.msg }
      
      





クライアントを作成します。







 type ZClient struct { client *http.Client helloToken string accessToken string staticKey string }
      
      





 func NewZClient(httpClient *http.Client, token, sKey string) *ZClient { if httpClient == nil { httpClient = http.DefaultClient } return &ZClient{client: httpClient, accessToken: token, staticKey: sKey} }
      
      





トップリストクエリ関数:







 func (zc *ZClient) Top(page int) (r *ZTop, err error) { r = &ZTop{} if err = zc.checkAccessToken(); err != nil { return r, err } values := url.Values{} values.Add("page", strconv.Itoa(page)) values.Add("access_token", zc.accessToken) if err := zc.fetchApiJson(topURL, values, r); err != nil { return r, err } return r, err }
      
      





HTTPリクエストを実行する関数:







 func (zc *ZClient) makeApiGetRequest(fullUrl string, values url.Values) (resp *http.Response, err error) { req, err := http.NewRequest("GET", fullUrl+values.Encode(), nil) if err != nil { return resp, err } resp, err = zc.client.Do(req) if err != nil { return resp, err } if resp.StatusCode != 200 { var msg string = fmt.Sprintf("Unexpected status code: %d", resp.StatusCode) resp.Write(os.Stdout) return resp, ClientError{msg: msg} } return resp, nil }
      
      





JSONデコード用の関数:







 func (zc *ZClient) fetchApiJson(actionUrl string, values url.Values, result interface{}) (err error) { var resp *http.Response resp, err = zc.makeApiGetRequest(actionUrl, values) if err != nil { return err } defer resp.Body.Close() dec := json.NewDecoder(resp.Body) if err = dec.Decode(result); err != nil { return err } return err }
      
      





ログイン
 func (zc *ZClient) Auth() (err error) { if err = zc.checkStaticKey(); err != nil { return err } return zc.hello() } func (zc *ZClient) hello() (err error) { if err = zc.checkStaticKey(); err != nil { return err } t := &ZToken{} if err := zc.fetchApiJson(helloURL, url.Values{}, t); err != nil { return err } zc.helloToken = t.Token return zc.auth() } func (zc *ZClient) auth() (err error) { if err = zc.checkHelloToken(); err != nil { return err } r := &ZToken{} hash := MD5Hash(zc.helloToken + zc.staticKey) values := url.Values{} values.Add("code", zc.helloToken) values.Add("hash", hash) if err := zc.fetchApiJson(authURL, values, r); err != nil { return err } zc.accessToken = r.Token return err }
      
      





Md5カウント機能:







 func MD5Hash(text string) string { hasher := md5.New() hasher.Write([]byte(text)) return hex.EncodeToString(hasher.Sum(nil)) }
      
      





ソースは以下のリンクから入手できます。







PS:コードは完璧にはほど遠い。 その修正と改善についての考えがある場合-私はあなたの要求に喜んでいるでしょう。







参照:







Jadx: https : //github.com/skylot/jadx

github: https : //github.com/pixfid/go-zaycevnet

zaycev.net_4.9.3_10.apk: http ://bit.ly/1MZW7UA



All Articles