Logo
Overview

frida-ios-playground部分题解

August 5, 2025

在找资料的时候看到了这个playground,那就做一下?‣

BASIC

if(ObjC.available) {
    console.log("ObjC.available: true");
    const VulnerableVault = ObjC.classes.VulnerableVault;
    // -[VulnerableVault setSecretInt:]
    const VulnerableVault_setSecretInVault = VulnerableVault["- setSecretInt:"]
    const VulnerableVault_setSecretInVault_old = VulnerableVault_setSecretInVault.implementation;
    VulnerableVault_setSecretInVault.implementation = ObjC.implement(VulnerableVault_setSecretInVault, (self,sel,any) => {
        console.warn(`----------\nI: -[VulnerableVault setSecretInVault:] (${any})`);
        let retval = VulnerableVault_setSecretInVault_old(self,sel,any);
        console.warn(`O: -[VulnerableVault setSecretInVault:] (${retval})\n----------`);
        return retval;
    });
    // -[VulnerableVault setSecretNumber:]
    const VulnerableVault_setSecretNumber = VulnerableVault["- setSecretNumber:"]
    const VulnerableVault_setSecretNumber_old = VulnerableVault_setSecretNumber.implementation;
    VulnerableVault_setSecretNumber.implementation = ObjC.implement(VulnerableVault_setSecretNumber, (self,sel,any) => {
        console.warn(`----------\nI: -[VulnerableVault setSecretNumber:] (${new ObjC.Object(any)})`);
        let retval = VulnerableVault_setSecretNumber_old(self,sel,any);
        console.warn(`O: -[VulnerableVault setSecretNumber:] (${retval})\n----------`);
        return retval;
    });
    // -[VulnerableVault setSecretString:]
    const VulnerableVault_setSecretString = VulnerableVault["- setSecretString:"]
    const VulnerableVault_setSecretString_old = VulnerableVault_setSecretString.implementation;
    VulnerableVault_setSecretString.implementation = ObjC.implement(VulnerableVault_setSecretString, (self,sel,any) => {
        console.warn(`----------\nI: -[VulnerableVault setSecretString:] (${new ObjC.Object(any)})`);
        let retval = VulnerableVault_setSecretString_old(self,sel,any);
        console.warn(`O: -[VulnerableVault setSecretString:] (${retval})\n----------`);
        return retval;
    });
    // -[VulnerableVault winIfTrue:]
    const VulnerableVault_winIfTrue = VulnerableVault["- winIfTrue:"]
    const VulnerableVault_winIfTrue_old = VulnerableVault_winIfTrue.implementation;
    VulnerableVault_winIfTrue.implementation = ObjC.implement(VulnerableVault_winIfTrue, (self,sel,any) => {
        console.warn(`----------\nI: -[VulnerableVault winIfTrue:] (${any}) -> (1)`);
        let retval = VulnerableVault_winIfTrue_old(self,sel,1);
        console.warn(`O: -[VulnerableVault winIfTrue:] (${retval})\n----------`);
        return retval;
    });
    // -[VulnerableVault getSecretString:]
    const VulnerableVault_getSecretString = VulnerableVault["- getSecretString"]
    const VulnerableVault_getSecretString_old = VulnerableVault_getSecretString.implementation;
    VulnerableVault_getSecretString.implementation = ObjC.implement(VulnerableVault_getSecretString, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault getSecretString:]`);
        let retval = VulnerableVault_getSecretString_old(self,sel);
        console.warn(`O: -[VulnerableVault getSecretString:] (${new ObjC.Object(retval).toString()})\n----------`);
        return retval;
    });
    // -[VulnerableVault hasWon]
    const VulnerableVault_hasWon = VulnerableVault["- hasWon"]
    const VulnerableVault_hasWon_old = VulnerableVault_hasWon.implementation;
    VulnerableVault_hasWon.implementation = ObjC.implement(VulnerableVault_hasWon, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault hasWon]`);
        let retval = VulnerableVault_hasWon_old(self,sel);
        console.warn(`O: -[VulnerableVault hasWon:] (${retval}) -> (1)\n----------`);
        return 1;
    });
    // -[VulnerableVault getSecretKey:]
    const VulnerableVault_getSecretKey = VulnerableVault["- getSecretKey"]
    const VulnerableVault_getSecretKey_old = VulnerableVault_getSecretKey.implementation;
    VulnerableVault_getSecretKey.implementation = ObjC.implement(VulnerableVault_getSecretKey, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault getSecretKey:]`);
        let retval = VulnerableVault_getSecretKey_old(self,sel); // is a byte
        let retval_str = ObjC.classes.NSString.alloc()["- initWithData:encoding:"](retval, 4);
        console.warn(`O: -[VulnerableVault getSecretKey:] bytes(${retval_str})\n----------`);
        return retval;
    });
    // -[VulnerableVault getself]
    const VulnerableVault_getself = VulnerableVault["- getself"]
    const VulnerableVault_getself_old = VulnerableVault_getself.implementation;
    VulnerableVault_getself.implementation = ObjC.implement(VulnerableVault_getself, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault getself:]`);
        let retval = VulnerableVault_getself_old(self,sel);
        console.warn(`O: -[VulnerableVault getself:] (${retval})\n----------`);
        // call self.winIfFrida_and27042_
        let vault = new ObjC.Object(retval)
        // vault.winIfFrida_and_27042_("Frida",27042);
        vault["- winIfFrida:and27042:"]("Frida",27042);
        // new ObjC.Object(retval)["- win"]();
        return retval;
    });
    // -[VulnerableVault doNothing]
    const VulnerableVault_doNothing = VulnerableVault["- doNothing"]
    const VulnerableVault_doNothing_old = VulnerableVault_doNothing.implementation;
    VulnerableVault_doNothing.implementation = ObjC.implement(VulnerableVault_doNothing, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault doNothing:]`);
        let retval = VulnerableVault_doNothing_old(self,sel);
        let HiddenVault = ObjC.classes.HiddenVault;
        let hiddenVault = ObjC.chooseSync(HiddenVault)[0];
        // hiddenVault["- win"]();
        hiddenVault["- super_secret_function"]();
        console.warn(`O: -[VulnerableVault doNothing:] (${retval})\n----------`);
        return retval;
    });
    // -[VulnerableVault generateNumbers]
    const VulnerableVault_generateNumbers = VulnerableVault["- generateNumbers"]
    const VulnerableVault_generateNumbers_old = VulnerableVault_generateNumbers.implementation;
    VulnerableVault_generateNumbers.implementation = ObjC.implement(VulnerableVault_generateNumbers, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault generateNumbers:]`);
        let retval = VulnerableVault_generateNumbers_old(self,sel);
        let retval_obj = new ObjC.Object(retval)
        for(let i = 0; i < retval_obj["- count"](); i++) {
            let number = retval_obj["- objectAtIndex:"](i)
            if(number > 42) {
                retval_obj["- setObject:atIndex:"](42,i);
            }
        }
        console.warn(`O: -[VulnerableVault generateNumbers:] (${retval})\n----------`);
        return retval;
    });
    console.log("Hooked");
}

Advanced

if(ObjC.available) {
    // -[VulnerableVault lose]
    const VulnerableVault = ObjC.classes.VulnerableVault;
    const VulnerableVault_lose = VulnerableVault["- lose"]
    const VulnerableVault_lose_old = VulnerableVault_lose.implementation;
    VulnerableVault_lose.implementation = ObjC.implement(VulnerableVault_lose, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault lose:]`);
        // let retval = VulnerableVault_lose_old(self,sel);
        let self_obj = new ObjC.Object(self);
        let retval = self_obj["- win"]();
        console.warn(`O: -[VulnerableVault lose:] (${retval})\n----------`);
        return retval;
    });
    // another way to do it
    // Interceptor.replace(VulnerableVault_lose_old, new NativeCallback(args => {
    //     console.warn(`----------\nI: -[VulnerableVault lose:]`);
    //     let self_obj = new ObjC.Object(args[0]);
    //     let retval = self_obj["- win"]();
    //     console.warn(`O: -[VulnerableVault lose:] (${retval})\n----------`);
    //     return retval;
    // }, 'void', ['pointer', 'pointer']))
    // 这里几个Native都爆炸了,应该写不了,或者是在ida里看然后走hex地址patch。
    
}

Interact with UI

function convertDict(dict){
    let keys = dict.allKeys();
    let ob = {};
    for (let index = 0; index < keys.count(); index++) {
        let k = keys.objectAtIndex_(index);
        let v = dict.objectForKey_(k);
        if (["svce", "pdmn" ,"mdat", "cdat", "agrp"].includes(k.toString())) {
            v = new ObjC.Object(v).toString()
        }
        if(k == "acct" || k == "v_Data"){
            let data = new ObjC.Object(v)
            v = data.bytes().readUtf8String(data.length());
        }
        ob[k] = v;
    }
    return ob;
}

if(ObjC.available) {
    function getConstant(name){
        var pptr = Module.getGlobalExportByName(name);
        // return ObjC.Object(readPointer(pptr));
        return new ObjC.Object(pptr);
    }
    const UIAlertController = ObjC.classes.UIAlertController;
    const UIAlertAction = ObjC.classes.UIAlertAction;
    const UIApplication = ObjC.classes.UIApplication;

    console.log("ObjC.available: true");
    // -[VulnerableVault doNothing]
    const VulnerableVault = ObjC.classes.VulnerableVault;
    const VulnerableVault_doNothing = VulnerableVault["- doNothing"]
    const VulnerableVault_doNothing_old = VulnerableVault_doNothing.implementation;
    VulnerableVault_doNothing.implementation = ObjC.implement(VulnerableVault_doNothing, (self,sel) => {
        console.warn(`----------\nI: -[VulnerableVault doNothing:]`);
        let retval = VulnerableVault_doNothing_old(self,sel);
        console.warn(`O: -[VulnerableVault doNothing:] (${retval})\n----------`);
        const handler = new ObjC.Block({
            retType: "void",
            argTypes: ["object"],
            implementation: () => {}
        })
        ObjC.schedule(ObjC.mainQueue, () => {
            let alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_("Alert", "Called by Frida", 1);
            let defaultAction = UIAlertAction.actionWithTitle_style_handler_("OK", 1, handler);
            alert.addAction_(defaultAction);
            UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL);
        })
        return retval;
    });
    console.log("Hooked")
}

Keychain


    // -[VulnerableVault saveKey]
    (() => {
        console.log("Hook SecItemAdd")
        let Security = Process.enumerateModules().find(m => m.name === "Security");
        let SecItemAdd = Security.findExportByName("SecItemAdd");
        Interceptor.attach(SecItemAdd, {
            onEnter: function(args) {
                console.log(JSON.stringify(convertDict(ObjC.Object(args[0])), null, 2))
            }
        })
    })();
    // dump keychain
    (() => {
        console.log("Dump keychain")
        let Security = Process.enumerateModules().find(m => m.name === "Security");
        let getConstWrapper = (module, name) => ObjC.Object(ptr(module.findExportByName(name)).readPointer())
        let kCFBooleanTrue = getConstWrapper(Security, "kCFBooleanTrue");
        let kSecReturnAttributes = getConstWrapper(Security, "kSecReturnAttributes");
        let kSecMatchLimitAll = getConstWrapper(Security, "kSecMatchLimitAll");
        let kSecMatchLimit = getConstWrapper(Security, "kSecMatchLimit");
        let kSecClassGenericPassword = getConstWrapper(Security, "kSecClassGenericPassword");
        let kSecClassInternetPassword = getConstWrapper(Security, "kSecClassInternetPassword");
        let kSecClassCertificate = getConstWrapper(Security, "kSecClassCertificate");
        let kSecClassKey = getConstWrapper(Security, "kSecClassKey");
        let kSecClassIdentity = getConstWrapper(Security, "kSecClassIdentity");
        let kSecClass = getConstWrapper(Security, "kSecClass");

        let NSMutableDictionary = ObjC.classes.NSMutableDictionary;
        let query = NSMutableDictionary.alloc().init();

        let SecItemCopyMatching = new NativeFunction(Security.findExportByName("SecItemCopyMatching"), "int", ["pointer", "pointer"]);
        [kSecClassGenericPassword, kSecClassInternetPassword, kSecClassCertificate, kSecClassKey, kSecClassIdentity].forEach(secItemClass => {
            query.setObject_forKey_(kCFBooleanTrue, kSecReturnAttributes);
            query.setObject_forKey_(kSecMatchLimitAll, kSecMatchLimit);
            query.setObject_forKey_(kSecClassGenericPassword, kSecClass);
            var result = Memory.alloc(8);
            result.writePointer(ptr("0"));
            SecItemCopyMatching(query.handle, result);
            var pt = result.readPointer();
            if (!pt.isNull()) {
                console.log(ObjC.Object(pt).toString());
            }
        });
    });
    

Biometry

if(ObjC.available) {
    const pendingBlocks = new Set();
    var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
    Interceptor.attach(hook.implementation, {
        onEnter: function(args) {
            console.log("Hooking Touch Id..")
            console.log(args[4])
            var block = new ObjC.Block(args[4]);
            pendingBlocks.add(block); // Keep it alive
            
            const appCallback = block.implementation;
            block.implementation = function (error, value)  {
                const result = appCallback(1, null);
                pendingBlocks.delete(block);

                return result;
            };
        },
    });
    console.log("Hooked")
}

这个没有entitlement也许大概是用不了?

NSUserDefaults

if(ObjC.available) {
    const pendingBlocks = new Set();
    const NSUserDefaults = ObjC.classes.NSUserDefaults;
    // setObject:forKey:
    const NSUserDefaults_setObject_forKey = NSUserDefaults["- setObject:forKey:"]
    const NSUserDefaults_setObject_forKey_old = NSUserDefaults_setObject_forKey.implementation;
    NSUserDefaults_setObject_forKey.implementation = ObjC.implement(NSUserDefaults_setObject_forKey, (self,sel,any,any2) => {
        console.warn(`----------\nI: -[NSUserDefaults setObject:forKey:] (${new ObjC.Object(any)}) (${new ObjC.Object(any2)})`);
        let retval = NSUserDefaults_setObject_forKey_old(self,sel,any,any2);
        console.warn(`O: -[NSUserDefaults setObject:forKey:] (${retval})\n----------`);
        if(new ObjC.Object(any2).toString() == "bio") {
            console.log("bio")
            console.log(new ObjC.Object(any).toString())
        }
        return retval;
    });
    // standardUserDefaults
    const NSUserDefaults_standardUserDefaults = NSUserDefaults["+ standardUserDefaults"] // class method, will not require any instance.
    console.log(NSUserDefaults["+ standardUserDefaults"]().dictionaryRepresentation().toString())
    console.log("Hooked")
}

Detect Frida/Jailbreak

27042,改端口就好了。看源码发现是这样:

所以就是尝试bind,如果没bind上说明被占用了,那么solution就是把bind给干掉(一般场景还是不能这么干的,会有问题)

同样hook掉_dyld_get_image_name

这个是一样的,把_dyld_get_image_name换成安全状态下的。

comment

留言 / 评论

如果暂时没有看到评论,请点击下方按钮重新加载。