[Android-Diagnostics] Apktool

2015. 6. 11. 17:41 - Song's IT

 

(Source : http://ibotpeaches.github.io/Apktool/documentation/)

 

1. 소개




(1) Basic



 

첫번째로 apk파일에 대해 알아보자. apk파일은 Resource와 Compile된 java파일을 모두 포함하는 압축파일이다. apk파일의 압축을 해제하면 내부에  classes.dex  resources.arsc  등의 파일이 존재하는 것을 볼 수 있다.

$ unzip testapp.apk
 Archive:  testapp.apk
 inflating: AndroidManifest.xml
 inflating: classes.dex
 extracting: res/drawable-hdpi/ic_launcher.png
 inflating: res/xml/literals.xml
 inflating: res/xml/references.xml
 extracting: resources.arsc


하지만, 현 시점에서 이것들은 컴파일된 소스일 뿐이다. 따라서  AndroidManifest.xml 을 열어본다면 아래와 같은 소스가 보일 것이다.

P4F0\fnversionCodeversionNameandroid*http://schemas.android.com/apk/res/androidpackageplatformBuildVersionCodeplatformBuildVersionNamemanifestbrut.apktool.testapp1.021APKTOOL


컴파일된 파일을 보거나 수정하는 것은 불가능할 것이다. 아래는 Apktool을 사용하여 디컴파일한 화면이다.

$ apktool d testapp.apk
I: Using Apktool 2.0.0 on testapp.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: 1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
$


 AndroidManifest.xml 을 다시 열어보면 위 경우보다 고수준의 친숙한 소스가 보일 것이다.

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="brut.apktool.testapp" platformBuildVersionCode="21" platformBuildVersionName="APKTOOL"/>


추가적으로 xml, resources(images, layouts, strings 등)이 소스코드 형태도 역시 디코드 된 것을 확인할 수 있다.



(2) Decoding



 

디코드 옵션은 아래와 같이  - d  혹은  --decode 이다.
$ apktool d foo.jar
// decodes foo.jar to foo.jar.out folder

$ apktool decode foo.jar
// decodes foo.jar to foo.jar.out folder

$ apktool d bar.apk
// decodes bar.apk to bar folder

$ apktool decode bar.apk
// decodes bar.apk to bar folder

$ apktool d bar.apk -o baz
// decodes bar.apk to baz folder


(3) Building



 

빌드 옵션은 아래와 같이  -b 혹은  --build 이다.

$ apktool b foo.jar.out
// builds foo.jar.out folder into foo.jar.out/dist/foo.jar file

$ apktool build foo.jar.out
// builds foo.jar.out folder into foo.jar.out/dist/foo.jar file

$ apktool b bar
// builds bar folder into bar/dist/bar.apk file

$ apktool b .
// builds current directory into ./dist

$ apktool b bar -o new_bar.apk
// builds bar folder into new_bar.apk

$ apktool b bar.apk
// WRONG: brut.androlib.AndrolibException: brut.directory.PathNotExist: apktool.yml
// Must use folder, not apk/jar file


(4) Frameworks



 

Framework들은  if 혹은  install-framework 로 설치할 수 있다.

추가적으로 아래 옵션을 사용하여 파일의 명명과 저장되는 것을 조절할 수 있다.

 >> "-p" 혹은 "--frame-path" <dir> // 해당 <dir>경로 내 framework파일 저장

 >> "-t" 혹은 "--tag" <tag> // 해당 <tag>를 이용하여 명명


(5) Version



 

현재 최신버전은 2.0.x이며 기존 1.5.x버전과 일부 명령어가 상이하며, 동일하게 자바 1.7버전의 설치가 필요하다.





2. 상세 설명



 

(1) Framework Files

안드로이드 앱은 안드로이드 OS내에 존재하는 Code와 Resource를 활용한다. 이것들은  Framework Resources 고 알려져 있으며, Apktool은 이것에 의존하여 apk를 적절히 decode하거나 build한다.


모든 Apktool 버전은 최신버전의 공식  Android Open Source Project(이하 AOSP) 프레임워크를 포함한다. 이것은 대부분의 apk파일들을 문제없이 decode 할 수 있도록 지원한다. 하지만 제조사들은 AOSP내 그들만의 Framework를 추가하여 탑재하기도 한다. 그렇기 때문에 Apktool을 사용하여 해당 apk를 디컴파일 하기 위해서는 제조사의 Framework파일을 추가로 설치해야 한다.


[ Example ]

HTC社의 기기로부터  HtcContacts.apk 를 추출해 decode를 시도하면 아래와 같은 error가 발생할 것이다.

$ apktool d HtcContacts.apk
I: Loading resource table...
I: Decoding resources...
I: Loading resource table from file: 1.apk
W: Could not decode attr value, using undecoded value instead: ns=android, name=drawable
W: Could not decode attr value, using undecoded value instead: ns=android, name=icon
Can't find framework resources for package of id: 2. You must install proper framework files, see project website for more info.


해당 APK를 decoding하기 위해서는 먼저 HTC Framework resource를 획득해야한다. adb를 사용하여 기기 내  com.htc.resource.apk 를 추출 후 아래와 같이 추가로 설치하자.

$ apktool if com.htc.resources.apk
I: Framework installed to: 2.apk


이제 decode를 다시 시도해 보자.

$ apktool d HtcContacts.apk 
I: Loading resource table...
I: Decoding resources...
I: Loading resource table from file: /home/brutall/apktool/framework/1.apk
I: Loading resource table from file: /home/brutall/apktool/framework/2.apk
I: Copying assets and libs...


위와 같이 Apktool은 적절한 decode를 위해  1.apk  2.apk Framework 를 활용하여 앱을 decode하는 것을 볼 수 있다.


① Framework 찾기

대부분의 apk파일의 framework파일은 기기 내  /system/framework 에 위치한다. 몇몇 기기에서는  /data/system-framework 에 존재하며, 간혹  /system/app 이나  /system/priv-app 에 존재하기도 한다. 해당 파일은 대부분 "resources", "res" 혹은 "framework"라 명명되어 있다. Framework파일을 찾은 후  adb pull /path/to/file  혹은 FileManager 앱을 통해 Local로 추출한다.


② 내부 Frameworks

Apktool의 내부에는 위에서 언급한 Framework가 내장되어있다. 이 파일들은 사용 시  $HOME/apktool/framework/1.apk  로 복사된다. 


③ Framework 파일들의 관리

Framework들은 Windows와 UNIX를 지원하기 위해  $HOME/apktool/framework 에 저장되어 있다. MAC OS는 조금 다르게  $HOME/apktool/framework 에 저장되어 있다. 만약 이 디렉터리를 사용할 수 없는 경우 보통 /tmp로 알려진 java.io.tmpdir이 기본값이 된다. 이것은 주기적으로 휘발되는 디렉토리이므로, Framework파일들을 위해 --frame-path 파라미터를 반드시 설정해야 한다. 참고로, Apktool은 제어하지 못하지만, 사용자는 자유롭게 이러한 Framework들을 관리할 수 있다.


④ Framework 파일의 Tagging

Framework들은  <id>-<tag>.apk  형식의 명명규칙으로 저장된다. id는 pkgid이고 추가로 Custom tag를 통해 각 Framework를 식별할 수 있다. 일반적으로 Custom Tag는 필수가 아니다. 하지만 App을 다양한 기기나 서로 호환되지 않는 Framework들에서 작동시킬 경우 유용하다. 


Tag는 아래와 같이 사용 : 

$ apktool if com.htc.resources.apk -t hero
I: Framework installed to: /home/brutall/apktool/framework/2-hero.apk
$ apktool if com.htc.resources.apk -t desire
I: Framework installed to: /home/brutall/apktool/framework/2-desire.apk

그 결과 decoding 시 : 

$ apktool d HtcContacts.apk -t hero
I: Loading resource table...
I: Decoding resources...
I: Loading resource table from file: /home/brutall/apktool/framework/1.apk
I: Loading resource table from file: /home/brutall/apktool/framework/2-hero.apk
I: Copying assets and libs...
$ apktool d HtcContacts.apk -t desire
I: Loading resource table...
I: Decoding resources...
I: Loading resource table from file: /home/brutall/apktool/framework/1.apk
I: Loading resource table from file: /home/brutall/apktool/framework/2-desire.apk
I: Copying assets and libs...

Apk building 시 Tag를 선택하지 않아도 된다. - apktool이 decoding시 자동으로 같은 tag를 사용해준다.




 

(2) Smali 디버깅

Apktool을 사용하면 Smali코드의 코드 진행, 변수 확인, BreakPoint등의 설정이 가능하다.


① 개요

일반적으로 우리가 Java디버깅을 하기위해서 필요한 몇몇 요소들이 있다.

    • Debugger Server (Ex. Java VM)
    • Debugger Client (EX. IDE(IntelliJ, Eclipse or Netbeans)
    • [Client] : 디버깅할 App의 소스코드
    • [Server] : 소스코드를 참조할수 있는 디버깅 심볼과 함께 컴파일 된 바이너리 소스코드
    • 반드시 최소한 패키지와 클래스가 선언된 자바파일이어야하며, 소스들의 디버깅 심볼과 적절히 이어져 있어야 한다.



# 디버깅 심볼(Debugging Symbol) ?

디버깅에 필요한 정보들을 하나의 파일로 묶어 놓은 것. 예를들어 프로그램 내부 충돌발생 시 에러메시지 내 충돌이 발생한 주소대신 xxxFunc()와 같이 출력되는 것은 디버깅 심볼을 참조하기 때문에 가능하다. 디버깅 심볼은 디버깅 모드로 컴파일 시 자동으로 생성되지만 파일크기가 매우 커져 일반 배포 시 적합하지 않다.



Smali코드를 통해 디버깅을 하기위해선 위의 과정과 별도의 과정이 필요하다. 

    • [Server] : DDMS(Dalvic Debug Monitor Service)와 같은 모니터, Android SDK (DDMS사용법)  
    • [Client] : JPDA client - IDE의 대부분이 이 프로토콜을 지원
    • [Sources] : Apktool로 수정된 적절한 Smali코드(".java"확장자, Class선언 등)
    • [Binaries] : Apktool의 디버깅 모드로 빌드 시, 기존의 심볼을 제거한 후 Smali코드가 참조가능한 디버깅 심볼을 추가한다.
    • apk는 반드시 decode되어  -d / --debug 옵션을 통해 Build되어야 한다.



② 사용법

위 정보들을 통해 apk를 smali코드로 디버깅 하는 것은 충분하다. 하지만 DDMS와 Java디버깅에 익숙하지 않다면 아래의 IntelliJ 또는 Netbeans의 사용법을 따라해보기 바란다.

    • Debug모드로 Apk파일 Decode :  $ apktool d -d -o out app.apk 
    • Decompile된 폴더의  AndroidManifest.xml 파일을 열어  <application> Tag  내  android:debuggable="true"  지정
    • Debug모드로 Apk파일 Build :  $ apktool b -d out 
    • Sign, Install후 Apk 실행
    • 이후, 아래의 IDE에 의존한 세부 사용법을 참조한다.



# IntelliJ (Android Studio) 사용법

    • IntelliJ 내 새 Java Module Project 추가를 선택한 후 위의 "out"디렉터리를 Project위치로 선택한다. 그리고 "smali"하위 디렉터리를 Content root dir로 선택한다.
    • Android SDK의 tools폴더 내 Android Device Monitor를 실행한 후, 해당 리스트에서 당신의 애플리케이션을 찾아 클릭한다. 이때, 마지막 컬럼에 위치한 Port정보를 확인하자. 아마 대부분 "86XX""8700"일 것이다.
    • IIntelliJ 내 [Debug]→[Edit Configurations]를 선택한다. 새 프로젝트를 추가하였으니, Debugger를 추가로 생성해야 한다.
    • "Attache"옵션을 선택하고 이전에 확인한 Port번호를 입력하자. 나머지 항목을 ok로 선택 후 "OK"를 클릭하여 Remote Debugger를 생성하자.
    • Debugging연결을 시작하자. 몇몇의 로그와 함께 창의 윗부분에 Debugging버튼이 나타날 것이다.
    • Breakpoint를 지정하자. ".", "#", ":" 으로 시작하는 Line에는 Breakpoint를 지정할 수 없다.
    • Application내 Breakpoint와 연관된 동작을 실행해보자. 만약 breakpoint를 실행한다면, 쓰레드는 정지상태가 되며 debug를 순차적으로 진행하며 변수 등을 관찰할 수 있다.


다른 카테고리의 글 목록

Android/Diagnostics 카테고리의 포스트를 톺아봅니다