좀 열심히 쓴 글

Frida 및 nodejs 사용할 때 TMI

ch4rli3kop 2020. 6. 4. 17:32
반응형

Frida 및 nodejs 사용할 때 TMI

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