Android 抓包相关 SSL相关

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

https无法明文抓包

Android P版本开始强制App使用Https协议否则访问崩溃如下所示错误

java.lang.ClassCastException: 
com.android.okhttp.internal.huc.HttpURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection

可参阅
Android 9.0强制使用https会阻塞http请求如果app使用的第三方sdk有http将全部被阻塞

因此在Android P基本很少能能抓取http请求多数以https为主。

但是你在参考网上https抓包配置后发现还是无法解密内容
在这里插入图片描述

Android 7 以上系统默认会让App不信任默认用户证书只信用系统证书。
默认情况我们只能导入用户证书系统证书导入需要一些root权限。如下图所示导入的charles证书。
在这里插入图片描述
如果你是App所有用户可以参阅下面文章操作

Android 7及以上信任用户证书

如果你有root权限直接导入系统证书即可

(1) 首先安装代理的证书(此时会将会自动将证书转化为特定格式)用户证书安装目录
/data/misc/user/0/cacerts-added
如笔者次目录下的用户证书
在这里插入图片描述
(2) 将证书文件拷贝到系统证书目录/system/etc/security/cacerts
举例命令

cp 97fa6c80.0  /system/etc/security/cacerts  

如果出现 Read-only file system 重新挂载分区android 8以上版本请关闭dm-verity以及avb在尝试网上重新挂载命令。

//针对ROM是userdebug版本可以使用下面的命令
adb disable-verity
adb reboot
adb root
adb remount

对于root手机可以考虑使用mask的一个模块:
movecert_iyue
这个模块原理非常简单和新源码如下

mv -f /data/misc/user/0/cacerts-added/* $MODDIR/system/etc/security/cacerts/

就是将用户证书移动mask规范下的system目录(会被映射到真实的/system)
在这里插入图片描述

安装后重启即可看到系统证书

在这里插入图片描述
抓包效果图可以明显看到成功解析了数据
在这里插入图片描述

app不走代理

很多网络框架可以自主选择是否选择路由代理。如下代码

//
  val httpHost = System.getProperties().getProperty("http.proxyHost");
  val httpPort = System.getProperties().getProperty("http.proxyPort");
  val httpsHost = System.getProperties().getProperty("https.proxyHost");
  val httpsPort = System.getProperties().getProperty("https.proxyPort");
  //输出代理信息
  Log.e( "test", "httpHost ${httpHost} httpPort ${httpPort} \r\n httpsHost ${httpsHost} httpsPort ${httpsPort} ")

  System.getProperties().remove("http.proxyHost");
  System.getProperties().remove("http.proxyPort");
  System.getProperties().remove("https.proxyHost");
  System.getProperties().remove("https.proxyPort");

  val url = URL("https://www.google.com")
  val urlConnection = url.openConnection() as HttpsURLConnection
  urlConnection.connect()
  val readBytes = urlConnection.inputStream.readBytes()
  Log.e("test", "${String(readBytes)}")

上面的这种方式会迫使wifi手动设置代理抓包方式失效

解决方案:

  1. 电脑开个热点然后抓电脑无线网卡即可(软路由差不多,比较简单不做举例)
  2. 手机开代理软件转发流量到代理
  3. 手机刷Nethunter 直接当电脑玩抓手机网卡即可(我现在还想不到不走网卡上网的app)

举例代理流量 (2)

VPN方式转发流量可以通杀市面大多数的APP但是部分app会检测但是免root。proxydroid需要root但基本可以做到市面上百分之99%的应用无感知透明代理

我们以charles+proxydroid举例

charles 开启socks代理
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

流量转发VPN
Charles+postern抓包教程 转载
流量转发 proxydroid

举例(3) 刷入Nethunter内核
在这里插入图片描述
刷入后你将是一个完整linux系统通过vnc链接手机桌面然后开启wireshark解析数据包

在这里插入图片描述
当然wireshare比较适合做自定义协议的分析https还是建议在新的内核系统再装一个charles在通过iptable转发流量。

SSL Pinning客户端固定证书

客户端可以强制只信任某一证书那么代理抓包的中间人证书将失效并且可以风控异常设备进行上报。

我们首先用keytool生产一个PKCS12文件(里面包含密钥以及证书)

keytool -genkeypair --ext SAN=IP:192.168.38.70  -alias baeldung -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore baeldung.p12 -validity 3650

其中注意上面需要改成自己的IP

生产的文件会交付给我们的Demo服务端使用然后我们需要再提起里面的证书给客户端

//生产处一个证书但是这个证书不是我们需要的格式
openssl pkcs12 -in   baeldung.p12 -out your-cert.pem  -nokeys
//将证书转化成Android 客户端证书格式
openssl x509 -outform der -in your-cert.pem  -out your-cert.crt

我们服务端使用spring boot作为案例:

//UserControl.kt
//接口如下
@Controller
@RequestMapping("test")
class UserControl {
    @RequestMapping("xx")
    @ResponseBody
    fun myout(): String {
        return "hello"
    }
}

最后是我们的ssl配置如下即可拷贝证书到工程的keystore/baeldung.p12

#application.properties 文件
# The format used for the keystore. It could be set to JKS in case it is a JKS file
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=classpath:keystore/baeldung.p12
# The password used to generate the certificate
server.ssl.key-store-password=123456
# The alias mapped to the certificate
server.ssl.key-alias=   baeldung


最后就是我们的客户端案例

 fun test(){
         val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
         val caInput: InputStream = BufferedInputStream(this.resources.assets.open("your-cert.crt"))
         val ca: X509Certificate = caInput.use {
             cf.generateCertificate(it) as X509Certificate
         }
         System.out.println("ca=" + ca.subjectDN)

         // Create a KeyStore containing our trusted CAs
         val keyStoreType = KeyStore.getDefaultType()
         val keyStore = KeyStore.getInstance(keyStoreType).apply {
             load(null, null)
             setCertificateEntry("baeldung", ca)
         }

         // Create a TrustManager that trusts the CAs inputStream our KeyStore
         val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()
         val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {
             init(keyStore)
         }

         // Create an SSLContext that uses our TrustManager
         val context: SSLContext = SSLContext.getInstance("TLS").apply {
             init(null, tmf.trustManagers, null)
         }

         // Tell the URLConnection to use a SocketFactory from our SSLContext
         val url = URL("https://192.168.38.70:8080/test/xx")
         val urlConnection = url.openConnection() as HttpsURLConnection
         urlConnection.sslSocketFactory = context.socketFactory
         urlConnection.connect()
         val inputStream: InputStream = urlConnection.inputStream

        Log.e("test", "${String(inputStream.readBytes())}")
     }

开启代理抓包后
在这里插入图片描述
在这里插入图片描述

解决方案1:

对于客户端固定证书个人推荐推荐frida hook方式进行解绑
这里给出一个开源的解决脚本
frida-android-unpinning

举例clone 上面的脚本执行后
frida -U  -f com.example.learnssldemo   -l frida-script.js 

输出
JackdeMacBook-Pro:frida-android-unpinning-main jack$ frida -U  -f com.example.learnssldemo   -l frida-script.js 
     ____
    / _  |   Frida 16.0.8 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to Pixel 6 (id=19291FDF6006PD)
Spawned `com.example.learnssldemo`. Resuming main thread!               
[Pixel 6::com.example.learnssldemo ]-> ---
Unpinning Android app...
[+] SSLPeerUnverifiedException auto-patcher
[+] HttpsURLConnection (setDefaultHostnameVerifier)
[+] HttpsURLConnection (setSSLSocketFactory)
[+] HttpsURLConnection (setHostnameVerifier)
[+] SSLContext
[+] TrustManagerImpl
[ ] OkHTTPv3 (list)
[ ] OkHTTPv3 (cert)
[ ] OkHTTPv3 (cert array)
[ ] OkHTTPv3 ($okhttp)
[ ] Trustkit OkHostnameVerifier(SSLSession)
[ ] Trustkit OkHostnameVerifier(cert)
[ ] Trustkit PinningTrustManager
[ ] Appcelerator PinningTrustManager
[ ] OpenSSLSocketImpl Conscrypt
[ ] OpenSSLEngineSocketImpl Conscrypt
[ ] OpenSSLSocketImpl Apache Harmony
[ ] PhoneGap sslCertificateChecker
[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string)
[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string array)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSocket)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (cert)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (string string)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSession)
[ ] Conscrypt CertPinManager
[ ] CWAC-Netsecurity CertPinManager
[ ] Worklight Androidgap WLCertificatePinningPlugin
[ ] Netty FingerprintTrustManagerFactory
[ ] Squareup CertificatePinner (cert)
[ ] Squareup CertificatePinner (list)
[ ] Squareup OkHostnameVerifier (cert)
[ ] Squareup OkHostnameVerifier (SSLSession)
[+] Android WebViewClient (SslErrorHandler)
[ ] Android WebViewClient (WebResourceError)
[ ] Apache Cordova WebViewClient
[ ] Boye AbstractVerifier
[ ] Appmattus (Transparency)
Unpinning setup completed
---

在这里插入图片描述

解决方案2

如果你是App所有者那么你只需要在如下地方配置即可
在这里插入图片描述
在这里插入图片描述

HTTPS using Self-Signed Certificate in Spring Boot
通过 HTTPS 和 SSL 确保安全

mutual authentication

如法炮制首先是生产一个客户端使用的证书

keytool -genkeypair   -alias baeldung -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore cbaeldung.p12 -validity 3650

生产的p12文件格式Android 无法直接使用需要转化为bks格式这里使用portecle 进行转化。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

此时我们还需要导出一个jks给服务端使用教程如下
首先执行命令导出客户端p12的证书

openssl pkcs12 -in cbaeldung.p12 -out OUTFILE.crt -nodes

然后重新打开portecle然后执行如下操作将证书放入jks格式的一个封装文件中
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

首先我们编写服务端代码只需要将上文证书固定代码稍微增加即可

# application.properties
# The format used for the keystore. It could be set to JKS in case it is a JKS file
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=classpath:keystore/baeldung.p12
# The password used to generate the certificate
server.ssl.key-store-password=123456
# The alias mapped to the certificate
server.ssl.key-alias=  baeldung
spring.rsocket.server.ssl.key-store-type=PKCS12
#开启验证客户端证书
server.ssl.client-auth=need
server.ssl.trust-store=classpath:keystore/clienttrus.jks
server.ssl.trust-store-password=123456
server.ssl.trust-store-type=JKS


在这里插入图片描述

客户端代码

 fun test() {
        
        
        //服务端证书固定代码
        val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
        val caInput: InputStream = BufferedInputStream(this.resources.assets.open("your-cert.crt"))
        val ca: X509Certificate = caInput.use {
            cf.generateCertificate(it) as X509Certificate
        }
        System.out.println("ca=" + ca.subjectDN)

        // Create a KeyStore containing our trusted CAs
        val keyStoreType = KeyStore.getDefaultType()
        val keyStore = KeyStore.getInstance(keyStoreType).apply {
            load(null, null)
            setCertificateEntry("baeldung", ca)
        }

        // Create a TrustManager that trusts the CAs inputStream our KeyStore
        val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()
        val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {
            init(keyStore)
        }


        
        //客户端证书导入
        val keyStore2 = KeyStore.getInstance("BKS");
        val ksIn = this.resources.getAssets().open("cbaeldung.bks");
        ksIn.use {ksIn->
            keyStore2.load(ksIn, KEY_STORE_PASSWORD.toCharArray())
        }
        val keyManagerFactory = KeyManagerFactory.getInstance("X509");
        keyManagerFactory.init(keyStore2, KEY_STORE_PASSWORD.toCharArray());


        // Create an SSLContext that uses our TrustManager
        val context: SSLContext = SSLContext.getInstance("TLS").apply {
            init(
                keyManagerFactory.keyManagers,//开启客户端证书
                tmf.trustManagers, //服务端证书固定
                null)
        }

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        val url = URL("https://192.168.38.70:8080/test/xx")
        val urlConnection = url.openConnection() as HttpsURLConnection
        urlConnection.sslSocketFactory = context.socketFactory
        urlConnection.connect()
        val inputStream: InputStream = urlConnection.inputStream

        Log.e("test", "${String(inputStream.readBytes())}")
    }

不要忘记将bks文件拷贝到Android工程哦

当你开启抓包软件后你会发现链接失败。。.。

解决方案
如果你不是app拥有者首先先用上网的Frida解绑然后再逆向App取出客户端bks文件以及密码然后导入chales。
在这里插入图片描述

本节服务端代码
本节客户端

参阅文章1 开源app-从0到1实现四Android端自制https证书实现双向认证.md

🍶 为什么你的 Charles 会抓包失败

Hook通用抓包

= =无视所谓的证书绑定双向认证1232
一个firda 脚本

在这里插入图片描述

原理看了下非常简单可参阅我的另一篇博文Android hook方式抓包

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android