보안/android

[앱][리버싱] 7주차 정리+과제 ; frida를 이용한 rooting 탐지 우회

y00&z1 2021. 10. 28. 15:39

7주차

앱 후킹 도구 frida 사용하여 rooting 탐지 우회하기

 

frida ? DBI(Dynamic Binary Instrumentation) 프레임워크(도구들의 집합)

; A -> B 함수 호출하는 시점에 어떤 코드를 실행하고 싶어..! (바이너리 실행 중에 분석, 수정함)

 

 

어디까지 탐색했는지 알아야 하니까 -> 보통 함수 사이사이에 실행 주소를 측정함

앱 실행되는 사이사이에 자바스크립트 삽입 가능

 

 

+) DBI에 대한 추가 자료

https://code13.tistory.com/267

 

DBI(Dynamic Binary Instrumentation) 겉핥기로 알아보기

DBI (Dynamic Binary Instrumentation) printf("test %s", buf); 등과 같이 특정 시점에서의 변수값 등을 확인하거나 프로그램의 행위를 조사하는 일을 Instrumentation 라고 부른다 바이너리 분석을 할때 각종..

code13.tistory.com

 

 

 


[Frida 주요 기능]

 

-함수 후킹? 함수를 훔쳐간다/채간다
-> 함수 실행되는 시점에 잡아가서 원하는 코드 실행/함수 재작성


-adb(애플리케이션 디버깅 가능)

-힙 메모리 내 객체 인스턴스 검색 및 사용

-실시간 트래픽 스니핑 또는 암호 해독

-탈옥/루팅 되지 않은 단말기에서도 사용 가능. (ios, 맥os에서도 가능!)

 

 

 

[동작 방식]


host == me (frida server랑 연결되어있는 pc)
frida server == 에뮬레이터 안에서 돌고 있는 서버! 

->drozer와 유사

+) host용은 pc에 설치, 서버용은 에뮬레이터에 설치
rooting 탐지를 하려면 rooting이 된 상태에서 해야 됨!

 

 


[Frida Tools]

  • frida 
  • frida-ps (frida에 연결된 프로세스 목록 출력)
  • frida-ls-devices (연결된 디바이스 목록 출력)
  • frida-trace (함수 호출 동적으로 추적)
  • frida-kill (프로세스 종료)

 

 

 


[frida-script ]

:frida에서 제공하는 스크립트 API(javaScript, C, SWIFT API)

-> 자바스크립트로 API 재정의하거나 우리가 원하는 방향으로 수정 가능

 

 

https://frida.re/docs/javascript-api/

 

JavaScript API

Inject JavaScript to explore native apps on Windows, macOS, GNU/Linux, iOS, Android, and QNX

frida.re

ㄴ> javascript API

-기본 뼈대 구조 

java.perform(function() { /*

...

*/

} );

 

-Java.use(className)

 

var myClass = Java.use(com.mypackage.name.class)
//앱에서 사용하는 클래스와 연동되는 myclass 정의하기

var myClassInstance = myClass.$new()
//myClass를 통해 객체 인스턴스 생성 및 정의

var result = myClassInstance.myMethod("param")
//클래스 내부에 있는 메소드에 접근 -> 인자 값을 넘겨주고 해당 결과 값을 result에 받음

myClass.myMethod.implementation = function(param) {
//앱에서 정의된 메소드의 구현 내용을 재작성
}

 

 

 

**메소드 재구현시 주의점**

  • 입력받은 인수가 없는 메소드
  • 두 개의 바이트 배열 인수로 입력받는 메소드
  • context, boolean 형태의 인수로 입력받는 메소드

-> 위 세 가지 경우에는 overLoad() 사용하여 재구현 필요!! 

 

 

 


[Frida 실습]

#환경 구축

 

#rooting 탐지

테스트 앱 실행 -> "root detected"  : 우회해서 정상적으로 실행하는 것이 목표! 


루팅? 루트 권한을 가져가는 것


[루팅 탐지 7가지]

  • su 탐지 ; su 명령어의 바이너리 파일 검색을 통해 검사
  • 프로세스 리스트 탐지 
  • 설치된 패키지 목록 탐지
  • 루팅 카운트 탐지
  • busybox 및 명령어 탐지 ; 안드로이드 제공 커널 이외에,  원래 adb 쉘에서 제한적인 명령어가 루팅 후에는 가능한 명령어 많음
  • build.prop 파일 기본값 확인
  • 폴더 권한 확인

->이러한 탐지 방법들을 우회해야 함

; 루팅을 하면 바뀌는 파일이 있고, 파일 권한이 바뀌거나, 프로세스 생성함 


==> 보통 smali 코드 패치 or 후킹으로 우회. ( 이번 실습에서는 코드는 그대로 두고 후킹으로 우회)

 

https://blog.kinesis.kr/139

 

루팅 탐지 및 우회

안드로이드 루팅의 탐지와 루팅 탐지의 우회의 가능여부에 대해 요약하면 다음과 같다. 가능하지만 절대적이지 않고, 지금은 가능하나 차후에 막힐 수 있고, 지금은 막혔으나 차후에 가능할 수

blog.kinesis.kr

 

 

 

#실습과정

목표 : frida 활용하여 테스트 앱 루팅 탐지 기능 우회하기 

 

1. 환경 구동 (frida 서버 실행)

$activate py3

$adb devices

$adb shell 

 

 

2. 디컴파일 & 분석
>d2j-dex2jar.bat C:\Users\yoon2\Downloads\UnCrackable-Level1.apk

분석 : 우회할 부분을 가장 빨리 알 수 있는 것 ->  Root로 검색!
mainActivity에서 발견

 

  ...
  protected void onCreate(Bundle paramBundle) {
    if (c.a() || c.b() || c.c())
      a("Root detected!");
      ....

=> 후킹 지점

  • 메시지가 떴을 때
  • ok를 눌렀을 때(가장 최종적인 부분..?)

-> System.exit(0); 부분을 재정의해서 실행하기

 

 

3. 우회 스크립트 작성

 

setImmdeiate(function(){ //여기서 실행
	Java.perform(function() {
    /*
		var exit_bypass = Java.use("android.content. DialogInterface.OnClickListener")
        // android onclincklinstner 검색 -> android api dialogInterface onclickListener !!
        
		exit_bypass.onClick.implementation = function(arg1, arg2) {  //인수 타입은? js는 타입정의 없어서 알아서 맞춰줌
			console.log("[*] Exit bypass");
			}
	*/
    
		var exit_bypass = Java.use("android.content.DialogInterface.OnClickListener");
		// 인터페이스를 받아와서 사용할 수 있는 Frida API가 존재하지 않음. 따라서 System.exit(0)을 사용
		// (Java.lang.System의 exit 메소드를 재작성)
		exit_bypass.onClick.implementation = function(arg, arg2) {
			console.log("[*] Exit Bypass");
			}
	)}
)}

 

 

frida에서 인터페이스 받아오지 못한다. (onClickListener)
-> exit()을 재정의 해야 함!!


4. 결과 확인

>frida -U --no-pause -l [테스트 스크립트. js] -f [테스트 apk]

 

★★apk명 말고 패키지명!!! 
-> UnCrackable-Level1 말고 owasp.mstg.uncrackable1 입력해야 한다. ★★

※ 패키지명 어떻게 알아내는지 ? adb로 들어가서 패키지명 검색하는 명령어 입력하기.  
$pm list packages -f | grep crack 




+) 자꾸 terminated 되는 현상? 

frida.* 탐지해서 이름 바꿔서 실행 / nox 다운그레이드 /  busybox 설치 확인 하기 

 

 

 

 

 

 

 

 


과제

✔️ success 메시지 출력될 수 있는 입력 값 찾기

1. 환경 구동

에뮬레이터 구동 후 adb shell 명령어 입력 후 입력

 

2. 디컴파일 & 분석 

MainActivity에서 입력 값 검증하는 함수 발견. 

-> if문에서 a클래스에 있는 a함수를 호출한 뒤 반환 값에 따라 메세지를 출력하고 있다.

 

 

uncrackable1패키지의 a 클래스를 따라 살펴보면, 

 

a 함수에서 입력 받은 값과 arrayOfByte를 equal 함수로 비교하여 boolaen 값을 리턴하고 있다. 

b 함수는 arrayOfByte 값을 매핑하는 과정에서 포함되는 함수이다. 

여기서 a 함수에 인자 두 개를 전달하고 있는데, this 클래스의 a 함수가 아님을 알 수 있다. 

 

즉, 

sg.vantagepoint.uncrackable1 패키지의 a클래스의 a 함수에서, (<-현재 분석중인 부분) 

sg.vantagepoint.a 패키지의 a 클래스의 a 함수를 호출하고 있다. 

 

a.a.a 함수를 찾아보면, 암호화 관련 함수로 보인다. 

 

==>  uncrackable1.a.a 함수의 리턴 값을 무조건 true로 후킹해주면 되지 않을까? 

 

메소드를 어떻게 찾는지????????!! 

-> API가 아닌 일반 메소드라서 따로 내부적으로 찾아야하는 줄 알았는데, 디컴파일한 패키지명+메소드명 그래도 입력해도 된다. 

 

+) 다른 방법론은 찾다가 해본 작업 (후킹하는데는 필요없는 작업이었다.)

-> 저번 과제와 비슷한 과정으로 다시 앱 분석 해보기! 

 

1) apk 내부 파일 추출>apktool.bat d UnCrackable-Level1.apk     

 

2)\res\values에서 필요한 값 찾기 

2130837505 -> 7F020001(hex값으로 변환)

 

\res\values public.xml 파일에서 해당 값 검색

 

 

\res\values strings.xml  파일에서 thanks  이름 가진 값 찾기

With special thanks to Bernhard Mueller for creating the app. Now maintained by the MSTG project. Want more? Check the MSTG playground! 

 

 

 

3. 우회 스크립트 작성


[1] equals 함수 후킹 - 구글에 android api equals  검색! -> "java.lang.String" 

argu 전달이랑 log에 argu 프린트
매개변수 잘 보기!!
but, log 너무 많아서 찾기 어려움 (하나하나 입력해야 됨)

 


[2] 복호화된 평문 프린트 하기  - sg.vantagepoint.a.a a함수 재정의하기 

 바이트 배열로 반환되기 때문에 아스키 코드 변환하여  출력 필요. 

 

setImmediate(function() { // 바로 다음을 실행
	Java.perform(function() { // 스레드가 연결되어 있으면 아래의 코드를 실행

			var exit_bypass = Java.use("java.lang.System");
			// 인터페이스를 받아와서 사용할 수 있는 Frida API가 존재하지 않음. 따라서 System.exit(0)을 사용
			// (Java.lang.System의 exit 메소드를 재작성)
			exit_bypass.exit.implementation = function() {
				console.log("[*] Exit Bypass");
			}
	});


//[1]번방법
	//equals 함수를 검색했을 때 처음에 -> java.lang.Object 로 나왔는데, 
    //java.lang.String가 맞다. String 객체 전달하고 있는 것을 코드에서 찾을 수 있다. 

	//이부분에 string 알아내는 코드 추가! setImmediate 안에만 넣어주면 된다. 
	// Java.perform(function() {
	// 	var equalsFunc = Java.use("java.lang.String");
	// 	equalsFunc.equals.implementation = function(arg1) {
	// 		console.log(arg1);
	// 		return true;
	// 	}

	// })

//[2]번 방법
		Java.perform(function() {
		var pw = Java.use("sg.vantagepoint.a.a");
		pw.a.implementation = function(arg1, arg2) {
			var secret_text = this.a(arg1, arg2);
			var tmp = '';
			console.log("\n[*]Decode before : " + secret_text);

			for (var i=0; i<secret_text.length; i++) {
				tmp += String.fromCharCode(secret_text[i]);
			}
			console.log("\n[*]Decrypted : " + tmp);
			return secret_text;
			
		}

	})

});

 

스크립트 작성 완료 후에 cmd 창에 명령어 입력하기 

$ frida -U --no-pause -l test.js -f owasp.mstg.uncrackable1 

-> 콘솔 창에 평문이 출력된다.