從原始碼了解 Pokémon Go

最近 Pokémon Go 實在太紅了,加上自己是技術控,看到這篇文 ”Unbundling Pokémon Go”:在講如何用逆向工程得到 App 的原始碼,並分析其運作機制,在此翻譯分享給大家。
評論
評論

原文為《Unbundling Pokémon Go》。譯者簡志偉,為軟體開發者,譯文刊載於 Medium ,INSIDE 獲授權轉載。

最近 Pokémon Go 實在太紅了,加上自己是技術控,看到這篇文 ”Unbundling Pokémon Go”:在講如何用逆向工程得到 App 的原始碼,並分析其運作機制,在此翻譯分享給大家。

本翻譯文已取得 Adrien Couque 的同意,全文如下:

Photo Credit: Applidium

最近不知從哪兒冒出來,Pokémon Go 在一個禮拜內席捲了全世界,我們從裡面發現一些有趣的東西。 

雖然,這個 App 目前只在三個國家公開下載 (美國、澳洲和紐西蘭),但  它仍然讓 Twitter 和 Facebook 相形失色 。它  打敗了 Candy Crush 成為美國最成功的手機遊戲,不僅證明了對  開發者帶來收益 ,在地商家也注意 Pokémon Go 會為他們帶來  客源 ,任天堂公司的市值因而增加 90%。

這個遊戲在這麼短的時間就成為家喻戶曉的話題,激勵著我們想去看看它內部的構造。這篇文以 Pokémon Go App 為例子,說明如何透過逆向工程取得 Android App 的程式碼,同時分析其網路連線請求來得知更多的資訊。

準備 APK

要做逆向工程之前你必須先有 APK 檔案,而取得 Pokémon Go 的 APK 檔案並不困難,這裡就不詳述。請注意安裝來源不明的 APK 會有很大的安全風險,但其實 Google Play 會對 App 做一些分析以降低風險,因此一般人最好還是透過 Google Play 下載安裝 App。但對於逆向工程來說,最喜歡這些惡意的 APK,因為很有趣。在這裡,我們是針對 7/7 釋出的 Pokémon Go 0.29.0 版本進行分析。

先講一下,做了逆向工程後,我們仍然會看不到一些東西:

  • 任何跟 build code 有關的東西。
  • 任何跟測試和持續整合 (continuous integration) 的東西。
  • 其他特殊版本 (例如 debug 版本):在開發當中,你可能會有某些特殊的功能但不會放在最終的產品裡,因為我們是要分析 Pokémon Go 正式釋出的版本,所以特殊功能應該不會被放在這裡面。
  • 後端服務的程式碼。很多人可能想知道演算法是如何決定神奇寶貝出現的地點。但這個演算法是放在後端,我們只能知道如何跟後端傳送資料,無法知道內部演算法的邏輯。

APK 的內容

我們來看一下 APK 的內部構造。事實上,APK 只是一個 zip 壓縮擋,其包含:

Photo Credit: Applidium

這裏描述一下每個檔案 (綠色) 和檔案夾 (紅色) 的功用:

  • Manifest 就是 Android Manifest,它就像 App 的身分證,裡面提供名字、圖示、版本、權限、硬體限制和其他元件等資訊。當系統在安裝或升級 App 時也會需要它。
  • 程式被編譯後都放在 classes.dex,你可以有一個以上的 classes.dex。
  • lib 檔案夾裝著的是函式庫。
  • res 和 assets 檔案夾裝著的是靜態資源檔案。
  • resources.arsc 是 Android 的特殊檔案,由編譯 R.java 產生的,它是用來連結程式和靜態資源。
  • META-INF 檔案夾裝著的是中介資料 (metadata), 但我們在這裡不需要。

以上就是當你解壓縮 APK 後會看到的東西。

我們開始來看第一個檔案:classes.dex

反編譯程式碼

dex Dalvik Executable 的縮寫 (Dalvik 是 Android 系統裡的舊版虛擬器,現在新的叫 ART,全名是 Android Runtime,但檔案的副檔名仍用 dex)。這是 Android 系統專用的檔案格式,而且不容易讀取其內容。有兩個方式可以做到:第一種使用 smali 反組譯工具將 dex 檔案內容轉成可易於閱讀的 bytecode,第二種使用 dex2jar 將內容轉成傳統的 Java 檔案。

我們打算使用第二種方法將 dex 轉成 jar 檔 (jar 是一種壓縮檔,其包含所有的 .class 檔案)。接下來我們需要反編譯工具再將 .class 檔案轉換成 Java 程式碼。有很多現成的反編譯工具,有各自個優缺點,我們使用  Jadx,你可以使用你慣用的,甚至可以找到 線上版的反編譯器

我們現在有的大部份易於閱讀的 Java 程式碼,受限於反編譯器的限制,仍然有一部分的程式碼無法被看見。事實上,還有一個反編譯器 Procyon,可能可以有更好的輸出結果。

有一點很重要:我們得到的程式碼並不是當初開發者所寫的原始碼,就像使用 Google 翻譯將英文翻成法文後,再翻回英文,你會得到另一串新的英文。原因是當要翻成法文時,根據英文的內容會針對單字或片語決定最佳的對應詞或句,再次翻回英文時,根據法文的內容會再做一次決定最佳的對應詞或句的運算,這來回的過程各自獨立,結果就會產生差異。這和程式碼的逆向工程的結果很像:我們反編譯出來的程式碼,其運作的行為會跟原始碼一樣,但程式碼內容不會完全跟原始碼一樣,差異可能有函數名稱、變數名稱和註解。

幸運的是,我們可以清楚得知 app 裡所用到的函式庫:

如果你是 Android 開發者的話,可能會覺得奇怪:為什麼有兩個 JSON parser?一個做 reactive programming(譯注:作者  Ray Shihreactive programming 的見解),一個做 event bus?這其實是 transitive dependencies:函式庫會有相依性才能運作,但寫程式有時候只會呼叫到其中幾個函式庫,你可以到 這裡 了解我們如何分析 transitive dependencies。

清理掉一些沒有呼叫的函式庫後,得到一份更簡潔的清單:

  • Gson
  • Crittercism
  • Upsight
  • Admob/firebase-ads
  • Google VR SDK, Unity and associated

另外有種相依性則是由外到內,一層層包裹起來,像是 Upsight 裡頭包了大量的函式庫,列出清單和函式數目:RxAndroid (4k), Dagger (~200), Commons IO (1k), Jackson (10k), Otto (~50), various Play Services (12k), 自己開發的函式 (3k)。

+--- com.upsight.android:all:4.1.3 |    +--- io.reactivex:rxandroid:1.0.1 |    |    \--- io.reactivex:rxjava:1.0.13 |    +--- com.upsight.android:analytics:4.1.3 |    |    +--- io.reactivex:rxandroid:1.0.1 (*) |    |    +--- com.google.dagger:dagger:2.0.2 |    |    |    \--- javax.inject:javax.inject:1 |    |    +--- com.upsight.android:core:4.1.3 |    |    |    +--- io.reactivex:rxandroid:1.0.1 (*) |    |    |    +--- com.google.dagger:dagger:2.0.2 (*) |    |    |    +--- commons-io:commons-io:2.4 |    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 |    |    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.6.0 |    |    |    |    \--- com.fasterxml.jackson.core:jackson-core:2.6.3 |    |    |    \--- com.squareup:otto:1.3.8 |    |    +--- commons-io:commons-io:2.4 |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 (*) |    |    \--- com.squareup:otto:1.3.8 |    +--- com.google.dagger:dagger:2.0.2 (*) |    +--- com.upsight.android:google-advertising-id:4.1.3 |    |    +--- io.reactivex:rxandroid:1.0.1 (*) |    |    +--- com.upsight.android:analytics:4.1.3 (*) |    |    +--- com.google.dagger:dagger:2.0.2 (*) |    |    +--- com.android.support:support-v4:23.2.1 (*) |    |    +--- com.google.android.gms:play-services-ads:8.4.0 -> 9.2.0 (*) |    |    +--- com.upsight.android:core:4.1.3 (*) |    |    +--- com.upsight.android:marketing:4.1.3 |    |    |    +--- io.reactivex:rxandroid:1.0.1 (*) |    |    |    +--- com.upsight.android:analytics:4.1.3 (*) |    |    |    +--- com.google.dagger:dagger:2.0.2 (*) |    |    |    +--- com.upsight.android:core:4.1.3 (*) |    |    |    +--- commons-io:commons-io:2.4 |    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 (*) |    |    |    \--- com.squareup:otto:1.3.8 |    |    +--- commons-io:commons-io:2.4 |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 (*) |    |    \--- com.squareup:otto:1.3.8 |    +--- com.upsight.android:google-push-services:4.1.3 |    |    +--- io.reactivex:rxandroid:1.0.1 (*) |    |    +--- com.upsight.android:analytics:4.1.3 (*) |    |    +--- com.google.dagger:dagger:2.0.2 (*) |    |    +--- com.android.support:support-v4:23.2.1 (*) |    |    +--- com.google.android.gms:play-services-gcm:8.4.0 -> 9.2.0 (*) |    |    +--- com.upsight.android:core:4.1.3 (*) |    |    +--- com.upsight.android:marketing:4.1.3 (*) |    |    +--- commons-io:commons-io:2.4 |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 (*) |    |    \--- com.squareup:otto:1.3.8 |    +--- com.upsight.android:managed-variables:4.1.3 |    |    +--- io.reactivex:rxandroid:1.0.1 (*) |    |    +--- com.upsight.android:analytics:4.1.3 (*) |    |    +--- com.google.dagger:dagger:2.0.2 (*) |    |    +--- com.upsight.android:core:4.1.3 (*) |    |    +--- commons-io:commons-io:2.4 |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 (*) |    |    \--- com.squareup:otto:1.3.8 |    +--- com.upsight.android:marketing:4.1.3 (*) |    +--- com.upsight.android:core:4.1.3 (*) |    +--- commons-io:commons-io:2.4 |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.3 (*) |    \--- com.squareup:otto:1.3.8

這表示你有數以千計的函式要分析。

雖然函式庫很多,但去掉了分析用工具、監測工具、當機回報和廣告,最主要的剩下 Pokémon Go 用的遊戲引擎 Unity。這就是為什麼你打開 app 會有一個 Niantic 的標誌,為的是讓用戶稍待片刻讓 Unity 引擎啟動,然後再出現一個進度條,顯示引擎讀取靜態檔的狀態。你所有的互動操作都是在 Unity 的執行環境裡,所以不會看到任何 Android 原生的介面。

另一個受到注意的是:VR SDK。在 Pokémon Go Beta 的階段,有人用跟我們一樣的方法發現 Cardboard/VR 等字眼在程式碼裡,在正式版的 app 使用聲明裡也提到 Cardboard。但從我的分析來看,未來並不會有 VR 或 Cardboard 的相應功能。從我們的專業來看,VR SDK 這個函式庫只是用來串接 Android framework 和 Unity,但如果真的要和 Cardboard 整合,就必須讓 Android framework 和 Unity 可以交互溝通,因此必須引用大量的開源程式才能做到。但我們從現在的程式碼中並沒有看到。

到這裡,我們花了很多時間在清理程式,但還沒有一個真正能執行專案,因為還需要 resources 和 assets,讓我們繼續往下看。

靜態資源檔

要得到 resources 和 assets 比原始碼還簡單。事實上,assets 會原封不動地被打包進 App,幾乎所有的 assets 都用在 Unity,所以我們暫且先不管它們。Resources 比較有趣,它們包括了 icons、layouts 和 wording。Resources 的內容會在 build 後變得不易於閱讀或編輯,例如 xml layouts 檔案會轉為二進位格式,9-patches 檔案則失去判讀縮放的依據。

好消息是有個工具叫 apktool,它可以幫助我們將 Manifest 和 resources 檔案轉會成易於閱讀的格式內容,並且產生一個可執行的 Android 專案。一開始我們沒有用是因為 apktool 會將 classes.dex 轉成 smali 檔案,而不是我們要的 Java 程式碼。

現在有了反編譯的 resources 和 Manifest,另外也有 assets,再加上早些將程式碼先清理乾淨,我們可以開始建立和執行一個完整的 Android 專案了。

編譯和執行

為了產生 APK,我們要編譯的 Java 程式碼前,需要建立一個 Android 專案和 build 的指令。如果你還記得的話,因為這些東西並不在 APK 裡,所以我們得自己來,靠的是:Gradle。

其中有一件有趣的事情就是「最低 Android 版本需求」。App 在 Google Play 上的最低需求是 Android KitKat(Android 4.4, API level 19),但在函式庫的分析中,Google VR SDK 最高需求也只有到 API level 16(JellyBeans, or Android 4.1),我們不清楚為什麼在 Google Play 的聲明要高於實際 API 需求 3 個版本。這麼做一開始就排除了 20% 的 Android 使用者 (根據 Google’s latest numbers ),也許是故意的,也或許是失誤。

不過目前最重要的是,我們已經有一個可以執行在手機上的專案了。如果你想要安裝這個逆向工程版的 App,建議在你的 build.gradle 和 Manifest components/permissions 裡面先改掉 application id,避免和官方版的發生衝突,以確保官方版隨時可以更新。

安裝成功後,你會發現你卡在登入畫面。第一個登入選項是用 Google Sign-In。但是當你點擊它時,它會進行驗證 App 簽署的憑證,顯然的是我們並沒有憑證,所以跳出錯誤訊息:

GoogleAuthException: INVALID_AUDIENCE

為了避開這個限制,我們得花很大的力氣才有辦法,所以最簡單的做法是直接到 Google Developer Console 申請一個新的 App,這樣逆向工程版 App 就可以有自己的憑證了,登入成功後取得 token,但還是不能跟後端做資料交換。

第二個登入選項是透過 Pokémon Trainer Club 申請帳號。但因為太多人申請,伺服器似乎已經關閉,等它恢復後,我們會再試看看逆向工程版 App 是否可以登入。

分析程式碼

這裡開始我們會簡短看一下程式碼。雖說這篇文是在講述逆向工程的概論,但這部分我們會著重在 Pokémon Go App,而且每支 App 的分析可能都不太一樣。

我們稍早看到大部份的程式碼都執行在 Unity 引擎中,因為 Unity 是跨平台的,所以這些程式碼可以執行在 iOS 和 Android 上。但有些則是基於 Android 原生的功能,例如:

  • Sign-in / Registration (inside the package com.nianticlabs.nia.account)
  • In-App purchases (inside com.nianticlabs.nia.iap)
  • Interaction with Location, Network and Sensors (inside com.nianticlabs.nia.location/ network/sensors)
  • Communication via Bluetooth with the Pokémon Go Plus (inside com.nianticproject.holoholo.sfida)

第一眼看到最有趣的是 location/network/sensors 程式碼 (如果你假造你的位置或速度,第一時間知道出現的位置和種類,然後可以抓到更多神奇寶貝的話…)

跟 Pokémon Go Plus 溝通,應該就是當你的手機放在背包或口袋的時候,能通知你附近出現神奇寶貝。這部分程式碼可以和網路請求的分析做結合,讓 App 只通知你所感興趣的神奇寶貝,例如你還沒蒐集到的那隻。

稍微看一下與 Pokémon Go Plus 溝通的程式碼:

boolean notifyCancelDowser(); boolean notifyError(); boolean notifyFoundDowser(); boolean notifyNoPokeball(); boolean notifyPokeballShakeAndBroken(String str); boolean notifyPokemonCaught(); boolean notifyProximityDowser(String str); boolean notifyReachedPokestop(String str); boolean notifyReadyForThrowPokeball(String str); boolean notifyRewardItems(String str); boolean notifySpawnedLegendaryPokemon(String str); boolean notifySpawnedPokemon(String str); boolean notifySpawnedUncaughtPokemon(String str); boolean notifyStartDowser();

這是非常有價值的資料!你可以打造你自己的裝置:

截取網路連線

做逆向工程不代表就要大費周章地去拆解程式碼,你可以從 App 如何和外界事物互動,這個方法適用於任何軟體。

App 基本上都會與螢幕連動,來做顯示或觸控的互動,另外還有:檔案系統、感測器、網路等。

這裏我們最感興趣的是網路請求。如我們稍早提到的,遊戲最重要的邏輯運算都在伺服器上頭,App 需要與伺服器做資料交換才可以運作,如果能擷取這些傳輸的資料,我們也許可以不用再透過 App 就可以和伺服器溝通。

實際上,Pokémon Go 在處理網路請求時,用了一個叫 Optimistic Models 的方法。Optimistic Models 讓使用者在 app 上做一個動作後,不需要等待伺服器的回應,就直接往下一動作繼續操作,讓使用者感覺很流暢。如果後來伺服器報錯,它才會跳出警示。所以你可以看到當你在傳送神奇寶貝的時候,並沒有顯示任何等待提示。目前 App 在這個機制上還沒有運作得很流暢,主要是因為伺服器滿載,相信接下來幾個禮拜會改善。

所以,我們如何擷取網路請求?最簡單的方式是在 App 和伺服器中間架一個 proxy。可是如果資料被 HTTPS 加密,你只能看到無關緊要的 metadata。

有一種方式叫 Man-in-the-Middle 攻擊。這種方式是你用 proxy 來騙 App 你是 Server,然後騙 Server 你是 App。當你收到 App 的請求,用你的 app-side key 先解密,再用 server-side key 加密送到 Server 取得回應,再用 server-side key 解密,再用 app-side key 加密送回 App。這樣你就可以取得完整的資料,而且 App 和 Server 並不會知道你的存在。

顯然,如果故事就這樣結束,那所有在網路上的資料都會被看光光。事實上,這些加解密用的 key 是需要被第三方驗證過的,就是 Certificate Authorities。你的手機或瀏覽器只會信任驗證過的 key,否則回跳出警告訊息。因為手機是我們自己的,我們可以把 key 先裝在手機上,來擷取資料。

有現成的工具可以幫我們完成 proxy 的設置,像  mitmproxyCharles。Charles 要付費,但有使用介面可以導引我們做 設定 。下圖是 App 啟動時所截取到的網路請求:

從這裡面可以學到很多東西,來看看頭幾個請求:

  • https://android.clients.google.com/c2dm/register3 : 註冊 push notifications
  • https://stats.unity3d.com/HWStatsUpdate.cgi : 可能是一個跟 Unity 有關的分析事件
  • https://bootstrap.upsight-api.com/config/v1/a9cc12f87adc420baf964f187672ecb4/ : Upsight 的第一個分析事件
  • https://appload.ingest.crittercism.com/v0/appload : Crittercism 的第一個分析事件
  • https://pgorelease.nianticlabs.com/plfe/rpc : 底下會詳述這項
  • https://play.googleapis.com/log : 跟 Play Services 後端溝通
  • http://lh4.ggpht.com/LakctgAXpXwe-3PMCWws8rCoVn1_TmyfAiWjWXm6VtsRjRl5v53n1JrWBumWmldzsBFxIUdRLXgsMewLjuyN: 這是一個對 Picasa 的請求,就是 PokéStop 的圖片
  • https://e.crashlytics.com : 跟 Crashlytics 溝通,但看起來是失敗
  • https://www.google.com/loc/m/api : GPS 位置

我們可以看到 App 很頻繁地跟 https://pgorelease.nianticlabs.com/plfe/ 做溝通,而且一個 226 的數字接在 URL 後面,我猜這是為了做 Load balancing:也就是第一個請求會被指定到某台伺服器去,接下來在同個 session 的所有請求都會導向一樣的伺服器。

最後,「rpc」這個接在 URL 最後的東西代表 App 是透過 Remote Procedure Call 跟 Server 做溝通,因此所有的請求才都發到同一個 URL,這跟用 REST 方式不一樣。

看看請求的內容,既不像 JSON,也不是 XML,而且也沒有壓縮或加密過:所以我們可以清楚看到 UUIDs 和 “pm0015” 等字串,這可能是使用 protocol buffers (或是 flat buffers) 做序列化後的格式。Charles 會幫忙整理乾淨,也可以使用 protocol buffers 的 command line,所以從:

5‚€€€€ÉßÛS#pgorelease.nianticlabs.com/plfe/226:[ @nrÝZ†¡Ï¯½”'ëXÖÐ_}Î~—ñ÷0'@…Ít‘›-C÷‰ <j8y”Êvâ–9~Ă/§¾ñ¶,s^å†ïúÞ*$Äß.¸ñŒD©nz»fM¢¢

整理成:

1: 53 2: 6032429073588813826 3: "pgorelease.nianticlabs.com/plfe/226" 7 {   1: "nr\026\335Z\206\241\317\257\275\224\'\353X\326\320_}\220   \316~\227\361\3670\'@\205\315t\221\233-C\367\211\r<j8y\024   \224\312v\342\2269~\304\202/\036\247\276\361\266,\033s\027\006\f^"   2: 1468599616357   3: "$\002\304\337.\034\270\361\214D\251nz\273fM" } 100 { } 100 { }

這是請求 pgorelease.nianticlabs.com/plfe/rpc 返回的內容,其中有一個新的請求端點:pgorelease.nianticlabs.com/plfe/226,是給之後的所有請求使用。

還可以看到很多 “\xxx”,這是「octal escaping」。使用  解碼器 ,內容從:

nr\026\335Z\206\241\317\257\275\224\'\353X\326\320_}\220 \316~\227\361\3670\'@\205\315t\221\233-C\367\211\r<j8y\024 \224\312v\342\2269~\304\202/\036\247\276\361\266,\033s\027\006\f^

變成:

nr5Z617754\'3X60_}06~7170\'@55t13-C71\r<j8y42v269~42/7616,s\f^

從結果推測,這像是出現在附近的神奇寶貝的物件列表,每個物件有自己的 UUID 和屬性 (例如 pm0015 代表 pokémon 015 號: Beedrill),其他可能是座標、戰鬥力和統計數據。我們可以從請求 https://storage.googleapis.com/cloud_assets_pgorelease/bundles/android/pm0126 來證明這個假設,因為這個請求可以得到 pm0126 相關的 assets。

繼續看其他的請求的返回內容。例如,底下這應該是玩家的相關資訊:

100 {   1: 1   2 {     1: 1467925951134     2: "REDACTED: player name"     7: "\000\001\003\004\a"     8 {       8: 1     }     9: 250     10: 350     11 {     }     12 {     }     13 {     }     14 {       1: "POKECOIN"     }     14 {       1: "STARDUST"       2: 500     }   } }

數字 1467925951134 是 Unix timestamp,指的是 07/07/2016 21:12,這應該是玩家的註冊時間。在請求和返回的內容中,到處都可以看到 timestamp,有的精確度到 millisecond,有的到 nanosecond。

再深入些,我們可以看到很多成對的數字,像:0x40486ddc40000000, 0x4002d99520000000。這應該是座標,但不是被編碼成十六進制,而是 IEEE 754 doubles。這對十六進制的值轉成數字是:

是我們辦公室的座標!我們將可以拿到的所有座標,猜想它的意義,都標記在地圖上:the position of the user (黃色), points of interests / PokéStops (紅色) and possible spawn points (綠色)

 

到目前為止,我們會讀取網路交換的資料、序列化的格式,還會分辨一些 id、timestamps 和 GPS 座標,其他的留給有興趣的人研究。

結論:如何避免被逆向工程

看到這裡,身為開發者也許會覺得沒辦法防止被別人做逆向工程分析,其實是有的。

模糊你的 Java 程式碼是第一步:使用 Proguard。它會把所有的 package、fields 和 methods 的名字以亂數取代,讓分析更困難。如果你想要對這種 App 做分析,從 framework classes 開始。Proguard 不只用在模糊程式碼,也可以移除沒用到的 resources 和 methods。Proguard 很好用,我想 Pokémon Go 未來應該會用。

還有一種方式是減少 Java 程式碼,將部分功能改寫成 native libraries,這會增加分析的難度,但對開發很不方便,而且有太多的 Java 與 native 串接,會導致效能下降。

我們能截取網路請求是因為 App 沒有使用 Certificate pinning。使用 basic Android classesOkHttp 是很平常的,而且很容易。但就像模糊程式碼,它並不能抵擋偏激的攻擊者 (因為憑證也可以被逆向工程),但可以拖延他們一些時間。

最後,本文是相當基本的分析,我們沒有揭露任何遊戲的秘密,公開作弊的方法讓遊戲產生不公平。但對開發者來說,你必須謹慎防範專業級的駭客。

這裡條列一下我們的發現:

  • 程式碼沒有模糊化,這會很容易進行逆向工程分析。
  • 我們可以重建可執行的專案
  • 庫的依賴管理可以更好
  • 未來沒有 VR 或 Cardboard 版本的跡象
  • 可能可以降低 Android 版本需求
  • 我們可以讀取跟位置 / 網路 / 感測器和 Pokémon Go Plus 相關的程式碼
  • 容易擷取網路請求,因為缺少 certificate pinning
  • 網路請求是透過 protobuffers-RPC 完成

你可以找到我們的逆向工程版程式碼:Github
與我們聯絡: Twitter


開發者享受 CI/CD 價值!運用 Amazon EKS 整合 GitLab 創建自動化部署

企業如何在 Amazon EKS(Elastic Kubernetes Services)上使用 GitLab 創建自動化部署,減輕人力負擔,提升專案服務運作效率?
評論
評論

所謂現代化智慧 IT,所有工程師最希望的境界,莫過於只要輕鬆點幾下設定,系統就會自動跑起來,管理者再也不用隨時待命在機台旁邊,從此工作悠哉又快樂!儘管這樣情境還沒到來,但隨著敏捷式開發的流行,除了 DevOps 人員,有越來越多開發者將 CI/CD 概念融入到工作流程當中,例如從 build code、執行 unit test、到部署應用程式。

打造第一個在 AWS 上的應用程式

上述種種反覆步驟自動化執行,也就能提昇服務品質、主動通知開發人員以減輕人力負擔,讓專案服務能持續運作。

其中,GitLab 是執行 CI/CD 常用的工具之一,也是開發者使用程式碼儲存庫的地方。為了讓 GitLab Runner 在雲端快速實踐 CI/CD,《AWS 開發者系列》透過影片分享,如何在 Amazon EKS(Elastic Kubernetes Services)上使用 GitLab 創建自動化部署。

以下節錄工作坊影音內容,幫助開發者快速理解如何運用 Amazon EKS 的高可用性且安全的叢集,將修補、部署節點、更新等關鍵任務,全部做到自動化設定。同時影片也會示範 Amazon EKS 搭配 GitLab 如何展開自動部署,幫助工程團隊實踐 CI/CD 價值。

Amazon EKS 對容器管理輕鬆簡單、維運省時省力

容器化服務越來越興盛,當容器(Container)越來越多,在複雜的微服務(Microservice)系統環境之下,運維團隊的管理成本可能相對會增加不少,為了有效調度容器部署, 導入Kubernetes 無疑是近年企業熱門的話題之一。

建構 Kubernetes Cluster 流主要可區分兩大塊,一是安排容器調度的Control Plane、另一則是容器運行時需要用到的 Worker Node。

Control Plane 裡面涵蓋有儲存狀態的 ETCD、CoController manager 、Scheduler 的調度管理、甚至是操作時進行互動的 APIServer,若是自己創建 的 Kubernetes Cluster ,需要自己安裝這些元件,後續仍需要對 Control Plane 進行相關管理、維護、升級工作。為了減少上述 Components 的繁複維護,在透過 AWS EKS 代管的 Kubernete Control Plane 部可以獲得以下三大好處。

透過 AWS 增加雲端技能 在組織發揮影響力

Amazon EKS 一鍵式部署,展現三大優勢

第一,Amazon EKS代管的 Control Plane實踐了跨AZ的高可用部署,使用者不需要擔心單一節點故障的風險。

第二,Amazon EKS 支持至少四個 Kubernetes版本,持續跟進每季 CNCF 的發佈,同時 EKS 也完全符合上游 CNCF 規範。

第三,部署 Amazon EKS 之後,可直接使用 AWS 平台上現成的服務工具,在安全性管理、網路設定方面,可以做到無縫整合。

最後 AWS 台灣解決方案架構師也提到,若想在容器環境進行 CI/CD 及應用程式的管理,可以進一步透過 IaC 整合部署 Amazon EKS 叢集,透過使用 Console、把 EKS 變成 Cloudformation 的模板、使用 AWS 所開發出來的 eksctl.io、或指令是採用 AWS CDK 可以讓開發者用自身熟悉的語言,在 AWS 平台整合 CI/CD 工具進行維運及部署 EKS。

了解 Amazon EKS 整合 GitLab ,獲得三面向價值

對開發者而言,想把 Amazon EKS 整合到 CI/CD 工具之一的 GitLab 平台上,可以看到那些實際的優勢?

在 DevOps 開發者示範工作坊當中,GitLab 資深解決方案架構師指出,GitLab 使用到 Kubernetes 技術,主要有三種搭配方法,包含 GitLab Server、GitLab Runner、以及創建 Deployment Environment。

本次示範教學會主要聚焦在 GitLab Runner 如何採取 Auto-scaled 方式進行 Build、Test、Package Apps;以及在 Deployment Environment 運用 Kubernetes 技術,做到 Auto Deploy、Review App。

正因為 Amazon EKS 能夠在 DevOps 過程提供所需要的彈性計算資源,幫助開發者在 GitLab 平台上面獲得以下三個層次的優勢:

  • 在 GitLab 內建的部署工作流程當中,自動生成整套 CI/CD 最佳實踐腳本。
  • Review App 過程,從 Merge Request 中可直接訪問應用程式 /App 的 UI 介面,並且根據 Git branch 名稱、專案名稱,自動生成 Review App 的 URL,以及在 Merge 前的最後防線進行 Approval 檢查。
  • 加速 CI/CD 流水線,GitLab Runner 運行時候還可藉由 Amazon EKS Cluster 進行 Auto-scaled 的支援。

Amazon EKS 整合 GitLab ,需要兩大流程

影片最後,GitLab 資深解決方案架構師示範如何把 Amazon EKS 整合至 GitLab 執行 Auto Deploy,主要可分為兩大區塊流程,第一部分聚焦在 Amazon EKS cluster 的設置,第二部分則執行 Auto Deploy 設置。

第一塊可拆分為四個階段,首先教學怎麼創建 EC2 節點的 EKS cluster,第二階段示範把 EKS Cluster 連接到開發者的 GitLab Instance、Group 或 Project,下一步則使用 Cluster Management Project Template 創建一個 Cluster Management Project,以及最後一階段透過 Cluster Management Project 自帶的 Helm Chart,安裝在 Cluster 所需要的內建 App。

第二塊執行 Auto Deploy 設置,針對需要部署的 App 創建一個 GitLab Project,接著再把 gitlab-ci.yml 添加到 Project,並從 Web IDE 選擇及導入 Auto Deploy 的 CI 模版,讓 GitLab 自動生成最佳實踐的整套流水線。

幫助開發者更了解 Amazon EKS 整合 GitLab 的 QA 系列

Q:使用 Amazon EKS 之後,如何更有效率或優化資源去配置 Worker Node 的機器數量,以及如何有效空管開發維運的成本?

A:Kubernetes 除了本身有 HPA(Horizontal Pod Autoscaling)可根據使用程度自動調整資源流量,另外也能延伸使用 AWS Auto Scaling 方案,針對可擴展資源去設定自動擴展管理。另外在成本管控,雖然 Amazon EKS 會收取額外管理費用,但可透過 AWS 平台的 Calculato r計算每個 EKS 的價格,你會發現自動化部署及管理的費用,相對工程師人力的成本更加便宜。

Q:越來越多客戶考慮把現有 Application 變成容器部署,大多是爲了加快部署的效率,那麼變成容器模式之後,對 CI/CD 的工作流程有什麽影響嗎?

A:運用容器技術最直接的效果,可以讓應用程式的環境更一致化,例如 testing 環節、stage production,讓容器避開一些差異問題。至於 CD 部分要 delivery 一些 usage 不太一樣的時候,容器會幫忙做配置,所以 CI/CD 對容器的效益是相輔相成的。

Q: 客戶在開發流程漸漸會把 Infrastructure 變成代碼或文檔,是不是可以把程式碼跟現有的應用程式的 CI/CD 流水線整合在一起,達到一套完整的 CI/CD 部署流程?

A:觀察目前市場作法,主要分成兩個階段去做整體部署。如果規模比較小的團隊,會把 Infrastructure 代碼跟 App 代碼分開,在管理上會比較靈活;如果企業規模比較大,會有另外一個 Infrastructure 團隊來控制部署事情,這種情况之下,APP 的項目會生成一個 APP package,主要做到 delivery 這個階段爲止。而 Infrastructure 的項目會指定把需要版本的文檔,部署到他們的 Kubernetes Cluster。

填寫表單 找到適合的快速上雲服務與工具!