Frida中ObjC的参数

  • 当触发到Interceptor.attachonEnter函数后
    • 举例:相关的典型的代码
      function hook_specific_method_of_class(className, funcName)
      {
        var curClass = ObjC.classes[className];
        if (typeof(curClass) !== 'undefined') {
          var curMethod = curClass[funcName];
          if (typeof(curMethod) !== 'undefined') {
            Interceptor.attach(curMethod.implementation, {
              onEnter: function(args) {
        ...
      
  • 传入Frida中iOSObjC的参数 == ObjC的底层函数objc_msgSend的参数
    • 注:objc_msgSend函数的定义
      id objc_msgSend(id self, SEL _cmd, ...)
      
  • 此时默认参数名叫做args的参数 == 是一个参数的数组列表,每个元素的类型是NativePointer
  • 此时对应(Frida中iOSObjC)的参数逻辑是:
    • 第一个参数 = args[0] == objc_msgSendself(类型是id)
    • 第二个参数 = args[1] == objc_msgSend_cmd(类型是SEL
    • 后续才是:真正的函数的参数
      • 函数的真正的第1个参数 = args[2]
      • 函数的真正的第2个参数 = args[3]
      • ...

Frida中获取ObjC的参数

第一个参数args[0]

  • 获取第一个参数args[0]
      // args[0] is self = id
      const argSelf = args[0];
      console.log("argSelf: ", argSelf);
    
    • 典型log输出
      argSelf:  0x105744c00
      

第二个参数args[1]

获取第二个参数args[1]

  // args[1] is selector
  const argSel = args[1];
  console.log("argSel: ", argSel);
  • 典型log输出
    argSelf:  0x105744c00
    

ObjC函数的真正参数

  • 获取 函数的真正的第1个参数 = args[2]
      // args[2] holds the first function argument
      const args2 = args[2];
      console.log("args2: ", args2);
    
    • 典型log输出
      args2:  0x105743850
      
  • 获取 函数的其他更多参数(如果有的话)

      const args3 = args[3];
      console.log("args3: ", args3);
    
      const args4 = args[4];
      console.log("args4: ", args4);
      ...
    

计算ObjC的函数的真正参数的个数 + 打印全部参数


TODO:把这部分代码,整理成 独立的工具类函数,转移到:工具类函数


根据ObjC的基础知识,目前得知:可以通过SEL=selector字符串中的:=冒号的个数,判断后续真正有几个参数

所以,可以用如下代码,循环的、批量的、挨个、打印所有参数:

/** Function that count occurrences of a substring in a string;
 * @param {String} string               The string
 * @param {String} subString            The sub string to search for
 * @param {Boolean} [allowOverlapping]  Optional. (Default:false)
 *
 * @author Vitim.us https://gist.github.com/victornpb/7736865
 * @see Unit Test https://jsfiddle.net/Victornpb/5axuh96u/
 * @see https://stackoverflow.com/a/7924240/938822
 */
function occurrences(string, subString, allowOverlapping) {
    // console.log("string=" + string + ",subString=" + subString + ", allowOverlapping=" + allowOverlapping)
    string += "";
    subString += "";
    if (subString.length <= 0) return (string.length + 1);

    var n = 0,
        pos = 0,
        step = allowOverlapping ? 1 : subString.length;

    while (true) {
        pos = string.indexOf(subString, pos);
        // console.log("pos=" + pos)
        if (pos >= 0) {
            ++n;
            pos += step;
        } else break;
    }

    return n;
}

...

function hook_specific_method_of_class(className, funcName)
{
        var iOSObjCallStr = toiOSObjcCall(className, funcName)
    // var hook = ObjC.classes[className][funcName];
    // var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');
        var curClass = ObjC.classes[className];
        if (typeof(curClass) !== 'undefined') {
            var curMethod = curClass[funcName];
            if (typeof(curMethod) !== 'undefined') {
                Interceptor.attach(curMethod.implementation, {
                    onEnter: function(args) {
                        console.log("=========== [*] Detected call to: " + iOSObjCallStr);
            ...

            const argSelStr = ObjC.selectorAsString(argSel);
            console.log("argSelStr: ", argSelStr);
            const argCount = occurrences(argSelStr, ":");

            // console.log("funcName=", funcName);
            // const argCount = occurrences(funcName, ":");
            // console.log("argCount: ", argCount);

            for (let curArgIdx = 0; curArgIdx < argCount; curArgIdx++) {
              const curArg = args[curArgIdx + 2];

              // const usePtr = false;
              const usePtr = true;
              if (usePtr) {
                // console.log("usePtr=", usePtr);
                const curArgPtr = ptr(curArg);
                console.log("---------- [" + curArgIdx + "] curArgPtr=" + curArgPtr);
                if (!curArgPtr.isNull()) {
                  const curArgPtrObj = new ObjC.Object(curArgPtr);
                  console.log("curArgPtrObj: ", curArgPtrObj);
                  console.log("curArgPtrObj className: ", curArgPtrObj.$className);
                }
              } else {
                console.log("---------- [" + curArgIdx + "] curArg=" + curArg);
                if (curArg && (curArg != 0x0)) {
                  // console.log("curArg className: ", curArg.$className);
                  const curArgObj = new ObjC.Object(curArg);
                  console.log("curArgObj: ", curArgObj);
                  console.log("curArgObj className: ", curArgObj.$className);
                }
              }
            }

            ...

某次的输出日志的效果:

=========== [*] Detected call to: +[NSURLRequest requestWithURL:cachePolicy:timeoutInterval:]
funcName= + requestWithURL:cachePolicy:timeoutInterval:
argSelStr:  requestWithURL:cachePolicy:timeoutInterval:
argCount:  3
---------- [0] curArgPtr=0x2831f2530
curArgPtrObj:  https://setup.icloud.com/setup/signin/v2/login
curArgPtrObj className:  NSURL
---------- [1] curArgPtr=0x0
---------- [2] curArgPtr=0x0
  • 注意:
    • 当Frida调试期间,输出log日志太多,或者打印参数太多时,经常会导致app或进程崩溃而无法继续调试
      • 所以当遇到Frida调试导致的崩溃问题时,可以适当的减少log日志的输出,以缓解崩溃问题

results matching ""

    No results matching ""