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

Android 분석 중, .so파일은 보통 IDA를 이용해서 분석했는데 좋은 툴이 있어 소개드립니다


Radare2라는 툴인데 IDA CLI버전 느낌이네요. 가볍기도 하고 GDB + IDA느낌입니다.(오늘 처음 사용해봐서 잘모르지만 느낌은)


http://radare.org/r/   -  Radare2공홈입니다


https://radare.gitbooks.io/radare2book/content/  - Radare2 가이드 페이지입니다


Windows 버전과 리눅스 버전이 있는데, 리눅스는 github에서 받으시면 됩니다


#git clone https://github.com/radare/radare2.git

#cd radare/sys

#./install.sh


설치가 완료되면 r2명령어를 이용하여 실행할 수 있습니다


OWASP의 uncrackable2 app을 이용하여 Radare2를 소개하겠습니다


앱 실행시 다음화면과 같습니다. 루팅 탐지 로직을 확인하기 위해 디컴파일 후에 소스코드를 확인하겠습니다


init()함수 후에 b.a , b.b , b.c함수를 이용해 루팅탐지를 합니다.

소스코드를 보니 init함수가 jni를 이용하여 작성된 so파일이니 r2를 이용해 분석하겠습니다


iE명령을 이용하여 Java_sg_vantagepoint_uncrackable2_CodeCheck_bar, Java_sg_vantagepoint_uncrackable2_MainActivity_init 함수를 확인하였습니다


[]>s 0x00000b0c
[]>pd $r @

명령어를 이용해 MainActivity_init함수를 확인할 수 있습니다


fork()함수를 이용하여 다른 작업을 하는 것 같습니다


[]>s 0x000008b8

[]>pd $r @



ptrace를 이용한 anti-debug 로직인 것 같네요


Anti-Debugging 및 루팅탐지를 우회하기 위해 Frida를 이용하기로 합니다.

import sys
import frida

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


PACKAGE_NAME = "sg.vantagepoint.uncrackable2"

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() 함수를 후킹하여 앱 종료를 막는 스크립트입니다

스크립트 실행 시, 루팅탐지가 되지만 exit함수가 후킹되어 앱이 종료되지 않습니다 



다시 복호화된 소스를 확인합니다


위 캡처화면의 분기문에서 스트링을 검사한 후에 Alert창을 띄우는 것을 확인하였으며, this.m.a의 로직을 살펴보면 다음과 같습니다


jni파일의 bar함수에서 스트링 검사를 진행하여 결과값을 리턴받아 판별하는 소스입니다

상단의 radare2를 이용하여 libfoo.so파일을 살펴본 결과 CodeCheck_bar를 확인할 수 있었고 해당 함수를 디스어셈블하면 다음과 같은 화면이 출력됩니다


0x00000c88부분에 strncmp함수를 확인할 수 있습니다

조금더 올라가서 0x00000c74부분을 살펴보면


cmp w0,0x17

b.ne 0xc90


를 확인할 수 있고 0x17글자수를 검증한 후 기존의 문자열과 입력된 문자열을 비교합니다

Frida를 이용해서 strncmp를 후킹하여 파라미터를 출력하는 방법으로 secret key를 확인하겠습니다


import sys
import frida

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


PACKAGE_NAME = "sg.vantagepoint.uncrackable2"

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");
        }
       
        var strcmp=undefined;
        module=Module.enumerateImportsSync("libfoo.so");
        for(i=0;i<module.length;i++){
            if(module[i].name=="strncmp"){
                strcmp=module[i].address;
                break;
            }
        }

        Interceptor.attach(strcmp, {
            onEnter: function (args) {
               if(args[2].toInt32() == 23 && Memory.readUtf8String(args[0],23) == "01234567890123456789012") {
                    console.log("[*] Secret string at " + args[1] + ": " + Memory.readUtf8String(args[1],23));
                }
             },
        });
    });
"""
   
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)


코드 실행 시, secret key를 확인할 수 있습니다


※Radare2에서 Ida와 동일하게 스페이스 바를 이용하여 그래프 모드가 가능합니다



출처 : https://www.codemetrix.net/hacking-android-apps-with-frida-2/

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

앱 진단 중 테스트 할 것이 있어, 안드로이드 스튜디오에서 빌드하고 apk파일을 추출하려고 하니 다음과 같은 상황이 발생하였습니다.



apk파일이 split_lib_~~~~로 쪼개져서 있는 것인데 base.apk를 추출해 보면 다음과 같습니다.



원본 소스는 버튼하나 있고, 함수 호출하는 소스인데 이상하게 변경되어 있네요


찾아보니 해당 기능은 안드로이드 스튜디오의 Instant run이라는 기능이었습니다.


간단하게 설명하면 안드로이드 스튜디오에서 앱 최초 빌드 후 수정할 경우 다시 앱 빌드해서 넣어줬었는데 이때문에 시간이 오래걸렸습니다.


이를 해결하기 위해 split로 나눠 필요한 부분만 빌드한다는 것 같은데... 저에게는 딱히 쓸일이 없을 것 같네요


Instant Run 옵션을 제거하는 방법은 다음과 같습니다.


[File] - [Settings]


[Build, Execution, Deployment] - [Instant Run] 에서 첫번째 옵션을 제거해 주시면 됩니다.


다시 단말로 들어가서 보니 split가 없어지고 base.apk만 덩그러니 남아 있네요.


복호화도 잘 됩니다 :)






'Mobile' 카테고리의 다른 글

Uncrackable2 - Radare2  (0) 2018.01.23
Android Anti-Debugging  (1) 2018.01.23
Frida를 이용한 앱 실행 중 로드하는 클래스 확인  (2) 2018.01.03
Apktool Error (apktool.yml)  (0) 2018.01.03
IOS SSL(HTTPS)통신하기  (0) 2018.01.02

앱 실행시 어떤 class가 로드되는지 앱 진단 중 필요해서 뒤적이다가 

Java.enumerateLoadedClasses라는 함수를 발견했습니다.


아래 내용은 프리다 공홈에서 가져왔습니다.



Java.enumerateLoadedClasses를 이용한 앱 실행 중 로드되는 클래스를 출력하는 함수입니다.


Usage : python <하단소스저장파일이름>.py <패키지이름>


import sys
import frida

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


PACKAGE_NAME = sys.argv[1]

jscode = """
Java.perform(function(){
    Java.enumerateLoadedClasses(
    {
      onMatch: function(className)
      {
        send(className);
      },
      onComplete:function(){}
    });

});
"""
   
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)




코드 실행 시 화면이며, 안드로이드 내장 클래스까지 출력되어 상당히 지저분합니다.


시간 날때 실행 패키지의 클래스만 출력되도록 다듬도록 하겠습니다.



================================= 18.01.08 추가 ========================================================


난독화되어 있는 앱으로 확인한 결과, 실행시 난독화가 풀리는게 아니고 난독화가 진행되면 앱에서 해당 클래스 및 함수의 이름이 바뀌어서 저장되는 것 같습다.


실행 시 클래스 로드 할때도 변경된 함수로 로드하네요.


솔루션을 이용하는 앱의 경우 솔루션이 .dex의 헤더를 망가트려 디컴파일이 안되게 하는데 이를 믿고 난독화를 안하는 앱이 대다수입니다.


이런 앱들은 해당 스크립트로 함수 내부 클래스 네임을 불러올 수 있으므로 난독화는 필수로 해야 할 듯 하네요



'Mobile' 카테고리의 다른 글

Android Anti-Debugging  (1) 2018.01.23
Instant Run(Split apk)  (1) 2018.01.10
Apktool Error (apktool.yml)  (0) 2018.01.03
IOS SSL(HTTPS)통신하기  (0) 2018.01.02
Android Proxy Check 우회(Frida)  (0) 2017.12.28

apktool을 이용하여 앱 리패키징 시 아래와 같은 에러가 발생했습니다.


Exception in thread "main" brut.androlib.AndrolibException: brut.directory.PathNotExist: apktool.yml
    at brut.androlib.Androlib.readMetaFile(Androlib.java:164)
    at brut.androlib.Androlib.build(Androlib.java:183)
    at brut.androlib.Androlib.build(Androlib.java:176)
    at brut.apktool.Main.cmdBuild(Main.java:228)
    at brut.apktool.Main.main(Main.java:79)
Caused by: brut.directory.PathNotExist: apktool.yml
    at brut.directory.AbstractDirectory.getFileInput(AbstractDirectory.java:103)
    at brut.androlib.Androlib.readMetaFile(Androlib.java:160)
    ... 4 more


apktool 2.2.0 버전을 사용하였으며, apktool이 리패키징 시 apktool.yml의 위치를 찾지 못해서 생기는 에러입니다.


해당 에러는 apktool의 일종의 버그이며, 최신버전을 사용하면 해결이 됩니다.


apktool 2.3.1버전을 설치하여 재 실행하였을때는 에러없이 실행되었습니다.

'Mobile' 카테고리의 다른 글

Instant Run(Split apk)  (1) 2018.01.10
Frida를 이용한 앱 실행 중 로드하는 클래스 확인  (2) 2018.01.03
IOS SSL(HTTPS)통신하기  (0) 2018.01.02
Android Proxy Check 우회(Frida)  (0) 2017.12.28
Android Device Monitor 실행 에러  (0) 2017.12.28

IOS 앱 진단시 PROXY를 잡고 SSL통신을 해야하는 상황이 존재합니다.


Android의 경우 .cer파일을 push로 넣어서 설치하면 되지만 IOS의 경우 어떤 경로에 burp인증서파일을 넣어야 하는지 몰라서 찾다가 포스팅을 합니다.


 경로는 찾지 못했지만 결론은 프록시를 잡은 후, 웹에서와 같이 http://burp를 통해 인증서를 받아서 설치합니다.




검은박스의 CA Certificate를 클릭하여 인증서를 설치합니다.



동일하게 우측 상단의 설치를 터치하여 인증서를 설치합니다.



마지막으로 설치를 눌러주면 인증서 설치가 완료됩니다.



설치 후, naver.com에 접속하여 ssl통신을 확인합니다.

※ 혹시 인증서파일을 단말기에 직접 업로드하여 설치할 경우 어느 경로에 넣어주어야 하는지 아시는분은 댓글로 부탁드립니다.


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')

Device Monitor 실행 시, 에러와 함께 로그파일 확인하라는 알람이 나타남.



※하단 에러 로그 참조


!SESSION 2017-12-28 11:50:33.244 -----------------------------------------------
eclipse.buildId=unknown
java.version=9.0.1
java.vendor=Oracle Corporation
BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=ko_KR
Command-line arguments:  -os win32 -ws win32 -arch x86_64 -data @noDefault

!ENTRY org.eclipse.osgi 4 0 2017-12-28 11:50:33.666
!MESSAGE Application error
!STACK 1
java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:74)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:353)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:180)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
 at java.base/java.lang.reflect.Method.invoke(Unknown Source)
 at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:629)
 at org.eclipse.equinox.launcher.Main.basicRun(Main.java:584)
 at org.eclipse.equinox.launcher.Main.run(Main.java:1438)
 at org.eclipse.equinox.launcher.Main.main(Main.java:1414)



너무 높은 JDK 버전(현재 JDK 9 )에서 실행해서 나는 에러로 JDK 8을 설치하고 실행하니 제대로 실행되는 것을 확인함

Android에서 Proxy를 사용하는지 체크하는 경우 System.getProperty를 이용하는 방법이 존재한다.


System.getProperty(key) - 운영체제나 JVM에 의존적인 정보를 알아낼 때 사용하는 함수로써 시스템정보를 불러옴


Example)

package com.example.taehwan.proxy_integrity;

import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.net.Proxy;
import android.util.Log;
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);
// String proxy=Settings.Secure.getString(getBaseContext().getContentResolver(),Settings.Secure.HTTP_PROXY);
if(System.getProperty("http.proxyHost")!=null) {
textview.setText(System.getProperty("http.proxyHost"));
}
else
{
textview.setText("NULL!!");
}
}
}



위 방법 외에도 Settings.Secure를 이용한 방법도 존재하지만 조금더 찾아 봐야 할 것 같다..


혹시 Settings.Secure를 이용하여 Proxy Check하는 방법을 알고 계신 분은 댓글로 

예제 부탁드립니다.

====================================================================='17.12.28추가

Settings.Secure의 HTTP_PROXY옵션은 API 17 이상부터 보안취약점 때문에 사용하지 않는다고 합니다.




+ Recent posts