인시큐어 뱅크앱을 통해 앱 모의해킹을 공부하다가 Smali 코드란 것을 변경하여 기존의 앱의 코드를 수정한 후 앱을 재설치 하는 "리패키징" 이란 과정을 거치는 것을 여럿 보았습니다. 그래서 Smail 코드란게 대체 뭐지? 라는 의문이 들어 본 포스팅을 작성하게 되었습니다! Smali에 대한 내용은 아래 naroSEC님 블로그에서 참고하여 공부하였습니다!
https://naro-security.tistory.com/41
안드로이드 smali 코드 분석에 관한 기초 지식
개요smali 코드는 안드로이드 애플리케이션의 Dalvik 가상 머신 코드를 표현한 언어이다. Dalvik VM은 안드로이드 앱의 실행을 위한 가상 머신이며(지금은 ART를 사용한다.), smali는 이 VM에서 동작하는
naro-security.tistory.com
1. Smali 이란?
2. Smali 지시자
3. Smali 데이터 타입
4. Smali 제어문
5. Smali 레지스터 변수
6. Jadx를 통한 Smali 코드 실습
1. Smali 이란?
앱을 실행할때 컴퓨터는 Dalvik 가상머신에 프로세서를 부여한 뒤 거기에 메모리를 올려서 앱에 필요한 동작들을 하게 되는데 , Smali 코드란 이 과정에서 안드로이드 애플리케이션의 Dalvik 가상 머신 코드를 표현한 언어입니다. Smali 코드는 리버싱 엔지니어링 이나 수정작업에서 발생하며 , 일반적인 APK 컴파일 과정에는 포함되지 않습니다! apktool 도구를 사용해서 Dex 파일을 Smali로 변환 후 코드를 수정후 다시 Dex 파일로 변환시켜서 리패키징 할 수 있게됩니다.
일반적인 APK 컴파일 과정은 다음과 같습니다.
Java/Kotlin > Class > DEX > APK
그러나 리버스 엔지니어링이나 코드 수정후 앱을 다시 컴파일 하는 리패키징 시 과정은 다음과 같습니다.
Java/Kotiln > Class > DEX > Smali > DEX > APK
리패키징 과정을 아래 그림을 통해 조금 더 풀어서 설명해보자면...
- 우선 저희가 작성한 Java & Kotlin 언어가 .class 파일로 컴파일 됩니다.
- 이후 .class 파일은 Android에서 실행할 수 있는 파일인 Dex파일로 컴파일 됩니다. 여기서 여러개의 .class 파일들이 하나의 Dex 파일에 결합되는 과정을 거칩니다.(1차변환)
- 리버스 엔지니어링 혹은 APK수정을 위하여 Dex 파일은 Smali코드로 변환이 됩니다. 여기서 Smali이란 Dex파일을 사람이 읽을수 있을 정도의 어셈블리어를 뜻합니다. 이 어셈블리어를 수정하여 APK 를 리패키징 할 수 있게됩니다.
- 이후 다시 Smali코드를 Dex 로 변환하여 애플리케이션이 동작할 수 있도록 컴파일합니다.
- 최종적으로 Dex파일은 APK패키지에 포함이 됩니다.APK는 안드로이드 애플리케이션의 실행 파일로써 ,Dex 파일이 포함된 형태로 장치에 설치됩니다!
2. Smali 어셈블리 지시자
구문 이름 | 설명 |
.class | 클래스 이름 |
.super | 상속 관계의 상위 클래스 이름 |
.source | 소스 이름 |
.field | 멤버 변수 정의 |
.method | 함수 이름 & 시작 지점 |
.register | 함수에서 사용되는 임시 변수(레지스터 변수) |
.end method | 함수 종료 지점 |
public | 외부에서 접근 가능 |
protected | 상속관계에 있는 하위 클래스에서만 접근가능 |
private | 클래스 내부에서만 접근 가능 |
.parameter | 매개변수 지시어 |
.prologue | 함수 진입 부분을 나타내는 지시어 |
.line (숫자) | 소스 코드의 줄 번호를 나타내는 지시어 |
3.Smali 데이터 타입
Smali 코드 데이터 타입 | Java 코드 데이터 타입 |
V | void |
Z | boolean |
B | byte |
S | short |
C | char |
I | int |
J | long |
F | float |
D | double |
string | String |
Lxxx/xxx/xxx | object |
4. Smali 제어문
구문 이름 | 설명 |
const | 상수 값을 정의하는 지시어 |
const-string | 문자열 상수를 정의하는 지시어 |
const-wide | 64비트 부동 소수점 상수를 정의하는 지시어 |
return | 결과 값 반환 |
if-eq | if-eq [reg1],[reg2]. :[label] 두 값이 서로 같을 경우 레이블로 분기 |
if-ne | if-ne [reg1],[reg2]. :[label] 두 값이 서로 다를 경우 레이블로 분기 |
if-eqz | if-eqz [register] :[label] 레지스터 값이 0일 경우 레이블로 분기 |
if-nez | if-nez[register]. :[label] 레지스터 값이 0이 아닐경우 레이블로 분기 |
if-ge | if-ge [reg1],[reg2]. :[label] reg1 이 reg2 보다 크거나 같을경우 레이블로 분기 |
if-le | if-eq[reg1],[reg2]. :[label] reg1 이 reg2 보다 작거나 같을경우 레이블로 분기 |
goto | goto :[lable] 지정된 레이블로 우선적으로 분기 |
switch | sparse-switch vA, [:label_1, :label_2,...] 조건 분기 구문, 주어진 값에 따라 분기경로 선택 |
iget | 객체의 인스턴스 변수에 저장된 값을 불러옴 |
5.Smali 코드에서 사용되는 레지스터 변수
[v 레지스터]
> 해당 레지스터는 Smali 코드에서 데이터를 저장하고 조작 시 사용되고, 실제 메모리에는 올라가지 않고 프로그램 실행중 임시로 사용되는 변수로써 주로 연산,비교,조건,분기 구문에서 사용됩니다.
[p레지스터]
>매개 변수 레지스터로 함수로 전달되는 인수 값을 저장하는데 사용됩니다. 예를들어 testfunc(parm1 , parm2) 라고 한다면 parm1 , parm2 의 값을 전달하기 위해 사용되는 레지스터입니다.
6.Jadx를 통한 Smali 코드 실습
Insecure Bank어플에 있는 코드 일부분을 가지고 비교해보며 Smali코드 흐름을 살펴보겠습니다.
우선 Smali 코드는 Jadx로 파일을 여신 후 왼쪽 하단을 보시면 Smali 코드로 변환할 수 있게 버튼이 있습니다!
왼쪽이 Java코드이고 오른쪽이 Dex파일에서 디컴파일된 Smali 코드입니다.
- boolean onOptionsItemSelected(MenuItem item) 이라는 함수가 선언이되었습니다. 타입은 Boolean 이고 들어간 파라미터의 이름은 item 입니다.
=> Smali 코드에서 보면 .method 를 통해 함수선언을 알렸고 , Landroid/view/MenuItem 경로를 통해 위에서 살펴보았던 데이터 타입중 Object 타입을 함수의 인자타입으로 설정했습니다. 또한 함수선언 마지막 글자를보면 Z 라는 글자가 있는데 이 또한 함수의 반환값은 Boolean 데이터 타입 이라는 것을 명시하고 있습니다.
107번째 줄을 보시면 p레지스터를 사용하여 p1 이라는 레지스터에 item 이라는 변수값을 전달해주고 있는 것으로 보아 위에서 설명했던 내용의 과정이 이루어 지고있는것을 확인 하실 수 있습니다.
또한 109 번째 줄을 통해 .prologue 는 위에 표를 보시면 알겠지만 함수의 진입부분을 나타내는 지시어 입니다. 이를통해 onOptionsItemSelected 라는 함수가 시작된다는 것을 알수있죠!
실제로 그런지 확인해봅시다. 그 다음으로 적혀있는것은 int id = item.getItemId(); 라는 코드입니다. int 형의 id 라는 이름의 변수에다가 item의 Id값을 저장해주는것 같습니다. Smali 코드를 보시면 실제로 getItemId()I 라는 부분을 보면 getItemlId()라는 함수를 쓰고 반환값은 Int 형인것을 확인하실 수 있습니다. 위에서 설명했던 코드 이후 진행부분을 Smali 코드가 따라가고 있는것을 확인하실 수 있습니다.
이후 if 분기문을 통해 (id == R.id.action_settings) 결과에 따라 분기가 되는 코드를 보실수 있습니다. 이 또한 Smali 코드를 확인해보면 if-ne v1, v3, :cond_e 라는 부분을 통해 v1 ,v3 레지스터의 id 와 R.id.action_settings의 값을 각각 넣은후 비교해서 값이 같지 않으면 cond_e 라는 분기점으로 넘어가는 것을 확인하실 수 있습니다.
이런식으로 Java코드와 Smali 코드를 비교하여 코드의 흐름을 따라갈 수 있고 Smali 코드를 수정하여 앱을 리패키징 한 후 앱이 수정한 대로 돌아가게 만들수 있기에 Smali 코드는 유용하게 사용이 됩니다!