hook iOS的libcurl

0x00 背景

我们知道,为了防止中间人攻击(MITM ),一般iOS应用会使用SSL Pinning。大多数情况下,这些App会使用AFNetworking或者NSURLSession这两个库来做SSL Pinning。但是这两个库使用了iOS提供的AFSecurityPolicy类,该类又调用了SSLHandshake,SSLCreateContext等iOS的API,只要hook了这几个API(ssl-kill-switch2),即可bypass SSL Pinning。但是最近发现某App并未使用这两个库进行SSL Pinnig,而是使用了libcurl和openssl来做SSL Pinning,这两个库被编译成native代码,静态链接到App binary中,没有symbol了,让我们的分析与bypass难度大大增加。

0x01 寻找libcurl的位置

上网下载build-libcurl-ios,按照README编译出iOS对应的libcurl.a文件(注意需要自己下载curl源码CURL Realeases)。然后用IDA打开libcurl.a文件,选择libcurl_la-easy.o,就能看到诸如_curl_easy_perform_curl_easy_setopt等函数了。

__text:00000000000001F4 ; CURLcode __cdecl curl_easy_perform(CURL *curl)
__text:00000000000001F4 _curl_easy_perform
__text:00000000000001F4
__text:00000000000001F4 var_70          = -0x70
__text:00000000000001F4 var_68          = -0x68
__text:00000000000001F4 var_64          = -0x64
__text:00000000000001F4 var_60          = -0x60
__text:00000000000001F4 var_50          = -0x50
__text:00000000000001F4 var_40          = -0x40
__text:00000000000001F4 var_30          = -0x30
__text:00000000000001F4 var_20          = -0x20
__text:00000000000001F4 var_10          = -0x10
__text:00000000000001F4
__text:00000000000001F4                 STP             X28, X27, [SP,#var_60]!
__text:00000000000001F8                 STP             X26, X25, [SP,#0x60+var_50]
__text:00000000000001FC                 STP             X24, X23, [SP,#0x60+var_40]
__text:0000000000000200                 STP             X22, X21, [SP,#0x60+var_30]
__text:0000000000000204                 STP             X20, X19, [SP,#0x60+var_20]
__text:0000000000000208                 STP             X29, X30, [SP,#0x60+var_10]
__text:000000000000020C                 ADD             X29, SP, #0x60+var_10
__text:0000000000000210                 SUB             SP, SP, #0x10
__text:0000000000000214                 MOV             X19, X0
__text:0000000000000218                 CBZ             X19, loc_23C
__text:000000000000021C                 LDR             X8, [X19,#0x60]
__text:0000000000000220                 CBZ             X8, loc_244
__text:0000000000000224                 ADRP            X1, #ltmp2@PAGE ; "easy handle already used in multi handl"...
__text:0000000000000228                 ADD             X1, X1, #ltmp2@PAGEOFF ; "easy handle already used in multi handl"...
__text:000000000000022C                 MOV             X0, X19
__text:0000000000000230                 BL              _Curl_failf
__text:0000000000000234                 MOV             W21, #2
__text:0000000000000238                 B               loc_3A8

注意倒数第五第六行的字符串"easy handle already used in multi handl"...这个字符串,我们可以用IDA在我们的App binary里查找该字符串,即可定位到curl_easy_perform的位置。如下(该函数本来是没有symbol的,函数名是我修改过后的名字):

__text:0000000100A94AF0                               curl_easy_perform                       ; CODE XREF: sub_1003CD74C+28p
__text:0000000100A94AF0                                                                       ; sub_10042732C+20p
__text:0000000100A94AF0
__text:0000000100A94AF0                               var_80          = -0x80
__text:0000000100A94AF0                               var_78          = -0x78
__text:0000000100A94AF0                               var_70          = -0x70
__text:0000000100A94AF0                               var_68          = -0x68
__text:0000000100A94AF0                               var_5C          = -0x5C
__text:0000000100A94AF0                               var_58          = -0x58
__text:0000000100A94AF0                               var_54          = -0x54
__text:0000000100A94AF0                               var_50          = -0x50
__text:0000000100A94AF0                               var_40          = -0x40
__text:0000000100A94AF0                               var_30          = -0x30
__text:0000000100A94AF0                               var_20          = -0x20
__text:0000000100A94AF0                               var_10          = -0x10
__text:0000000100A94AF0
__text:0000000100A94AF0 FA 67 BB A9                                   STP             X26, X25, [SP,#var_50]!
__text:0000000100A94AF4 F8 5F 01 A9                                   STP             X24, X23, [SP,#0x50+var_40]
__text:0000000100A94AF8 F6 57 02 A9                                   STP             X22, X21, [SP,#0x50+var_30]
__text:0000000100A94AFC F4 4F 03 A9                                   STP             X20, X19, [SP,#0x50+var_20]
__text:0000000100A94B00 FD 7B 04 A9                                   STP             X29, X30, [SP,#0x50+var_10]
__text:0000000100A94B04 FD 03 01 91                                   ADD             X29, SP, #0x50+var_10
__text:0000000100A94B08 FF C3 00 D1                                   SUB             SP, SP, #0x30
__text:0000000100A94B0C F3 03 00 AA                                   MOV             X19, X0
__text:0000000100A94B10 33 01 00 B4                                   CBZ             X19, loc_100A94B34
__text:0000000100A94B14 68 32 40 F9                                   LDR             X8, [X19,#0x60]
__text:0000000100A94B18 28 01 00 B4                                   CBZ             X8, loc_100A94B3C
__text:0000000100A94B1C 01 0B 00 D0                                   ADRP            X1, #aEasyHandleAlre@PAGE ; "easy handle already used in multi handl"...
__text:0000000100A94B20 21 BC 0B 91                                   ADD             X1, X1, #aEasyHandleAlre@PAGEOFF ; "easy handle already used in multi handl"...
__text:0000000100A94B24 E0 03 13 AA                                   MOV             X0, X19
__text:0000000100A94B28 21 D0 FF 97                                   BL              sub_100A88BAC
__text:0000000100A94B2C F5 03 1F 32                                   MOV             W21, #2

而紧邻该函数的上方就是curl_easy_setopt函数,该函数是一个不定长参数的函数(见该函数原型curl_easy_setopt),不容易hook,我们可以找到它调用的下一层函数Curl_setopt,hook该函数会容易一些。在我们的App binary中跳转到该函数地址,使用frida hook它。

0x02 通过hook bypass SSL Pinning

原本我以为SSL Pinning仅跟CURLOPT_PINNEDPUBLICKEY有关,按照该页面的教程将burp的证书sha256哈希值写入到原App binary,覆盖掉App的原哈希值,结果发现并没有效果。经反复实验,我发现传给Curl_setopt的opt中与SSL Pinning相关的参数一共有三个(参见curl.inc.in):

  1. CURLOPT_SSL_VERIFYHOST ---- 00081
  2. CURLOPT_SSL_VERIFYPEER ---- 00064
  3. CURLOPT_PINNEDPUBLICKEY ---- 10230

通过patch binary的方法太麻烦了,我们可以使用hook方法。hook Curl_setopt函数,当传入的opt为以上三个数字时,全部忽略掉或者将第三个参数指向的内存设为0即可。

在调试自己的hook时还可以hook curl_easy_perform函数,打印出返回值,返回值表示的意义在curl.h里可以查找到,便于我们调试。

0x03 hook代码

涉及到一些机密,暂时不能公开。