Java에서 자신을 호출한 클래스나 함수는 Throwable의 getStackTrace를 이용하여 확인할 수 있습니다

Android에서도 Frida를 이용해 무결성을 훼손하지 않고 호출한 함수를 확인해 보겠습니다

예제로 작성한 APP 소스입니다

package com.example.gnsan.chfrida;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.button).setOnClickListener(Click);
}
Button.OnClickListener Click = new View.OnClickListener(){
public void onClick(View v){
chfrida();
}
};
void chfrida(){
chfrida2();
TextView textview = (TextView)findViewById(R.id.test);
textview.setText("Hello World!!!");
}
void chfrida2(){
}
}

간단하게 버튼을 클릭하여 텍스트를 변경하는 앱입니다. 호출 함수를 확인하기 위해 chfrida / chfrida2를 작성하였습니다


Frida 소스입니다


line:13에서 Throwable를 로드하고 line:16에서 instance 생성 및 line:17에서 getStackTrace를 호출합니다



실행하면 위에서 부터 차례로 호출했던 함수를 출력하네요 :)




'Mobile' 카테고리의 다른 글

Uncrackable3 - frida  (0) 2018.01.30
Uncrackable1 - frida  (4) 2018.01.30
Uncrackable2 - Radare2  (0) 2018.01.23
Android Anti-Debugging  (1) 2018.01.23
Instant Run(Split apk)  (1) 2018.01.10

App 진단 중 간단한 Anti-Debugging 기법을 알게되었습니다.


ptrace를 이용한 기법인데 ptrace에 대한 자세한 내용은


http://research.hackerschool.org/temp/ptrace.txt


위 주소에 더 잘나와있네요. 해커스쿨에서 작성된 내용을 보자면 


/*


ptrace의 특징은, 다양한 기능들을 함수 인자로 처리한다는 점입니다.


ptrace(PTRACE_ATTACH, ... ); // 이렇게 하면 process를 attach하겠다는 것이고요


ptrace(PTRACE_GETREGS, ... ); // 이건 대상 process의 레지스터 목록을 받아오라는 거죠


ptrace(PTRACE_PEEKDATA, ... ); // 이처럼 데이터를 쓰거나


ptrace(PTRACE_POPEDATA, ... ); // 가져올 수 있습니다.

*/ 등등인데 유명한 GDB, Frida도 ptrace를 이용해서 동작한다고 하네요


1. Ptrace Anti-Debugging 


Android Anti-Debugging 기본 예제는 다음과 같습니다.


void anti_debug() {

    child_pid = fork();

    if (child_pid == 0)
    {
        int ppid = getppid();
        int status;

        if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0)
        {
            waitpid(ppid, &status, 0);

            ptrace(PTRACE_CONT, ppid, NULL, NULL);

            while (waitpid(ppid, &status, 0)) {

                if (WIFSTOPPED(status)) {
                    ptrace(PTRACE_CONT, ppid, NULL, NULL);
                } else {
                    // Process has exited for some reason
                    _exit(0);
                }
            }
        }
    }
}

출처 : www.vantagepoint.sg/blog/89-more-android-anti-debugging-fun



Anti-Debugging 기본 예제 소스는 위와 같습니다.


원리는 fork()를 이용해서 자식프로세스에서 부모프로세스를 ptrace하면 다른 프로세스에서 해당 프로세스로 ptrace 시도 시, 이미 점유되어 있어서 다른 디버거에서 attach가 불가능한 원리입니다.


2. Anti-Debugging 우회


2.1 With Frida

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

PACKAGE_NAME = sys.argv[1]
jscode = """
/// Inject_Jscode
"""
   
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)


Frida의 spawn을 이용하여 앱 실행 전에 프리다가 먼저 후킹을 시도하여 Anti-Debugging을 우회하는 방법과


frida -U -f <Package_Name> --no-pause


명령어를 입력하는 방법이 있습니다.


2.2 Integrity 


이건 뭐 별달리 설명드릴 부분은 아니지만.. 해당 로직 Smali를 변조하여 앱 리빌드 후에 실행시키는 방법인데, 이를 이용하려면 무결성 체크로직이 없거나 무결성 체크로직도 우회해야 합니다.



출처 : http://www.vantagepoint.sg/blog/89-more-android-anti-debugging-fun

http://taesun1114.tistory.com/entry/안드로이드-프록시-사용-유무-체크에서 포스팅했던 Proxy Check하는 App을 이용하여 Proxy Check를 우회해 보려고 합니다.


Frida 공식 홈페이지는 아래와 같으며, 홈페이지를 통해 여러 정보를 획득할 수 있습니다.

https://www.frida.re/docs/home/



Test App Souce)


package com.example.gnsan.myapplication;

import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.*;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textview = (TextView)findViewById(R.id.test);
if(System.getProperty("http.proxyHost")!=null) {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
android.os.Process.killProcess(android.os.Process.myPid());
}
});
alert.setMessage("Denied Proxy");
alert.show();
textview.setText(System.getProperty("http.proxyHost"));
}
}
}

프록시 유무를 체크한 후, 프록시를 사용중이라면 알림을 띄운 후 앱을 종료하는 간단한 소스입니다.


          

<프록시 사용 x>                                <프록시 사용 o>





프록시를 우회하는 Frida Code는 다음과 같습니다.



import sys
import frida



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



PACKAGE_NAME = "com.example.gnsan.myapplication"

jscode = """
 Java.perform(function(){
  var System = Java.use("java.lang.System");   //System.getProperty함수는 java.lang.System
  System.getProperty.overloads[0].implementation = function(prop){ 
   return ;  // 프록시를 사용하지 않을 때 getProperty함수는 null을 리턴함
  }
 });
"""


try:
     device = frida.get_usb_device(timeout=10)
     pid = device.spawn([PACKAGE_NAME])    //Packagename으로 앱을 찾음
     print("App is starting ... pid : {}".format(pid))
     process = device.attach(pid)        
     device.resume(pid)    //resume을 해주지 않으면 화면을 불러올 수 없음
     script = process.create_script(jscode)
     script.on('message',on_message)
     print('[*] Running Frida')
     script.load()
     sys.stdin.read()
except Exception as e:
     print(e)



출처: http://hyunmini.tistory.com/111?category=523090 [Hyunmini]


-----------------------------------------------------------------------------------

실패한 jscode


var isproxy = Java.use("java.lang.System");
 isproxy.getProperty.implementation=function(a){   

 

  //overloads를 해주지 않으면 error 

  //어느경우에 overload를 하는지 아시는분은 댓글로 부탁드립니다

  //overload는 다음을 통해 알게됨 : 출처: http://hyunmini.tistory.com/111?category=523090 [Hyunmini]


 console.log("getProperty's called!");
 this.getProperty();
 };



var test = Java.use("java.lang.System");
 Interceptor.attach(Module.findExportByName(null,'getProperty'),{

             //findExportByName으로 getProperty를 찾으려했으나 실패함
  onLeave: function(retVal){
   console.log("HI");
   var orig_rtn = retVal.toString();
   send("return vallue :"+orig_rtn);
   retVal.replace("null");
  }
 };


-----------------------------------------------------------------------------------

  .overload() Example
    .overload('java.lang.String')
    .overload('android.app.Activity')
    .overload('int')
    .overload('[B') // byte array
    .overload('float')
    .overload('android.content.Context')
    .overload('[C')
    .overload('android.content.Context', 'android.view.View')
    .overload('android.app.Activity', 'com.cherrypicks.hsbcpayme.model.object.PayMeNotification')
    .overload('android.content.Context', 'boolean')
    .overload('android.content.Context', 'int')
    .overload('android.content.Context', 'java.lang.String')
    .overload('android.app.Activity', 'int')
    .overload('java.lang.String', 'java.lang.String')
    .overload('android.content.Context', 'android.graphics.Bitmap')
    .overload('java.lang.String', 'java.io.File')
    .overload('android.content.Context', 'java.lang.String', 'java.util.List')
    .overload('java.lang.String', 'java.lang.String', 'java.lang.String')
    .overload('java.lang.String', '[B', '[B')
    .overload('java.lang.String', 'java.lang.String', 'android.content.Context')
    .overload('android.app.Activity', 'com.cherrypicks.hsbcpayme.model.object.PayMeNotification', 'int')
    .overload('[B', '[B', '[B')
    .overload('android.content.Context', 'java.lang.String', 'java.lang.String')
    .overload('android.app.Activity', 'int', 'int', 'int', 'boolean')

+ Recent posts