MainActivity에서 R.id.check의 text 값이 Confirm과 일치하는지 확인해주는 함수를 호출함
조금 찾아보니 리소스 영역을 직접 바꾸는 것은 불가능하다고 하고, 대신 findViewById로 Button 객체를 불러와서 setText 함수를 호출함으로써 내부 텍스트를 변경해줄 수 있다고 함
관련 기법을 잘 몰라서 검색을 좀 해봤는데 MainActivity 인스턴스에 붙어서 findViewById로 내가 원하는 객체의 핸들러를 가져온 뒤, Java.cast(handle, klass) 함수를 호출하여 자바 클래스로 형변환을 하고, setText 함수를 직접 호출할 수 있나 봄
Java.cast
는 주어진 객체를 다른 타입으로 캐스팅하는 데 사용됩니다. 이는 주로findViewById
같은 메서드로 반환된View
객체를 더 구체적인 뷰 타입, 예를 들어Button
이나TextView
로 캐스팅할 때 사용됩니다.
R.id.check를 더블클릭 해보면 실제 리소스 ID 값을 확인할 수 있음. 이 숫자 값을 findViewById 인자로 넣어주면 View 객체를 불러올 수 있음
이 View 객체를 Button 으로 캐스팅 하기 위해서는 Jadx-gui에서 import 해주는 부분을 참고하면 됨. String 타입은 import 구문에서 안 보이고 기본적으로 외워두는 것이 좋을 듯 (java.lang.String)
그리고 setText 함수를 호출할 때 바로 문자열을 넘겨주면 안 됨. 인자 또한 Java의 String클래스 타입이어야 함. Java.use(’java.lang.String’).$new(’문자열’); 이렇게 넘겨줘야 문자열 타입으로 인식하고 오류가 안 남
var Button = Java.use('android.widget.Button');
var buttonInstance = Java.cast(viewInstance, Button);
if (buttonInstance !== null) {
var StringClass = Java.use('java.lang.String');
buttonInstance.setText(StringClass.$new("Confirm"));
} else {
console.log("Casting failed.");
}
$new는 Java.use로 불러온 프록시 객체로부터 새로운 인스턴스를 생성해주는 함수라고 함. Java.use와의 정확한 차이점은 아래 GPT의 답변을 참고
Java.use
와Java.use().$new
는 서로 관련이 있지만, 각기 다른 역할을 수행하는 두 가지 주요 기능입니다. 이 두 기능을 이해하려면 Java의 리플렉션과 객체 생성 방식에 대한 기본적인 이해가 필요합니다. 자세히 설명해 드리겠습니다.Java.use
Java.use
는 Frida의 Java 브리지를 통해 특정 Java 클래스에 접근할 수 있게 해주는 함수입니다. 이 함수는 해당 클래스의 메타데이터를 로드하여, 그 클래스의 인스턴스 메서드, 정적 메서드, 필드 등을 사용할 수 있는 프록시 객체를 반환합니다. 이 프록시 객체를 통해 클래스의 메서드를 호출하거나 필드 값을 변경할 수 있습니다.예제:
javascriptCopy code var Button = Java.use('android.widget.Button'); Button.setEnabled(true); // 정적 메서드 호출 (가정)
여기서
Button
은android.widget.Button
클래스의 모든 메서드와 필드에 접근할 수 있는 프록시 객체입니다.Java.use().$new
Java.use().$new
는Java.use
로 얻은 프록시 객체를 통해 해당 클래스의 새로운 인스턴스를 생성하는 함수입니다. Java의new
키워드와 유사하게 작동하며, 클래스의 생성자를 호출하여 객체를 생성합니다. 이 방법은 주로 생성자에 인자가 필요한 경우 또는 새로운 객체 인스턴스가 필요할 때 사용됩니다.예제:
javascriptCopy code var StringClass = Java.use('java.lang.String'); var newString = StringClass.$new("Hello, World!"); // 새 String 인스턴스 생성
여기서
newString
은 "Hello, World!" 값을 가진 새로운 Java 문자열 객체입니다.차이점
- Java.use: 특정 클래스의 정적 구성요소(메서드, 필드)에 접근할 수 있게 해주는 프록시 객체를 반환합니다.
- Java.use().$new: 해당 클래스의 생성자를 호출하여 새로운 인스턴스를 생성합니다. 생성된 인스턴스를 통해 인스턴스 메서드와 필드에 접근할 수 있습니다.
Java.perform(function () {
// 1
var challenge_01 = Java.use('uk.rossmarks.fridalab.challenge_01');
challenge_01.getChall01Int.implementation = function () {
send('challenge_01::getChall01Int called');
var result = this.getChall01Int();
send('getChall01Int returned: ' + result);
send('modified result: 1\n');
return 1; // Or challenge_01.chall01.value = 1
};
// 2, 3, 4, 6, 7, 8
Java.choose('uk.rossmarks.fridalab.MainActivity', {
onMatch: function (instance) {
send('Found instance: ' + instance);
// 2
send('call MainActivity::chall02\n');
instance.chall02();
// 3
// instance.chall03.implementation = function () {
// send('call MainActivity::chall03');
// var result = instance.chall03();
// send('chall03 returned: ' + result);
// send('modified result: true\n');
// return true;
// }
// 4
send('call MainActivity::chall04("frida")\n')
instance.chall04('frida');
// 6
var challenge_06 = Java.use('uk.rossmarks.fridalab.challenge_06');
challenge_06.confirmChall06.implementation = function (i) {
send('call callenge_06::confirmChall06 with i: ' + i + '\n');
send('timeStart: ' + this.timeStart.value);
send('chall06: ' + this.chall06.value);
send('call challenge_06:confirmChall06 with i: ' + this.chall06.value + '\n');
var result = this.confirmChall06(this.chall06.value);
return result;
};
send('sleep 10 seconds');
// Thread.sleep(10);
send('call MainActivity::chall06');
instance.chall06(10000);
// 7
var challenge_07 = Java.use('uk.rossmarks.fridalab.challenge_07');
send('brute force check07Pin');
for (let index = 1000; index < 10000; index++) {
var result = challenge_07.check07Pin(index.toString());
if (result) {
send('check07Pin result: ' + index.toString() + '\n');
instance.chall07(index.toString());
}
}
// 8
var checkButtonId = instance.findViewById(0x7f07002f);
send('checkButton id: ' + checkButtonId);
var checkButton = Java.cast(checkButtonId, Java.use('android.widget.Button'));
checkButton.setText(Java.use('java.lang.String').$new('Confirm'));
send('Change check button text value to Confirm \n');
},
onComplete: function () { }
});
// 3
var MainActivity = Java.use('uk.rossmarks.fridalab.MainActivity');
MainActivity.chall03.implementation = function () {
send('call MainActivity::chall03');
var result = this.chall03();
send('chall03 returned: ' + result);
send('modified result: true\n');
return true;
};
// 5
MainActivity.chall05.implementation = function (str) {
send('call MainActivity::chall05 with str: ' + str);
send('call MainActivity::chall05 with str: frida\n');
this.chall05('frida');
};
});