if (ObjC.available)
{
try
{
//hook - ZYOperationView operationCopyLink
var className = "ZYMediaDownloadHelper";
var funcName = "+ downloadMediaUrl:isVideo:progress:finishBlock:";
var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
// args[0] is self
// args[1] is selector (SEL "sendMessageWithText:")
// args[2] holds the first function argument, an NSString
var mainPath = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
XLOG(mainPath)
var slide = get_image_vm_slide(mainPath)
XLOG(slide)
}
});
}
catch(err)
{
console.log("[!] Exception2: " + err.message);
}
}
else
{
console.log("[-] Objective-C Runtime is not available!");
}
if(ObjC.available){ //判断Object-C类方法是否已经加载进来
console.log('\n[*] Starting Hooking');
var _className = "JailbreakDetectionVC"; //类名
var _methodName = "- isJailbroken"; //方法名
var hooking = ObjC.classes[_className][_methodName]; //通过ObjC.classes返回当前注册类的映射表找到想要hook的类名、方法名
console.log('className is: ' + _className + ' and methodName is: ' + _methodName);
Interceptor.attach(hooking.implementation,{ //Interceptor.attach拦截函数 hooking.implementation即我们需要拦截的函数地址 是有一个
//NativePointer参数
//onEnter.function(args)被拦截函数调用之前回调 其中原始函数的参数使用args数组 有使用过Xposed则它有点类似于 XPosed 的 beforeHookedMethod
onEnter: function(args) {
//args[0]:self
//args[1]:The selector
//args[2]:方法的第一个参数开始
//如下代码则是我们在函数调用之前 打印函数的调用堆栈 便于回溯函数的整个调用过程
console.log(' hook success ')
this._className = ObjC.Object(args[0]).toString();
this._methodName = ObjC.selectorAsString(args[1]);
console.log('Detected call to: ');
console.log(' ' + this._className + ' --> ' + this._methodName);
console.log('isJailbroken called from:\n' + Thread.backtrace(this.context,Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n') + '\n');
//print_arguments(args);
},
//onLeave.function(returnValue)被拦截函数调用之后回调 其中returnValue表示原始函数的返回值
//有使用过Xposed则它有点类似于 Xposed 的 afterHookedMethod
onLeave:function(returnValue){
//如下代码则是我们在函数调用之后 打印函数的返回值及函数返回值类型
console.log('Return value of: ');
console.log(' ' + this._className + ' --> ' + this._methodName);
var typeValue = Object.prototype.toString.call(returnValue);
console.log("\t[-] Type of return value: " + typeValue);
console.log("\t[-] Return Value: " + returnValue);
}
});
}
//如下代码则是我们在函数调用之后 打印函数的返回值及函数返回值类型
var method = ObjC.Object(args[4]).toString();//NSString
var typeValue = Object.prototype.toString.call(args[3]);
var Name = ObjC.Object(args[2]).toString();
// console.log("\t[-] Type of return value: " + typeValue);
console.log("\t[-] _requestWithURLString method: " + method);
console.log("\t[-] _requestWithURLString Name: " + Name);
//if(method == "GET")
{
//[NSThread callStackSymbols];
console.log(' processJsonObject callStackSymbols statr')
var NSTharr = ObjC.classes.NSThread.callStackSymbols();
//NSString *tempString = [mutableArray componentsJoinedByString:@","];--分隔符 将array数组转换为string字符串
console.log('processJsonObject call to: \n'+NSTharr.componentsJoinedByString_('\n'));
console.log(' processJsonObject callStackSymbols ===end ')
{
//[NSThread callStackSymbols];
console.log(' processJsonObject callStackSymbols statr')
var NSTharr = ObjC.classes.NSThread.callStackSymbols();
//NSString *tempString = [mutableArray componentsJoinedByString:@","];--分隔符 将array数组转换为string字符串
console.log('processJsonObject call to: \n'+NSTharr.componentsJoinedByString_('\n'));
console.log(' processJsonObject callStackSymbols ===end ')
//===========
// xia0 log
function XLOG(log) {
console.log("[*] " + log)
}
function XLibLOG(log) {
console.log(log)
}
// format string with width
function format(str,width){
str = str + ""
var len = str.length;
if(len > width){
return str
}
for(var i = 0; i < width-len; i++){
str += " "
}
return str;
}
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) {
//XLOG(Memory.readUtf8String(image_name_ptr) + " slide:"+image_silde_ptr)
return image_silde_ptr;
}
//XLOG(Memory.readUtf8String(image_name_ptr) + "slide:"+image_silde_ptr)
}
return 0;
}
function get_all_objc_class(modulePath){
// 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']);
// if given modulePath nil, default is mainBundle
if(!modulePath){
var path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
}else{
var path = modulePath;
}
// create args
var pPath = Memory.allocUtf8String(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);
// XLOG(classes)
return classes;
}
function get_all_class_method(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 pObjMethodName = Memory.readCString(Memory.readPointer(pObjMethodSEL))
var objMethodIMP = Memory.readPointer(pObjMethodSEL.add(2*Process.pointerSize))
// XLOG("-["+classname+ " " + pObjMethodName+"]" + ":" + objMethodIMP)
curObjMethod.push(pObjMethodName)
curObjMethod.push(objMethodIMP)
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 pMetaMethodName = Memory.readCString(Memory.readPointer(pMetaMethodSEL))
var metaMethodIMP = Memory.readPointer(pMetaMethodSEL.add(2*Process.pointerSize))
//XLOG("+["+classname+ " " + pMetaMethodName+"]" + ":" + metaMethodIMP)
curMetaMethod.push(pMetaMethodName)
curMetaMethod.push(metaMethodIMP)
allMetaMethods.push(curMetaMethod)
}
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);
//XLOG(addrInfo)
return addrInfo;
}
function find_symbol_from_address(modulePath,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 = get_all_objc_class(modulePath);
for(var i = 0, len = allClass.length; i < len; i++){
var mInfo = get_all_class_method(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
}
return symbol;
}
//================================
var onlyMainModule=this.context;
function getExeFileName(modulePath){
modulePath += ""
return modulePath.split("/").pop()
}
var mainPath = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
var mainModuleName = getExeFileName(mainPath)
var backtrace = Thread.backtrace(this.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]){
console.log(' processJsonObject get_info_form_address ===nli ');
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];
}
}
XLOG(format(i, 4)+format(getExeFileName(info[0]), 20)+"mem:"+format(ptr(curAddr),13)+"file:"+format(ptr(fileAddr),13)+format(symbol,80))
}
return;
}
var nsDictArg = new ObjC.Object(args[3]); // NSDictionary
//https://blog.csdn.net/dieyu4807/article/details/102296246 参照
// convert nsDictArg to JSON
var err = ptr(ObjC.classes.NSError.alloc())
//NSData *jsonData = [NSJSONSerialization dataWithJSONObject:objct options:0 error:&error];
var json = ObjC.classes.NSJSONSerialization.dataWithJSONObject_options_error_(nsDictArg , 0, err);
// let jsonStr = json.bytes().readUtf8String(json.length());
//-var jsonBinary = json.bytes().readByteArray(json.length());
//jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
var nsting = ObjC.classes.NSString.alloc();
var jsonBinary = nsting.initWithData_encoding_(json , 0);
// Send JSON data back to Frida
console.log("\t[-] _requestWithURLString params: " + jsonBinary);
//如下代码则是我们在函数调用之前 打印函数的调用堆栈 便于回溯函数的整个调用过程
console.log(' hook success ')
this._className = ObjC.Object(args[0]).toString();
this._methodName = ObjC.selectorAsString(args[1]);
console.log('Detected call to: ');
console.log(' ' + this._className + ' --> ' + this._methodName);
console.log('isJailbroken called from:\n' + Thread.backtrace(this.context,Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n') + '\n');
//[NSThread callStackSymbols];
console.log(' hook callStackSymbols statr')
var NSTharr = ObjC.classes.NSThread.callStackSymbols();
console.log(' hook callStackSymbols ===end ')