본문 바로가기

모바일/보안

Frida : 쓸만한 스크립트 모음

클래스 추출

 

import frida, sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

PACKAGE_NAME = sys.argv[1]

jscode = #"""
Java.perform(function(){
 var pattern = #\""""+sys.argv[2]+"""\"
 

 Java.enumerateLoadedClasses({
  onMatch:function(aClass) {
   if (aClass.match(pattern)) {
    console.log(aClass);
   }
  },
  onComplete: function() {}
 });
 
});
#"""

try:
    device = frida.get_usb_device(1)
    pid = device.spawn([PACKAGE_NAME])
    print("App is starting ... pid : {}".format(pid))
    process = device.attach(pid)
    device.resume(pid)
    script = process.create_script(jscode)
    script.on('message',on_message)
    print('[*] Running Frida')
    script.load()
    sys.stdin.read()
except Exception as e:
    print(e)

 

 

 

메소드 추출

 

import frida, sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

PACKAGE_NAME = sys.argv[1]

if len(sys.argv) < 3:
    print("usage: python.exe test.py [Class1|Class2] [filter1|filter2...]")

if len(sys.argv)==4 :
    e_pat = sys.argv[3]
else:
    e_pat = "~_~dummy"



jscode= #"""
console.log("[*] Starting script");

Java.perform(function(){

    var pattern = #\""""+sys.argv[2]+"""\";
    var reg = new RegExp("("+pattern+")+");
    console.log("reg: "+reg);

    var e_pattern = #\""""+e_pat+"""\";
    var e_reg= new RegExp("("+e_pattern+")+");
    console.log("e_reg: "+e_reg);

        
    Java.enumerateLoadedClasses({
        onMatch: function(className){
            if(className.match(reg)){
                console.log(className);
                hookMethod(className)
            }
        },
        onComplete:function(){}
    });


    function hookMethod(targetClass){
        var hookClass = Java.use(targetClass);
        var methods = hookClass.class.getDeclaredMethods();
        
        console.log("");
        console.log("ClassName : "+targetClass);
        console.log("+hooking start... ");

        methods.forEach(function(method) {
            //method ex)
            //public void sg.vantagepoint.uncrackable1.MainActivity.verify(android.view.View)
            if (method.toString().match(e_reg)){
                //console.log("!!! EREG SUCCESS !!!");
                return false;
            }
            var overloadCount=0;
            var targetMethodArg;
            var targetRettype;
            var argumentsStr;
            
            //parse MethodName
            var targetMethod = method.toString();
            targetMethod = targetMethod.split(" ")
            targetRettype = targetMethod[targetMethod.length-2];
            targetMethod = targetMethod[targetMethod.length-1].split("(");
            targetMethodArg = "(" + targetMethod[1];
            targetMethod = targetMethod[0].split(".");
            targetMethod = targetMethod[targetMethod.length-1];
            
            console.log(targetMethod);

            try {
                overloadCount = hookClass[targetMethod].overloads.length;
            } catch (e) {
                console.log("   -> "+e);
            }
            
            for(var i=0; i<overloadCount; i++){
                hookClass[targetMethod].overloads[i].implementation=function(){
                    console.log("***Entered Method is: "+targetRettype+" "+targetClass+"."+targetMethod+targetMethodArg);

                    var argumentsStr = "";
                    var retval = "";

                    retval = this[targetMethod].apply(this,arguments);
                    
                    for(var j = 0 ; j < arguments.length; j++){
                        argumentsStr = argumentsStr + arguments[j] + " ; ";
                    }
                    
                    console.log(arguments.length + ": arg> "+argumentsStr);
                    console.log("   ret> "+retval);
                    return retval;
                }
            }
        });
        
        hookClass.$dispose;
        console.log("-hooking end... ");
    }
});
#"""

try:
    #process = frida.get_usb_device(1).attach(PACKAGE_NAME)
    
    #===spawn===
    device = frida.get_usb_device(timeout=10)
    pid = device.spawn([PACKAGE_NAME]) 
    print("App is starting ... pid : {}".format(pid))
    process = device.attach(pid)
    device.resume(pid)
    
    script = process.create_script(jscode)
    script.on('message', on_message)
    print('[*] Running Hook')
    script.load()
    sys.stdin.read()
except Exception as e:
    print(e)

 

 

 

exit 함수 후킹 (js)

console.log("[*] Start script");

Java.perform(function(){
    var Cls=Java.use("java.lang.System");
	
    Cls.exit.implementation=function(){
        console.log(" im here!");
        return true;
    }
});

console.log("[*] End script");

 

 

 

 

로드된 라이브러리 확인 

Process.EnumerateModule

 

 

로드된 네이티브 함수 명 확인

라이브러리가 로드된 이후에 스크립트가 실행되어야 하기 때문에 메인스레드가 실행된 상태여야 한다.

파이썬으로 코딩한 이유는 루팅탐지 로직으로 인해 프로세스가 종료되기 때문에 js로는 올릴 수 없다. 

js는 -f, -l 옵션을 사용하면 스레드 실행전에 스크립트가 실행되어버리고, -f를 사용하지 않는다면 프로세스가 실행된 상태에서만 붙을 수 있기 때문에 사람의 속도로는 불편하다. 

Imports를 Exports로 변경하면 외부에서 가져오는 함수 확인 가능

 

import frida, sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

PACKAGE_NAME = sys.argv[1]

jscode = #"""
console.log("[*] Start script");

Java.perform(function(){
	var funcs;
	var strncmp;
	funcs = Module.enumerateExportsSync("libAppSuit.so"); //libAppSuit.so libjnisumc.so
	for(var i = 0 ; i < funcs.length; i++) {
        console.log("+func name: "+funcs[i].name+", addr: "+funcs[i].address);

	}
});

console.log("[*] End script");
#"""

try:
    device = frida.get_usb_device(1)
    pid = device.spawn([PACKAGE_NAME])
    print("App is starting ... pid : {}".format(pid))
    process = device.attach(pid)
    device.resume(pid)
    script = process.create_script(jscode)
    script.on('message',on_message)
    print('[*] Running Frida')
    script.load()
    sys.stdin.read()
except Exception as e:
    print(e)

 

 

 

native 함수(strncmp) 후킹

Module.enumerateImportsSync("libfoo.so") 함수를 통해 libfoo.so에 임포트된 모든 함수(IAT테이블 참조)에 대한 정보를 추출해온다. 

이후 반복문을 통해 함수배열의 인덱스 0부터 접근하며 임포트 되어있는 모든 함수의 이름과 주소를 출력하고, strncmp 라는 이름을 가진 함수를 찾는다. 함수의 주소값은 안드로이드가 재시작할 때마다 변경된다.

 

strncmp함수는 자주 사용되는 함수이기 때문에 모든 조건에서 데이터를 출력하게 되면 쓸모없는 데이터가 많이 발생한다. 이를 방지하기 위해 if문으로 원하는 상황에만 출력하도록한다. 

String은 주소값이 전달되며 Memory.readUtf8String(해당주소, 몇바이트) 함수를 통해 해당 주소부터 몇바이트 문자열로 반환할 것인지를 정해서 로그로 출력한다. 

console.log("[*] Start script");

Java.perform(function(){
	var funcs;
	var strncmp;
	funcs = Module.enumerateImportsSync("libfoo.so");
	for(var i = 0 ; i < funcs.length; i++) {
        console.log("+func name: "+funcs[i].name+", addr: "+funcs[i].address);
		if(funcs[i].name.match("strncmp")){
			strncmp = funcs[i].address;
            console.log("   -> hook func addr:"+strncmp);
		}
	}
	Interceptor.attach(strncmp, {
		onEnter: function(args){
			if(Memory.readUtf8String(args[0],23) == "01234567890123456789012"){
				console.log("[*] string: " + args[1] + ": " + Memory.readUtf8String(args[1],23))
			}
		}, onLeave: function(){}
	});
});

console.log("[*] End script");

 

 

 

 

메모리액세스모니터 - 아랑 코드 일부

var origin_Addr = 0x1001D3EA4
var offset = method.implementation - origin_addr
console.log("offset");
console.log(offset);

vr func = ptr(0x100694788 + offset);
console.log(func);

Memory.protect(func, 16, 'rwx'); // 에러나는 경우가 있어서 r__로 설정
MemoryAccessMonitor.enable({
    base: func,
    size: 8
}, {
    onAccess: function(details){
        console.log("----------*_------------");
        details.foreach(function(i){
            console.log(i);
        })
        console.log("------------------------");
    }
})