自动定位webview中的SLL_read和SSL_write

简介

在Android上抓https包是一件非常头疼的事情,随着android的不断升级,你可能面临导入用户证书、导入根证书、解决证书绑定、双向绑定等等一系列复杂的操作,之前的文章 Android Https抓包实践 中记录了APP抓包时踩过的很多坑,抓包配环境的繁琐令人非常痛苦。

基于frida的ssl_logger直接从底层hook SSL_read和SSL_write函数进行抓包,似乎解决了导证书、SSL pining等麻烦,但无法抓取APP内置webview发出的包,这是因为ssl_logger是通过hook 系统的ssl库来抓包,而Webview(Chromium)将boringssl的代码静态编译到了自己的二进制文件中,在收发https数据包时,不会调用系统的SSL库,因此也就无法抓包了。

由于编译后的webview二进制文件是无符号的,要通过hook的方式抓webview发出的https包,就要找到SSL_read和SSL_write符号。

定位SSL_read和SSL_write

找到SSL_read和SSL_write偏移的方法参考Hooking Chrome’s SSL functions 。文章中定位方法是在windows上操作的,android上的webview共享同一套代码,因此同样适用。

定位SSL_read和SSL_write的方法主要如下:

(1)根据webview版本号(chrome源码)找到chromium源码中使用的boringssl版本(DEPS文件);

(2)找到对应boringssl代码中的ssl_lib.cc文件,SSL_read和SSL_write代码中ERR_put_error函数会输出行号;

ssl_write和ssl_read(调用ssl_peek)调用了OPENSSL_PUT_ERROR,最后一个参数未代码行号,因此根据代码行号去汇编中查找对应指令即可:

upload successful

upload successful

(3)根据错误行号,在binary中搜索特征片段,定位到对应函数;

binary特征片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#define OPENSSL_PUT_ERROR(library, reason) ERR_put_error(ERR_LIB_##library, 0, reason, __FILE__, __LINE__)

SSL_read(SSL_peek) 32位特征:ERR_put_error(16, 0, 66, "../../third_party/boringssl/src/ssl/ssl_lib.cc", 0x431);
PUSH {R3-R7,LR} F8 B5
MOV R7, R0
LDR R0, [R0,#0x50]
CBZ R0, loc_1779BC6
MOVW R0, #0x431 40 F2 31 40
MOVS R1, #0 00 21
STR R0, [SP,#0x18+var_18]
MOVS R0, #0x10 10 20
LDR R3, loc_1779BFC
MOVS R2, #0x42 ; 'B' 42 22
MOVS R4, #0
ADD R3, PC ; "../../third_party/boringssl/src/ssl/ssl"...
BL sub_17359F2

SSL_write 32位特征:ERR_put_error(16, 0, 66, "../../third_party/boringssl/src/ssl/ssl_lib.cc", 0x446);
MOVW R0, #0x446
MOVS R1, #0 00 21
STR R0, [SP,#0x20+var_20]
MOVS R0, #0x10 10 20
LDR R3, =(aThirdPartyBori_31 - 0x1779DC2)
MOVS R2, #0x42 ; 'B' 42 22
MOVS R7, #0
ADD R3, PC ; "../../third_party/boringssl/src/ssl/ssl"...
BL sub_17359F2


SSL_write 64位特征:
FD 7B BD A9 STP X29, X30, [SP,#-0x30]!
F5 0B 00 F9 STR X21, [SP,#0x10]
F4 4F 02 A9 STP X20, X19, [SP,#0x20]
FD 03 00 91 MOV X29, SP
F3 03 02 2A MOV W19, W2
F4 03 01 AA MOV X20, X1
F5 03 00 AA MOV X21, X0
31 FA FF 97 BL sub_2356E78
A8 4E 40 F9 LDR X8, [X21,#0x98]
A8 01 00 B4 CBZ X8, loc_23585F0
03 30 FF 90 ADRL X3, aThirdPartyBori_4 ; "../../third_party/boringssl/src/ssl/ssl"...
63 F0 11 91
00 02 80 52 MOV W0, #0x10
42 08 80 52 MOV W2, #0x42 ; 'B'
C4 88 80 52 MOV W4, #0x446 //第四个参数
E1 03 1F 2A MOV W1, WZR
C0 B3 FE 97 BL sub_23054D8
E0 03 1F 2A MOV W0, WZR

SSL_read(SSL_peek)64位:
FD 7B BD A9 STP X29, X30, [SP,#-0x10+var_20]!
F5 0B 00 F9 STR X21, [SP,#0x20+var_10]
F4 4F 02 A9 STP X20, X19, [SP,#0x20+var_s0]
FD 03 00 91 MOV X29, SP
08 4C 40 F9 LDR X8, [X0,#0x98]
C8 01 00 B4 CBZ X8, loc_2358324
03 30 FF 90+ ADRL X3, aThirdPartyBori_4 ; "../../third_party/boringssl/src/ssl/ssl"...
63 F0 11 91
00 02 80 52 MOV W0, #0x10
42 08 80 52 MOV W2, #0x42 ; 'B'
24 86 80 52 MOV W4, #0x431 //第4个参数
E1 03 1F 2A MOV W1, WZR
74 B4 FE 97 BL sub_23054D8

测试记录

基于上述特征,我编写了自动化脚本,对webview二进制文件中的SLL_read和SSL_write符号进行自动化提取。尽管原理简单,但实现起来需要区分32位和64位,以及需要解决一些编译器引发的指令差异问题,实现起来也比较麻烦,我用了keystone和capstone来辅助搞定了自动化工具,这里就不分享了。

用我的自动化脚本在基于Chromium的几大主流webview进行了测试,输入binary路径和版本号就可以稳定运行,跑出SSL_read和SSL_write的偏移,以下是常见的几种webview:

Chrome APP

Chrome APP自带webview引擎,安装包内的crazy.ibchrome.so对应webview的二进制文件,安装完成后,在不同版本的系统上的名字不同:

  • Android L & M libchrome.so
  • Android N, O & P libmonochrome.so

当手机上安装Chrome时,系统内置的webview会设置为chrome的webview,当然定制的系统除外,例如国内的华米OV使用自己的webview。

系统内置webview

Android系统内置webview的版本可用(adb shell dumpsys webviewupdate)查到:

upload successful

installed/enabled的即为系统默认的webview。

使用(pm path com.google.android.webview)得到webview的路径:

upload successful

浏览器APP的webview

Android上的浏览器APP通常会使用自己的webview引擎,一般来说安装包体积小于十多兆的浏览器(例如X浏览器、隐私浏览器等等)使用的系统自带的webview,安装包较大(50M+)的浏览器会使用自己的webview。例如:

手机厂商自带的浏览器:

  • libheytapwebview.so OPPO
  • libhwwebviewchromium.so 华为
  • libmiui_chromium.so 小米
  • libwebviewchromium_vivo.so vivo

第三方浏览器:

  • libmttwebview.so qq浏览器
  • libwebviewuc.so UC浏览器

基于Chromium的第三方浏览器内核

  • libmttwebview.so x5内核,腾讯系的APP使用
  • libwebviewuc.so UC的U4内核 阿里小的APP使用
  • libxwalkcore.so CrossWalk内核 常用于各种小程序的实现

使用多内核的APP

有些APP会使用多个内核,以微信为例,同时使用了X5和CrossWalk内核:

  • x5 com.tencent.mm:tools进程 聊天页面点开的webview、支付里的页面(长按下拉提示使用x5内核的都可以)
  • CrossWalk (长按下拉未提示x5)
    • com.tencent.mm:toolsmp 进程 公众号文章、搜一搜
    • com.tencent.mm:appbrand 进程 小程序

通用的APP抓https包方案

通过上文的分析,通用的APP抓https包的方案需要结合以下三种情况:

  1. hook 系统的SSL_read和SSL_write
  2. hook webview的SSL_read和SSL_write
  3. 必要时hook APP自带ssl库的SSL_read和SSL_write

此方案的优点是不需要设置代理、导证书、解决证书绑定等繁琐操作,直接从底层获取明文的https包,无视上层实现。缺点是需要找到SSL_read和SSL_write的偏移,当然有了自动化脚本,抓起包来会非常轻松。

参考链接:

  1. chrome源码 https://chromium.googlesource.com/chromium/src.git
  2. boringssl源码 https://boringssl.googlesource.com/
  3. 通过Hooking Chrome浏览器的SSL函数实现读取SSL通信数据
  4. chrome webview docs https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/build-instructions.md
  5. Shared Libraries on Android https://chromium.googlesource.com/chromium/src.git/+/master/docs/android_native_libraries.md