Frida로 APK 분석을 시작할 때 해당 툴에 익숙해지도록 거쳐가면 좋은 튜토리얼 앱.
1번부터 8번까지의 문제가 있으며 클리어 후 check를 하면 빨간 글자에서 초록 글자로 변환됨.
먼저 jadx로 fridalab.apk를 decompile 해보면 아래와 같다.

static int chall01; 로 선언된 변수를 후킹을 통해 1로 변경.
import frida
jscode = """
Java.perform(function () {
var challenge_01 = Java.use("uk.rossmarks.fridalab.challenge_01");
challenge_01.chall01.value = 1;
});
"""
process = frida.get_usb_device().attach("FridaLab")
script = process.create_script(jscode)
script.load()

chall02()를 실행하라고 해서 보니 MainActivity에 private void chall02() 함수가 존재함. 내부적으로는 어디에서도 호출하지 않는 함수이며 static으로 선언되어 있지 않아 Java.use()의 사용은 불가능.
import frida
jscode = """
Java.perform(function(){
var main;
Java.choose('uk.rossmarks.fridalab.MainActivity', {
onMatch: function(instance) {
main = instance;
},
onComplete: function() {}
});
main.chall02();
});
"""
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
Java.use()의 경우 프록시 객체(proxy object)를 반환
Java.choose()의 경우 객체 인스턴스를 반환

리턴 값을 True로 변환해야함.
import frida
jscode = """
Java.perform(function(){
var main;
Java.choose('uk.rossmarks.fridalab.MainActivity', {
onMatch: function(instance) {
main = instance;
},
onComplete: function() {}
});
main.chall03.overload().implementation = function () {
return true;
};
});
"""
process = frida.get_usb_device().attach('FridaLab')
script = process.create_script(jscode)
script.load()
input()
choose()와 .overload().implementation를 사용했지만,
아래와 같이 use()와 .implementation을 사용해도 된다.
let challenge_03 = Java.use('uk.rossmarks.fridalab.MainActivity');
challenge_03.chall03.implementation = function() {
return true;
};

chall04()의 인자로 "frida"라는 문자열을 주어야 함.
import frida
jscode = """
Java.perform(function(){
//chall04
var main;
Java.choose('uk.rossmarks.fridalab.MainActivity', {
onMatch: function(instance) {
main = instance;
},
onComplete: function() {}
});
main.chall04("frida");
});
"""
process = frida.get_usb_device().attach("FridaLab")
script = process.create_script(jscode)
script.load()
input()


onClick에서 "frida"문자열인지 계속 감시하는 중
"notfrida!"를 "frida"로 변경해야함
import frida
jscode = """
Java.perform(function(){
let challenge_05 = Java.use('uk.rossmarks.fridalab.MainActivity');
challenge_05.chall05.implementation = function(str) {
str = "frida";
this.chall05(str);
};
});
"""
process = frida.get_usb_device().attach("FridaLab")
script = process.create_script(jscode)
script.load()
input()


MainActivity.onCreate에서 chall6 호출

confirmChall06(i)의 return이 True가 되려면
시작 시간으로부터 10초 뒤 i == chall06
chall06은 1초마다 1~50 사이의 값이 더해지다가 9000을 초과하면 i 값으로 초기화
1초마다 값이 바뀌므로 addChall06을 오버라이드해서 해당 값을 더할 때 main에서 chall06함수를 해당 인자로 실행하도록 함.
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.rossmarks.fridalab.challenge_06')
challenge_06.addChall06.overload('int').implementation = function (arg0) {
challenge_06.addChall06.overload('int').call(this,arg0);
main.chall06(this.chall06.value)
return ;
};
});
"""



MainActivity onCreate시 setChall07을 실행하여 chall07에 랜덤값을 넣는다.
check07Pin에 적합한 인자를 Bruteforce를 통해 찾아낸다.
jscode = """
Java.perform(function(){
let main;
Java.choose('uk.rossmarks.fridalab.MainActivity', {
onMatch: function(instance) {
main = instance;
},
onComplete: function() {}
});
let challenge_07 = Java.use('uk.rossmarks.fridalab.challenge_07')
for(let i = 1000; i<10000; i++){
if(challenge_07.check07Pin(i.toString())){
main.chall07(i.toString())
break;
}
}
});
"""

jscode = """
Java.perform(function(){
let main;
Java.choose('uk.rossmarks.fridalab.MainActivity', {
onMatch: function(instance) {
main = instance;
},
onComplete: function() {}
});
let id = main.findViewById(0x7f07002f);
let button = Java.use('android.widget.Button');
let check = Java.cast(id, button);
let confirm = Java.use('java.lang.String').$new('Confirm');
check.setText(confirm);
});
"""
어떤 메소드가 존재하는지 어떻게 사용해야하는지에 대한 부분은 외우지 않고 있어 그때그때 검색하거나 gpt의 도움을 많이 받았다.
큰 개념만 잡고 있으면 gpt로 금방 서치 가능하니 문제 없는 세상인듯 하다.