Frida 实战一:在 iOS 上分析应用

一、查看设备上当前所有运行的进程

zeno@zeno-macbook ~ % frida-ps -H 172.16.4.75:27042 | grep Cydia
 662  Cydia

二、我们来跟踪Cydia的按钮的方法

frida-trace 用于跟踪函数或者 Objective-C 方法的调用,frida-trace -h 能够查看它的帮助,最重要的有下面几个参数:

-i 跟踪某个函数,-x 排除某个函数。
-m 跟踪某个 Objective-C 方法,-M 排除某个 Objective-C 方法。
-a 跟踪某一个地址,需要指名模块的名称。

接下来实际操作一下

1.比如需要跟踪 Cydia 点击“关于”的操作,输入以下命令,它会在当前目录下生成一个 handlers 目录,然后再生成一个 HomeController_aboutButtonClicked.js 文件,在 Cydia 点击“关于”时,会输出以下信息,按 Ctrl-C 可以停止跟踪。

zeno@zeno-macbook ~ % frida-ps -H 172.16.4.75:27042 | grep Cydia
 662  Cydia                                                   

zeno@zeno-macbook ~ % frida-trace -H 172.16.4.75:27042 -m "-[HomeController aboutButtonClicked]" Cydia
Instrumenting...                                                        
-[HomeController aboutButtonClicked]: Auto-generated handler at "/Users/zeno/__handlers__/HomeController/aboutButtonClicked.js"
Started tracing 1 function. Press Ctrl+C to stop.                       
           /* TID 0x303 */
  3404 ms  -[HomeController aboutButtonClicked]

2.打开 HomeController_aboutButtonClock.js 文件,会看到有两个函数,onEnter 是进入该函数时会执行的代码,onLeave 是该函数执行完离开时会执行的代码,在相应的位置添加 Thread.backrace 这句代码可以打印出调用栈。

/*
 * Auto-generated by Frida. Please modify to match the signature of -[HomeController aboutButtonClicked].
 * This stub is currently auto-generated from manpages when available.
 *
 * For full API reference, see: https://frida.re/docs/javascript-api/
 */

{
  /**
   * Called synchronously when about to call -[HomeController aboutButtonClicked].
   *
   * @this {object} - Object allowing you to store state for use in onLeave.
   * @param {function} log - Call this function with a string to be presented to the user.
   * @param {array} args - Function arguments represented as an array of NativePointer objects.
   * For example use args[0].readUtf8String() if the first argument is a pointer to a C string encoded as UTF-8.
   * It is also possible to modify arguments by assigning a NativePointer object to an element of this array.
   * @param {object} state - Object allowing you to keep state across function calls.
   * Only one JavaScript function will execute at a time, so do not worry about race-conditions.
   * However, do not use this to store function arguments across onEnter/onLeave, but instead
   * use "this" which is an object for keeping state local to an invocation.
   */
  onEnter(log, args, state) {
    log(`-[HomeController aboutButtonClicked]`);
    log('\tBacktrace:\n\t' + Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t'));

  },

  /**
   * Called synchronously when about to return from -[HomeController aboutButtonClicked].
   *
   * See onEnter for details.
   *
   * @this {object} - Object allowing you to access state stored in onEnter.
   * @param {function} log - Call this function with a string to be presented to the user.
   * @param {NativePointer} retval - Return value represented as a NativePointer object.
   * @param {object} state - Object allowing you to keep state across function calls.
   */
  onLeave(log, retval, state) {
  }
}

3.再次执行 frida-trace, HomeController_aboutButtonClicked.js 不会覆盖,会调用刚才我们添加好的代码,打印调用栈信息如下:

Instrumenting...                                                        
-[HomeController aboutButtonClicked]: Loaded handler at "/Users/zeno/__handlers__/HomeController/aboutButtonClicked.js"
Started tracing 1 function. Press Ctrl+C to stop.                       
           /* TID 0x303 */
  8927 ms  -[HomeController aboutButtonClicked]
  8927 ms  	Backtrace:
	0x19f81d190 UIKitCore!-[UIApplication sendAction:to:from:forEvent:]
	0x19ed7f778 UIKitCore!-[UIBarButtonItem _triggerActionForEvent:]
	0x19ed5703c UIKitCore!__45-[_UIButtonBarTargetAction _invoke:forEvent:]_block_invoke
	0x19ed56eec UIKitCore!-[_UIButtonBarTargetAction _invoke:forEvent:]
	0x19f81d190 UIKitCore!-[UIApplication sendAction:to:from:forEvent:]
	0x19f153fc4 UIKitCore!-[UIControl sendAction:to:forEvent:]
	0x19f154318 UIKitCore!-[UIControl _sendActionsForEvents:withEvent:]
	0x19f152b7c UIKitCore!-[UIControl touchesEnded:withEvent:]
	0x19f357cb0 UIKitCore!_UIGestureEnvironmentUpdate
	0x19cde3358 CoreFoundation!__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
	0x19cddd5c4 CoreFoundation!__CFRunLoopDoObservers
	0x19cdddb74 CoreFoundation!__CFRunLoopRun
	0x19cddd21c CoreFoundation!CFRunLoopRunSpecific
	0x1b3e5c784 GraphicsServices!GSEventRunModal
	0x19f816200 UIKitCore!-[UIApplication _run]
	0x19f81ba74 UIKitCore!UIApplicationMain

4.frida-trace 可以支持通配符,比如我们想跟踪 Home 开头的类,方法名是任意的,可以执行下面的命令:

zeno@zeno-macbook ~ % frida-trace -H 172.16.4.75:27042 -m "-[Home* *]" Cydia

会抓取所有包含名字的类

-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent dictionaryRepresentation]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/dictionaryRepresentation.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent description]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/description.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent hash]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/hash.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent .cxx_destruct]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/.cxx_destruct.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent readFrom:]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/readFrom_.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent writeTo:]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/writeTo_.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent copyWithZone:]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/copyWithZone_.js"
-[HomeControlAnalysisPETCoreBehaviorAnalysisEvent isEqual:]: Auto-generated handler at "/Users/zeno/__handlers__/HomeControlAnalysisPETCoreBehaviorAnalysisEvent/isEqual_.js"
-[HomeController navigationURL]: Auto-generated handler at "/Users/zeno/__handlers__/HomeController/navigationURL.js"
-[HomeController aboutButtonClicked]: Loaded handler at "/Users/zeno/__handlers__/HomeController/aboutButtonClicked.js"
-[HomeController leftButton]: Auto-generated handler at "/Users/zeno/__handlers__/HomeController/leftButton.js"
-[HomeController init]: Auto-generated handler at "/Users/zeno/__handlers__/HomeController/init.js"
-[HomeController dealloc]: Auto-generated handler at "/Users/zeno/__handlers__/HomeController/dealloc.js"

三、跟踪加密函数实例

很多应用在登录时都会加密数据,我们的目标是获取某个应用的 AES 加密的原文和密钥。AES 加解密一般会调用 CCCrypt 这个函数,该函数原型如下:

CCCryptorStatus CCCrypt(   
    CCOperation op, //kCCEncrypt为加密,kCCDecrypt为解密   
    CCAlgorithm alg, //加密方式         kCCAlgorithmAES128为AES加密  
    CCOptions options, //增充方式  
    const void *key,   //密钥  
    size_t keyLength,  //密钥长度  
    const void *iv,    // IV  
    const void *dataIn, //待加密的原文  
    size_t dataInLength, //原文长度  
    void *dataOut,       //加密后输出的数据  
    size_t dataOutAvailable,    
    size_t *dataOutMoved
)

尝试对 CCCrypt 进行跟踪,命令如下:

zeno@zeno-macbook ~ % frida-trace -H 172.16.4.75:27042 -i CCCrypt 弹壳特攻队
Instrumenting…
CCCrypt: Auto-generated handler at “/Users/zeno/handlers/libcommonCrypto.dylib/CCCrypt.js”
Started tracing 1 function. Press Ctrl+C to stop.
/* TID 0xfd0b / 8437 ms CCCrypt(op=0x0, alg=0x0, options=0x1, key=0x28087b100, keyLength=0x10, iv=0x28087b0c0, dataIn=0x102f74000, dataInLength=0x91e, dataOut=0x10290be00, dataOutAvailable=0x92e, dataOutMoved=0x170f425a0) / TID 0xbca3 / 9264 ms CCCrypt(op=0x0, alg=0x0, options=0x1, key=0x280d02980, keyLength=0x10, iv=0x280d03ce0, dataIn=0x10f850000, dataInLength=0x910, dataOut=0x10d845000, dataOutAvailable=0x920, dataOutMoved=0x1704125a0) 18443 ms CCCrypt(op=0x0, alg=0x0, options=0x3, key=0x280852ea0, keyLength=0x10, iv=0x280853790, dataIn=0x152aec000, dataInLength=0xbfe1, dataOut=0x163a50000, dataOutAvailable=0xbff1, dataOutMoved=0x170412430) 21575 ms CCCrypt(op=0x0, alg=0x0, options=0x1, key=0x280c82520, keyLength=0x10, iv=0x280c824e0, dataIn=0x101feb000, dataInLength=0x914, dataOut=0x102b89600, dataOutAvailable=0x924, dataOutMoved=0x1704125a0) / TID 0x303 */

在 CCCrypt.js 添加如下代码:

{
  onEnter: function (log, args, state) {
    log("CCCrypt(" +
      "op=" + args[0] +
      ", alg=" + args[1] +
      ", options=" + args[2] +
      ", key=" + args[3] +
      ", keyLength=" + args[4] +
      ", iv=" + args[5] +
      ", dataIn=" + args[6] +
      ", dataInLength=" + args[7] +
      ", dataOut=" + args[8] +
      ", dataOutAvailable=" + args[9] +
      ", dataOutMoved=" + args[10] +
    ")");
 
    //保存参数
    this.operation   = args[0]
    this.CCAlgorithm = args[1]
    this.CCOptions   = args[2]
    this.keyBytes    = args[3]
    this.keyLength   = args[4]
    this.ivBuffer    = args[5]
    this.inBuffer    = args[6]
    this.inLength    = args[7]
    this.outBuffer   = args[8]
    this.outLength   = args[9]
    this.outCountPtr = args[10]
 
    //this.operation == 0 代表是加密
    if (this.operation == 0) {
        //打印加密前的原文
        console.log("In buffer:")
        console.log(hexdump(ptr(this.inBuffer), {
            length: this.inLength.toInt32(),
            header: true,
            ansi: true
        }))
        //打印密钥
        console.log("Key: ")
        console.log(hexdump(ptr(this.keyBytes), {
            length: this.keyLength.toInt32(),
            header: true,
            ansi: true
        }))
        //打印 IV
        console.log("IV: ")
        console.log(hexdump(ptr(this.ivBuffer), {
            length: this.keyLength.toInt32(),
            header: true,
            ansi: true
        }))
    }
  },
  onLeave: function (log, retval, state) {
  }
}

再次执行 frida-trace 跟踪,在登录时可以发现原文、密钥,IV 都打印出来了,一目了然。

zeno@zeno-macbook ~ % frida-trace -H 172.16.4.75:27042 -i CCCrypt 弹壳特攻队
Instrumenting…
CCCrypt: Loaded handler at “/Users/zeno/handlers/libcommonCrypto.dylib/CCCrypt.js”
Started tracing 1 function. Press Ctrl+C to stop.
In buffer:
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
101f96c00 7b 22 74 69 6d 65 73 74 61 6d 70 22 3a 22 31 36 {“timestamp”:”16
101f96c10 36 30 39 39 30 30 32 32 2e 39 33 34 32 33 32 22 60990022.934232″
101f96c20 2c 22 75 69 64 22 3a 22 31 36 36 30 38 39 30 32 ,”uid”:”16608902
101f96c30 34 31 35 39 38 2d 39 34 31 38 38 34 36 22 2c 22 41598-9418846″,”
101f96c40 62 75 6e 64 6c 65 6e 61 6d 65 22 3a 22 e5 bc b9 bundlename”:”…
101f96c50 e5 a3 b3 e7 89 b9 e6 94 bb e9 98 9f 22 2c 22 63 …………”,”c
101f96c60 65 6c 6c 22 3a 7b 22 6d 63 63 22 3a 22 39 39 39 ell”:{“mcc”:”999
101f96c70 22 2c 22 6d 6e 63 22 3a 22 39 39 22 2c 22 72 61 “,”mnc”:”99″,”ra
101f96c80 22 3a 22 61 66 5f 72 65 73 74 72 69 63 74 65 64 “:”af_restricted
101f96c90 22 7d 2c 22 61 74 74 5f 73 74 61 74 75 73 22 3a “},”att_status”:
101f96ca0 32 2c 22 70 6c 61 74 66 6f 72 6d 22 3a 22 69 50 2,”platform”:”iP
101f96cb0 68 6f 6e 65 31 31 2c 38 22 2c 22 63 6f 75 6e 74 hone11,8″,”count
101f96cc0 65 72 22 3a 22 31 32 22 2c 22 70 72 65 76 5f 73 er”:”12″,”prev_s
101f96cd0 65 73 73 69 6f 6e 5f 64 75 72 22 3a 33 30 30 38 ession_dur”:3008
101f96ce0 2c 22 72 65 69 6e 73 74 61 6c 6c 43 6f 75 6e 74 ,”reinstallCount
101f96cf0 65 72 22 3a 22 34 22 2c 22 61 64 76 65 72 74 69 er”:”4″,”adverti
101f96d00 73 65 72 49 64 22 3a 22 22 2c 22 73 79 73 74 65 serId”:””,”syste
101f96d10 6d 76 65 72 73 69 6f 6e 22 3a 22 31 34 2e 32 22 mversion”:”14.2″
101f96d20 2c 22 69 61 65 63 6f 75 6e 74 65 72 22 3a 22 31 ,”iaecounter”:”1
101f96d30 33 22 2c 22 4a 42 44 65 76 69 63 65 22 3a 74 72 3″,”JBDevice”:tr
101f96d40 75 65 2c 22 64 61 74 65 33 22 3a 22 32 30 32 32 ue,”date3″:”2022
101f96d50 2d 30 38 2d 31 39 5f 31 34 34 32 33 33 2b 30 38 -08-19_144233+08
101f96d60 30 30 22 2c 22 64 65 76 69 63 65 44 61 74 61 22 00″,”deviceData”
101f96d70 3a 7b 22 63 70 75 5f 74 79 70 65 22 3a 22 41 52 :{“cpu_type”:”AR
101f96d80 4d 36 34 22 2c 22 63 70 75 5f 73 70 65 65 64 22 M64″,”cpu_speed”
101f96d90 3a 22 2d 31 22 2c 22 6f 73 56 65 72 73 69 6f 6e :”-1″,”osVersion
101f96da0 22 3a 22 39 39 2e 39 20 28 42 75 69 6c 64 20 39 “:”99.9 (Build 9
101f96db0 39 39 39 39 29 22 2c 22 72 61 6d 5f 73 69 7a 65 9999)”,”ram_size
101f96dc0 22 3a 22 39 39 39 22 2c 22 63 70 75 5f 36 34 62 “:”999”,”cpu_64b

通过一个实例我们见证到 Frida 的强大,不需要编译代码,所有的操作都是编写脚本语言进行的,区区十几行 JavaScript 脚本代码即可轻松获取 AES 加密的密钥和相关参数。试想如果没有 Frida,我们的分析方法可能是编写动态库注入,对 CCCrypt 进行 Hook,然后打印相关的参数,或者是使用 LLDB 连接程序给 CCCrypt 添加断点,这些方法都不如使用 Frida 跟踪函数功能的效率高、上手快。

关于Zeno Chen

本人涉及的领域较多,杂而不精 程序设计语言: Perl, Java, PHP, Python; 数据库系统: MySQL,Oracle; 偶尔做做电路板的开发,主攻STM32单片机
此条目发表在Objective-C分类目录。将固定链接加入收藏夹。