![[붉은외계인] Mobile - FridaLab 풀이](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL1a9S%2FbtsFhSIqoc8%2FzjYeUKNq33S45lfw5AhAy1%2Fimg.png)
들어가며
Frida를 배우고 난 후, 연습을 위하여 FridaLab 을 풀어보았다
1 ~ 5단계는 쉽기 때문에, 해당 포스트에서는 6 ~ 8 단계만 다루고 있다
나머지 Solution Scripts는 아래 Github에 올려놓았으니, 참고하길 바란다
https://github.com/RedAlien00/FridaLab
GitHub - RedAlien00/FridaLab
Contribute to RedAlien00/FridaLab development by creating an account on GitHub.
github.com
서론
FridaLab을 풀어볼 때,
외우는것이 아닌 왜 이러한 메소드를 썼고, 이러한 코드를 짰는가에 포인트를 두고 진행하였다
그리고 많은 풀이들을 살펴보고, 이리저리 바꾸어가며 나의 걸로 만들려고 시도하였다
그 결과, 몇 가지 깨달은점이 있다
◆ JavaScript의 화살표 함수를 사용할 경우, 오류가 발생한다는 것
◆ 조건문의 경우, True로 바꿔버리면 된다는 것 ( 이것은 아래에서 다룰 것이다 )
◆ use와 choose의 차이점을 확실히 이해해야 한다는 것
위의 그림을 살펴보자
Java.perform에서 사용하는 화살표 함수는 대부분의 경우에서 문제 없이 작동하였지만,
그 외에서 사용할 경우, 위와 같이 에러가 발생하는 경우가 많았다
에러가 발생하는 원인을 모르겠다면, 화살표 함수를 변경해보자
let Activity = Java.use(""uk.rossmarks.fridalab.MainActivity"")
let Activity = Java.choose("uk.rossmarks.fridalab.MainActivity", {
onMatch : function(){}
onComplete : function(){}
})
개인적으로, 가장 많은 시간을 할애했던 것은 use와 choose의 사용 방식이다
시간이 많이 들었던 이유는, 메뉴얼이 생각보다 자세하지 않았고,
검색을 통해 찾아보아도 각자 말하는 정의들이 달랐기 때문이다
Frida의 메뉴얼과 구글링, 그리고 FridaLab을 여러 번 풀어보면서, 나름대로 정의한 것은 아래와 같다
◆ Java.use
- Static 맴버(정적 맴버)에 접근하는 메소드
◆ Java.choose
- Heap을 스캔하여, 타겟 클래스의 인스턴스를 열거하는 메소드
- 메모리에 적재된 인스턴스 맴버에 접근하는 메소드
이제부터 본론으로 들어가보자
Challenge 6
해당 단계의 목표는 올바른 Value를 통해, 10초 뒤에 chall06을 실행하는 것이다
코드를 살펴보자
// Solution 1
Java.perform(function(){
console.log("[+] Frida Start ...");
let Main_Act = Java.choose("uk.rossmarks.fridalab.MainActivity",{
onMatch : function(arg){
let chall06_Act = Java.use("uk.rossmarks.fridalab.challenge_06")
chall06_Act.confirmChall06.implementation = function(){return true}
arg.chall06(1);
},
onComplete : function(){console.warn("Done !");}
})
})
위에 코드는 올바른 Value를 삽입하는 것이 아닌,
chall06_Act_confirmChall06 메소드가, true를 반환하도록 재구현하여, 해결하는 방법이다
내가 위에서 언급한 " 조건문의 경우, True로 바꿔버리면 된다는 것 " 은 위와 같은 케이스를 의미한다
앱의 코드를 살펴보자
// MainActivity
public class MainActivity extends AppCompatActivity {
public void chall06(int i) {
if (challenge_06.confirmChall06(i)) {
this.completeArr[5] = 1;
}
}
}
// challenge_06
public static boolean confirmChall06(int i) {
return i == chall06 && System.currentTimeMillis() > timeStart + 10000;
}
결론적으로, chall06() 메소드를 실행하였을 때 True가 된다면 해결된다
그렇기 때문에, chall06_Act_confirmChall06 메소드가, True를 반환하도록 재구현한 것이다
제작자가 원하는 방식과는 다르지만, 어쨌든 하나의 풀이 방법이 될 수 있다
해당 코드는 challenge 3단계의 풀이 방법과 비슷하며, 조금 더 응용했다고 보면 된다
// Solution 2
Java.perform( console.warn("[+] Frida Start ..."))
setTimeout(function(){
Java.perform(function(){
Java.choose("uk.rossmarks.fridalab.MainActivity",{
onMatch : function(arg){
let Activity = Java.use("uk.rossmarks.fridalab.challenge_06")
const value = Activity.chall06.value
arg.chall06(value)
},
onComplete : function(){}
})
console.warn("[+] chall06 Success ! !");
})
}
, 10000);
반대로, 위에 코드는 제작자가 원하는 방식의 풀이 방법을 기술한 것이며, 해결 포인트는 아래와 같다
1. MainActivity가 Oncreate 될 때, 위의 그림처럼 메소드가 호출되면서 challenge_06 클래스의
chall06, timeStart 필드값이 설정된다는 것
2. 10초가 지난 뒤에, chall06값과 일치하는 int 자료형을 넣으면 해결된다는 것
10초가 지난 뒤에 실행은 setTime() 함수로 구현하면 된다
문제 해결을 위해서는 MainActivity.chall06() 메소드를 호출하여야 하는데, 해당 메소드는
인스턴스 맴버이기 때문에, Java.use가 아닌, Java.choose를 사용해야 한다
( 위에서 언급하였지만, Java.choose는 메모리에 적재된 인스턴스 맴버에 접근하는 메소드라고 하였다 )
하지만 challenge_06의 chall06 필드는 static 맴버이기 때문에 Java.use를 사용하여 접근하여야 한다
Challenge 7
이번 단계의 목표는 check07pin()메소드를 Brute Force하여 chall07을 confirm 하는 것이다
// Solution 1
Java.perform(function(){
console.log("[+] Frida Start ...");
Java.choose("uk.rossmarks.fridalab.MainActivity",{
onMatch: function(arg){
let Act_chall07 = Java.use("uk.rossmarks.fridalab.challenge_07")
Act_chall07.check07Pin.implementation = function(){return true}
arg.chall07('A')
},
onComplete : function(arg){console.log("[+] Done !");}
})
})
위에 코드는 6단계 과정과 같은 원리로, true를 반환하도록 재구현 한 것이다
같은 원리이기 때문에 설명은 생략하겠다
// Solution 2
Java.perform(function(){
console.warn("[+] Frida Start ...")
Java.choose("uk.rossmarks.fridalab.MainActivity",{
onMatch: function(arg){
const chall07 = Java.use("uk.rossmarks.fridalab.challenge_07")
for(let i=1000; i<9999+1; i++){
if(chall07.check07Pin(String(i))){
console.warn(`Found ! ${i}`);
arg.chall07(String(i))
break
}
console.warn(`Brute Force : ${i}`);
}
},
onComplete : function(arg){}
})
console.warn("[+] chall07 Success ! !");
})
위에 코드는 제작자가 원하는 방식의 해결 방법을 구현한것이다
이것 또한 위에서 설명한 것과 마찬가지로, 인스턴스 멤버는 Java.choose를,
Static 맴버는 Java.use를 사용하여 접근하면 된다
0.0 ~ 0.9 의 난수를 생성하는 Math.random()함수 뒤에 추가로 연산이 있는데,
이는 결국 1000 ~ 9999이하 범위의 난수를 생성하는 것이다
그리고 랜덤 숫자는 "" + <랜덤숫자> 의 연산을 통해 chall07 필드에 저장된다
빈 문자열과 문자열 연결 연산이 되기 때문에, 만약 랜덤 숫자가 8이라면 "8" 문자열이 저장될 것이다
이러한 이유로, chall07() 메소드에 매개변수를 집어넣을 때는 문자열로 자료형 변환을 해주어야 한다
challenge 8
해당 단계에서 목표는 check 버튼의 text value를 Confirm으로 변경하는 것이다
// Solution 1
Java.perform(function(){
const Activity = Java.use("uk.rossmarks.fridalab.MainActivity")
Java.choose("uk.rossmarks.fridalab.MainActivity", {
onMatch: function(arg){
Activity.chall08.implementation = function(){return true}
arg.chall08()
},
onComplete : function(){}
})
})
// Solution 2
Java.perform(function(){
console.warn("[-] Frida Start ...");
Java.choose("uk.rossmarks.fridalab.MainActivity", {
onMatch: function(arg){
const checkId = arg.findViewById(0x7f07002f) // View 객체 리턴
const button = Java.use("android.widget.Button")
const check = Java.cast(checkId, button) // Button으로 타입 변환
const string = Java.use("java.lang.String")
check.setText(string.$new("Confirm")) // String 객체 Confirm 생성
},
onComplete : function(){console.warn("[+] chall08 Success ! !");}
})
})
Solution 1은 생략하고, 바로 2를 설명하겠다
여기서, Java.cast는 형 변환 or 타입 변환을 해주는 메소드인데, Java 코드로 예를들면 아래 코드와 같다
Button button = (Button) findViewById(R.id.mybutton);
findViewById 메소드에 Resourece Id를 매개변수로 주면 해당 ID에 해당하는 View 객체가 리턴된다
그것을 Button으로 형 변환을 하는 코드이다
다음으로, $new 키워드는 객체의 생성자를 호출하여 인스턴스화 시키는 키워드이다
setText() 메소드에 바로 Confirm 을 삽입하면, 에러가 발생하기 때문에 $new 키워드를 통해
String 객체를 생성한 후, 삽입해주어야 한다
마무리
완전히 이해하는데까지 생각외로 시간이 꽤 걸린 것 같았다
위에서 언급하였듯, Java.use와 Java.choose가 시간을 많이 잡아먹었는데
대충하고 넘어가면 나중에 발목을 잡을 것 같아서 끝까지 파고드느라 그렇다
아! 그리고 아래 사이트에서도 많은 공부를 할 수 있었으니, 해당 게시물을 접하는 분들도
한번씩 꼭 들러보면 좋겠다
Frida HandBook
learnfrida.info
끝 ~
'Security > Mobile' 카테고리의 다른 글
[붉은외계인] Mobile - OWASP UnCrackable L3 (0) | 2024.03.12 |
---|---|
[붉은외계인] Mobile - OWASP UnCrackable L2 (0) | 2024.03.05 |
[붉은외계인] Mobile - OWASP UnCrackable L1 (0) | 2024.03.04 |
[붉은외계인] Mobile - smali 코드 분석 1 with KGB Messenger (0) | 2024.02.14 |
[붉은외계인] Mobile - Diva Hardcoding Issues - Part 2 (0) | 2024.02.07 |
IT / Android
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!