1. ptrace 

 ptrace는 여러 유닉스와 유닉스 계열 운영 체제에서 발견되는 시스템 호출이다. ptrace(이 이름은 "process trace"의 축약형이다)를 통해 컨트롤러가 대상의 내부 상태를 조사하고 조작하게 함으로써, 한 프로세스가 다른 프로세스를 제어할 수 있다. ptrace는 디버거와 다른 코드 분석, 특히 소프트웨어 개발을 도와주는 도구들에서 사용된다.

[출처] 위키백과 https://ko.wikipedia.org/wiki/Ptrace


2. 목적

 본래의 목적은 안드로이드 LD_PRELOAD 환경 변수에 라이브러리명을 인젝션하여 특정 함수를 후킹하는 것

2-1) LD_PRELOAD 

 프로세스 실행 전 라이브러리 로딩 과정에서 해당 환경변수에 추가되어있는 라이브러리를 우선적으로 로딩하게 만드는 환경변수

2-2) 메인아이디어 

 zygote를 kill하게되면 init프로세스에서 zygote프로세스를 재생성하며, 이때 LD_PRELOAD환경 변수가 재로드되고, 이때 값을 조작하여 원하는 라이브러리를 인젝션한다.


2-3) 목적 달성 과정

 - zygote process kill (성공)

 - new zygote process의 pid를 알아냄 (성공)

 - new zygote process에서 실행하는 execve 실행 시점에서 환경변수 값 조작 (실패)

 - 후킹 여부 확인 (실패)


3. aarch32 vs aarch64

3-1) register갯수가 다름

 - register갯수가 대폭 추가되면서 ptrace를 이용하여 system call을 이해하기 위해 사용되는 register번호 또한 달라졌으며, 그에 따라 코드에서 사용하는 구조체 및 상수 또한 달라졌다.

 

aarch32 

aarch64 

system call number표시 reg 

 8

 IP reg

ARM_ip 

 12

 return 값 reg

reg표시 structure

pt_regs

 user_regs_struct

 syscall number 참조

 리눅스 syscall number와 동일

ndk-bundle\include\asm-generic\unistd.h


 - aarch64 레지스터 설명

r30 (LR): The Link Register, is used as the subroutine link register (LR) and stores the return address when Branch with Link operations are performed.

r29 (FP): The Frame Pointer

r19…r28: Callee-saved registers

r18: The Platform Register, if needed; otherwise a temporary register.

r17 (IP1): The second intra-procedure-call temporary register (can be used by call veneers and PLT code); at other times may be used as a temporary register.

r16 (IP0): The first intra-procedure-call scratch register (can be used by call veneers and PLT code); at other times may be used as a temporary register.

r9…r15: Temporary registers

r8: Indirect result location register

r0…r7: Parameter/result registers


3-2) zygote 프로세스

 - aarch32에서는 zygote process밖에 존재 하지 않지만, aarch64에서는 zygote / zygote64 process가 존재함 

 - aarch64에서 zygote 프로세스는 두 개지만 zygote process를 kill하면 zygote64 또한 재 실행되는 것으로 보임


3-3) zygote 프로세스 kill 후

 - 새 프로세스 생성 전 aarch32에서는 stat64 system call을 사용하여 실행 바이너리(app_process)의 정보를 얻어 오지만, aarch64에서는 fstat64 system call을 사용하여 실행 바이너리(app_process32 / app_process64)의 정보를 얻어오며, aarch64의 app_process 바이너리는 app_process64의 심볼릭 링크이다.

 - aarch32에서는 fork system call로 zygote process를 생성하지만 aarch64에서는 clone system call을 이용

 - 개인적으로 fork / clone system call의 가장 큰 차이점은 프로세스 복사(생성) 이 후 실행 할 바이너리를 경로로 넘겨주느냐(fork), file descriptor(clone)의 차이가 가장 큰 외적 차이로 보이며, 조사를 통한 내적 차이는 parent process로부터 메모리 스택을 공유(clone) / 비공유(fork)라고 생각하고, 전반적으로 clone의 system call이 fork에 비해 비교적 비용이 낮은 것으로 설명이 되어있었다.


3-4) execve system call 탐지 가능 여부

이 부분은 aarch64에서도 아마도(?) 탐지가 가능할 것이나 내 능력/자료가 부족한 것으로 생각된다.

 - ptrace를 사용하여 레지스터 값들을 조사할 때 실행되는 system call number에 execve system call에 해당하는 220번이 탐지 되지 않음

 - strace를 이용하여 값을 조사했을 때는 execve system call이 실행되는 것이 확인됨


4. aarch64에서 ptrace사용

4-1) 레지스터 값을 가져오기 위한 구조체 선언

1
2
3
4
5
6
7
8
9
10
//#include <linux/PTRACE.h>
//#include <sys/PTRACE.h>
 
//aarch64에서 달라진 구조체
struct user_regs_struct gregs;
 
//aarch64에서 추가로 필요한 부분
struct iovec iovecs;
iovecs.iov_base = &gregs;
iovecs.iov_len  = sizeof(gregs);


4-2) ptrace attatch

1
2
3
4
5
6
7
8
// step 1. init프로세스에 PTRACE attatch 
ret = PTRACE(PTRACE_ATTACH, INIT_PID, (void *)10);
if(ERROR == ret){
    printf("[ERROR] PTRACE_ATTACH failed return = %d\n", ret);        
}
else{
    printf("[INFO] PTRACE_ATTACH SUCCESS\n");    
}



4-3) 레지스터 값 얻어오기

1
2
3
4
5
6
7
8
9
10
11
// cpu 레지스터 획득
ret = PTRACE(PTRACE_GETREGSET, INIT_PID, (void *)NT_PRSTATUS, &iovecs);
        
if ( ret < 0 ) {
    printf("[ERROR] PTRACE_GETREGSET ERROR : %d\n", ret);
            
}       
else{
    printf("[INFO] PTRACE_GETREGSET SUCCESS : %d\n", ret);                
}








4-4) system call 탐지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define SYSCALL_REGISTER(r)     r.regs[8]
#define RESULT_REGISTER(r)      r.regs[0]
#define IP_REGISTER(r)          r.regs[12]
 
//gregs.regs[28]이 0이 무엇을 뜻하는 지는 알수 없으나,
//여러 번 반복해보는 과정을 통해 0인 경우일 때가 new pid가 리턴되는 것을 확인함
//그 중에서도 리턴 값이 정상인 것과 비정상인 값이 있었는데,
//정상 값을 찾기 위해 maximum pid값을 지정함(cat /proc/sys/kernel/pid_max -> 32768)
if ( 220 == SYSCALL_REGISTER(gregs) && 
      40 == IP_REGISTER(gregs)        && 
       0 == gregs.regs[28]             &&
   32768 >= RESULT_REGISTER(gregs) ) {
            printf("[*] Found a new zygote64 process: %ld\n", RESULT_REGISTER(gregs));
            
            new_zygote_pid = RESULT_REGISTER(gregs);
}



4-5) ptrace detach

1
2
3
4
5
6
7
//ptarce detach
if(PTRACE(PTRACE_DETACH, INIT_PID, (void*)10< 0) {
        printf("[ERROR] PTRACE_DETACH error\n");
}
else{
        printf("[INFO] PTRACE_DETACH SUCCESS\n");
}



1. JDB

The Java Debugger, jdb, is a simple command-line debugger for Java classes. It is a demonstration of the Java Platform Debugger Architecture that provides inspection and debugging of a local or remote Java Virtual Machine.

[출처] http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html

간단히 정리하면 로컬 혹은 원격 자바 가상머신에서 실행되는 자바 프로그램을 디버깅할 수 있는 디버거입니다.

이 JDB를 이용하여 앱을 디버깅 할 수 있습니다.

위치 : ..../JDK_HOME/bin/jdb


2. 안드로이드 앱에 JDB Attach방법

2.1 디버깅 앱 포트번호 알아내기

먼저, 안드로이드 앱에서 사용하고 있는 포트번호를 알아내야합니다. 

앱 실행 전 adb jdwp 실행 결과와 디버깅할 앱 실행 후 adb jdwp 결과를 비교하여 앱에서 실행하는 포트를 알아냅니다.

아래는 결과물을 diff하여 알아낸 창입니다.

 보통은 맨 아래에 추가가 되는 형태인 듯하네요. 예시에서 디버깅 할 앱에서 사용하고있는 포트 번호는 8160입니다.


2.2 디버깅 포트를 로컬 포트로 포워딩

adb 명령어를 통해 디버깅 포트를 로컬 포트로 포워딩해줍니다.

adb forward tcp:[로컬포트] jdwp:[디버깅포트]


2.3 jdb실행

로컬포트를 이용하여 jdb를 attach합니다.

위의 예시는 디버깅포트가 8218을 사용하고 있고, 로컬포트 11111로 포워딩 해주고 난 후

jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=11111(포워딩한 로컬포트) 를 실행하여

JDB를 Attach하였습니다.


3. 기본 JDB 명령어

명령어 

사용법 

설명 

next

next 

다음 라인 실행 

local

local 

현재 로컬 영역 변수 보기 

threads 

threads 

현재 실행 중인 스레드 확인 

methods 

methods [Class Name] 

해당 클래스가 사용하는 

메서드 출력 

stop in

stop in [method name] 

해당 메서드에 

브레이크포인트 설정 

print

print [value] 

특정 변수 값 출력 

eval 

eval [Java Code] 

자바코드를 실행시킴 

 run

run 

현재 브레이크 포인트
무시하고 실행 

[참조] 안드로이드 모바일 앱 모의 해킹(지은이. 조정원, 김명근, 조승현, 류진영, 김광수)




Drozer 기본 명령어 정리 

Drozer_command.xlsx



1. 애플리케이션 인증서란?

 1) 어플리케이션 인증서는 개발자가 어플리케이션 마켓에 등록할 때 어플리케이션의 신뢰성을 나타내기 위해 사용하는 것이다.   모든 어플리케이션은 설치되기 전에 인증서와 함께 서명되어야 한다.

 2) 안드로이드 어플리케이션 서명은 Jar서명을 수정한 것이며 Jar서명은 암호 해시 함수를 어플리케이션의 구성요소에 적용하는 방식으로 동작한다. 이때 정해진 해시들은 인증서와 함께 배포된다. 인증서는 개발자의 개인키를 이용해서 암호화가 되고, 이것은 서명된 인증서라는 것을 의미한다.


2. apk 압축해제 및 구성요소 확인

 1) apk를 zip으로 확장자를 변경한 후 압축을 해제한다.

 

  2) 공개키 인증서와 서명파일이 존재하는 META-INF폴더로 이동한다.

META-INF 폴더는 어플리케이션 무결성을 성립하는 데 도움이 되기 때문에 매우 중요한 리소스이다. 

MANIFEST.MF :  CERT.SF파일과 매우 유사한 리소스를 정의하고있다. 

CERT.SF : 이 파일은 어플리케이션 서명으로 처리되는 어플리케이션의 모든 리소스를 포함하고 있다. 추가로 Jar암호 서명 공간을 가진다.

펌-1 )

MANIFEST.MF / CERT.SF 의 차이점은 MANIFEST.MF는 각 엔트리(실제 파일)에 대한 SHA1-Digest의  Base64로 인코딩된 값을  나타내고,

CERT.SF는 MANIFEST.MF에 기입되어 있는 각 요소들 예를들어 아래와 같이 "\r\n"를 포함한다.

-----------------------------------------------------------------------------------
Name: res/drawable-xhdpi/ic_launcher.png\r\n
SHA1-Digest: 8nhRYA3Pn75x/54RW/GQ97miQkk=\r\n

\r\n

----------------------------------------------------------------------------------- 
의 Digest의  Base64로 인코딩된 값을  나타낸다.
추가로, CERT.SF의 최상단에 기입되어 있는 SHA1-Digest-Manifest는 MANIFEST.MF의 SHA1-Digest의 Base64로 인코딩된 값을 나타낸다.

///////////////////////////////////////////////

CERT.RSA : X.509 v3 인증서이고, 내부 정보는 keytool에 의해 구조화된다.

- Owner : 공개키 소유자를 정의, 소유자 관련 국가/조직에 대한 기본 정보 포함

- Issuer : 소유의 공개키와 관련된 X.509 인증서의 발급자를 정의

- Serial Number : 발행된 인증서의 식별자

- Valid from ... until : 인증서 관련 속성이 검증되는 기간

- Certificate fingerprints: 인증서의 다이제스트 합, 인증서가 변조되지 않았다는 것을 확인하는데 사용


CERT.RSA파일을 확인하려면 openssl이 설치 되어 있어야한다.

openssl pkcs7 -inform DER -in META-INF/CERT.RSA -noout -print_cert -text

내용은 위와 같다.


3. 재서명할 키스토어 생성

윈도우와 유닉스/리눅스에서 새로운 키스토어를 생성하려면 다음 명령어를 수행한다.

keytool -genkey -v -keystore [name of keystore] -alias [your_keyalias] -keyalg RSA -keysize 2048 -validity [number of days]

keytool은 키스토어에 암호를 설정하도록 도와주며 입력사항은 잊지 않아야한다. 

인증서 생성 중 위와 같은 질의사항이 있고, 과정이 끝나면 키스토어가 생성된다.


4. 생성한 키스토어로 서명

서명전에 재서명할 apk파일에서 위에서 처럼 압축 해제한 후 META-INF를 삭제하고 다시 압축, 확장자 apk로 변경의 과정을 거친다.

jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore [name of your keysotre] [your .apk file] [your key alias]


5. 재서명한 내역 확인

재서명이 제대로 이루어졌는지 확인하기 위해서 apk를 압축해제한 후 openssl로 CERT.RSA파일을 확인한다.


추가 ) jarsigner: unable to sign jar: java.util.zip.ZipException: invalid entry compressed size (expected 766 but got 770 bytes) 등의 에러 메시지가 발생 하는 경우 apk안의 META-INF폴더를 삭제한 후 서명을 시도한다.



펌-1 )의 출처 : http://shloves.tistory.com/entry/CERTSF-MANIFESTMF-%ED%8C%8C%EC%9D%BC%EC%9D%98-%EB%8B%A4%EB%A5%B8%EC%A0%90

  1. 2017.08.24 09:26

    비밀댓글입니다

 우선 안드로이드 APK 파일은 JAR 와 호환되는 형태로 되어있다. 즉, 코드를 빌드를 하여 나오는 APK  파일은 zip 포맷과 유사한 jar 와도 비슷하다. 특히 서명에 대한 방식은 JAR 와 APK 와 매우 비슷하다. 실제로 서명을 사용해 빌드된 APK 파일 확장명을  zip 으로 변경하고 압축을 풀어보면 META-INF 디렉토리 내부에 이와 관련한 파일이 생성된 것을 볼 수 있다. 

  이 파일은 MANIFEST.MF, CERT.SF, CERT.RSA 파일이다. 


  MANIFEST.MF 파일은 텍스트로 되어있으며 META-INF 디렉토리 내부의 파일을 제외한 APK 패키지 내부 모든 파일(Entry)에 대한 리스트를 보여준다. 각 리스트의 항목에는 파일 경로와 각 파일의 SHA1 해쉬 값(파일을 읽어서 만든다.)을 Base64 로 인코딩 하여 나타내고 있다. 



  CERT.SF 도 마찬가지로 텍스트로 되어있으며, MANIFEST.MF 파일과 구조가 비슷하다. 다만  MANIFEST.MF 에 등록된 각 파일에 대한 해쉬 값은 개인키(APK 빌드할 때 사용되는 인증서, 패스워드와 관련되어 있다고 생각하면 된다) 를 이용하여 SHA1 RSA 로 암호화 한 뒤에 Base64 로 인코딩 되어 있다. 또,  MANIFEST.MF 파일 자체의 위변조를 막기 위하여 MANIFEST.MF 파일의 값도 포함되어 있다. 개인키로 암호화된 값들은 공개키로 복호화할 수 있다. 




 CERT.RSA 는 공개키 인증서이며 소유자, 발행자, 일련번호, 유효기간, 인증서 지문,  알고리즘등이 표시되어 있다.  keeytool 을 이용하여 값을 확인할 수 있다.






   안드로이드 APK 패키지 파일의 서명은 기존 JAVA 에서 제공하는 JAR 파일의 서명과 동일한 방식을 사용한다. 마찬가지로 APK 파일 설치시 JAVA 에서 기본적으로 제공하는 jar 서명 검증 관련 API 를 이용하여 APK 의 무결성을 확인한다. 실제 예를 들자면 Android 내부의  PackageParser 클래스에서도 JarFile 클래스를 이용하여 APK 파일을 열고 내부의 Entry(파일) 리스트를 받아와서 InputStream  을 통하여 파일을 읽는다. 이 과정에서 내부적으로 JarVerifier 클래스가 서명 정보를 확인한다. 서명 정보를 확인할 때, CERT.SF 내부의 암호화된 값들을 공개키를 이용하여 복호화 하고 MANIFEST.MF 의 파일 해쉬 값들과 비교한다. 또, 실제 파일의 SHA1 값들과 MANIFEST.MF 내부의 값들이 일치하는지 검사한다.(순서는 확인하지 못 하였다.) 

   

  이런 과정 덕분에 안드로이드 앱은 빌드시에 사용되는 인증서가 있지 않는한 위변조된 APK 파일은 원본과 구분될 수 있다. 즉, 원본 앱을 업데이트 하는 방식으로 위변조 APK 를 설치할 수 없다. (물론 인증서를 탈취하면 이야기가 달라진다.) 그렇다고 해서 악의적으로 위변조된 APK 파일에 대한 위협이 사라지는 것은 아니다. 만약 원본과 거의 비슷한 모양새로 만들어 빌드하고 다른 인증서로 배포하면 사용자 입장에서는 속아 넘어가서 설치하고 스미싱을 당하거나, 서버와 통신하는 클라이언트 앱의 경우 서버 공격에 대한 빌미를 줄 수 있다. 


  앱이 위변조 되었는지 확인하기 위하여 불가피하게 서버와의 통신이 필요하다. (이 내용은 지금 포스팅과 시리즈로 엮어서 다음 포스팅에서 다루겠다.) 서버로 APK 파일에 대한 고유 키 값을 보내줘야할 때, 인증서 정보(인증서 지문) 만큼 확실한 값은 없다. 


  우선 APK 파일을 압축을 풀면 나오는 META_INF/CERT.RSA  파일에 대한 인증 정보를 확인하는 방법은 다음과 같다.


 keytool -printcert -file  CERT.RSA 

  

  이 명령어를 실행하면 인증서 정보(인증서 지문, Certificate Fingerprint)를 얻을 수 있다.

   

안드로이드 코드상에서 얻는 방법은 다음과 같다. 

Context context = getApplicationContext();
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
String cert = null;
try {
    PackageInfo packageInfo = packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
    Signature certSignature =  packageInfo.signatures[0];
    MessageDigest msgDigest = MessageDigest.getInstance("SHA1");
    msgDigest.update(certSignature.toByteArray());
    cert = Base64.encodeToString(msgDigest.digest(), Base64.DEFAULT);
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}

  

     

  위 코드에서 cert  변수에 이 앱을 빌드할 때 사용된 인증서의 정보 값이 들어간다. 이 값과 APK 파일의 CERT.RSA 파일로부터 'keytool -printcert -file CERT.RSA' 으로 얻은 값을 Base64 로 인코딩 하여 비교하면 동일함을 알 수 있다.  물론 해쉬 알고리즘을 SHA-256 또는 MD5 를 이용하여 요약해도 마찬가지다.  


  하지만, 서버나 외부에서 APK 파일의 인증서 정보 값을 얻을 때는 안드로이드 내부에서 얻는 코드를 이용할 수 없다. 

  그래서 다음과 같은 클래스를 만들어 보았다.   APK 파일의 인증을 검증하는 과정도 포함되어 있으며 잘못된 인증 값을 갖고 있을 경우 예외를 발생시킨다.

  테스트 완료 하였으며, 아마도 안드로이드 내부에서 다른 APK 파일 인증 정보를 읽어오는 것도 가능할 것이다. 


  (코드가 다소 길다. 읽기 쉽도록 소스 파일도 함께 첨부한다.  )

 APKCertExtractor.java



APKCertExtractor:

import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.xml.bind.DatatypeConverter; import kr.re.dev.server.APKCertExtractor.APKCertExtractionException.ErrType; /** * APKCertExtractor * * - APK 파일내의 인증 정보를 SHA1, base64 로 만들어 반환한다. * - APK 파일의 유효성을 검사한다. * - 2개 이상의 서명을 사용하는 APK 파일에 대하여 사용할 수 없다. * 하지만, Play store 에서도 2개 이상의 서명을 갖고 있는 APK 를 허용하지 않는다. * * ::사용법 * - APKCertExtractor.execute(apk 파일 경로); * - 인증 검증 실패시 APKCertExtractionException 예외 발생. * * http://dev.re.kr */ public class APKCertExtractor { public static void main(String[] args) { String cert = ""; try { cert = APKCertExtractor.execute( "test.apk"); } catch (APKCertExtractionException e) { e.printStackTrace(); cert = e.getMessage(); } System.out.println(cert); } /** * 인증서 지문을 반환한다. * @param apkFilePath APK 파일의 경로. * @return * @throws APKCertExtractionException */ @SuppressWarnings("resource") public static String execute(String apkFilePath) throws APKCertExtractionException { try { // JarFile 클래스는 java.util.zip.ZipFile 를 상속받아 구현되었다. // jar 파일에 대한 설명은 다음 블로그 페이지에 자세히 소개되어 있다. // http//www.yunsobi.com/blog/62 // 이 클래스는 jar(zip) 의 엔트리(파일 정보) 및 파일을 읽기 위하여 사용된다. JarFile jarFile = new JarFile(apkFilePath); // APK 파일 내의 AndroidManifest.xml 의 엔트리와 인증 정보를 읽어온다. JarEntry manifestEntry = jarFile.getJarEntry("AndroidManifest.xml"); if(manifestEntry == null) throw APKCertExtractionException.newInstance(ErrType.WrongAPKFormat, apkFilePath, null); Certificate[] certs = loadCertificates(jarFile, manifestEntry); if(certs == null || certs.length == 0) throw APKCertExtractionException.newInstance(ErrType.WrongCert, manifestEntry.getName(), null); Certificate cert= certs[0]; // APK 파일 내의 모든 엔트리의 인증을 검증한다. verifCertificates(jarFile, cert); // 바이트 배열 타입의 인증 정보를 SHA1 Base64로 변환. String hash; try { hash = certToSHA1(cert); } catch (CertificateEncodingException e) { e.printStackTrace(); // 인증서에 문제 있을 때 발생. throw APKCertExtractionException.newInstance(ErrType.WrongCert, apkFilePath, e); } return hash; } // 파일 경로등에 문제 있을 때 발생한다. catch (IOException e) { throw APKCertExtractionException.newInstance(ErrType.ReadFail, apkFilePath, e); } } /** * APK 내부 파일의 인증을 확인한다. * @param jarFile * @param cert */ private static void verifCertificates(JarFile jarFile,Certificate cert) throws APKCertExtractionException { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = (JarEntry) entries.nextElement(); // 서명되지 않는 디렉토리인 META-INF 는 건너뛴다. if (jarEntry.isDirectory() || jarEntry.getName().startsWith("META-INF/") || jarEntry.getName().contains(".DS_Store")) { continue; } Certificate[] certs = null; certs = loadCertificates(jarFile, jarEntry); if(certs == null || certs.length == 0) throw APKCertExtractionException.newInstance(ErrType.WrongCert, jarEntry.getName(), null); Certificate localCert = certs[0]; // 인증 정보가 없는(서명이 되지 않은) 파일 발견. // APK 압축을 풀고 임의의 파일을 넣거나 제거하여 다시 APK 파일로 압축했을 때 발생한다. if (localCert == null) { try { jarFile.close();} catch (IOException e) { e.printStackTrace(); } throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), null); } // 인증 정보가 서로 다른 엔트리 발견. // 물론 설치는 안 되겠지만, 위변조 시도되는 앱으로 의심된다. else if (!cert.equals(localCert)) { try { jarFile.close();} catch (IOException e) { e.printStackTrace(); } throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), null); } } } /** * Certificates 객체를 반환한다. * @throws APKCertExtractionException */ private static java.security.cert.Certificate[] loadCertificates(JarFile jarFile, JarEntry jarEntry) throws APKCertExtractionException { if(jarEntry == null || jarFile == null) return null; try { // JarEntry 로부터 Certificate 객체를 얻기 위해서는 JarEntry 를 검증하기 위하여 끝까지 다 읽어야 한다. // 이 과정에서 내부적으로 JarVerifier 클래스를 통하여 인증에 대한 검증이 이뤄어진다. byte[] buffer = new byte[1024]; InputStream is = jarFile.getInputStream(jarEntry); try { while (is.read(buffer, 0, buffer.length) != -1) {} // 테스트 결과 MATA-INF 폴더의 파일 내에 해당 엔트리의 인증값들은 있지만, // 실제 파일이 존재하지 않을 경우 아래 예외가 발생한다. // 역시 위변조된 앱일 가능성이 크다. // 물론 이 경우도 일반적인 안드로이드 폰에서는 설치조차 되지 않는다. } catch(SecurityException e) { throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), e); } is.close(); buffer = null; return (java.security.cert.Certificate[])jarEntry.getCertificates(); } catch (IOException e) { System.err.println("Exception reading " + jarEntry.getName() + " in " + jarFile.getName() + " " + e); } return null; } /** * 서명 깂을 SHA1 해쉬로 변경하여 Base64 로 만든 String 값으로 반환한다. * @param cert * @return * @throws CertificateEncodingException */ private static String certToSHA1(Certificate cert) throws CertificateEncodingException { byte[] certWith = null; // X509 인증 정보를 ASN.1 DER 구조의 byte 배열로 반환한다. certWith = cert.getEncoded(); MessageDigest md = null; try { // 인증 정보를 SHA1 해쉬로 변경. md = MessageDigest.getInstance("SHA1"); md.update(certWith); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return DatatypeConverter.printBase64Binary(md.digest()); } /** * 인증서 추출 예외상황. * @author ice3x2 */ public static class APKCertExtractionException extends Exception { private static final long serialVersionUID = 6796836839897143903L; private ErrType mError; protected static APKCertExtractionException newInstance(ErrType errType, String path, Throwable throwable) { String message = ErrType.toMessage(errType) + " (" + path + ")"; APKCertExtractionException apkCertExtractionException = (throwable == null)?new APKCertExtractionException(message):new APKCertExtractionException(message, throwable); apkCertExtractionException.mError = errType; return apkCertExtractionException; } private APKCertExtractionException(String message, Throwable throwable) { super(message, throwable); } private APKCertExtractionException(String message) { super(message); } /** * 에러 타입을 반환한다. * @return */ public ErrType getError() { return mError; } public static enum ErrType { /** * 잘못된 APK 파일 포맷. */ WrongAPKFormat("Wrong APK file format."), /** * APK 파일을 읽을 수 없음. */ ReadFail("APK file read failed."), /** * 잘못된 인증서. 파일에 대한 인증 정보가 존재하지 않는다. */ WrongCert("Wrong certificate. Certificate verified failed."), /** * 위변조가 의심되는 APK. 인증 정보 검증에 문제가 있다. */ ForgeryAPK("Wrong certificate. This package is suspected with forgery apk."); private String message; private ErrType(String value) { message = value; } protected static String toMessage(ErrType errType) { return errType.message; } } } }



출처 : Dev.re.kr (http://dev.re.kr/70)

안드로이드 최신 버전의 SDK에서 pm명령어 모음입니다. adb shell로 접근하여 얻을 수 있는 명령어입니다. adb shell 접근 방법은 아래에서 설명하겠습니다.

명령어를 통해 앱을 설치하거나, 삭제하고, 사용자를 추가하고, 삭제 할 수 있는 명령어도 있습니다. 현재 기본값으로 셋팅 된 설치 경로를 가져오는 get-install-location 도 있습니다. 일반적으로 [0/auto]가 설정되어 있습니다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
usage: pm list packages [-f] [-d] [-e] [-s] [-e] [-u] [FILTER]
       pm list permission-groups
       pm list permissions [-g] [-f] [-d] [-u] [GROUP]
       pm list instrumentation [-f] [TARGET-PACKAGE]
       pm list features
       pm list libraries
       pm path PACKAGE
       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH
       pm uninstall [-k] PACKAGE
       pm clear PACKAGE
       pm enable PACKAGE_OR_COMPONENT
       pm disable PACKAGE_OR_COMPONENT
       pm disable-user PACKAGE_OR_COMPONENT
       pm set-install-location [0/auto] [1/internal] [2/external]
       pm get-install-location
       pm createUser USER_NAME
       pm removeUser USER_ID
  
pm list packages: prints all packages, optionally only
  those whose package name contains the text in FILTER.  Options:
    -f: see their associated file.
    -d: filter to only show disbled packages.
    -e: filter to only show enabled packages.
    -s: filter to only show system packages.
    -3: filter to only show third party packages.
    -u: also include uninstalled packages.
  
pm list permission-groups: prints all known permission groups.
  
pm list permissions: prints all known permissions, optionally only
  those in GROUP.  Options:
    -g: organize by group.
    -f: print all information.
    -s: short summary.
    -d: only list dangerous permissions.
    -u: list only the permissions users will see.
  
pm list instrumentation: use to list all test packages; optionally
  supply <target-package> to list the test packages for a particular
  application.  Options:
    -f: list the .apk file for the test package.
  
pm list features: prints all features of the system.
  
pm path: print the path to the .apk of the given PACKAGE.
  
pm install: installs a package to the system.  Options:
    -l: install the package with FORWARD_LOCK.
    -r: reinstall an exisiting app, keeping its data.
    -t: allow test .apks to be installed.
    -i: specify the installer package name.
    -s: install package on sdcard.
    -f: install package on internal flash.
  
pm uninstall: removes a package from the system. Options:
    -k: keep the data and cache directories around after package removal.
  
pm clear: deletes all data associated with a package.
  
pm enable, disable, disable-user: these commands change the enabled state
  of a given package or component (written as "package/class").
  
pm get-install-location: returns the current install location.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media
  
pm set-install-location: changes the default install location.
  NOTE: this is only intended for debugging; using this can cause
  applications to break and other undersireable behavior.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media
</target-package>

 

 

이 중 가장 많이 사용하는 명령어가 앱을 강제로 SD카드로 이동하는 명령어 일 겁니다. 해당 명령어도 아래와 같이 변경되었습니다.

설치 경로의 기본 값을 가져오는 명령어와 설정하는 명령어입니다.

 

pm get-install-location //기본값으로 지정된 설치 경로를 불러옵니다.
pm set-install-location 0/1/2 //강제로 경로를 설정합니다.

 

 

[adb를 이용한 apk 파일 추출 및 인스톨]

 

c:\> adb shell


# pm list packages -f

...

package:/system/app/SettingsProvider.apk=com.android.providers.settings
package:/system/app/TtsService.apk=android.tts
package:/system/app/Mms.apk=com.android.mms << 이걸 가져오고 싶으면...
package:/system/app/MediaProvider.apk=com.android.providers.media
package:/system/app/CertInstaller.apk=com.android.certinstaller
package:/system/app/DownloadProvider.apk=com.android.providers.downloads

...

 

# exit

 

c:\>adb pull /system/app/Mms.apk .

adb pull (가져올 파일 경로/파일명.apk) (받을 경로/파일명.apk)




출처 : 코드 클리핑 (http://www.dreamy.pe.kr/zbxe/CodeClip/163974)

+ Recent posts