1. Memory.readByteArray()로 읽어온 데이터에 접근할 때
Memory.readByteArray의 리턴 형은 ArrayBuffer인데, 얘로 가져온 결과는 index로 접근할 수가 없다.
다음과 같이 Uint8Array로 변화하여 접근하면 됨.
var data2 = Memory.readByteArray(save, 0x18);
var data = new Uint8Array(data2);
console.log(data[0])
2. nodejs char <-> int
fromCharCode와 charCodaAt을 이용하여 변환하면 됨.
var tmp = String.fromCharCode(data[i] ^ xor_key[i].charCodeAt());
3. Native Lib 후킹하기 (Interceptor)
getBaseAddress나 findBaseAddress로 라이브러리의 Base 주소를 구한 뒤, 후킹하고자 하는 함수의 offset을 더해서 사용한다. 그 뒤 Interceptor.attach()를 이용하면 됨.
onEnter에서 this.context.ecx 등으로 레지스터 값까지 확인 및 변조할 수 있음.
전체 레지스터를 살펴보려면 JSON.stringify(this.context)로 확인할 수 있음.
var foo = Module.getBaseAddress('libfoo.so');
if (!foo){
console.log('libfoo not loaded!');
return 0;
}
console.log('[+] libfoo.so @ ' + foo.toString());
var target = foo.add(0xfa0);
var save;
Interceptor.attach(target, {
onEnter: function(args){
console.log('OnEnter :');
save = ptr(args[0]);
console.log(save);
// console.log(Memory.read)
},
onLeave: function(retval){
console.log('OnLeave :');
var data2 = Memory.readByteArray(save, 0x18);
console.log(data2);
var data = new Uint8Array(data2);
var result = '';
for (var i=0; i<0x18; i++){
var tmp = String.fromCharCode(data[i] ^ xor_key[i].charCodeAt());
//console.log(tmp);
result += tmp;
}
console.log('[Result] : ' + result);
}
});
4. Native Lib 후킹하기 (replace)
Interceptor 말고도 다음과 같이 replace로 아예 해당 JNI function을 새로 정의해서 사용할 수도 있음.
var il2cpp = Module.getBaseAddress('libil2cpp.so');
send('[*] libil2cpp.so @ ' + il2cpp.toString());
var offset0 = 0x2E0CFC;
var target0 = il2cpp.add(offset0);
send(' internal int GetHashCodeOfString() @ ' + target0.toString());
Interceptor.replace(target0, new NativeCallback(function(){
send("ignored!");
}, "void", ['float','float','float','float','float','float','float','float','bool', 'bool']));
5. Java method 후킹하기
implementation을 사용하면 되는데, 원래 메소드의 인자 개수에 따라서 새로 정의한 함수의 인자를 맞춰줘야함.
기존 메소드를 사용하려면, this.{원래 메소드 이름}(argv...) 형식으로 사용할 수 있음. 아래의 예시에서는 a 메소드를 다시 실행함.
var MainActivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
var a = Java.use("sg.vantagepoint.a.a");
a.a.implementation = function(args1, args2){
var retval = this.a(args1, args2);
console.log(retval);
var result = '';
for (var i=0; i<retval.length; i++){
result += String.fromCharCode(retval[i]);
}
console.log(result);
return retval;
}
6. 리턴 값 변조하기
다음과 같이 replace를 이용하여 리턴 값을 변조할 수 있다.
var t = Module.findExportByName('libc.so', 'strstr');
console.log("strstr @ " + t.toString());
Interceptor.attach(t, {
onEnter: function(args){
},
onLeave: function(retval){
retval.replace(0);
}
});
7. overload 사용하기
후킹하려는 메소드가 다양한 인자에 대해 정의되어 있다면, overload를 통하여 후킹하려는 메소드를 명시해줘야한다.
var System = Java.use("java.lang.System");
System.exit.overload('int').implementation = function(args){
console.log("System.exit called");
};
8. setInterval로 특정 함수 실행시키기
NativeFunction()으로 새로 함수를 지정하여 대상으로 할 수 있다. 마지막 인자는 특정 시간마다 실행시키는 것을 의미하며, 1/1000 단위이다.
var my_plus_gold = new NativeFunction(target0, 'pointer', ["pointer"]);
setInterval(function(){
my_plus_gold(save);
}, 10);
9. libc.so 후킹하기
libc의 함수가 어느시점에 호출되는지에 따라 다르겠지만, 후킹이 너무 늦게되서 문제가 발생한다면 Java.perfom() 문 안이 아닌 밖에 적는게 좋음.
var t = Module.findExportByName('libc.so', 'strstr');
console.log("strstr @ " + t.toString());
Interceptor.attach(t, {
onEnter: function(args){
},
onLeave: function(retval){
retval.replace(0);
}
});
setImmediate(function(){
Java.performNow(function(){
10. process에 attach
그냥 nodejs
> frida -l script.js -U com.example.aaa
python
if __name__ == "__main__":
print("[*] Start Process ...")
PACKAGE_NAME = sys.argv[1]
try:
process = frida.get_usb_device().attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
except Exception as error:
print(error)
프리다 버전마다 조금씩 달라질 수 있음! 12.6.18 기준
11. process load할 때
nodejs
> frida -l script.js -U -f com.example.aaa
python
if __name__ == "__main__":
print("[*] Start Process ...")
PACKAGE_NAME = sys.argv[1]
try:
device = frida.get_usb_device()
pid = device.spawn(PACKAGE_NAME)
process = device.attach(pid)
device.resume(pid)
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
except Exception as error:
print(error)
12. Java.perform() 이 안될 때
Java.performNow()를 써보자...
setImmediate(function(){
Java.performNow(function(){
console.log('Hooking Start!');
var System = Java.use("java.lang.System");
System.exit.overload('int').implementation = function(args){
console.log("System.exit called");
native_hook();
};
});
});
'좀 열심히 쓴 글' 카테고리의 다른 글
Android 동작 구조 (0) | 2020.06.08 |
---|---|
Unity Mono 분석 (0) | 2020.06.08 |
Format String field width (0) | 2020.04.09 |
[Project Zero] Bad Binder: Android In-The-Wild Exploit 분석글 (0) | 2020.03.07 |
Unity IL2CPP 분석 (0) | 2020.03.06 |