Appleのアプリ内購入のリバースエンジニアリング。 (または「そこに」誰もが怠け者です)

イントロ



こんにちは、Habr! おそらく、アップルのアプリ内ショッピングシステムの「ハッキング」としてインターネット上で広まっている最近のイベントについてご存知でしょう。 ですから、そうではありませんでした。 それはハックでさえありませんでした。 そして、私が行った重要な発見:







だから、私はどのように、何が行われたかを伝え、いくつかの種類を追加し、実際に正しい方向に思考を向けようとします。



技術



クラウドとサービスインフラストラクチャの全盛期では、サーバー側に大きく依存しています。 そして無駄に。 実践が示しているように、クライアント開発者とサーバー開発者はどちらも非常に怠け者です。 後者の場合にのみ、これは大きなスキャンダルをもたらします。



それでは始めましょう。 目的のアプリ内購入を行うには、Appleサーバーへの4〜6件のリクエストを完了する必要があります。サーバーでの購入を検証する場合は、サーバーへのリクエストを最大で完了する必要があります。 アップルサーバーから買い物リストを受け取ることは考えませんが、購入の事実を直接考えます。 一般的な行動計画は次のとおりです。







それぞれについて説明しましょう。

1.提供するものは重要ではありません


購入パラメータの取得は、GETリクエストです p()-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/offerAvailabilityAndInfoDialog



p()-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/offerAvailabilityAndInfoDialog



、数値の生成方法はわかりませんでしたが、 $ _COOKIE ['Pod']に反映され、ほとんどの場合、ユーザー領域。

GETに次のパラメーターを渡します。



 'restrictionLevel' => '1000', // ? 'id' => '522704697', // ID  'versionId' => '7736106', //    'guid' => '074b684aa46990f92b60c374611e59a82xxxxxfe', //   GUID/UDID,     UDID,    'quantity' => '1', //  in-app ?   1 'offerName' => 'com.gameloft.TDKR.cashpack1', //  in-app  'lang' => 'en', // ? 'bid' => 'com.gameloft.TDKR', // bundle id  'bvrs' => '1.0.0', //   'icuLocale' => 'ru_RU' //    PLIST   
      
      







応答でPLISTを取得します。 答えはgzipにパックされているため、最初にアンパックする必要があります。



 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jingleDocType</key><string>inAppSuccess</string> <key>jingleAction</key><string>offerAvailabilityAndInfoDialog</string> <key>dsid</key><string></string> <key>dialog</key> <dict> <key>message</key><string>   ?</string> <key>explanation</key><string> -  $1.99?</string> <key>defaultButton</key><string>Buy</string> <key>okButtonString</key><string>!</string> <key>okButtonAction</key><dict> <key>kind</key><string>Buy</string> <key>buyParams</key><string>quantity=1&salableAdamId=525477928&appExtVrsId=7736106&bvrs=1.0.0&offerName=com.gameloft.TDKR.cashpack1&productType=A&appAdamId=522704697&price=1990&bid=com.gameloft.TDKR&pricingParameters=STDQ</string> <key>itemName</key><string>com.gameloft.TDKR.cashpack1</string> </dict> <key>cancelButtonString</key><string></string> </dict> </dict> </plist>
      
      







はい、私はそれが与える方法を正確にコピーしました。 彼らはクリヴォルキー・マカクを書いたようです。

ここでは、buyParamsのappAdamIdに最も関心があり、残りはすべてのアプリケーションで同じです(取得するために渡されたものを取得してから、A + STDQを取得します)。 楽しい部分は、ほとんどのアプリケーションがappAdamIdで吐き出すことです。 タイトルが言うように、私たちに与えられることは重要ではありません



2. gzipはどこにありますか? (またはApple IDからパスワードを取得する方法)


次に、POSTリクエストを完了して購入する必要があります p()-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy



p()-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy



およびアプリケーションからの p()-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy



れていないPLISTをPOSTデータに渡します。 はい、URLencodeまたは圧縮なし-PLISTのみ:



 <?xml_version' => '"1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>appAdamId</key> <string>522704697</string> <key>appDsid</key> <string>1341894157</string> <key>appExtVrsId</key> <string>7736106</string> <key>bid</key> <string>com.gameloft.TDKR</string> <key>bvrs</key> <string>1.0.0</string> <key>guid</key> <string>xxxxxxxxx</string> <key>offerName</key> <string>com.gameloft.TDKR.cashpack1</string> <key>price</key> <string>1990</string> <key>pricingParameters</key> <string>STDQ</string> <key>productType</key> <string>A</string> <key>quantity</key> <string>1</string> <key>salableAdamId</key> <string>525477928</string> </dict> </plist>
      
      







長期間アプリストアを使用していない場合、Appleは承認を要求するPLISTで応答します。

認証のために、GETまたはPOSTリクエストを送信する必要があります p()-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate



p()-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate



通常のPOST(urlencode)を送信するか、以下を p()-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate



します。



  'appleId' => 'appleid', 'password' => '  , ', 'rmp' => '0', 'attempt' => '0', 'accountKind' => '0', 'guid' => 'xxxx'
      
      







それに応じて、あなたはあなたに関するほとんどすべてのデータを非圧縮形式で受け取ります。 ただplistとすべて:



 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>accountInfo</key> <dict> <key>appleId</key><string>apleid</string> <key>accountKind</key><string>0</string> <key>address</key> <dict> <key>firstName</key><string></string> <key>lastName</key><string></string> </dict> </dict> <key>passwordToken</key><string>  ( 15 )</string> <key>clearToken</key><string> -  ( 15 )?</string> <key>is-cloud-enabled</key><string>false</string> <key>dsPersonId</key><string>ID ?</string> <key>creditDisplay</key><string></string> <key>creditBalance</key><string>1311811 (  ,   ,      )</string> <key>freeSongBalance</key><string>1311811 (  ,   ,      )</string> <key>status</key><integer>0</integer> </dict> </plist>
      
      







再び不器用なplist ...



@記号を生成


まあ、あなたはそのようなPLISTを受け取ることが許可されているので、 p()-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy



p()-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy



、最後に、購入の詳細は次のとおりであるというPLISTを取得します。



 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jingleDocType</key><string>inAppSuccess</string> <key>jingleAction</key><string>inAppBuy</string> <key>dsid</key><string></string> <key>download-queue-item-count</key><integer>1</integer> <key>app-list</key> <array> <dict> <key>item-id</key><integer>525477928</integer> <key>app-item-id</key><integer>522704697</integer> <key>version-external-identifier</key><integer>7736106</integer> <key>bid</key><string>com.gameloft.TDKR</string> <key>bvrs</key><string>1.0.0</string> <key>offer-name</key><string>com.gameloft.TDKR.cashpack1</string> <key>transaction-id</key><string>170000030394952</string> <key>original-transaction-id</key><string>170000030394952</string> <key>purchase-date</key><date>2012-07-28T14:30:19Z</date> <key>original-purchase-date</key><date>2012-07-28T14:30:19Z</date> <key>quantity</key><integer>1</integer> <key>receipt-data</key><data>base64 </data> </dict> </array> </dict> </plist>
      
      







ここで、上記から判断すると、すべてが明確だと思います。 Base64は興味深いもので、エンコードされたNSDictionaryで構成されています。



 { "signature" = "AmJ2SQJx5yZI+t1XRiPBmRVxuoj8jatJkQ+VHCiMLA3Vek48A45NR02AJRNJkKG9+Ry3YgPBjZxifwnYZv1Ylm18NFblnmgDkValnktoL+5wFHcZZGN6//cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ=="; "purchase-info" = "base64   "; "pod" = ""; "signing-status" = "0"; }
      
      



+ VHCiMLA3Vek48A45NR02AJRNJkKG9 + Ry3YgPBjZxifwnYZv1Ylm18NFblnmgDkValnktoL + 5wFHcZZGN6 // cmHs8p / RWV / RT / 91XKVhNl4XIBimKjQQNfgHsDs6yju ++ DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB / wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH / BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN + mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C / IB3QEpK32RxacCDXdVXAeVReS5FaZxc + t88pQP93BiAxvdW / 3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ + / AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV / UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE / UC6Y7053pGXBk51NPM3woxhd3gSRLvXj + loHsStcTEqe9pBDpmG5 + sk4tw + GK3GMeEN5 { "signature" = "AmJ2SQJx5yZI+t1XRiPBmRVxuoj8jatJkQ+VHCiMLA3Vek48A45NR02AJRNJkKG9+Ry3YgPBjZxifwnYZv1Ylm18NFblnmgDkValnktoL+5wFHcZZGN6//cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ=="; "purchase-info" = "base64 "; "pod" = ""; "signing-status" = "0"; }







署名に興味があります。 彼女のレイアウトは次のとおりです。



領収書| 署名| 証明書のサイズ| 証明する

1バイト128 4バイト...



つまり、通常、1つのボトルに証明書と署名があります。 証明書に署名します(キーの長さは1024です!)、署名します。そして、ほら、これが有効なレシピです。 ところで、receipt_version + base64(purchase_info)は署名されています。 ところで、Mac App Storeのレシピとは異なり、証明書は1つしかありません。 そして、MASレシピにはすでにチェーンがあります:



画像



purchase_infoは、NSDictionaryで構成されます。

 { "original-purchase-date-pst" = "2012-07-28 07:30:19 America/Los_Angeles"; "purchase-date-ms" = "1343485819442"; "unique-identifier" = "xxxx"; "original-transaction-id" = "170000030394952"; "bvrs" = "1.0.0"; "app-item-id" = "522704697"; "transaction-id" = "170000030394952"; "quantity" = "1"; "original-purchase-date-ms" = "1343485819442"; "item-id" = "525477928"; "version-external-identifier" = "7736106"; "product-id" = "com.gameloft.TDKR.cashpack1"; "purchase-date" = "2012-07-28 14:30:19 Etc/GMT"; "original-purchase-date" = "2012-07-28 14:30:19 Etc/GMT"; "bid" = "com.gameloft.TDKR"; "purchase-date-pst" = "2012-07-28 07:30:19 America/Los_Angeles"; }
      
      







そして、PLISTで与えられたものを複製します。



ああ、RLY?


突然、トランザクションが与えられたのに、パスしなかったのですか? そういうわけで、あなたはもう1つのGETリクエストをする必要があります p()-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/inAppTransactionDone





そしてそこにトランザクションIDとGUIDを渡します:



 'transactionId' => '170000030394952', 'guid' => 'xxxxx',
      
      







以下が返されます。



 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jingleDocType</key><string>inAppSuccess</string> <key>jingleAction</key><string>inAppTransactionDone</string> <key>dsid</key><string>DSID (ID /?)</string> </dict> </plist>
      
      







それだけです! 購入が完了し、誰もが幸せです。

サーバーまたはアプリケーションから購入を確認する必要がある場合は、 apple from appleを使用しますが、上記のすべてと同じ費用がかかります。



PS:気をつけて!




ヘッダーとCookieを追跡することが重要です。AppleWebオブジェクトではなく、何かを突然配った場合、$ _COOKIE ['Pod']がないと、アプリケーションは誓い、それ以上先に進まなくなります。



さて、パルプ、コード!



GitHubで取得できます 。 曲がって書かれていますが、動作します。 +同じ場所でマニュアルをスキャンします。



まあ、保護オプション






現在、証明書間の信頼のコードがAppleコードで厳密に設定されているため、サービスは正確に機能しません。=>偽の署名は機能しませんが、署名を検証しない場合、すべてが機能します。



androydについて



市場は接続を壊します、なぜなら 証明書が無効です。 誰が私を助けたい、私に連絡したい、私はいくつかのアイデアを持っています。



All Articles