티스토리 뷰

안드로이드 실행파일은 APK파일로, 컴파일 한 결과물을 ZIP으로 묶은 것이다.

파일 내부를 살펴보면 기본적으로 다음과 같은 구조로 되어 있다.

o META-INF
o res
- AndroidManifest.xml
- classes.dex
- resources.arsc

META-INF : 개발자의 키로 APK파일을 사인한 결과값들이 저장되어 있다. 파일마다 SHA1값이 저장되어 있기 때문에 파일 변조시 기기에 설치가 되지 않는다.

res : 소스의 그림파일들과 바이너리화된 xml파일들이 저장되어 있다.

AndroidManifest.xml : 바이너리화되어 있음

classes.dex : 컴파일된 소스

resources.arsc : 컴파일된 리소스 관련 파일

이 상태에선 파일을 수정할 수 없지만, apktool이란 툴을 사용하면 수정이 가능하게 된다.

APKTOOL : http://code.google.com/p/android-apktool/

이 툴을 사용하여 apk파일을 디코드 해보자. 디코딩 후 결과를 보면 다음과 같다.

o res
o smali
- AndroidManifest.xml
- apktool.yml

res폴더와 AndroidManifest.xml는 주석을 제외하면 기존 소스의 res폴더와 동일하게 얻을 수 있다. xml파일은 일반 텍스트문서로 디코딩된다. apktool.yml은 apktool 내부에서 사용하는 파일이다.

남은 smali폴더는 기존소스의 src폴더와 대응되는데, smali폴더를 살펴보면 기존 소스의 src폴더와 동일한 구조에 확장자가 smali로 바뀐 것을 알 수 있다.(크기가 큰 클래스는 여러 파일로 분리되어 있다.)

 

1 package com.test; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 6 public class TestActivity extends Activity { 7 /** Called when the activity is first created. */ 8 @Override 9 public void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 setContentView(R.layout.main); 12 } 13 }

위 소스가 smali문으로 바뀌면 다음과 같이 변한다.

1 .class public Lcom/atest/AtestActivity; 2 .super Landroid/app/Activity; 3 .source "AtestActivity.java" 4 5 # direct methods 6 .method public constructor <init>()V 7 .locals 0 8 9 .prologue 10 .line 7 11 invoke-direct {p0}, Landroid/app/Activity;-><init>()V 12 13 return-void 14 .end method 15 16 # virtual methods 17 .method public onCreate(Landroid/os/Bundle;)V 18 .locals 2 19 .parameter "savedInstanceState" 20 21 .prologue 22 .line 11 23 invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V 24 25 .line 12 26 const/high16 v0, 0x7f03 27 28 invoke-virtual {p0, v0}, Lcom/atest/AtestActivity;->setContentView(I)V 29 30 .line 15 31 return-void 32 .end method 33

 

쉽게 생각하면 smali는 dex포맷을 사람이 쉽게 읽을 수 있도록 표현된 형식으로, C와 어셈블리의 관계 정도로 생각하면 된다.

이제 적절한 수정을 한 후 재컴파일 하면 dist폴더에 apk파일이 생성된다. 이 파일을 기기에 설치하려 하면 설치가 되지 않는데, 인증관련 파일이 빠져있기 때문이다. 개인키 또는 motizen-sign으로 사인후 설치할 수 있다.

추가로 dex2jar와 jad를 사용하면 java코드로도 디컴파일할 수 있다.

문제는 이렇게 하면 파일 변조가 가능하기 때문에 개발자들, 특히 유료 앱을 개발하는 경우 조금 더 신경써야 한다.

예를 들어, 번들로 들어간 앱들을 보면 특정 기기에만 동작하도록 되어 있는데, 코드 구조를 생각해보면 다음과 같다.

 

1 ... 2 const-string v0, 기기명 3 const-string v1, "SHW-M200K" 4 if-eq v1, v2, :cond_0 5 # 기기명이 틀리니 종료 6 cond_0: 7 # 기기명이 같으니 계속 8 ...

 

여기서 다른 기기들은 if문에서 분기되어 종료되는데, if-eq를 if-ne로만 바꾸어도 기기락을 풀어버리게 된다. 물론 요즘 출시되는 폰들의 어플들은 기기 의존적인 라이브러리를 필요로 하는 경우가 많아서 이렇게 간단하게 풀리지는 않는다.

이번에는 구입한 제품인지를 인증하는 경우를 생각해보자.

1 const v0, 인증결과; 2 if-eqz v0, cond_0 3 # 인증실패, 종료 4 cond_0: 5 # 인증성공, 계속 6

 

코드 내용이 어떠하던지, 결국 위와 같은 분기가 있을 것이기 때문에, 마찬가지로 코드를 수정해주면 인증과정이 풀리게 된다. 인증 실패시 나오는 문자열의 resource id값과 logcat을 활용하면 도움이 된다.

NDK와 JNI를 사용하면 코드 내용을 숨길 수 있지만, 결국 인증되어 있는지 여부는 JAVA 코드에서 판정하고, C코드에서 처리한다고 하더라도 작성 및 디버깅이 어렵기 때문에 쉽지 않은 일이다.

Twitter와 Facebook같이 서버와 통신하기 위해 인증을 하는 어플들은 어플 등록을 할때 받게 되는 APP ID와 같은 값이 필요하다. 이런 값들은 소스에 포함되는데, 암호화 되어 있지 않기 때문에 apktool이나 strings.exe 등으로 쉽게 확인할 수 있다.

그렇다면 값을 암호화해서 저장한 후 필요할 때 복호화 한다면 어떨까? 이런 경우 코드상으로는 알 수 없으나 실행시 로그를 출력하게 한다면 충분히 알아낼 수 있다.

 

1 ...복호화해서 v1에 저장한다... 2 const-string v0, "Test" 3 invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I 4 ...v1으로 인증요청... 5

 

위와 같이 코드 사이에 Log.d()함수를 추가시키면 값이 고스란히 드러나게 된다. 트위터나 페이스북의 인증키 값은 그렇게 중요하지 않지만, 특정 서버에 접속하기 위한 패스워드 등 중요한 값들은 저장 및 사용에 신경을 써야 할 것이다.

이외에 proguard라는 툴로 코드난독화할 수도 있다. 클래스나 메소드, 변수명 등을 a, b, dd등과 같이 의미 없는 이름으로 바꾸어주는 것으로, 이렇게 된 어플 들은 임의 수정이 어려워지게 된다.
 
그러나 난독화하면 안되는 부분을 구분해야 하고, 디버깅이 극단적으로 어려워지게 되는 등의 문제들이 있고, 변수 값들은 보호되지 않는다. 그리고 분석이 매우 어려워지는 것이지 절대 불가능 한 것은 아니기 때문에, 오랜 시간과 노력으로 풀 수도 있다.

'학교 > 졸업작품' 카테고리의 다른 글

날씨 업데이트 과정  (0) 2011.08.22
C2DM Push 사용하기  (0) 2011.08.19
Facebook로 내보내기  (0) 2011.08.19
Twitter와 연동해보자  (0) 2011.08.19
이전 주소  (0) 2011.08.19
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함