keniu007 发表于 2024-6-1 18:07

新人求助,Mac平台,Swift 语言一款软件的frida hook

目前我在练习的这软件,接口请求时,客户端会生成一个 JWT (HS256),我希望通过动态分析。找到生成 JWT 的 Secret

目前用 hopper 已经找到了我要抓数据包(请求参数中 JWT 生成)的位置。如下图




根据查阅资料,看到 sub_10103a838 可以使用 0x0103a838 这个内存地址调试,在 frida 使用以下函数,确实可以调试,但是 onEnter 进来之后,这个 swift 对象没有办法正常打印。也就无从得知 jwt 的 secret 。

/**
        * 根据module名字和目标方法的偏移地址获得方法的绝对地址
        */
function get_func_addr(module, offset) {
        // 根据名字获取module地址
        var base_addr = Module.findBaseAddress(module);
        console.log("base_addr: " + base_addr);
        console.log(hexdump(ptr(base_addr), {
                length: 16,
                header: true,
                ansi: true
        }));
        var func_addr = base_addr.add(offset);
        var return_addr;
        if (Process.arch == 'arm')
                return_addr = func_addr.add(1);//如果是32位地址+1
        else
                return_addr = func_addr;
        console.log('func_addr: ' + return_addr);
        console.log(hexdump(ptr(return_addr), {
                length: 16,
                header: true,
                ansi: true
        }));


        Interceptor.attach(ptr(return_addr), {
                // 接受可变参数
                onEnter: hook,
                onLeave: function (retval) {
                        send("====onLeave=====");
                        send("retval: " + retval);
                }
        });

        return return_addr;
}


console.log("[+] script started...")
if (ObjC.available) {
    if (Process.isDebuggerAttached() === true) {
      console.log("[+] Debugger attached, in addition to Frida.");
    }
    var a = Process.arch.toString()
        get_func_addr("Raycast", "0x0103a838");
    console.log("[+] Device chip: " + a);

   
} else {
    console.log("[+] Objective-C Runtime not available!");
}
console.log("[+] ...script completed")



function hook(args) {
        console.log("=================Hooked=================")
    // String is parsed by value
    let ptr1 = args;
    let ptr2 = args;

        console.log(ptr1)
        console.log(ptr2)
       

   
    let ptr1hex = '0x' + ptr1.toString(16);
    let ptr2hex = '0x' + ptr2.toString(16);

    let ptr1value = BigInt(ptr1hex);
    let ptr2value = BigInt(ptr2hex);
    let smallObject = ptr2value & 0xFFn; // the last byte
   
    // first, try parse smallstring
    if (isSmallString(smallObject)) {
      let smallStr = new SDSwiftSmallString(ptr1hex, ptr2hex);
      console.log(`${smallStr.desc()}`)
                return
    }
   
    // Large String
    let largeStr = new SDSwiftLargeString(ptr1value, ptr2value);
    console.log(`${largeStr.desc()}`)
}


function isSmallString(value) {
    let smth = (value >> 4n) & 0xFn;
    return (smth & 2n) > 0n;
}


class SDSwiftSmallString {
    strValue;
    count;
    isHex;
    constructor(h1, h2) {
      // max small string length is 15 bytes
      let h1Array = hexStrToUIntArray(h1).reverse();
      let h2Array = hexStrToUIntArray(h2).reverse();
      function isValidChar(element, index, array) {
            return (element > 0);
      }
      let dataArr = h1Array.concat(h2Array).slice(0, 15);
      
      let data = dataArr.filter(isValidChar);
      let str = String.fromCharCode.apply(null, data);
      if (isPrintableString(str)) {
            this.strValue = str;
            this.count = str.length;
            this.isHex = false;
      } else {
            this.strValue = uintArrayToHexStr(dataArr)
            this.count = dataArr.length;
            this.isHex = true;
      }
      
    }

    desc() {
      let hexTip = this.isHex ? "hex" : "str";
      return `<Swift.String(Small), count=${this.count}, ${hexTip}='${this.strValue}'>`;
    }
}


class SDSwiftLargeString {
    // https://github.com/TannerJin/Swift-MemoryLayout/blob/master/SwiftCore/String.swift

    _countAndFlagsBits;
    _object;

    isASCII;
    isNFC;
    isNativelyStored;
    isTailAllocated;

    count;
    strValue;

    constructor(inCountAndFlag, inObject) {
      this._countAndFlagsBits = inCountAndFlag;
      this._object = inObject;

      // 1. parse _countAndFlagsBits
      let abcd = inCountAndFlag >> 48n >> 12n & 0xFn; // 16bits, 2bytes
      this.isASCII = (abcd & 0x8n) > 0n;
      this.isNFC = (abcd & 0x4n) > 0n;
      this.isNativelyStored = (abcd & 0x2n) > 0n;
      this.isTailAllocated = (abcd & 0x1n) > 0n;

      this.count = inCountAndFlag & 0xFFFFFFFFFFFFn ; // 48bits,6bytes

      // 2. parse _object
      let objectFlag = inObject >> 56n & 0xFFn;
      let strAddress = '0x' + (inObject & 0xFFFFFFFFFFFFFFn).toString(16); // low 56 bits
      
      let strPtr = new NativePointer(strAddress);
      let cstrPtr = strPtr.add(32);
      this.strValue = cstrPtr.readCString() ?? "";
    }

    desc() {
      return `<Swift.String(Large), count=${this.count}, str='${this.strValue}'>`;
    }
}


function isPrintableChar(val) {
    //
    //0-90x30-0x39
    //A-Z0x41-0x5a
    //a-z97-122
    //0x5f 0x24 0x20
    let isNumber = (val >= 0x30 && val <= 0x39);
    let isUpper = (val >= 0x41 && val <= 0x5a);
    let isLower = (val >= 0x61 && val <= 0x7a);
    let isSpecial = (val == 0x5f) || (val == 0x24) || (val == 0x20);
    return isNumber || isUpper || isLower || isSpecial;
}

function isPrintableString(str) {
    for(var i = 0; i < str.length; i++) {
      let val = str.charCodeAt(i);
      if (!isPrintableChar(val)) {
            return false;
      }
    }
    return true;
}


function hexString(str) {
    var ret = "0x";
    for(var i = 0; i < str.length; i++) {
      let val = str.charCodeAt(i);
      var valstr = val.toString(16);
      if (valstr.length == 1) {
            valstr = '0' + valstr;
      }
      ret = ret + valstr;
    }
    return ret;
}

function readUCharHexString(ptr, maxlen) {
    var idx = 0;
    var hexStr = "";
    while (true) {
      let val = ptr.add(idx).readU8()
      if (val == 0) {
            break;
      }
      var valstr = val.toString(16);
      if (valstr.length == 1) {
            valstr = '0' + valstr;
      }
      hexStr += valstr;
      idx++;
      if (idx >= maxlen) {
            break;
      }
    }

    if (hexStr.length > 0) {
      hexStr = "0x" + hexStr;
    }

    return hexStr;
}

function swapInt16(val) {
    return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}

function swapInt32(val) {
    return (
      ((val & 0xff) << 24) |
      ((val & 0xff00) << 8) |
      ((val & 0xff0000) >> 8) |
      ((val >> 24) & 0xff)
    );
}

function hexStrToUIntArray(inputStr) {
    var str = inputStr
    if (str.startsWith('0x')) {
      str = str.substr(2);
    }
    var hex = str.toString();
    var result = [];
    for (var n = 0; n < hex.length; n += 2) {
      result.push(parseInt(hex.substr(n, 2), 16));
    }
    return result;
}

function uintArrayToHexStr(array) {
    var str = "";

    for (var n = 0; n < array.length; n += 1) {
      let val = array;
      var valstr = array.toString(16);
      if (valstr.length == 1) {
            valstr = '0' + valstr;
      }
      str += valstr;
    }
    if (str.length > 0) {
      str = "0x" + str;
    }
    return str;
}



使用 ipsw swift-dump 可以看到这样一个结构体,看起来 JWT 可能在这里。但是不知道如何使用 frida 获取这个结构体的实例。


struct Environment {
    let VERSION: Swift.Int;
    let RAYCAST_API_TOKEN: Swift.String;
    let RAYCAST_STORE_CLIENT_ID: Swift.String;
    let RAYCAST_STORE_CLIENT_SECRET: Swift.String;
    let RAYCAST_STORE_JWT_SECRET: Swift.String;
    let SENTRY_DSN: Swift.String;
    let SEGMENT_WRITE_KEY: Swift.String;
    let RAYCAST_IMAGE_PROXY_KEY: Swift.String;
    let RAYCAST_IMAGE_PROXY_SALT: Swift.String;
    let AMPLITUDE_API_KEY: Swift.String;
    let AERODATABOX_API_KEY: Swift.String;
    let STRIPE_API_PRODUCT_ID_STAGING: Swift.String;
    let STRIPE_API_PRODUCT_ID_BETTER_AI_UPGRADE_STAGING: Swift.String;
    let STRIPE_API_KEY_STAGING: Swift.String;
    let STRIPE_API_PRODUCT_ID_PRODUCTION: Swift.String;
    let STRIPE_API_PRODUCT_ID_BETTER_AI_UPGRADE_PRODUCTION: Swift.String;
    let STRIPE_API_KEY_PRODUCTION: Swift.String;
    let STRIPE_API_PRODUCT_ID_DEVELOPMENT: Swift.String;
    let STRIPE_API_PRODUCT_ID_BETTER_AI_UPGRADE_DEVELOPMENT: Swift.String;
    let STRIPE_API_KEY_DEVELOPMENT: Swift.String;
    let OPENAI_API_KEY: Swift.String;
    let OPENAI_ORGANIZATION: Swift.String;
    let POSTHOG_API_KEY: Swift.String;
}


请大佬们指点。{:301_995:}

Vvvvvoid 发表于 2024-6-1 21:57

在 web 项目中,通常 jwt token 是server 端生成并返回的;
之后返回给 client ;
之后client 与 server 的通信都是携带 token 信息

keniu007 发表于 2024-6-1 22:26

Vvvvvoid 发表于 2024-6-1 21:57
在 web 项目中,通常 jwt token 是server 端生成并返回的;
之后返回给 client ;
之后client 与 server...

这个软件客户端也会生成,hs256,服务端生成的是rsa,我已经找到服务端的生成验签所需要的公钥了。
页: [1]
查看完整版本: 新人求助,Mac平台,Swift 语言一款软件的frida hook