- 중요정보가라이브러리파일내부에 암호화되어있는악성앱동적분석 - 2014. 8 코드분석팀송지훤 jihwonsong@kisa.or.kr 본보고서의전부나일부를인용시, 반드시 [ 자료 : 한국인터넷진흥원 (KISA)] 를명시하여주시기바랍니다.
[ 목차 ] 1. 개요 2 2. JNI 3 3. 악성앱동적분석 3 3-1. 악성라이브러리동적분석의필요성 3 3-2. 안드로이드테스트앱제작 4 3-3. IDA Pro 를이용한동적분석 7 4. 참고문헌 14 [ 붙임 ] 15
1. 개요 스마트폰악성앱도 PC 악성코드와마찬가지로악성행위를숨기고분석을어렵게하는방향으로발전하고있다. 그리고그방법중하나가중요한악성행위나통신모듈을 JAVA 코드가아닌네이티브 (native) 코드로작성하고난독화한후라이브러리형태로만들어악성앱에서 JNI(JAVA native interface) 로이라이브러리를연결하여사용하는것이다. 이방식은난독화때문에라이브러리의정적분석이힘들고, 동적분석을시도해도악성앱이특정조건에서만라이브러리를로드 (load) 하여악성행위를하거나원격디버거의 attach를방해한다면정확한분석이어렵게된다. 이러한유형은정보유출지가 QQEncryption 방식을사용하여암호화된형태로라이브러리에포함되어꾸준히출현하고있으며 2014년 5월마지막주기준으로 KISA에서분석되는스미싱악성앱중약 20% 정도를차지하고있다. [ 그림 1] 악성앱에서네이티브코드를호출하는코드일부 [ 그림 2] 라이브러리에서유출지를 복호화하는코드일부 - 2 -
2. JNI (JAVA Native Interface) JAVA로작성된앱들은실행속도나확장성을향상시키기위해서동적공유라이브러리 (Dynamic shared libraries) 를사용한다. 이라이브러리는보통 JAVA 언어가아닌 C 또는 C++ 언어로작성된네이티브코드이다. 그리고이라이브러리를 JAVA 앱에서사용하기위하여연결해주는인터페이스가 JNI이다. JNI를통해인자를전달받은네이티브코드는명령을수행한후에그결과값을다시 JNI를통해 JAVA 앱에게넘겨준다. 따라서악성앱에서추출한라이브러리인.so 파일 (Shared libraries) 을동적분석하기위해서는 JNI에대한이해가필요하다. 앱에서네이티브함수를호출하면, JNI는그호출함수에맞추어아래표의규칙대로 Java_ 패키지명 _ 클래스명 _ 함수명 형태의이름을갖는네이티브함수를.so 파일내부에서찾아호출한다. 이때문에테스트앱의패키지명, 클래스명, 함수명등을기존에정의된대로맞춰줘야악성라이브러리를동적으로분석할수있다. 인자의타입과리턴타입도마찬가지이다. 이글에서는현재대부분의악성앱이사용하는방식인기본적인 JNI 규칙을따르는악성앱을분석하였다. 그러나 JNI_OnLoad() 함수를이용하여네이티브코드의연결을새롭게정의한악성앱라이브러리라도연결함수를찾아분석하여이방법을응용할수있을것이다. < 표 1> JNI 의네이티브함수연결규칙 Ÿ Ÿ Ÿ Ÿ Ÿ 접두사 : Java_ 전체패키지명까지포함한클래스명언더바 ( _ ) 로구분함수명오버로딩된 (Overloaded) 함수가있을경우함수이름뒤두개의언더바 ( ) 붙인후인자 (arguments) 의시그니처를추가 - 3 -
3. 악성앱동적분석 3-1. 악성라이브러리동적분석의필요성 라이브러리내부에중요한정보가암호화되어있으면정적분석만으로는복호화한값을확인하기어렵다. 그리고패킷정보를확인하는동적분석은블랙박스테스트이기때문에특정상황에만작동하는조건이존재하거나악성앱이원격디버거의 attach를방해하면정확한분석이어렵다. 이에악성행위를하는라이브러리를자유롭게동적으로분석할수있도록고안한방법이본문서에서다루는악성앱에서추출한라이브러리와테스트앱을이용한분석방법이다. 3-2. 안드로이드테스트앱제작 (1) 악성앱에서라이브러리추출테스트앱을제작하는것이므로 PC에는기본적인안드로이드개발환경 (Android SDK, ADB, Eclipse, JDK 등 ) 이설정되어있어야한다. 이에대한것은안드로이드개발자홈페이지등을참고하도록한다. 테스트앱을만들기위해서는우선악성앱의확장자를 zip 으로바꾼후압축을풀어 lib/armeabi 폴더하위의.so 파일을추출한다. 이것이악성행위를포함한채난독화되어있는라이브러리파일이다. [ 그림 3] 악성앱내부에들어있는악성행위라이브러리파일 - 4 -
(2) 악성앱라이브러리사용설정확인 JAVA 디컴파일러를이용하여악성앱의라이브러리를로드하는부분의코드를확인한다. 정상적으로라이브러리파일을로드하기위해서는패키지명과클래스명, 라이브러리명, 네이티브함수명, 네이티브함수인자의타입을악성앱과동일하게해주어야한다. [ 그림 4] 악성앱내부의 JNI 부분코드 이악성앱은패키지명이 m**s.a**i.f*c 이고클래스명은 E**Service, 라이브러리 이름은 e**c, 네이티브함수명은 jndkaction, 인자와리턴값은각각바이트행 렬 (byte []) 이다. (3) 테스트앱제작 테스트앱을제작하기위해서는 Eclipse 를열고기본적인안드로이드앱프로 젝트를만든후 Main Activity 에버튼을하나만들어준다. 나머지코드는 [ 붙 - 5 -
임 ] 을참조한다. 테스트앱은실행시악성라이브러리를로드하며 Start! 라는버튼을보여주고, 이를누를때마다네이티브함수를호출하도록구현했다. 실제악성앱에서는스마트폰의개인정보를 byte 배열로변경한후이네이티브함수에처음 1회만넘겨주어라이브러리내에서유출지로전송하게되어있었다. [ 그림 5] 테스트앱의 JNI 코드부분 테스트앱의 libs/armeabi 폴더밑에악성앱에서추출한.so 파일을복사해둔다. 라이브러리파일명은악성앱코드내의 System.loadLibrary() 함수의파라미터값앞에 lib 접두어가추가된것이다. 그리고앞서언급했던라이브러리를로드하는부분의이름들을악성앱에맞게각각수정해주어야한다. 이름들은대소문자를구분하며, 디컴파일러에서함수의첫번째파라미터를자기자신 class로보여주는부분이있는데이는 context를넘겨주는부분에대한해석으로보이며테스트앱에서는삭제해야정상작동한다. 그리고라이브러리추가후에는 clean하고새로빌드해주는것이좋다. Eclipse 에서 Project>Clean - 6 -
이테스트앱을스마트폰에서실행시키기위해서는 Eclipse 메뉴의 Run/Run Configuration에서아래그림과같이 Target을 Active devices and AVD s 또는 Active devices로설정한다. 이후스마트폰을연결한상태로 Run을하면테스트앱이스마트폰에서실행된다. [ 그림 6] 테스트앱을스마트폰에서 실행하기위한 Eclipse 설정 [ 그림 7] 테스트앱실행화면 3-3. IDA Pro 를이용한동적분석 (1) 네트워크설정스마트폰에서실제실행중인앱에대한분석이기때문에루트권한을획득하기위하여루팅된스마트폰을사용하여야한다. 그리고동적분석을위해서는분석하는 PC와스마트폰이같은무선 AP(Access point) 에연결되어있어야한다. [ 그림 8] 동일한 AP 에연결된 PC 와스마트폰 - 7 -
(2) 스마트폰설정 IDA 를통한동적분석을위해서는 IDA Pro 설치폴더하위의 dbgsrv 폴더에 있는 android_server 파일을스마트폰의 /data/local/tmp 폴더에복사한다. [ 그림 9] IDA pro 설치폴더밑의 android_server 파일 그후, 이파일의권한을 755 로주고 su 명령어로루트권한을획득한다음 실행한다. [ 그림 10] android_server 파일을폰에 push 하고권한설정변경 * adb push android_server /data/local/tmp * chmod 755 /data/local/tmp/android_server [ 그림 11] android_server 를루트권한으로실행 - 8 -
* su *./android_server Listening on 메시지가뜨면정상적으로동작하는것이고, 이때 port 숫자를 기억해두어야한다. 이숫자는 IDA 에서디버거를 attach 할때에사용할 port 번호이다. * adb forward tcp:23946 tcp:23946 [ 그림 12] 스마트폰의 IP 확인과 port forward IDA에서스마트폰을연결하기위해서는스마트폰의 IP 주소정보가필요하다. 새로운 cmd 창에서 adb shell 로진입하여 netcfg 명령어로스마트폰의 wlan0 IP 정보를확인한다. 이후에 adb forward명령어를통해스마트폰의 tcp:23946(port) 의 output을 PC의 tcp:23946을통해출력하도록설정한다. (3) 테스트앱프로세스를 IDA 에 Attach Eclipse 를통해테스트앱을 Run 시키고, IDA 에서는 Debugger/Attach/Remote - 9 -
ARM Linux/Android debugger 메뉴를실행한다. [ 그림 13] IDA 의동적디버깅실행 아래와같은창이뜨면스마트폰의 IP 정보를 hostname 부분에넣고, Port 는 android_server 에서대기하고있는 port 숫자를써준다. ( 기본값은 23946) [ 그림 14] 스마트폰 IP 와 port 설정 연결이되면 adb shell 이떠있는 cmd 창에 Accepting connection... 이뜬다. [ 그림 15] PC 와스마트폰이동적디버깅을위한연결성공 그리고 IDA 화면에는현재실행되고있는 Process list 가뜬다. 이중테스트 앱의패키지명 (com.example.qqencrypt2) 을선택한다. - 10 -
[ 그림 16] 현재실행중인프로세스목록에서테스트앱선택 디버거가정상적으로연결되면아래와같이어셈블리어코드와각종정보가 IDA 화면에나오고 libc 부분에멈춰있다. [ 그림 17] 디버거가정상적으로 attach 되었을때의화면 (4) 라이브러리동적분석 분석할함수에브레이크포인트를설정한다. IDA 의 Modules 창에서악성앱에 서추출하여삽입한라이브러리를선택하면해당위치로점프한다. - 11 -
[ 그림 18] 테스트앱이로드한라이브러리목록에서악성라이브러리선택 JNI_Onload() 에서함수연결이재정의되어있지않으면, 일반적으로는앞에서살펴본 JNI 규칙에따라 JAVA_ 패키지명 _ 클래스명 _ 네이티브함수명 함수가이 JNI가연결하는함수이다. 이샘플에서는네이티브함수를호출할경우에라이브러리의 JAVA_m**s_a**i_f*c_E**Service_jndkAction() 이실행된다. 이함수에브레이크포인트를걸고, IDA의 Continue Process를눌러계속진행하게한다. 그리고스마트폰에서 Start! 버튼을눌러보면이곳의브레이크포인트에디버거가멈추는것을확인할수있다. 정리해보면스마트폰의버튼이눌릴때테스트앱에서라이브러리의네이티브함수를호출하고이것은 JNI 를통해연결되어라이브러리의 JAVA_m**s_a**i_f*c_E**Service_jndkAction() 가호출되는것이다. 디버거가멈춘상태이니이제이곳라이브러리내부에서부터동적분석을시행하면된다. - 12 -
[ 그림 19] 라이브러리에서 JNI 를통해연결되는함수 일반적으로악성앱라이브러리는 JNI를통해연결된이후에여러가지동작을거쳐유출지를복호화하고, 통신하게되어있다. 따라서유출지복호화가끝나고통신하는부분을찾아그위치로점프하는것이분석에대한시간과노력을아낄수있다. [ 그림 20] 악성앱에서유출지를복호화하여사용하는코드 - 13 -
이악성앱에서는위의 IsIP() 함수로유출지를확인가능하다. 위의코드는 qqdecrypt() 함수를통해암호화되어있던유출지를복호화하고, 그값을 IsIP() 함수내에서확인을하게되어있다. 그러니이부분에브레이크포인트를걸고진행해보면, 아래그림과같이스택포인터가가리키는위치에유출지 URL 정보가복호화되어저장되어있는것을확인할수있다. [ 그림 21] 스택주소값에복호화되어들어있는유출지정보 이샘플은두개의유출지정보를가지고있는데, 패킷정보확인으로만동적분석했을때는하나의유출지주소를놓쳤었다. 하지만이방법을사용하여추가적인 URL 정보도확인가능했다. 이렇게라이브러리내부에중요유출정보가들어있는앱이지속적으로신고되는바, 이방법을활용하여보다정확한악성앱분석과차단이가능할것이라생각한다. 4. 참고문헌 http://developer.android.com/training/articles/perf-jni.html http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jnitoc.html - 14 -
[ 붙임 ] 테스트앱소스코드 악성라이브러리경로와이름의일부를 * 로가림 1. MainActivity.java package com.example.qqencrypt2; import android.app.activity; import android.os.bundle; import android.util.log; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; public class MainActivity extends Activity{ Button btn; protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); m**s.a**i.f*c.e**service.loadlib(); // 라이브러리로드 btn = (Button) findviewbyid(r.id.button1); btn.setonclicklistener(new OnClickListener() { public void onclick(view v) { // 버튼클릭시호출 byte[] outbyte = new byte[]{'t','e','s','t'}; boolean result = m**s.a**i.f*c.e**service.send(outbyte); // 라이브러리함수호출 Log.d("test","result="+result); // 로그 }); } } } 2. E**Service.java package m**s.a**i.f*c; public class E**Service { public static void loadlib(){ System.loadLibrary("e**c"); // 라이브러리로드 } private static native byte[] jndkaction(byte[] arg1); // 네이티브함수 } public static boolean send(byte [] data){ boolean v1 = jndkaction(data) == null? false : true; return v1; } - 15 -