IOS의 Snoop-It 트윗과 같이 앱실행 시, 실시간으로 실행하는 Class를 알려주는 기능이 있으면 좋겠다고 생각하던 찰나, 이미 누군가가 해당 기능을 만들어서 깃헙에 올려놨네요.


다만 받아서 실행해보면 에러가.... 그래서 올라와 있는 코드 분석하여 로직은 동일하게 수정 및 작성해봤습니다.


기존 Method Trace코드 출처는 다음과 같습니다 : https://github.com/0xdea/frida-scripts


다음은 새로 제가 작성한 코드 입니다.


import sys
import frida

def on_message(message,data):
 print "[%s] -> %s" % (message, data)

PACKAGE_NAME = sys.argv[1]

jscode = """
Java.perform(function(){
 var pattern = \""""+sys.argv[1]+"""\"
 

 Java.enumerateLoadedClasses({
  onMatch:function(aClass) {
   var pattern2 = pattern.split('.');
   var temp = pattern2[0]+"."+pattern2[1];
   if (aClass.match(temp)) {
    traceClass(aClass);
   }
  },
  onComplete: function() {}
 });
 
 function traceClass(targetClass){
  var hook = Java.use(targetClass);
  var methods = hook.class.getDeclaredMethods();
  hook.$dispose;

  var parsedMethods=[];
  methods.forEach(function(method) {

   parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
   traceMethod(targetClass+"."+method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
  });
 }


 function traceMethod(targetClassMethod){

  var delim = targetClassMethod.lastIndexOf(".");
  if (delim === -1){ return;}
  var targetClass = targetClassMethod.slice(0, delim);
  var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);
  
  var hook = Java.use(targetClass);
  var overloadCount = hook[targetMethod].overloads.length;

  for(var i=0;i<overloadCount;i++){
   hook[targetMethod].overloads[i].implementation=function(){

    console.log("***Entered Method is : "+targetClass+"."+targetMethod);

    var retval = this[targetMethod].apply(this, arguments);
    return retval;
   }
  }
 }

 function traceMainActivity(MainActivity){
  
  var throwable = Java.use('java.lang.Throwable');
  var Main = Java.use(MainActivity);
  Main.$init.implementation=function(a){
   throwable2 = throwable.$new();
   Stack = throwable2.getStackTrace();
   for(var i=0;i<Stack.length;i++){
    console.log(Stack[i].getClassName()+"."+getMethodName());
   }
  }
 }
});
"""
 
try:
 device = frida.get_usb_device()
 pid = device.spawn([PACKAGE_NAME])
 print("App is starting ... pid : {}".format(pid))
 process = device.attach(pid)
 device.resume(pid)
 script = process.create_script(jscode)
 script.on('message',on_message)
 print('[*] Running Frida')
 script.load()
 sys.stdin.read()
except Exception as e:
 print(e)


찬찬히 뜯어보면


 Java.enumerateLoadedClasses({
  onMatch:function(aClass) {
   var pattern2 = pattern.split('.');
   var temp = pattern2[0]+"."+pattern2[1];
   if (aClass.match(temp)) {
    traceClass(aClass);
   }
  },
  onComplete: function() {}
 });


해당 부분에서 enumerateLoadedClasses를 이용해서 로드되는 클래스를 확인 후 split함수를 이용, sys.argv[1]에 입력된 앱이름의 두번째 .(구분자)까지 맞으면 해당 Class명을 traceClass함수에 인자로 호출합니다.

ex) com.abc.1234.taesun1114라고 하면 enumerateLoadedClasses로 출력되는 함수중 com.abc가 포함된 클래스를 traceClass로 넘기게됩니다.


function traceClass(targetClass){
  var hook = Java.use(targetClass);
  var methods = hook.class.getDeclaredMethods();
  hook.$dispose;

  var parsedMethods=[];
  methods.forEach(function(method) {

   parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
   traceMethod(targetClass+"."+method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
  });
 }

traceClass 함수에서는 getDeclaredMethods함수를 이용, 해당 Class에 존재하는 Method를 추출합니다.


getDeclaredMethods함수를 사용하게 되면 리턴타입 및 인자 타입등등의 내용도 출력되므로, replace함수를 이용하여 함수명까지만 추려내고 다시 traceMethod함수를 호출합니다.


 function traceMethod(targetClassMethod){

  var delim = targetClassMethod.lastIndexOf(".");
  if (delim === -1){ return;}
  var targetClass = targetClassMethod.slice(0, delim);
  var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);
  
  var hook = Java.use(targetClass);
  var overloadCount = hook[targetMethod].overloads.length;

  for(var i=0;i<overloadCount;i++){
   hook[targetMethod].overloads[i].implementation=function(){

    console.log("***Entered Method is : "+targetClass+"."+targetMethod);

    var retval = this[targetMethod].apply(this, arguments);
    return retval;
   }
  }
 }

traceMetdho함수에서는 인자로 받은 Method에 각각 후킹을 걸어 어느 함수가 호출되었는지 출력 및 기존 함수를 호출합니다.


언뜻보면 되게 간단한 코드인데, 조금 비효율적이기도 하고 솔루션이 씌워진 앱의 경우 APP_NAME이 com.abc.abcd.abcde여도 kr.net.~~~~.~~~로 시작할수도 있으므로 후킹이 되지 않는 메소드도 존재합니다. 그리고 enumerateLoadedClasses를 사용하다보니 처음에 실행하는 함수는 후킹을 못하는 경우도 있어요.. 후킹이 걸리기 전에 함수가 실행되면 후킹이 되지 않습니다...


그래서 코드 실행 시 메인 함수에 후킹을 걸어 StackTrace를 해보고 싶었으나... enumerateLoadedClasses의 특성상 실패...만약 메인함수를 두세번 호출하게 된다면 후킹이 가능하겠지만 처음 실행시에는 후킹이 걸리지 않네요..ㅠ  function traceMainActivity()는 삭제하셔도 됩니다~


그래도 앱 실행 시 호출되는 흐름을 얼추 확인할 수 있으므로 진단 시 조금 더 편의가 증가하긴 한것 같네요.


※ 기존 개발자가 개발한 버전은 어떤지 모르겠으나, 제가 작성한 코드는 버그나 에러가 조금 존재해서 참고용으로 보시면 될거 같네요 ^^:


사용방법은 python [코드를저장한파일이름].py [APP_NAME]으로 실행하시면 되고, 아래는 실행화면입니다. (SDMaid라는 앱 실행화면)




※ 개발 환경 및 단말 환경 

PC : Windows10 / Python 2.7

Mobile : 갤럭시 S6 / 6.0


'Mobile' 카테고리의 다른 글

Objection(with. iOS SSL Pinning)  (0) 2019.03.11
Cydia 에러(Could not connect to the server)  (0) 2019.02.25
Android Socket 통신 확인(with.Frida)  (2) 2019.01.18
apktool first type is not attr  (0) 2018.10.04
Android Proxy Burp 인증서 설치  (2) 2018.09.15

소켓으로 통신하는 앱의 경우, 어떻게 통신을 확인해야 할지 몰라서 이참에 Frida 코드를 작성해봤습니다.


일단 소켓의 경우 통신하는 IP/PORT를 확인해야 하므로 frida-trace를 이용해서 해당 어떤 함수를 후킹해야 하는지 확인해 보도록 하겠습니다.


frida-trace -U -i socket "[APP_NAME]"을 통해 생성되는 js파일에 이전 호출한 클래스 및 메소드를 확인하는 코드를 작성하여 어떤 클래스를 호출하는지 확인합니다.


※코드는 다음 포스트에 있는 코드를 사용했습니다.

https://taesun1114.tistory.com/entry/Android에서-호출한-ClassMethod-확인하기-with-Frida?category=729990


확인결과, java.net.Socket를 후킹하여 IP/PORT를 확인하면 될듯 합니다.



https://developer.android.com/reference/java/net/Socket에서 확인한 socket함수입니다.




예시로 사용한 앱은 다음과 같습니다. 별거 없이 버튼클릭하면 소켓을 발송하는 코드입니다.



후킹할 Frida code는 다음과 같습니다.



import sys
import frida

def on_message(message,data):
 print "[%s] -> %s" % (message, data)

PACKAGE_NAME = sys.argv[1]

jscode = """
Java.perform(function(){
 var sock = Java.use('java.net.Socket');

 console.log("Try to Hooking socket...");
 sock.$init.overload('java.lang.String','int').implementation = function(a,b){
  console.log(a.toString()+ " : " +b);
 };
});
"""
 
try:
 device = frida.get_usb_device()
 pid = device.spawn([PACKAGE_NAME])
 print("App is starting ... pid : {}".format(pid))
 process = device.attach(pid)
 device.resume(pid)
 script = process.create_script(jscode)
 script.on('message',on_message)
 print('[*] Running Frida')
 script.load()
 sys.stdin.read()
except Exception as e:
 print(e)



후킹 시, IP / PORT를 정상적으로 출력하는 것을 확인할 수 있습니다.


확인하기까지 상당히 어려웠는데.. 포스팅하다보니 별거 없네요...

$init을 써주는 부분에서 삽질을 많이 했네요


추가로, send 및 recv함수에 frida-trace를 이용해서 hexdump함수를 이용해서 통신내용을 확인할 수 있습니다.


'Mobile' 카테고리의 다른 글

Cydia 에러(Could not connect to the server)  (0) 2019.02.25
Android Method Trace(With.Frida)  (2) 2019.01.29
apktool first type is not attr  (0) 2018.10.04
Android Proxy Burp 인증서 설치  (2) 2018.09.15
Uncrackable3-1  (2) 2018.06.14

APK TOOL을 이용해 리패키징하는 도중, 다음과 같은 에러를 볼 수 있습니다.



W: A/        ( 5036): First type is not attr!

Exception in thread "main" brut.androlib.AndrolibException: brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 134): [/var/folders/dd/qm71z_9j6jd8yr8hfj0xj5j00000gp/T/brut_util_Jar_7912458143856853366.tmp, p, --forced-package-id, 127, --min-sdk-version, 21, --target-sdk-version, 25, --version-code, 1, --version-name, 1.0, --no-version-vectors, -F, /var/folders/dd/qm71z_9j6jd8yr8hfj0xj5j00000gp/T/APKTOOL1582439934222996058.tmp, -0, arsc, -0, res/raw/joda_atlantic_jan_mayen, -0, res/raw/joda_est, -0, res/raw/joda_etc_gmt, -0, res/raw/joda_etc_gmt_1, -0, res/raw/joda_etc_gmt_10, -0, res/raw/joda_etc_gmt_11, -0, res/raw/joda_etc_gmt_12, -0, res/raw/joda_etc_gmt_13, -0, res/raw/joda_etc_gmt_14, -0, res/raw/joda_etc_gmt_2, -0, res/raw/joda_etc_gmt_3, -0, res/raw/joda_etc_gmt_4, -0, res/raw/joda_etc_gmt_5, -0, res/raw/joda_etc_gmt_6, -0, res/raw/joda_etc_gmt_7, -0, res/raw/joda_etc_gmt_8, -0, res/raw/joda_etc_gmt_9, -0, res/raw/joda_etc_gmtplus1, -0, res/raw/joda_etc_gmtplus10, -0, res/raw/joda_etc_gmtplus11, -0, res/raw/joda_etc_gmtplus12, -0, res/raw/joda_etc_gmtplus2, -0, res/raw/joda_etc_gmtplus3, -0, res/raw/joda_etc_gmtplus4, -0, res/raw/joda_etc_gmtplus5, -0, res/raw/joda_etc_gmtplus6, -0, res/raw/joda_etc_gmtplus7, -0, res/raw/joda_etc_gmtplus8, -0, res/raw/joda_etc_gmtplus9, -0, res/raw/joda_etc_uct, -0, res/raw/joda_etc_utc, -0, res/raw/joda_hst, -0, res/raw/joda_mst, -0, res/raw/joda_pacific_johnston, -0, arsc, -I, /Users/daniel/Library/apktool/framework/1.apk, -S, /Users/daniel/Downloads/app-debug/res, -M, /Users/daniel/Downloads/app-debug/AndroidManifest.xml]

at brut.androlib.Androlib.buildResourcesFull(Androlib.java:493)

at brut.androlib.Androlib.buildResources(Androlib.java:427)

at brut.androlib.Androlib.build(Androlib.java:326)

at brut.androlib.Androlib.build(Androlib.java:264)

at brut.apktool.Main.cmdBuild(Main.java:231)

at brut.apktool.Main.main(Main.java:84)


코틀린으로 작성된 앱의 경우 APKTOOL로 리패징하려고 할 경우 다음과 같은 에러를 볼 수 있다고 합니다.


현재는 다음 내용도 반영된 패치버전이 나왔는데요.


현재 최신버전(2.3.4)으로 리패키징을 시도할 경우 에러없이 정상적으로 패키징되는 것을 확인할 수 있었습니다.


'Mobile' 카테고리의 다른 글

Android Method Trace(With.Frida)  (2) 2019.01.29
Android Socket 통신 확인(with.Frida)  (2) 2019.01.18
Android Proxy Burp 인증서 설치  (2) 2018.09.15
Uncrackable3-1  (2) 2018.06.14
APK IDA로 동적디버깅  (0) 2018.02.22

Android에서 Proxy를 잡을 경우 인증서 설치를 해줘야 HTTPS통신을 확인할 수 있습니다.


일단 [Proxy] - [Options] - [Proxy Listeners] - [Import/export CA certificate] 에서 인증서를 추출하여 파일로 저장합니다.





파일로 추출한 후에는 adb를 이용해 안드로이드 단말기에 넣어주면 되는데, 경로는 어디에 하든 굳이 상관없습니다. 





안드로이드 단말기에서 [설정] - [잠금화면 및 보안] 화면에서 [기타 보안 설정]으로 들어갑니다.



디바이스에 저장된 인증서 설치 클릭



아까 넣어주었던 cacert.der 파일이 보입니다. 클릭해서 완료 하면 설치됩니다.


인증서 설치 후 burp에서 정상적으로 https통신도 확인이 가능합니다 :)




'Mobile' 카테고리의 다른 글

Android Socket 통신 확인(with.Frida)  (2) 2019.01.18
apktool first type is not attr  (0) 2018.10.04
Uncrackable3-1  (2) 2018.06.14
APK IDA로 동적디버깅  (0) 2018.02.22
welcome to droid (codegate2018)  (2) 2018.02.11

저번에 Uncrackable3를 도전했었는데 당시에 다운받았던 앱이 오류가 있었던 건지 실행하면 바로 종료가 되어 분석을 하지 못했는데


며칠전 다시 다운받아 설치해보니 이번엔 잘돌아가네요 :) (당시에 앱 자체의 오류가 맞았을까요....)



앱 실행 시 위와 같이 루팅감지되어 바로 종료가 됩니다


디컴파일 후에 소스코드를 보면



MainActivity의 onCreate부분에서 루팅체크하는걸 볼 수 있습니다 


루팅체크로직을 따라가 보면



다음과 같은걸 볼 수 있는데 저는 Supersu를 사용해서 위 로직에서 탐지가 된듯합니다


간단하게 Frida를 이용해서 우회해 보겠습니다.


jscode = """

    Java.perform(function() {

        console.log("[*] Hooking calls to System.exit");

        root1 = Java.use("sg.vantagepoint.util.RootDetection");

        root1.checkRoot1.implementation = function() {

            console.log("[*] checkRoot1 Hook");

            return false;

        }

        root1.checkRoot2.implementation = function() {

            console.log("[*] checkRoot2 Hook");

            return false;

        }

        root1.checkRoot3.implementation = function() {

            console.log("[*] checkRoot3 Hook");

            return false;

        }

    });

"""


후킹 스크립트는 위와 같이 간단하게 탐지가 되어도 false를 리턴하게 작성하였습니다


실행해보면...



다음과 같이 에러가 나는데요 여기서 왜 에러가 나는지 몰라 한동안 깊은 삽질을 했네요 :(


결론은 MainActivity에서 libfoo.so파일을 호출해서 native코드를 실행하는데 .so파일의 경우 로드될때 .init_array가 먼저 실행된다고 합니다


그럼 .so파일의 .init_array를 살펴보겠습니다 ida에서 Control + S를 눌러 확인이 가능합니다



Ctrl + S를 이용하여 .init_array를 확인해 보면

아래 그림과 같이 offset sub_8D50을 호출하는 것을 알 수 있습니다

 


sub_8D50으로 들어가면 pthread_create를 이용한 anti-debugging 기법같은데요 thread가 생성되며 호출되는 함수(sub_8BC0)를 살펴보겠습니다



이 함수안에보면 메모리를 읽어 frida와 xposed를 확인하는 루틴이 존재하네요 ㅠㅠㅠ

frida를 이용해서 후킹하려고 하면 여기서 탐지되어 강제로 종료되는 것을 확인할 수 있습니다



android_log_print를 보면 37942자리에 있는 값을 ddms에 로그로 띄워주는 것 같네요 

37942 = 0x9436이므로 해당 주소로 가보면



탐지될 경우 UnCrackable3에 Tampering dtected! Terminating...이라고 출력될 것 같네요



ddms로 로그를 확인해보면 프리다로 실행시켰을 경우, 다음과 같이 탐지되는 것을 볼 수 있습니다



하나로 끝내려고 했는데 너무 길어져서 다음에 이어 작성해야겠네요 ;)

Uncrackable3-2로 끝내겠습니다 :)


문제 풀이(3-2)는 아래에서 확인하시면 됩니다~

https://taesun1114.tistory.com/entry/Uncrackable3-2

'Mobile' 카테고리의 다른 글

apktool first type is not attr  (0) 2018.10.04
Android Proxy Burp 인증서 설치  (2) 2018.09.15
APK IDA로 동적디버깅  (0) 2018.02.22
welcome to droid (codegate2018)  (2) 2018.02.11
INSTALL FAILED TEST_ONLY  (1) 2018.02.06

APK를 IDA로 동적디버깅하는 방법을 포스팅하겠습니다


인터넷에 Netbeans를 이용해서 디버깅하는 자료가 나와 있는데 안드로이드 6.0부터는 Netbeans가 지원이 안되는 것 같네요 ( 확실하진 않지만 제가 해봤을때는 안되네요 :/ )


디버깅 하고자 했던 원래 의도는 무결성을 훼손하지 않으면서 디버깅을 하고 싶었는데, 그건 아마 안되는 것 같습니다


예시를 위해 기존에 포스팅했던 Uncrackable1 앱을 이용하겠습니다


일단 APK파일의 AndroidManifest.xml 파일에 debuggable속성을 넣어줘서 리패키징을 해야 합니다



리패키징된 APK파일을 단말기에 설치한 후, APK파일을 IDA에서 로드합니다



APK파일이 zip파일이므로 로드하게 되면 내부 파일을 선택하라는 창이 나오는데 여기서 calsses.dex를 선택합니다



로드하면 classes.dex의 smali코드가 출력됩니다



smali코드의 원하는 지점에 브레이크포인트를 설정합니다


F2 혹은 smali코드 라인에서 우클릭하면 브레이크포인트를 설정할 수 있습니다



브레이크 포인트를 설정하고 나면 설정한 위치에 빨간 라인이 생깁니다



앱을 실행시켜주기전에 한가지 설정을 해줘야 합니다 


[Debugger] - [Debugger options]



생성되는 창에서 [Set sepecific options]로 들어가 줍니다



Package name 및 Activity에 알맞은 값을 설정해 줍니다



설정이 완료되면 Debugger의 Start Process 혹은 attach를 시켜주면 됩니다



그럼 시간이 좀 걸린 후에 브레이크포인트에 실행지점이 도달하게 되면 다음과 같이 디버깅 창을 확인하실 수 있습니다



[Debugger] - [Debugger windows] - [Locals]에서 변수값들 확인도 가능합니다 :)



IDA에서 Dex와 so를 같이 디버깅 하는 방법은 다음 포스팅에 있습니다.

https://taesun1114.tistory.com/entry/IDA-DEXSO-동적디버깅?category=729990




'Mobile' 카테고리의 다른 글

Android Proxy Burp 인증서 설치  (2) 2018.09.15
Uncrackable3-1  (2) 2018.06.14
welcome to droid (codegate2018)  (2) 2018.02.11
INSTALL FAILED TEST_ONLY  (1) 2018.02.06
Uncrackable3 - frida  (0) 2018.01.30

Welcome to Droid 


codegate 2018이 열렸는데요 


참가는 안했지만 모바일 문제가 하나 출제되었다길래 풀어봤습니다


일단 앱을 실행시켜 보겠습니다


    


에뮬레이터로 실행하면 처음에 ID를 입력받고 PW를 입력받습니다 


ID/PW를 임의의값으로 입력하니 PW입력 직후에 wrong! try again이라는 문구가 출력되네요


디컴파일 후에 소스를 분석해 보겠습니다



처음에 ID 입력부분에서 입력값의 길이가 10이상 26자리 이하여야만 통과가 되었네요 임의의 값이 우연찮게 10자리여서 통과가 되었나 봅니다



PW입력 부분인데 Main2Activity class의 a 함수에서 PW를 확인하는 로직을 확인할 수 있습니다



a 함수를 살펴보면 codegate2018hurry!HAHAHALOL 이었나.. 확실하게 기억이 안나는데 해당 문자열을 이용하여 패스워드를 생성한후 최종적으로 paramString변수에 담아 리턴 후 입력받은 문자열과 비교하게 됩니다



다음 Main3Activity도 확인해 보니 시리얼키를 입력해야 하는 것 같습니다


이번에는 Main3Activity의 k함수에서 비교하네요



k함수에서는 ABCDEFG.....Z123....9의 문자을 가지고 시리얼을 생성하여 입력값과 비교합니다



마지막으로 Main4Acitivity에서는 native-lib.so에 파일에 저장된 flag를 출력하네요


풀이법으로는 여러방법이 있겠지만 smali코드의 분기문을 변조하여 거짓일때 실행되도록 변경하겠습니다



Main2Activity.smali파일에서 분기문을 확인합니다 Main2Activity.smali파일을 확인하니 $2파일에 setOnClickListener가 정의되어 있네요



Main2Activity$2.smali , Main3Activity$2.smali의 분기문을 변경한 후에 리패키징하여 에뮬레이터에 다시 설치해줍니다



임의의 값을 입력하고 계속 넘어가면 Main4Activity가 불리면서 Flag를 확인할 수 있습니다 :)



Try & Error


문제를 푸는 도중 smali코드를 변경하고 평소처럼 리패키징하는 경우, 다음과 같은 에러를 확인할 수 있었습니다


resource를 찾을 수 없다는 에러인데... 


삽질하다가 apktool의 옵션중 리소스 디컴파일을 하지 않는 옵션을 주고 해결했습니다.


java -jar apktool.jar d -r droid.apk


이렇게 디컴파일한 상태에서 smali를 변경하고 리패키징하면 정상적으로 리패키징되는 것을 확인할 수 있습니다



또한, apk파일 설치 시에 에러를 확인 할 수 있었는데 해당 내용은


http://taesun1114.tistory.com/entry/INSTALL-FAILED-TESTONLY?category=729990 과 같습니다


마지막으로 Frida를 통해 무결성을 훼손하지 않고 패스워드와 시리얼을 획득하여 문제를 풀어보려 했는데 실패했습니다


아직 Frida가 능숙하지 않아서 에러가 나면 한참 헤매게 되네요ㅠ 후에 Frida를 통해 문제를 해결하고 포스팅하도록 하겠습니다 :)


'Mobile' 카테고리의 다른 글

Uncrackable3-1  (2) 2018.06.14
APK IDA로 동적디버깅  (0) 2018.02.22
INSTALL FAILED TEST_ONLY  (1) 2018.02.06
Uncrackable3 - frida  (0) 2018.01.30
Uncrackable1 - frida  (4) 2018.01.30
APK 설치 시, 다음과 같은 에러를 확인 할 수 있었습니다


검색을 해보니, 에러가 다음과 같이 정의되어 있네요



INSTALL_FAILED_TEST_ONLY는 test-only로 명시되어 있어서 에러가 납니다


adb install 옵션중에 -t를 넣어 설치를 하거나 androidmanifest.xml파일에서 test-only옵션을 제거해 주시면 됩니다:)


adb install -t <apk_name>


출처


'Mobile' 카테고리의 다른 글

APK IDA로 동적디버깅  (0) 2018.02.22
welcome to droid (codegate2018)  (2) 2018.02.11
Uncrackable3 - frida  (0) 2018.01.30
Uncrackable1 - frida  (4) 2018.01.30
Android에서 호출한 Class/Method 확인하기 - with Frida  (0) 2018.01.26

frida를 이용해서 다른 스크립트 작성 중 검색을 통해 Uncrackable 실습앱을 발견하다보니, 순서가 뒤죽박죽이네요


Uncrackable 실습앱은 level3까지 나와있습니다

마지막인데.. 앱을 설치하고 실행하면 그냥 종료되네요 

처음엔 이것도 우회해야 하는 건가 싶어 한참을 헤맷으나 확인해보니 출제자 에러인 것 같습니다

DDMS를 통해 실행 후 로그를 확인해 봤습니다


로그를 확인하니 세그멘테이션 오류인 듯 합니다


디컴파일 후 소스를 확인해 보겠습니다



앱 분석 당시에는 그냥 출제자 에러인가 보다 하고 넘겼는데... 글을 작성하면서 소스를 보니 뭔가 verifyLibs에서 뭔가를 해주네요


일단 글작성후에 다시 분석을 해보도록 하겠습니다



분석할 당시에는 libfoo.so파일의 strncpy 함수를 호출할 때 인자로 NULL이 들어가서 세그멘테이션 에러가 발생한다고 생각했는데..


글 작성하면서 다시 보니, lib에 뭔가 조치를 해줘야 앱이 실행되는 걸까요? 조금 더 분석해 봐야 겠네요 :)



'Mobile' 카테고리의 다른 글

welcome to droid (codegate2018)  (2) 2018.02.11
INSTALL FAILED TEST_ONLY  (1) 2018.02.06
Uncrackable1 - frida  (4) 2018.01.30
Android에서 호출한 Class/Method 확인하기 - with Frida  (0) 2018.01.26
Uncrackable2 - Radare2  (0) 2018.01.23

Frida 자료를 찾던 중 좋은 실습앱이 있어 Frida를 통해 Uncrackable1 앱 우회를 하겠습니다



앱 실행 시, 루팅탐지 후 종료하게 됩니다


디컴파일 후에 루팅 탐지 로직을 확인하면 다음과 같습니다



sg.vantagepoint.a.c.class에서 루팅을 확인하는데 총 3번에 걸쳐서 탐지를 합니다


해당 실습앱이 올라와 있는 사이트에서는 exit 함수를 후킹하여 exit함수 호출 시 아무런 동작도 하지 않도록 하는데, activity가 떠있는 상태에서는 굳이 루팅체크로직을 일일히 후킹하지 않더라도 이런 방법으로도 우회가 가능하네요 :)


그럼 exit를 후킹하여 앱종료 명령 시, 무시하는 코드를 작성하도록 합니다


import sys
import frida

def on_message(message,data):
    print "[%s] -> %s" % (message, data)


PACKAGE_NAME = "sg.vantagepoint.uncrackable1"

jscode = """
    Java.perform(function() {
        console.log("[*] Hooking calls to System.exit");
        exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function() {
            console.log("[*] System.exit called");
        }

     
    });
"""
   
try:
    device = frida.get_usb_device(timeout=10)
    pid = device.spawn([PACKAGE_NAME]) 
    print("App is starting ... pid : {}".format(pid))
    process = device.attach(pid)
    device.resume(pid)
    script = process.create_script(jscode)
    script.on('message',on_message)
    print('[*] Running Frida')
    script.load()
    sys.stdin.read()
except Exception as e:
    print(e)


exit호출 시 콘솔에 로그 출력만 하도록 후킹을 걸고 String을 입력 후 버튼을 눌러보겠습니다




문자열 비교 후 알람을 출력합니다 다시 디컴파일 된 소스코드를 확인해보면



분기문이 보입니다 sg.vantagepoint.a.a.class에서 문자열을 비교하는 것 같습니다



arrayOfByte2에 찾아야할 문자열이 암호화 후 저장되어 있고, sg.vantagepoint.a.a.a를 호출하여 arrayOfByte2를 복호화하여 입력값과 비교하는 것 같습니다


위 화면의 네모박스에 있는 스트링은 AES복호화할 때 사용하는 키값인 것 같네요


sg.vantagepoint.a.a.class를 확인해보니 맞는 것 같습니다 


이 class에서 복호화된 문자열을 return하므로 해당 함수를 후킹하여 복호화된 문자열을 추출하도록 하겠습니다


import sys
import frida
def on_message(message,data):
    print "[%s] -> %s" % (message, data)

PACKAGE_NAME = "sg.vantagepoint.uncrackable1"
jscode = """
    Java.perform(function() {
        console.log("[*] Hooking calls to System.exit");
        exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function() {
            console.log("[*] System.exit called");
        }
     
  a = Java.use("sg.vantagepoint.a.a");
        a.a.implementation = function(a,b){
            console.log("[*]Hooking in a.Class");
            retVal = this.a(a,b);
            passcode='';
            for(i=0;i<retVal.length;i++){
                passcode+=String.fromCharCode(retVal[i]);
            }
            console.log("Secret key :"+passcode);
            return retVal;
        }
    });
"""
   
try:
    device = frida.get_usb_device(timeout=10)
    pid = device.spawn([PACKAGE_NAME]) 
    print("App is starting ... pid : {}".format(pid))
    process = device.attach(pid)
    device.resume(pid)
    script = process.create_script(jscode)
    script.on('message',on_message)
    print('[*] Running Frida')
    script.load()
    sys.stdin.read()
except Exception as e:
    print(e)


위에서 작성한 exit()함수 후킹 소스에 덧붙여 sg.vantagepoint.a.a.a를 후킹하는 스크립트를 추가합니다


단순하게 기존 함수의 리턴값을 받아 출력하는 함수입니다


실행시켜 보면 복호화된 문자열이 출력되는 것을 볼 수 있습니다 :)




'Mobile' 카테고리의 다른 글

INSTALL FAILED TEST_ONLY  (1) 2018.02.06
Uncrackable3 - frida  (0) 2018.01.30
Android에서 호출한 Class/Method 확인하기 - with Frida  (0) 2018.01.26
Uncrackable2 - Radare2  (0) 2018.01.23
Android Anti-Debugging  (1) 2018.01.23

+ Recent posts