// 获取模块的随机地址
function get_image_vm_slide(modulePath){
    // intptr_t   _dyld_get_image_vmaddr_slide(uint32_t image_index)
    var _dyld_get_image_vmaddr_slide = new NativeFunction(
        Module.findExportByName(null, '_dyld_get_image_vmaddr_slide'),
        'pointer',
        ['uint32']
    );
    // const char*  _dyld_get_image_name(uint32_t image_index) 
    var _dyld_get_image_name = new NativeFunction(
        Module.findExportByName(null, '_dyld_get_image_name'),
        'pointer',
        ['uint32']
    );
    // uint32_t  _dyld_image_count(void)
    var _dyld_image_count = new NativeFunction(
        Module.findExportByName(null, '_dyld_image_count'),
        'uint32',
        []
    );

    var image_count = _dyld_image_count();

    for (var i = 0; i < image_count; i++) {
        var image_name_ptr = _dyld_get_image_name(i)
        var image_silde_ptr = _dyld_get_image_vmaddr_slide(i)
        var image_name = Memory.readUtf8String(image_name_ptr)

        if (image_name == modulePath) {
            send({msg: image_silde_ptr});
        }
     }

 }

// 根据模块路径获取所有类
function getAllClass(moduleName){
	var module_path = ''
	try {
		var module = Process.getModuleByName(moduleName)
		module_path = module.path;
	} catch {
		send({msg: `找不到 ${moduleName}moduleq信息,使用默认模块`});
		module_path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
	}
  
	// const char * objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
	var objc_copyClassNamesForImage = new NativeFunction(
		Module.findExportByName(null, 'objc_copyClassNamesForImage'),
		'pointer',
		['pointer', 'pointer']
	);
	// free
	var free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer']);
	


	// create args
	var pPath = Memory.allocUtf8String(module_path);
	var p = Memory.alloc(Process.pointerSize);
	Memory.writeUInt(p, 0);

	var pClasses = objc_copyClassNamesForImage(pPath, p);
	var count = Memory.readUInt(p);
	var classes = new Array(count);

	for (var i = 0; i < count; i++) {
		var pClassName = Memory.readPointer(pClasses.add(i * Process.pointerSize));
		classes[i] = Memory.readUtf8String(pClassName);
	}

	free(pClasses);	 
	return classes;
}

// 根据类名获取所有的方法信息
function getAllMethods(classname){
	var objc_getClass = new NativeFunction(
		Module.findExportByName(null, 'objc_getClass'),
		'pointer',
		['pointer']
	);
	var class_copyMethodList = new NativeFunction(
		Module.findExportByName(null, 'class_copyMethodList'),
		'pointer',
		['pointer', 'pointer']
	);

	var objc_getMetaClass = new NativeFunction(
		Module.findExportByName(null, 'objc_getMetaClass'),
		'pointer',
		['pointer']
	);
	
	var method_getName = new NativeFunction(
		Module.findExportByName(null, 'method_getName'),
		'pointer',
		['pointer']
	);
	
	var free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer']);
	
	// get objclass and metaclass
	var name = Memory.allocUtf8String(classname);
	var objClass = objc_getClass(name)
	var metaClass = objc_getMetaClass(name)
	
	// get obj class all methods
	var size_ptr = Memory.alloc(Process.pointerSize);
	Memory.writeUInt(size_ptr, 0);
	var pObjMethods = class_copyMethodList(objClass, size_ptr);
	var count = Memory.readUInt(size_ptr);
	
	var allMethods = new Array();
	
	var allObjMethods = new Array();
	
	// get obj class all methods name and IMP
	for (var i = 0; i < count; i++) {
		var curObjMethod = new Array();
		
		var pObjMethodSEL = method_getName(pObjMethods.add(i * Process.pointerSize))
		var objMethodIMP = Memory.readPointer(pObjMethodSEL.add(2*Process.pointerSize))
		curObjMethod.push(objMethodIMP)

		try {
			var pObjMethodName = Memory.readCString(Memory.readPointer(pObjMethodSEL))
			curObjMethod.push(pObjMethodName)
		} catch(error) {
			send({msg: `拦截到一个错误: ${error} -------${pObjMethodSEL}`});
		}
		
		allObjMethods.push(curObjMethod)
	}
	
	var allMetaMethods = new Array();
	
	// get meta class all methods name and IMP
	var pMetaMethods = class_copyMethodList(metaClass, size_ptr);
	var count = Memory.readUInt(size_ptr);
	for (var i = 0; i < count; i++) {
		var curMetaMethod = new Array();
		
		var pMetaMethodSEL = method_getName(pMetaMethods.add(i * Process.pointerSize))
		var metaMethodIMP = Memory.readPointer(pMetaMethodSEL.add(2*Process.pointerSize))
		curMetaMethod.push(metaMethodIMP)
		allMetaMethods.push(curMetaMethod)

		try {
			var pMetaMethodName = Memory.readCString(Memory.readPointer(pMetaMethodSEL))
			curMetaMethod.push(pMetaMethodName)
		} catch(error) {
			send({msg: `拦截到一个错误: ${error} -------${pObjMethodSEL}`});
		}
	}
	send({msg: '获取方法结束'});
	allMethods.push(allObjMethods)
	allMethods.push(allMetaMethods)
	
	free(pObjMethods);
	free(pMetaMethods);
	
	return allMethods;
}

// 地址信息
function get_info_form_address(address){
    
    // int dladdr(const void *, Dl_info *);
    
    //typedef struct dl_info {
    //        const char      *dli_fname;     /* Pathname of shared object */
    //        void            *dli_fbase;     /* Base address of shared object */
    //        const char      *dli_sname;     /* Name of nearest symbol */
    //        void            *dli_saddr;     /* Address of nearest symbol */
    //} Dl_info;
    
    var dladdr = new NativeFunction(
        Module.findExportByName(null, 'dladdr'),
        'int',
        ['pointer','pointer']
    );
    
    var dl_info = Memory.alloc(Process.pointerSize*4);

    dladdr(ptr(address),dl_info)

    var dli_fname = Memory.readCString(Memory.readPointer(dl_info))
    var dli_fbase = Memory.readPointer(dl_info.add(Process.pointerSize))
    var dli_sname = Memory.readCString(Memory.readPointer(dl_info.add(Process.pointerSize*2)))
    var dli_saddr = Memory.readPointer(dl_info.add(Process.pointerSize*3))
    
    //XLOG("dli_fname:"+dli_fname)
    //XLOG("dli_fbase:"+dli_fbase)
    //XLOG("dli_sname:"+dli_sname)
    //XLOG("dli_saddr:"+dli_saddr)
    
    var addrInfo = new Array();
    
    addrInfo.push(dli_fname);
    addrInfo.push(dli_fbase);
    addrInfo.push(dli_sname);
    addrInfo.push(dli_saddr);
    send({msg: addrInfo});



}

// 根据模块名称和函数地址 获取符号信息
function find_symbol_from_address(moduleName,addr){
    var frameAddr = addr
    
    var theDis = 0xffffffffffffffff;
    var tmpDis = 0;
    var theClass = "None"
    var theMethodName = "None"
    var theMethodType = "-"
    var theMethodIMP = 0
    
    var allClassInfo = {}


    var allClass = getAllClass(moduleName);
     for(var i = 0, len = allClass.length; i < len; i++){
        var mInfo = getAllMethods(allClass[i]);
        var curClassName = allClass[i]
        var objms = mInfo[0];
        for(var j = 0, olen = objms.length; j < olen; j++){
            var mname = objms[j][0]
            var mIMP = objms[j][1]
            if(frameAddr >= mIMP){
                var tmpDis = frameAddr-mIMP
                if(tmpDis < theDis){
                    theDis = tmpDis
                    theClass = curClassName
                    theMethodName = mname
                    theMethodIMP = mIMP
                    theMethodType = "-"
                }
            }
        }

        var metams = mInfo[1];
        for(var k = 0, mlen = metams.length; k < mlen; k++){
            var mname = metams[k][0]
            var mIMP = metams[k][1]
            if(frameAddr >= mIMP){
                var tmpDis = frameAddr-mIMP
                if(tmpDis < theDis){
                    theDis = tmpDis
                    theClass = curClassName
                    theMethodName = mname
                    theMethodIMP = mIMP
                    theMethodType = "+"
                }
            }
        }
    }

    var symbol = theMethodType+"["+theClass+" "+theMethodName+"]"

    if(symbol.indexOf(".cxx")!=-1){
        symbol = "maybe C function?"
    }
    
    // if distance > 3000, maybe a c function
    if(theDis > 3000){
        symbol = "maybe C function? symbol:" + symbol
    }
    send({msg: `------------------------符号信息${addr}-----------------`});
    send({msg: symbol});
}

function backtrace(onlyMainModule){
    console.log("================================================xCallStackSymbols==========================================")
    function getExeFileName(modulePath){
        modulePath += ""
        return modulePath.split("/").pop()
    }
    
    var mainPath = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
    
    var threadClass = ObjC.classes.NSThread
    var symbols = threadClass["+ callStackSymbols"]()
    var addrs = threadClass["+ callStackReturnAddresses"]()
    var count = addrs["- count"]();

    for(var i = 0, len = count; i < len; i++){
        
        var curAddr = addrs["- objectAtIndex:"](i)["- integerValue"]();
        var info = get_info_form_address(curAddr);
        // skip frida call stack
        if(!info[0]){
            continue;
        }
        
        var dl_symbol = info[2]+""
        var curModulePath = info[0]+""
        
        var fileAddr = curAddr-get_image_vm_slide(curModulePath);

        if (onlyMainModule) {
            if (curModulePath == mainPath) {
                var symbol = find_symbol_from_address(curModulePath,curAddr);
            }else{
                var symbol = info[2];
            }
        }else{
            if((!info[2] || dl_symbol.indexOf("redacted")!=-1) && curModulePath.indexOf("libdyld.dylib") == -1){
                var symbol = find_symbol_from_address(curModulePath,curAddr);
            }else{
                var symbol = info[2];
            }
        }

        console.log(format(i, 4)+format(getExeFileName(info[0]), 20)+"mem:"+format(ptr(curAddr),13)+"file:"+format(ptr(fileAddr),13)+format(symbol,80))
    }
    console.log("==============================================================================================================")
 }
 
function xbacktrace(context){
    console.log("================================================xbacktrace==========================================")
    function getExeFileName(modulePath){
        modulePath += ""
        return modulePath.split("/").pop()
    }
    
    var mainPath = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
    var mainModuleName = getExeFileName(mainPath)
    
    var backtrace = Thread.backtrace(context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress)
    for (var i = 0;i < backtrace.length;i ++)
    {
        var curStackFrame = backtrace[i] + ''
        var curSym = curStackFrame.split("!")[1]
        var curAddr = curStackFrame.split("!")[0].split(" ")[0]
        var curModuleName = curStackFrame.split("!")[0].split(" ")[1]

        var info = get_info_form_address(curAddr);
        // skip frida call stack
        if(!info[0]){
            continue;
        }
        
        var dl_symbol = info[2]+""
        var curModulePath = info[0]+""
        
        var fileAddr = curAddr-get_image_vm_slide(curModulePath);
        
        // is the image in app dir?
        if (curModulePath.indexOf(mainModuleName) != -1 ) {
            curSym = find_symbol_from_address(curModulePath,curAddr);
        }
        console.log(format(i, 4)+format(getExeFileName(curModulePath), 20)+"mem:"+format(ptr(curAddr),13)+"file:"+format(ptr(fileAddr),13)+format(curSym,80))
    }
    return

}
// 处理终端输入过来的指令
function handleMessage(message) {
	var parameter_1 = message.substring(0, 1)
    if (parameter_1 == 'c') {
		var parameter_2 = message.substring(2);
		const all_class = getAllClass(parameter_2);
		send({msg: all_class});
		send({msg: '获取类信息结束'});
    }

	else if (parameter_1 == 'm') {
		var parameter_2 = message.substring(2);
		var all_methods = getAllMethods(parameter_2);
		send({msg: all_methods});
		send({msg: '获取方法结束'});
	}
	else if (parameter_1 == 'a') {
		var parameter_2 = message.substring(2);
		// get_info_form_address(parameter_2);
		var symbol_info =  DebugSymbol.fromAddress(ptr(parameter_2));
		send({msg: `${symbol_info}`});
		send({msg: '获取地信息结束'});
	}
	else if (parameter_1 == 'n') {
		var parameter_2 = message.substring(2);
		var symbol_info =  DebugSymbol.findFunctionsNamed(parameter_2);
		send({msg: `${symbol_info.toString()}`});
		send({msg: '获取符号信息结束'});
	}

	else if (parameter_1 == 's') {
		var arr = message.split(' ');
		send({msg: arr});
		if (arr.length != 3) {
			send({msg: '参数有误'});
			return;
		}
		var parameter_2 = arr[1];
		var parameter_3 = arr[2];
		find_symbol_from_address(parameter_2, parameter_3);
		send({msg: '获取符号信息结束'});
	}

	else if (parameter_1 == 'q') {
		send({quit: "退出程序"});
	}
	// 响应python传递过来的方法
	recv(handleMessage);
}

// 响应python传递过来的方法
recv(handleMessage);