ADB

ADB 파일 경로는 안드로이드 스튜디오를 설치하고 MAC 기준으로 /Users/[USERID]/Library/Android/sdk/platform-tools 에서 확인할 수 있다. 원하는 APK를 얻고자 한다면 스토어 앱을 통해 앱을 설치한 후 아래 과정을 통해 APK를 받을 수 있다.

$ adb shell pm list packages -f -3

위와 같이 실행하면 연결된 기기에 설치된 앱 들을 경로와 함께 확인할 수 있다. -3 옵션은 서드파티 앱만을 반환한다.

package:/data/app/net.daum.android.map-1/base.apk=net.daum.android.map
package:/data/app/com.instagram.android-1/base.apk=com.instagram.android
package:/data/app/com.skt.prod.dialer-1/base.apk=com.skt.prod.dialer
package:/data/app/com.neurondigital.FakeTextMessage-1/base.apk=com.neurondigital.FakeTextMessage
package:/data/app/com.google.android.instantapps.supervisor-2/base.apk=com.google.android.instantapps.supervisor
package:/data/app/com.locnall.KimGiSa-2/base.apk=com.locnall.KimGiSa
package:/data/app/com.lge.sizechangable.weather.theme.optimus-1/base.apk=com.lge.sizechangable.weather.theme.optimus
package:/data/app/com.lge.qremote-1/base.apk=com.lge.qremote
2/base.apk=com.nhn.android.nmap
package:/data/app/io.crash.air-1/base.apk=io.crash.air
package:/data/app/io.zla.app-2/base.apk=io.zla.app
package:/data/app/com.fly.gps-1/base.apk=com.fly.gps
package:/data/app/com.facebook.katana-2/base.apk=com.facebook.katana
package:/data/app/com.skt.tmap.ku-2/base.apk=com.skt.tmap.ku
package:/data/app/com.google.android.inputmethod.korean-1/base.apk=com.google.android.inputmethod.korean
package:/data/app/com.croquis.zigzag-1/base.apk=com.croquis.zigzag
package:/data/app/com.lge.sizechangable.weather-1/base.apk=com.lge.sizechangable.weather
package:/data/app/com.google.android.projection.gearhead-1/base.apk=com.google.android.projection.gearhead
package:/data/app/com.nhn.android.search-1/base.apk=com.nhn.android.search
package:/data/app/com.nbdproject.macarong-2/base.apk=com.nbdproject.macarong

이제 APK를 다운로드 해보자. 위에 설치된 앱 들의 경로를 이용해 아래와 같이 실행하면 APK 파일을 다운로드 할 수 있다.

$ adb pull /data/app/com.instagram.android-1/base.apk ./

이제 현재 경로에 base.apk 파일이 다운로드 되었다.

AAPT

AAPT 파일을 이용하면 APK 파일의 추가적인 정보를 얻어 올 수 있다. AAPT는 APK의 패키징을 관리할 수 있는 툴이다. AAPT 파일은 MAC 기준으로 /Users/[USERID]/Library/Android/sdk/build-tools/27.0.3/aapt 에서 찾을 수 있고 설치된 빌드툴의 버전에 따라 다를 수 있다. AAPT 명령어는 여기에서 더 자세히 알아 볼 수 있다.

$ aapt
Android Asset Packaging Tool
Usage:
 aapt l[ist] [-v] [-a] file.{zip,jar,apk}
 	List contents of Zip-compatible archive.
...

아래와 같이 실행하면 APK가 어떤 리소스를 포함하고 있는지 확인 할 수 있다.

$ aapt list ./base.apk

실행 결과 중 일부는 다음과 같다.

AndroidManifest.xml
android-support-multidex.version.txt
assets/AvenyTMedium.otf
assets/AvenyTRegular.otf
assets/CosmopolitanScriptRegular.otf
assets/InstagramBundle.android.js.bytecode
assets/InstagramBundle.android.js.bytecode.sha256
assets/InstagramBundle.android.js.meta
assets/ReferenceFaceShapeConstants/high_poly_face_compressed.bin
assets/ReferenceFaceShapeConstants/super_high_poly_face_compressed.bin
assets/ReferenceFaceShapeConstants/v01_high_end_face_compressed.bin
assets/ReferenceFaceShapeConstants/v01_low_end_face_compressed.bin
assets/api_key.txt
assets/app_modules.json
assets/drawables.bin
assets/filters/1977/map.png
assets/filters/1977/screen_map.png
assets/filters/aden/map.png
assets/filters/amaro/map.png
assets/filters/amaro/overlay_map.png
assets/filters/brannan/blowout_map.png
assets/filters/brannan/border.png

아래와 같이 실행하면 어떤 권한을 사용하는지 선언된 Label이나 Icon등을 확일 할 수 있다.

$ aapt dump badging ./base.apk

실행 결과 중 일부는 다음과 같다.

package: name='com.instagram.android' versionCode='118342010' versionName='55.0.0.12.79' platformBuildVersionName=''
install-location:'auto'
compatible-screens:'200/560','300/560','400/560','500/560','200/420','300/420','400/420','500/420','200/640','300/640','400/640','500/640','200/400','300/400','400/400','500/400','200/280','300/280','400/280','500/280','200/360','300/360','400/360','500/360','200/480','300/480','400/480','500/480'
sdkVersion:'19'
targetSdkVersion:'25'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.WAKE_LOCK'
uses-permission: name='android.permission.GET_ACCOUNTS'
uses-permission: name='android.permission.USE_CREDENTIALS'
uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED'
uses-permission: name='android.permission.VIBRATE'
uses-permission: name='android.permission.CAMERA'
uses-permission: name='android.permission.READ_CONTACTS'
uses-permission: name='android.permission.READ_PROFILE'
uses-permission: name='android.permission.ACCESS_FINE_LOCATION'
uses-permission: name='android.permission.RECORD_AUDIO'
uses-permission: name='android.permission.MODIFY_AUDIO_SETTINGS'
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
uses-permission: name='android.permission.READ_PHONE_STATE'
uses-permission: name='android.permission.RECEIVE_SMS'
uses-permission: name='com.android.launcher.permission.INSTALL_SHORTCUT'
uses-permission: name='com.android.launcher.permission.UNINSTALL_SHORTCUT'
uses-permission: name='com.instagram.direct.permission.PROTECTED_DEEPLINKING'
uses-permission: name='com.instagram.direct.permission.DIRECT_APP_THREAD_STORE_SERVICE'
uses-permission: name='com.htc.launcher.permission.READ_SETTINGS'
uses-permission: name='com.htc.launcher.permission.UPDATE_SHORTCUT'
uses-permission: name='com.huawei.android.launcher.permission.CHANGE_BADGE'
uses-permission: name='com.sonyericsson.home.permission.BROADCAST_BADGE'
uses-permission-sdk-23: name='android.permission.UPDATE_APP_BADGE'
uses-permission-sdk-23: name='android.permission.CALL_PHONE'
uses-permission-sdk-23: name='android.permission.BLUETOOTH'
uses-permission-sdk-23: name='android.permission.BLUETOOTH_ADMIN'
uses-permission-sdk-23: name='android.permission.CHANGE_WIFI_STATE'
supports-gl-texture:'GL_OES_compressed_ETC1_RGB8_texture'
uses-permission: name='.permission.C2D_MESSAGE'
uses-permission: name='.permission.RECEIVE_ADM_MESSAGE'
uses-permission: name='com.google.android.c2dm.permission.RECEIVE'
uses-permission: name='com.amazon.device.messaging.permission.RECEIVE'

Unpacking APK

APK 파일을 해체하기 위해서 아래와 같이 unzip을 이용해도 됐지만 apktool을 이용하면 더 편하다. Apktool여기서 다운로드 받을 수 있다.

unzip을 이용한 방법은 다음과 같다. 여기서 전체 내용을 확인 할 수 있다.

#!/bin/bash
unzip -d zip-out "$1"
java -jar AXMLPrinter2.jar zip-out/AndroidManifest.xml > AndroidManifest.xml
/opt/dex2jar-0.0.9.15/d2j-dex2jar.sh “$1" # creates “${1%.apk}-dex2jar.jar”
mkdir cfr-extracted && /opt/cfr/cfr.sh “${1%.apk}-dex2jar.jar” --outputdir java-out
java -jar /opt/smali/baksmali-2.0.6.jar -o smali-out zip-out/classes.dex

apktool을 이용한 방법은 다음과 같다.

$ java -jar ./apktool.jar d ./base.apk

다음과 같이 실행 되었고 폴더가 하나 생성 되면서 apk 파일이 해체된것을 확인할 수 있다.

I: Using Apktool 2.3.3 on base.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
S: WARNING: Could not write to (/Users/user/Library/apktool/framework), using /var/folders/mn/gsg6gt5d2q3bgmq_h49vmq_c0000gn/T/ instead...
S: Please be aware this is a volatile directory and frameworks could go missing, please utilize --frame-path if the default storage directory is unavailable
I: Loading resource table from file: /var/folders/mn/gsg6gt5d2q3bgmq_h49vmq_c0000gn/T/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Baksmaling classes2.dex...
I: Baksmaling classes3.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

해체된 파일 리스트는 다음과 같았다.

$ ls -al
total 88
drwxr-xr-x   12 user  staff    384 Jul 31 11:51 .
drwx------+  80 user  staff   2560 Jul 31 11:50 ..
-rw-r--r--    1 user  staff  40840 Jul 31 11:50 AndroidManifest.xml
-rw-r--r--    1 user  staff    969 Jul 31 11:51 apktool.yml
drwxr-xr-x   17 user  staff    544 Jul 31 11:51 assets
drwxr-xr-x    3 user  staff     96 Jul 31 11:51 lib
drwxr-xr-x    4 user  staff    128 Jul 31 11:51 original
drwxr-xr-x  164 user  staff   5248 Jul 31 11:51 res
drwxr-xr-x    5 user  staff    160 Jul 31 11:51 smali
drwxr-xr-x    9 user  staff    288 Jul 31 11:51 smali_classes2
drwxr-xr-x    6 user  staff    192 Jul 31 11:51 smali_classes3
drwxr-xr-x    5 user  staff    160 Jul 31 11:51 unknown

AndroidManifest 확인

Manifest 파일을 보면 앱의 대략적인 윤곽을 확인 할 수 있다. Apktool을 이용하면 이 내용을 확인 할 수 있다. 아래는 AndroidManifest.xml 파일의 일부인데 어떤 Activity, Service가 있는지 어떤 intent-filter가 있는지 등 아주 유용한 정보를 확인 할 수 있다.

<activity  android:launchMode="singleTask"  android:name="com.instagram.bugreporter.BugReporterActivity"  android:screenOrientation="portrait"/>

<service  android:name="com.instagram.bugreporter.BugReporterService"/>

<activity  android:exported="false"  android:name="com.instagram.business.insights.activity.PostInsightsActivity"  android:screenOrientation="portrait"  android:theme="@style/IgTranslucentWindow"/>

<activity  android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize"  android:exported="false"  android:launchMode="singleTask"  android:name="com.instagram.canvas.CanvasActivity"  android:theme="@style/IgTranslucentWindow"  android:windowSoftInputMode="adjustResize"/>

<activity  android:configChanges="keyboardHidden"  android:exported="false"  android:name="com.instagram.challenge.activity.ChallengeActivity"  android:screenOrientation="portrait"  android:theme="@style/InstagramTheme.Fullscreen"  android:windowSoftInputMode="stateHidden"/>

<receiver  android:exported="true"  android:name="com.instagram.common.analytics.phoneid.InstagramPhoneIdRequestReceiver">

<intent-filter>

<action  android:name="com.facebook.GET_PHONE_ID"/>

</intent-filter>

</receiver>

Layout 확인

앱을 개발할 때 가장 많은 시간이 사용되는 부분 중 하나가 레이아웃 관련 작업일 것이다. 이제 res/layout 폴더를 확인해보자. Apktool을 이용한 결과 layoutxml 파일이 있는것을 확인할 수 있을 것이다. 크게 가독성이 높지는 않고 파일 별로 상세히 구분 되있어서 레이아웃간의 계층 구조를 알기가 쉽지않지만 이를 통해서 원하는 기능을 다른 서비스에서는 어떻게 구현했는지 대략적인 힌트를 얻을 수 있다.

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout  android:background="@drawable/action_log_chronological_background"  android:layout_width="fill_parent"  android:layout_height="wrap_content"

xmlns:android="http://schemas.android.com/apk/res/android">

<android.support.v7.widget.RecyclerView  android:id="@id/recycler_view"  android:paddingTop="@dimen/direct_action_log_row_spacing"  android:paddingBottom="8.0dip"  android:fadingEdge="horizontal"  android:fadingEdgeLength="@dimen/direct_action_log_row_fade"  android:layout_width="fill_parent"  android:layout_height="@dimen/direct_action_log_height"  android:fillViewport="false"  android:requiresFadingEdge="vertical"  />

<com.instagram.ui.widget.spinner.SpinnerImageView  android:layout_gravity="center"  android:id="@id/direct_recycler_view_spinner"  android:layout_width="wrap_content"  android:layout_height="wrap_content"  />

</FrameLayout>

Sign APK

apktool을 이용하면 이미 서명된 APK도 다른 서명을 하는것이 가능하다. 이 방법을 이용하면 기존 릴리즈된 APK도 새로 서명해서 디버깅할 수 있다.

$ apktool d -d -o base base.apk

서명하는 방법은 여기 링크를 참조했다.

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore my_application.apk alias_name

Classyshark

구글에서 만든 Classyshark 를 이용하면 사용자 인터페이스 방식으로 실행되어 좀 더 편하게 APK를 분석해 볼 수 있다. 특히 methods counter 기능도 제공하고 있어서 디버깅시 유용할게 활용될 수 있다.