吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5353|回复: 57
上一主题 下一主题
收起左侧

[Android 原创] Android app三种常见抓包场景及案例分析

  [复制链接]
跳转到指定楼层
楼主
scllqk 发表于 2024-9-15 22:45 回帖奖励
本帖最后由 scllqk 于 2025-1-23 15:24 编辑

一、未校验:配置证书就能抓
1.这种情况是最简单的情况,Android 7.0之前的设备,直接配置用户证书,就能进行抓包,Android 7.0之后的设备,需要获取root权限1后,把用户证书移到系统证书目录下,或者配置系统强制信任用户证书。
对于移动用户证书到系统证书目录下的情况,推荐使用这个插件:https://github.com/ys1231/MoveCertificate
对于强制信任用户证书的这种情况,推荐使用这个插件:https://github.com/NVISOsecurity/MagiskTrustUserCerts
2.对于这种情况,我们安装完证书后,直接使用代理/vpn的方式进行抓包就可以了,这里我们以某浏览器为例,进行抓包演示,因为我用的设备是Android 7.0以上的,所以我们首先是配置系统强制信任用户证书的插件。



3.配置完成后,我们在抓包软件上导出证书,然后在设备上进行安装。



4.接下来,我们就可以进行抓包了,抓包成功。



## 二、单向证书认证:客户端校验服务器证书--SSL Pinning
1.这是第二种情况,也就是我们常说的sslpinning,想具体了解sslpining技术,可以去看这2篇文章:
https://shunix.com/ssl-pinning/
https://yu-jack.github.io/2020/03/02/ssl-pinning/

2.针对这种情况,我们以x答x单app为例,进行抓包,上面我们已经试过了,用我们的测试设备是可以正常抓到https的数据包的,然后我们再去抓一下x答x单这个app的包,点击发送验证码按钮后会提示发送失败,请重试 ,说明我们抓包失败了。





3.这里报错:Client closed the connection before a request was made. Possibly the SSL certificate was rejected,表明在 SSL/TLS 握手阶段,客户端在没有发送 HTTP 请求之前就关闭了连接,客户端拒绝了服务器的证书,也就是上面我们所说的sslpinning技术,还有另外一种报错得情况:SSL handshake with client failed: An unknown issue occurred processing the certificate (certificate_unknown),也是用到了sslpinning技术。那遇到上述这2种情况,我们应该怎么处理呢?使用frIDA进行hook,我们这里直接用大佬们写好的脚本进行hook,地址:https://github.com/WooyunDota/DroidSSLUnpinning/blob/master/ObjectionUnpinningPlus/hooks.js

4.用了大佬的脚本后,发现还是不行,还是失败了,只能换一个再试试了,又尝试了justtrustme,结果还是不行。地址:https://github.com/Fuzion24/JustTrustMe



5.在辗转反侧之时,我想起之前用算法助手时,里面带着一个justtruatme的升级版,于是就拿来试了试,结果成功拿下。可以正常进行抓包了。



6.经过上述尝试,我们针对sslpinning这种,可以先用市面上已有的sslunpinning工具进行尝试,如果遇到都无法进行成功的情况,那就需要我们去手工进行hook了,大致有两种思路,一是对所有HTTP字符串相关类进行Hook,二是考虑到App在验证证书时会打开证书文件判断是否是App自身所信任的,因此一定会使用File类的构造函数打开证书文件获得文件的句柄,所以我们在测试时可以Hook上所有File类的构造函数,即对File.$init函数进行hook。这里我用了objection进行hook的:`objection -N -h 127.0.0.1 -p 26666  -g cn.ticktick.task explore -P ~/.objection/plugins -s "android hooking watch class_method java.io.File.\$init --dump-args --dump-backtrace --dump-return"`,这里因为$在命令行中有特殊含义,所以用\对它进行转义,避免被当成命令行变量。hook之后,我们在得到的数据里面搜索/system/etc/security/cacerts



7.我们在搜到的数据里面,找到了一个关于证书的堆栈信息,我们用jadx反编译后,找到这个方法



8.复制一下,丢给chatgpt分析一下,得出结论,这段代码是关于处理 SSL 证书验证的逻辑,是基于域名和证书的哈希值进行匹配,检查传入的证书是否符合某些预期的标准,那我们尝试hook它,并让它返回空,这样不就能绕过证书校验了嘛。

[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
function main(){
    Java.perform(function(){
        console.log("启动");
       let f = Java.use("ll.f");
        f["a"].implementation=function(str, list){
            console.log(`f.a is called: str=${str}, list=${list}`);
           return;
       };
   });
}
setTimeout(main,500)




9.非常幸运,经过hook后,我们成功抓到了这个数据包,至此完成。

三、双向证书认证:服务器校验客户端证书。
1.这是第三种情况,我们以x利蜂app为例,进行抓包尝试,经过尝试,还是和之前一样报错:SSL handshake with client failed: An unknown issue occurred processing the certificate (certificate_unknown)



2.我们先使用objection的android sslpinning disable把这个sslpinning过掉,过掉之后,我们再抓包,发现请求正常发出,响应返回报错:400 No required SSL certificate was sent。判断为服务器校验客户端证书。



3.当我们遇到这种情况,需要我们从app中找到内置的客户端证书,导入到抓包工具中,才能正常进行抓包,那怎么才能找到客户端的证书呢?通常是有二种方法,第一种是用r0ysue大佬写的r0capture进行hook导出,第二种是去hook Keystore,找到加载证书的地方,手动分析源码去找到证书和密码。我们先用一下第一种方案:直接上r0capture,有枣没枣打一杆子试试。



4.运气不错,证书找到了,我们直接在dowload目录下把证书拿出来,安装到Charles里



5.然后,再次进行抓包,可以看到,我们成功绕过了双向证书认证。



6.我们去看一下r0ysue大佬的脚本,简单来解释一下原理,在安卓开发中,系统包是无法混淆的,例如java.security.KeyStore不会被混淆,所以可以去hook这个类,并且在 Java 中,KeyStore$PrivateKeyEntry 是存储在 KeyStore 中,包含私鑰和相關的證書,即getPrivateKey() 和 getCertificateChain() 這兩個方法,也就是说当應用程序調用 getPrivateKey() 或 getCertificateChain() 方法來獲取私鑰和證書時,会被脚本拦截并提取返回的私钥和证书数据,然后storeP12() 函數,將提取的私鑰和證書組合起來,存儲為一個 .p12 文件,并使用密码r0ysue進行加密並寫入到指定的文件路徑。

[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
Java.perform(function(){
   functionuuid(len, radix){
       var chars ='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
       var uuid =[], i;
        radix = radix || chars.length;
       if(len){
           // Compact form
           for(i =0; i < len; i++) uuid[i]= chars[0| Math.random()* radix];
       }else{
           // rfc4122, version 4 form
           var r;
           // rfc4122 requires these characters
            uuid[8]= uuid[13]= uuid[18]= uuid[23]='-';
            uuid[14]='4';
           // Fill in random data. At i==19 set the high bits of clock sequence as
           // per rfc4122, sec. 4.1.5
           for(i =0; i <36; i++){
               if(!uuid[i]){
                    r =0| Math.random()*16;
                    uuid[i]= chars[(i ==19)?(r &0x3)|0x8: r];
               }
           }
       }
       return uuid.join('');
   }
   functionstoreP12(pri, p7, p12Path, p12Password){
       var X509Certificate = Java.use("java.security.cert.X509Certificate")
       var p7X509 = Java.cast(p7, X509Certificate);
       var chain = Java.array("java.security.cert.X509Certificate",[p7X509])
       var ks = Java.use("java.security.KeyStore").getInstance("PKCS12","BC");
        ks.load(null,null);
        ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain);
       try{
           var out = Java.use("java.io.FileOutputStream").$new(p12Path);
            ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray())
       }catch(exp){
            console.log(exp)
       }
   }
     // 在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue
    Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation=function(){
       var result =this.getPrivateKey()
       var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
       storeP12(this.getPrivateKey(),this.getCertificate(),'/sdcard/Download/'+ packageName +uuid(10,16)+'.p12','r0ysue');
       return result;
   }
    Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation=function(){
       var result =this.getCertificateChain()
       var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
       storeP12(this.getPrivateKey(),this.getCertificate(),'/sdcard/Download/'+ packageName +uuid(10,16)+'.p12','r0ysue');
       return result;
   }
});


7.这个x利蜂的例子到此为止,我们再拿出某Location,它也是双向证书认证,这次我们自己去找一下证书,还是一样启动charles进行抓包。



8.可以看到,报错和之前是一样的,我们之前说过了,这种是sslpinning的情况,我们用objection的android sslpinning disable把它过掉,过掉之后,再重新抓包,发现报错变了,请求包正常,响应包400的情况。



9.这种情况就是双向证书校验了,我们需要去解包找证书搜索.p12,.bks,.pem,还是一样没找到,我们只能去脱壳反编译代码,这次脱壳我用的是大佬给分享的一个脱壳网站:https://nop.gs/



10.然后我们去代码里找找看,这里我们直接搜索keystore,发现就这几个,而且就这俩货带着BKS的关键字样。



11.点进去看看,终于知道为啥我解包后,搜索常见证书后缀搜不着的原因了,原来是用了图片做证书。md



12.证书找到了,接下来就是找密码了,怎么找呢?当然是hook了,上脚本。之前也说过了,因为java.security.KeyStore是系统的类是不会被混淆的,所以我们hook它就行了。

[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function hook_KeyStore_load(){
    Java.perform(function(){
       var StringClass = Java.use("java.lang.String");
       var KeyStore = Java.use("java.security.KeyStore");
        KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation=function(arg0){
           // printStack("KeyStore.load1");
           // 输出调用栈
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
            console.log("KeyStore.load1:", arg0);
           this.load(arg0);
       };
        KeyStore.load.overload('java.io.InputStream','[C').implementation=function(arg0, arg1){
           // printStack("KeyStore.load2");
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
            console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1):null);
           this.load(arg0, arg1);
       };
        console.log("hook_KeyStore_load...");
   });
}
hook_KeyStore_load()


13.来,让我们来看看hook的效果如何?hook出来了,密码是lerist.key.2021



14.然后我们把证书拿出来



15.这里我用了keystore explorer对证书进行格式的转换



16.我们得把证书转成p12格式,才能在charles里安装,安装完成后,虽然是加密得,但是可以正常抓包了。



## 四、总结
在Android应用的抓包过程中,处理不同类型的证书验证机制的方法有所不同:

无证书校验:如果应用没有进行证书验证,我们只需配置抓包工具的证书即可进行抓包。

单向证书认证(SSL Pinning):对于SSL Pinning的应用,我们通常需要使用Frida等工具进行hook,以绕过SSL Pinning机制。在某些情况下,可能需要尝试多种方法,如使用JustTrustMe脚本,才能成功进行抓包。

双向证书认证:对于双向证书认证,我们可以使用r0capture直接dump证书。此外,也可以通过代码中找到证书和密码,然后将其转换为.p12证书,并导入到抓包工具中进行抓包。[/md]

免费评分

参与人数 24威望 +1 吾爱币 +42 热心值 +22 收起 理由
boryou + 1 + 1 谢谢@Thanks!
Jaop499 + 1 + 1 用心讨论,共获提升!
junjia215 + 1 + 1 用心讨论,共获提升!
superzhangxue + 1 + 1 我很赞同!
RedK + 1 谢谢@Thanks!
Courser + 1 + 1 谢谢@Thanks!
GISerliang + 1 + 1 谢谢@Thanks!
皇家诗人 + 1 + 1 我很赞同!
killjd + 1 + 1 我很赞同!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
宝明软件作者 + 1 + 1 谢谢@Thanks!
decor + 1 可以!
zhangsan173 + 1 + 1 我很赞同!
NonsenseMe + 1 + 1 鼓励转贴优秀软件安全工具和文档!
ioyr5995 + 1 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
twl288 + 1 谢谢@Thanks!
Roshan + 1 + 1 用心讨论,共获提升!
whichway + 1 谢谢@Thanks!
wangofjian + 1 + 1 整理的很全面,学习了!
debug_cat + 1 + 1 谢谢@Thanks!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
helian147 + 1 + 1 热心回复!
无问且问 + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
正己 发表于 2024-9-18 07:42
哦豁,撞车了,跟我下一期要讲的部分一致

点评

走你的路,让你无路可走  发表于 2024-9-18 14:37
沙发
reinmj 发表于 2024-9-17 22:22
3#
9812263 发表于 2024-9-17 22:32
5#
WeiZhiDeR8 发表于 2024-9-18 08:15
很有用,学习学习
6#
yxnwh 发表于 2024-9-18 09:11
很详细,适合新手小白入门,感谢!
7#
debug_cat 发表于 2024-9-18 09:27
很详细,如果可以提供样本的下载就更好了
8#
laustar 发表于 2024-9-18 10:24
学习了。很详细
9#
gto250 发表于 2024-9-18 10:31
Android 7.0之后的设备有没有免root的方法
10#
yunteng9527 发表于 2024-9-18 10:36
感谢分享,学习学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-22 02:31

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表