Everything is hacked.

There is no 100 % security.

Tools/Frida

FridaLab - Challenge

Kai_HT 2023. 8. 30. 16:23

 

https://www.google.com/url?sa=i&url=https%3A%2F%2Fyasoob.me%2Fposts%2Freverse-engineering-nike-run-club-using-frida-android%2F&psig=AOvVaw0ZaOhgUGVxdCt-tPGVnQ79&ust=1693466744207000&source=images&cd=vfe&opi=89978449&ved=0CBIQjhxqFwoTCODR9K_tg4EDFQAAAAAdAAAAABAE

 

FridaLab

I was struggling with a recent test using frida, knowing it could do what I want but unsure how. After lots of googling and trial and error I eventually got it working. So I decided

rossmarks.uk

FridaLab apk Files

 

[apk hooking tutorial] FridaLab 으로 frida 익히기

FridaLab란? https://rossmarks.uk/blog/fridalab/ 에서 제공되는 Frida를 익히기 위한 Challenge APK입니다. FridaLab I was struggling with a recent test using frida, knowing it could do what I rossmarks.uk apk파일은 위 링크에서 다운로

dev.exd0tpy.xyz

풀이참조

Application Index Display

 

FridaLab.apk
Check 버튼 클릭 시, 챌린지 클리어 : 초록색, 미해결 챌린지 : 빨간색


Challenge 01

EN :
Change class challenge_01's variable 'chall01' to: 1

KOR :
클래스 challenge_01의 변수 'challenge01'을 1로 변경해라.

 

우선 문제들을 풀기 위해선 해당 apk파일 (FridaLab.apk) 을 디컴파일할 필요가 있다. 챌린지 클리어를 위해 디컴파일 툴인 jdax 툴을 이용하여 디컴파일을 수행하였다.

 

GitHub - skylot/jadx: Dex to Java decompiler

Dex to Java decompiler. Contribute to skylot/jadx development by creating an account on GitHub.

github.com

 

Windows Choco Install : choco install jadx

먼저 이번 챌린지는 challenge01 에 대한 문제이므로, 해당 객체를 찾아보았다.

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.challenge01

challenge_01 문제의 의도는 apk 파일 내부 클래스 변수에 대한 값 변조가 가능하냐 묻는 문제이다. 챌린지 해결을 위해서 해야할 일은 challenge_01 이라는 클래스 내부에 존재하는 chall01 의 값을 1로 변경해주는 것이다.

챌린지 해결을 위해 python 으로 해당 변수를 조작하는 코드를 짜서 실행하였다.

# Frida 함수 사용을 위한 프리다 선언 
import frida

# Java.use 를 통해 해당 어플리케이션 내부 클래스를 변수로 가져오기
## Java.use(classname) 은 특정 클래스를 로드하는 명령       
                              
jscode ="""
Java.perform(function() {
    var challenge_01 = Java.use('uk.rossmarks.fridalab.challenge_01');

// chall01값 1로 선언
    challenge_01.chall01.value = 1;
    });
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()

challenge_01.py

해당 코드 실행 이후, 기기의 CHECK 버튼 클릭시 1번 문제가 초록색으로 바뀌는 것을 확인할 수 있다.


Challenge 02

EN :
Run chall02()

KOR :
chall02() 실행해라.

문제 그대로 chall02() 함수를 실행시키면 풀리는 챌린지다. 그럼 FridaLab 에서 chall02() 가 뜻하고 있는 것이 무엇인가 확인해보자.

 

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.MainActivity → chall02()

 

안드로이드 UI 구성을 위해 가장 기본이 되는 요소는 바로 Activity 라는 것 이다. 이 요소는 안드로이드 어플리케이션을 화면 UI 로 표시하기 위해선 최소 1개 이상을 가져야 하고, 사용자가 원하는 특정 어플리케이션 실행 시 지정된 Activity 를 실행해 사용자에게 UI를 표시해준다. 그 중에서 현재 FridaLab.apk 에 존재하는 chall02() 가 위치한 MainActivity 는 어플리케이션 실행 시 최초로 보여지는 Activity 다. (안드로이드 스튜디오에서 생성되는 템플릿 코드로 어플리케이션 실행 시 최초 실행하도록 기본 지정됨)

다시 문제로 돌아가 chall02() 를 살펴보자. 우선 해당 클래스 변수는 MainActivity 에 위치하여 선언되어 있으나, 다른 내부 어디에서도 호출되지 않고 있다.

추가적으로 1번과는 다르게 해당 함수는 static-인스턴스를 사용하지 않음 -함수가 아니라서 바로 불러서 사용할 수 없다. (use() 함수) 그렇기에 1번에서 사용했던 use() 함수가 아닌 Choose() 함수를 이용해 인스턴스를 호출해야 한다.

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용
jscode = """
Java.perform(function() {

    var main;

		// Java.choose 함수를 이용한 인스턴스 호출
    Java.choose('uk.rossmarks.fridalab.MainActivity', {
    onMatch: function(instance) {
        main = instance;
        },
    onComplete: function() {}
    });
    main.chall02();

    });
    """

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
process =frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()

Challenge_02.py

해당 코드 실행 이후, 기기의 CHECK 버튼 클릭시 2번 문제가 초록색으로 바뀌는 것을 확인할 수 있다.


Challenge 03

EN :
Make chall03() return true

KOR :
chall03() 를 true 로 반환해라.


이번 챌린지의 경우, chall03() 의 반환값을 true로 변환하면 되는 문제이다. 그렇기에 challenge_01 문제와 비슷하게 해당 값에 대한 변경이나 선언만 해주면 풀릴 것이라 예상하였다.

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.MainActivity → chall03()

challenge_03 에 해당하는 chall03MainActivity 내부에 선언되어있고, 본래 false 로 선언한다. 해당 값을 변경시켜주기 위해선 overload 라는 것을 진행해야 하는대, 해석 그대로 중복정의를 true 라는 값으로 해주는 것이라 생각했다.

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """ 
Java.perform(function() {
    var main;

		// Java.choose 함수를 이용한 인스턴스 호출
    Java.choose('uk.rossmarks.fridalab.MainActivity', {
    onMatch: function(instance) {
        main = instance;
        },
    onComplete: function() {}
    });

// overload 함수를 이용한 chall03 반환 값 변경
main.chall03.overload().implementation = function() {
			return true;
			};
});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = proces.create_script(jscode)
script.load()
input()

Challenge_03.py

해당 코드 실행하고 기기의 CHECK 버튼 클릭시 3번 문제가 초록색으로 바뀌는 것을 확인할 수 있다. 이때 초록색으로 변경이 완료되면 후킹코드 실행창에서 exit 눌러 종료시켜주자.


 

Challenge 04

EN :
Send "frida" to chall04()

KOR :
chall04() 로 "frida" 를 보내라.

 

2번 문제와 마찬가지로 main.chall04() 를 호출 한 뒤, 인자로 "frida" 를 입력하면 된다 생각하였고, 이전 코드에서 main.chall04("frida") 를 입력해 해당 챌린지를 풀어내었다. chall04 를 확인하면 이전 3번과 마찬가지로 MainActivity 내부에 존재하며, if문으로만 이루어져 있기 때문에 문자열을 입력하는 것으로 풀이하면 된다.

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.MainActivity → chall04()

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """ 
Java.perform(function() {
    var main;

		// Java.choose 함수를 이용한 인스턴스 호출
    Java.choose('uk.rossmarks.fridalab.MainActivity', {
    onMatch: function(instance) {
        main = instance;
        },
    onComplete: function() {}
    });
		
		// chall04() 문자열로 "frida" 입력
		main.chall04("frida")
});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = proces.create_script(jscode)
script.load()
input()

challenge_04.py

해당 코드 실행하고 기기의 CHECK 버튼 클릭시 4번 문제가 초록색으로 바뀌는 것을 확인할 수 있다. 이 챌린지도 초록색으로 변경이 완료되면 후킹코드 실행창에서 exit 눌러 종료시켜주자.


Challenge 05

EN :
Allways send "frida" to chall05()

KOR :
chall05() 로 지속적으로 "frida" 를 보내라.

chall05() 의 인자가 항상 frida가 되어야한다. chall04 에선 한번만 입력해주면 되어야 하기 때문에 일반적으로 선언만 해주었지만 MainActivity 내 선언된 chall05() 를 확인하게 되면 실행할 때 마다 확인하는 것을 볼 수 있다. 그렇기 때문에 항상 chall05("frida") 를 호출되게 해야한다.

 

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.MainActivity → chall05()

 

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """ 
Java.perform(function() {
    var main;

		// Java.choose 함수를 이용한 인스턴스 호출
    Java.choose('uk.rossmarks.fridalab.MainActivity', {
    onMatch: function(instance) {
        main = instance;
        },
    onComplete: function() {}
    });
		
		// overload 함수를 이용한 string 문자열 -> chall05로 오버로드 후
		// 메소드 내부 chall05("frida") 호출
		main.chall05.overload('java.lang.String').implementation = function (arg0) {
			this.chall05.overload('java.lang.String').call(this.'frida');
			return ;
		};
});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = proces.create_script(jscode)
script.load()
input()

challenge_05.py

해당 코드 실행하고 기기의 CHECK 버튼 클릭시 5번 문제가 초록색으로 바뀌는 것을 확인할 수 있다. 이 챌린지도 초록색으로 변경이 완료되면 후킹코드 실행창에서 exit 눌러 종료시켜주자.


Challenge 06

EN :
Run chall06() after 10 seconds with correct value

KOR :
10초 후 올바른 값으로 chall06() 실행해라

챌린지에 대한 설명을 보기 전에 우선 해당하는 코드를 보자.

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab → challenge_06

우선 chall06()MainActivity 에서 int형 인자를 challenge_06.confirmChall 에 전달하여 confirmChall06(i) 의 결과가 True 일 경우, 해결된다.

때문에 해당 문제 해결을 위해 따로 선언되어있는 challenge_06 을 확인해보면 confirmChall06 메소드는 인자를 받아 현재 시간이 timeStart+10000 보다 클 경우 인자와 chall06 의 값이 동일해야한다.

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.MainActivity → onCreate, startTime

MainActivityOnCreate() 함수는 challenge_06.startTime() 을 구하게 되고, Timer() 을 새로 설정, 1초가 지날 때마가 무작위(랜덤)로 나온 수를 chall06 에 더해주게 된다. (addChall06) 그렇기에 이번 챌린지를 풀기 위해서 chall06 값을 알아야한다.

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """ 
Java.perform(function() {
		// 해당 객체 가져오기
    var challenge_06 = Java.use('uk.rossmarks.fridalab.challenge_06')
		
		// overload 함수를 이용한 해당 값 출력하기
		challenge_06.addChall06.overload('int').implementation = function (arg0) {
				console.log(this.chall06.value);
				challenge_06.addChall06.overload('int').call(this, arg0);
				return ;
		};
});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
input()

Challenge_06-data.py

Challenge_05 와 마찬가지로 chall06 값을 증가시키는 addChall06 메소드를 오버라이드 시킨 다음, 본 함수를 호출하며 chall06 값을 출력하도록 짠 코드이다. 때문에 해당 코드를 실행시키고 난 이후, 앱을 종료하고 키게 되면 1초마다 숫자가 출력하게 된다.

Challenge_06-data.py Running Display

해당 값에 대한 확인이 끝났으니 10초뒤에 chall06() 의 인자로 전달하면 된다. 원래대로 하면 10초 이후를 계산해야 하나, 간단한 풀이를 위해 addChall06 이 호출될 때마다 현재 chall06 값을 전달하는 코드로 작성하게 되었다.

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """
		java.perform(function() {
				var main;
				Java.choose('uk.rossmarks.fridalab.MainActivity', {
				onMatch: function(instance) {
						main = instance;
				},
				onComplete: function() {}
		});

		// 해당 객체 가져오기
		var challenge_06 = Java.use('uk.rossarks.fridalab.challenge_06')
		challenge_06.addChall06.overload('int').implementation = function (arg0) {

				// overload 함수를 이용한 해당 값 출력하기
				console.log(this.chall06.value);
				challenge_06.addChall06.overload('int').call(this, arg0);

				// chall06 값 전달
				main.chall06(this.chall06.value)
				return ;
		};
});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
input()

Challenge_06.py

스크립트 실행 이후 10초가 지난 뒤에 CHECK 버튼을 클릭하면 챌린지가 클리어 된다.


 

Challenge 07

EN :
Bruteforce check07Pin() then confirm with chall07()

KOR :
check07Pin()에 무작위대입을 하고 난 다음, chall07() 로 확인해라

 

이전 챌린지들과 다르게 해당 문제는 chall07() 에 대한 값이 아닌 check07pin() 을 무작위대입을 해야한다.

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab.MainActivity → chall07

 

Location : FridaLab.apk → Source code → uk.rossmarks.fridalab → challenge_07

setChall07() 을 통해 랜덤으로 1,000 부터 9,999 까지 수를 chall07 에 저장한다. 그렇기에 check07Pin() 으로 chall07 과 인자를 비교해볼 수 있다.

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """

// // Java.choose 함수를 이용한 인스턴스 호출
Java.perform(function(){
		var main;
		Java.choose('uk.rossmarks.fridalab.MainActivity', {
		onMatch: function(instance) {
				main = instance;
		},
		onComplete: function() {}
});

// challenge_07 인스턴스 사용, check07Pin 인자로 return 값 True 로
var challenge_07 = Java.use('uk.rossmarks.fridalab.challenge_07')
for (var i = 1000 ; i < 10000 ; i++ ) {
		if (challenge_07.check07Pin(i.toString())){
				main.chall07(i.toString())
				break;
				}
		}

});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
input()

Challenge_07.py

반복문인 for 문을 이용해 1,000 부터 9,999 까지 반복 생성 후, check07Pin 의 인자로 넘겨 return 이 True가 되어 main chall07 을 호출하는 형태로 해당 챌린지 클리어가 가능하다.


 

Challenge 08

EN :
Change 'check' button's text value to 'Confirm'

KOR :
'check' 버튼의 텍스트 값을 'Confrim' 으로 바꿔라

 

생각 외로 간단한 문제인데, 이 챌린지의 해결을 위해선 안드로이드 UI 함수를 찾아봐야한다.
안드로이드 UI 함수는 Main UI Thread 에서 동작하게 되며, 이 스레드는 안드로이드 시스템 내에서 어플리케이션 프로세스를 실행해주는 역할을 하게 된다. 이는 안드로이드 시스템 내 생성된 Main UI thread 는 화명 구성에 관한 역할을 담당하게 되고 버튼이나 체크박스, 텍스트 뷰 등 UI도구 키트 구성요서를 생성이나 조작하였을 경우 상호작용의 동작을 하게 된다.
때문에 Main Thread 혹은 UI Thread 라고도 하게 된다.

 

 

Activity  |  Android Developers

 

developer.android.com

 

 

안드로이드 메인 스레드(Android Main Thread 또는 UI Thread)

안드로이드 어플리케이션이 실행되면 안드로이드 시스템은 하나의 실행 스레드로 어플리케이션의 프로세스를 실행합니다. 어플리케이션의 구성 요소가 생성될 때 별도의 스레드가 생성되는

codetravel.tistory.com

개념참고

Location : FridaLab.apk &rarr; Source code &rarr; uk.rossmarks.fridalab.MainActivity &rarr; chall08
Location : FridaLab.apk &rarr; Source code &rarr; uk.rossmarks.fridalab.R.id &rarr; check

해당 챌린지 해결을 위해선 id 를 통해 view 를 불러와서, button 으로 캐스팅 한 뒤, setText 로 버튼의 텍스트를 수정할 수 있다.

# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용

jscode = """

// Java.choose 함수를 이용한 인스턴스 호출
Java.perform(function(){
		var main;
		Java.choose('uk.rossmarks.fridalab.MainActivity', {
		onMatch: function(instance) {
				main = instance;
		},
		onComplete: function() {}
});

// 'check' 에 해당하는 id, 버튼 (객체), 체크박스, 체크 버튼 불러온 후 변경
		var id  = main.findViewById(2131165231);
		var button = Java.use('android.widget.Button');
		var checkButton = Java.cast(id, button);
		var string = Java.use('java.lang.String');
		checkButton.setText(string.$new('Confirm'));
});
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
input()

Challenge_08.py

$new 는 java 에서 new 와 같이 생성자를 부르게 된다.

 

FridaLab_Challenge-Oneque
# frida 함수 사용을 위한 프리다 선언
import frida

# Java.choose 를 통해 해당 어플리케이션 내부 instance 호출
## Java.choose(classname) 는 메소드가 정적이지 않을 경우, 이용
jscode = """

Java.perform (function() {
    // Challenge_01
    var challenge_01 = Java.use('uk.rossmarks.fridalab.challenge_01');
    challenge_01.chall01.value = 1;

    // Challenge_02
    var main;
    Java.choose('uk.rossmarks.fridalab.MainActivity', {
        onMatch: function(instance) {
            main = instance; 
    },
    onComplete: function() {}
    });

    main.chall02();

    // Challenge_03
    main.chall03.overload().implementation = function() {
        return true;
        };

    // Challenge_04
    main.chall04("frida");

    // Challenge_05
    main.chall05.overload('java.lang.String').implementation = function (arg0) {
        this.chall05.overload('java.lang.String').call(this,'frida');
        return ;
    };

    // Challenge_06
    var challenge_06 = Java.use('uk.rossmarks.fridalab.challenge_06')
    challenge_06.addChall06.overload('int').implementation = function (arg0) {
        console.log(this.chall06.value);
        challenge_06.addChall06.overload('int').call(this,arg0);
        main.chall06(this.chall06.value)
        return ;
        };

    // Challenge_07
    var challenge_07 = Java.use('uk.rossmarks.fridalab.challenge_07')
    for (var i = 1000; i < 10000; i++) {
        if (challenge_07.check07Pin(i.toString())) {
            main.chall07(i.toString());
            break;
        }
    }

    // Challenge_08
    var id = main.findViewById(2131165231);
    var button = Java.use('android.widget.Button');
    var checkButton = Java.cast(id, button);
    var string = Java.use('java.lang.String');
    checkButton.setText(string.$new('Confirm'));

    });
"""

# 프로세스 변수로 frida usb device (연결된 디바이스) 실행 
## input() 으로 버튼을 누르기 전까지 해당 함수 유효 유보
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
input()

'Tools > Frida' 카테고리의 다른 글

Frida SSL Pinning  (0) 2023.08.24
Frida - Application Dump  (0) 2023.08.22
Frida-iOS-DUMP IPA  (0) 2023.08.22
Frida BASIC  (0) 2023.08.22
Frida ERROR - SyntaxError: unexpected character  (0) 2022.08.25