Frida의 Java API로 제공되는 java.choose()는 Java 힙에서 특정 클래스의 인스턴스를 열거하려는 경우 사용되는데, 이 API는 런타임에서 특정 클래스의 현재 인스턴스를 찾거나 분석하려는 경우 매우 유용합니다.
해당 API 사용 시 클래스 이름과 콜백 두 가지 파라메터가 필요한데, 주요 콜백은 onMatch와 onComplete 두 가지 함수가 포함되어 있습니다.
onMatch 콜백은 java.choose()가 힙을 스캔하면서 해당 클래스의 각 인스턴스를 찾을 때마다 호출됩니다. 찾은 인스턴스에 대한 참조가 인자로 들어가며, 인스턴스를 분석하거나, 해당 인스턴스에 대한 작업을 수행하거나, 참조를 저장하는 등의 작업을 수행하기 위해 사용됩니다.
onMatch: function(instance) {
console.log("Field value: " + instance.someField.value);
}
onMatch: function(instance) {
var result = instance.someMethod();
console.log("Method result: " + result);
}
onMatch: function(instance) {
console.log("Instance: " + instance.toString());
}
onMatch: function(instance) {
console.log("Class name: " + instance.class);
}
모든 인스턴스가 onMatch를 통해 처리된 후에 실행되는 onComplete 콜백은 힙 스캔이 완료되면 호출됩니다. 이는 스캔 작업이 완료되었음을 알리거나, 후속 작업을 실행하기 위해 사용됩니다.
사용목적 | 내용 |
---|---|
특정 상태 확인 | 특정 시점에서 특정 클래스의 인스턴스 상태를 확인하려는 경우 |
앱의 디버깅 | 특정 앱의 특정 클래스 인스턴스가 얼마나 많이 생성되었는지, 혹은 특정 상태인지 확인하려는 경우 |
후킹 후의 상태 확인 | 다른 메서드를 후킹하고 그 메서드의 호출 결과로 생성된 특정 객체의 상태를 확인하려는 경우 |
// 타겟 클래스 및 메서드 정의
var MyClass = Java.use('com.example.MyClass');
// 첫 번째 java.choose: 원래의 메서드 구현을 출력
Java.perform(function() {
Java.choose('com.example.MyClass', {
onMatch: function(instance) {
console.log("Before change: " + instance.someMethod());
},
onComplete: function() {
console.log("Finished enumerating instances (Before change)");
// 메서드의 구현 변경
MyClass.someMethod.implementation = function() {
return "Changed Value";
};
// 두 번째 java.choose: 변경된 메서드의 구현을 출력
Java.choose('com.example.MyClass', {
onMatch: function(instance) {
console.log("After change: " + instance.someMethod());
},
onComplete: function() {
console.log("Finished enumerating instances (After change)");
}
});
}
});
});
이 예시는 java.choose를 사용하여 MyClass의 인스턴스를 먼저 열거하고 someMethod의 원래 값을 출력한 후, 해당 메서드의 구현을 변경하고 다시 java.choose를 사용하여 변경된 값을 출력합니다.