APP服务端挖掘
2024-1-10 12:54:42 Author: F12sec(查看原文) 阅读量:14 收藏

前言:几个月之前水和整理的文章了 不过一直没公开 发觉这方面的知识蛮少的 而且参差不齐 不过技术嘛 分享出来玩嘛

常用模拟器

①夜神模拟器7.0.0.6

②MuMu模拟器

burp抓包思路(真机或者虚拟机思路均同)

这里以真机来复现

电脑配置的

1.)电脑开热点

2.)查看本机的ip

ipconfig

3.)burp中抓包设置

然后点击ok 即启动监听了

手机配置

1.)连接wifi

2.长按该wifi

点击修改网络

去设置代理

3.浏览器中访问http://burp

然后点击ca 进行下载证书 (或者电脑下载一个然后传到手机去 建议这种方法)

4.)将下载下来的证书 改名为

crt格式的(手机和模拟器一般就是这种格式的)

5.)去安装证书

一般的位置在(跟手机相关)

设置-->安全-->加密与凭证-->从存储设置安装证书

然后找到证书的位置进行安装即可此时的证书是用户证书 虽然也能抓 但是最好转系统证书比较好

证书转换方法

Magisk 有个插件叫做 Move Certificates ,该插件可以一键将所有用户证书自动转换为系统区证书

使用方法:所有证书安装为用户证书后,安装此插件,并重启。

一般没有监测之类的app就可以愉快的抓包了

看了很多文章发觉核心就是三个对抗

一.root监测对抗

绕过思路

1.)xpose hide

这个能绕一些app的root监测 但是银行这些不行

2.) 利用magisk进行绕过

2.1)高版本自带zygisk进行

这个要比较新版本的magisk才可

直接在框架里面扔即可

实测可绕过银行

不过这个无法跟xpose的插件兼容 难受就难受到这里

2.2)手动安装riru-momohider模块绕过

这个核心就是安装的Riru-MomoHider 模块

低版本安装该模块方法

①下载Riru-MomoHider

https://github.com/canyie/Riru-MomoHider/releases/download/0.0.7/Riru-MomoHider.zip

将下载的zip文件拷贝到设备存储目录

②打开Magisk管理界面,进入插件模块,选择从本地安装插件

选中上传的Riru-MomoHider.zip 直接进行安装

③进行配置文件

安装重启后,对Riru-MomoHider 进行配置

执行adb shell,进入手机命令终端

cd /data/adb/modules/riru_momohider/config

分别在/data/adb/modules/riru_momohider/config目录下

创建以下几个文件

touch setns 
touch app_zygote_magic
touch initrc touch isolated

创建完成后,重启手机即可

3.)app anrc(这个我一般不咋用 不太好用 没上面两种方法好用)

一个绕root监测的app

直接把要绕的加到应用列表里面就好了

二.代理问题对抗

1.)APP不走代理问题

常见现象:burp 拦截时 抓不到包 但是可以正常访问页面

产生原理:

  • android系统设置的代理并不是强制对所有app生效的

  • app可以在网络请求类库中通过自定义代理设置,选择是否要走系统代理

通信协议可直接设置即默认app不走系统的代理了

proxy(Proxy.NO_PROXY)

代码如下

绕过方法:使用全局系统代理的工具即可,即抓的是系统本身的流量

最简单方法:

1.1)手机

抓的话直接使用:httpCanary绕过(需要root), Packet Capture(不需要root这个我用的很少 我一般都是使用前面的一个)

个人比较喜欢的

httpcanary+scrcpy结合使用很舒服

1.2)电脑:

结合模拟器使用绕的

使用proxifier直接抓模拟器进程的流量

真机

方法很多 不过个人喜欢burp+drony的方法抓,看个人习惯吧

①下载一个设置全局代理的软件

这里用Drony

https://files.cnblogs.com/files/lulianqi/Drony_102.apk

②安装后

点击进去选择设置

点击wifi

点进去后选择自己电脑设置的wifi

然后设置端口以及ip

这里已经就可以进行抓包了

过滤默认值默认为允许全部,点击编辑过滤规则

但是为了方便防止抓到额外不想要的包

去进行设置filter

进行设置

规则rule设置为本地代理链全部

然后选择自己要设置的apk即可

然后启动该代理后访问时电脑抓包即可

1.3)hook方法绕

就是该代码(这种没必要拿来绕no_proxy 要用绕的话肯定也拿去绕监测代理的)

使用frida来hook网络请求框架的限制函数,从而绕过验证。

2.)app做了代理监测

现象:设置手机代理后,APP无法获取网络数据。会出现无法连接网络的情况出现

简单看下原理:

一般都是代码中写了监测方法导致的

绕过方法:

这种就只能利用friba来hook请求框架的方法进行绕了

三.证书监测对抗

1.)基础知识:

SSL Pinning:

开发者预先把证书相关信息预置到 App 中再打包,这样在 https 通讯过程中 App 本地可以与服务器返回的 CA 证书可以做合法性校验,如果发现不一致,那么可能就是由于中间人攻击(比如 Fiddler/Charles 抓包工具),App 客户端可以终止 https 链接。而在新版本的系统规则中,应用只信任系统默认预置的 CA 证书,如果是第三方安装的证书(比如 Fiddler 安装的)则不会信任

即将服务端证书相关信息打包在客户端里,在进行通信时,对比客户端和服务端证书是否一致。

Android实现Https的几种方式:

1.通过 OkHttp 来实现
2.自定义证书和 HostnameVerify 来实现 Https 校验
3.通过 HttpsURLConnection 来实现HttpsURLConnection 中进行 SSL 证书校验

hook:

改变程序执行流程的一种技术,即改变最开始的代码执行流程 劫持成自己想执行的代码 进而绕过apk这些的执行流程

一般其实就两种情况

典型现象

也是抓包显示400 且小黄鸟抓包时显示使用固定证书

或者抓包时显示burp疯狂显示400这些东西

2.)单向固定证书问题

绕过ssl pinning的常用方法

2.1)利用xpose安装模块进行绕过

下载 JustTrustMe 安装包并安装到手机里,在 Xposted 中激活并重启手机,就可以抓取手机中的流量。

SSLUnpinning 2.0 插件和 JustMePlush 插件当然也可以

插件的原理:将 APK 中所有用于校验 SSL 证书的 API 都进行了 Hook,从而绕过证书检查

2.2)使用Objection绕过

(这个我用的少 毕竟xpose模块方便一点)

objection是基于frida开发的一个工具,将一些简单常见的操作进行集成,减少代码重复编写的工作。

frida-server下载的地址

https://pan.baidu.com/s/1YSgdh0ojCabbFGoRJqiGgA

密码:wagx

注意电脑配置

安装模块

python3 -m pip install frida
python3 -m pip install frida-tools
python3 -m pip install objection

一定要这个版本 不然后面会报错为

安装方法

①查看有几个设备

adb devices

②将friba-server拷贝到手机中

adb  push 电脑的文件路径 /data/local/tmp

如果有多个设备的话

就加上-s 就是指定上面的设备的

如 adb -s 6fb4046c push 电脑的文件路径 /data/local/tmp

然后进入操控手机

adb shell

su 且切换为root权限

如果遇到了禁止这个的话就看看手机是否root 且magisk中 是否允许shell为超管

cd /data/local/tmp

chmod 777 frida-server

./frida-server 这样已经算是启动成功了

在开一个窗口进行tcp转发

adb forward tcp:27043 tcp:27043

adb forward tcp:27042 tcp:27042

然后另外开一个窗口进行测试frida

frida-ps -U

如果出现这种情况就是上面的电脑环境没配置好

解决方法回到上面电脑配置的 依次输入三个命令

然后等待安装完成后

安装成功后再次运行即可完美解决问题

然后在使用

adb shell pm list packages 查看手机中的包名和数量

objection将附加到目标应用程序上

objection --gadget  包名 explore (此时 Objection 将注入到目标应用程序上

注意点:需要关闭手机上 Magisk 的 magisk hide 功能(不关闭会有冲突)

关闭 app 的 SSL 校验:

android sslpinning disable

即成功关闭,即可成功抓取到流量

2.3)注入burp证书

frida -U -f com.xxxx.app.ui -l C:\Users\xxx\Desktop\fridascript.js --no-pause

/* Android SSL Re-pinning frida script v0.2 030417-pier
$ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause
https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/ UPDATE 20191605: Fixed undeclared var. Thanks to @oleavr and @ehsanpc9999 !*/
setTimeout(function(){ Java.perform(function (){ console.log(""); console.log("[.] Cert Pinning Bypass/Re-Pinning");
var CertificateFactory = Java.use("java.security.cert.CertificateFactory"); var FileInputStream = Java.use("java.io.FileInputStream"); var BufferedInputStream = Java.use("java.io.BufferedInputStream"); var X509Certificate = Java.use("java.security.cert.X509Certificate"); var KeyStore = Java.use("java.security.KeyStore"); var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory"); var SSLContext = Java.use("javax.net.ssl.SSLContext");
// Load CAs from an InputStream console.log("[+] Loading our CA...") var cf = CertificateFactory.getInstance("X.509"); try { var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt"); } catch(err) { console.log("[o] " + err); } var bufferedInputStream = BufferedInputStream.$new(fileInputStream); var ca = cf.generateCertificate(bufferedInputStream); bufferedInputStream.close();
var certInfo = Java.cast(ca, X509Certificate); console.log("[o] Our CA Info: " + certInfo.getSubjectDN());
// Create a KeyStore containing our trusted CAs console.log("[+] Creating a KeyStore for our CA..."); var keyStoreType = KeyStore.getDefaultType(); var keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore..."); var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); var tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); console.log("[+] Our TrustManager is ready...");
console.log("[+] Hijacking SSLContext methods now...") console.log("[-] Waiting for the app to invoke SSLContext.init()...")
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) { console.log("[o] App invoked javax.net.ssl.SSLContext.init..."); SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c); console.log("[+] SSLContext initialized with our custom TrustManager!"); } });},0);

3.)双向证书问题

解决这类问题的核心获取两个东西:

证书文件
证书密码

3.1)确定方法:

如果抓包爆400且反编译的文件中存在证书+逻辑代码中存在引用定义

如下即存在双向证书问题

反编译app看看是否存在证书

然后查看re目录或者assets目录中是否存在证书

常见证书的格式

".pfx" "pkcs12" "p12" "keyStore" "cer"

3.2)获取证书的解密密钥:

3.2.1)通用思路

①hook底层框架代码java.security.KeyStore,实现密钥自吐

-f的是app的包名  ssl 是指的js的文件

代码如下


var N_ENCRYPT_MODE = 1var N_DECRYPT_MODE = 2
function showStacks() { var Exception = Java.use("java.lang.Exception"); var ins = Exception.$new("Exception"); var straces = ins.getStackTrace();
if (undefined == straces || null == straces) { return; }
console.log("============================= Stack strat======================="); console.log("");
for (var i = 0; i < straces.length; i++) { var str = " " + straces[i].toString(); console.log(str); }
console.log(""); console.log("============================= Stack end=======================\r\n"); Exception.$dispose();}
//工具相关函数 var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 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, (-1), (-1), (-1), (-1), (-1), (-1), 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, (-1), (-1), (-1), (-1), (-1));
function stringToBase64(e) { var r, a, c, h, o, t; for (c = e.length, a = 0, r = ''; a < c;) { if (h = 255 & e.charCodeAt(a++), a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4), r += '=='; break } if (o = e.charCodeAt(a++), a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2), r += '='; break } t = e.charCodeAt(a++), r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), r += base64EncodeChars.charAt(63 & t) } return r}function base64ToString(e) { var r, a, c, h, o, t, d; for (t = e.length, o = 0, d = ''; o < t;) { do r = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && r == -1); if (r == -1) break; do a = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && a == -1); if (a == -1) break; d += String.fromCharCode(r << 2 | (48 & a) >> 4); do { if (c = 255 & e.charCodeAt(o++), 61 == c) return d; c = base64DecodeChars[c] } while (o < t && c == -1); if (c == -1) break; d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2); do { if (h = 255 & e.charCodeAt(o++), 61 == h) return d; h = base64DecodeChars[h] } while (o < t && h == -1); if (h == -1) break; d += String.fromCharCode((3 & c) << 6 | h) } return d}function hexToBase64(str) { return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));}function base64ToHex(str) { for (var i = 0, bin = base64Decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) { var tmp = bin.charCodeAt(i).toString(16); if (tmp.length === 1) tmp = "0" + tmp; hex[hex.length] = tmp; } return hex.join("");}function hexToBytes(str) { var pos = 0; var len = str.length; if (len % 2 != 0) { return null; } len /= 2; var hexA = new Array(); for (var i = 0; i < len; i++) { var s = str.substr(pos, 2); var v = parseInt(s, 16); hexA.push(v); pos += 2; } return hexA;}function bytesToHex(arr) { var str = ''; var k, j; for (var i = 0; i < arr.length; i++) { k = arr[i]; j = k; if (k < 0) { j = k + 256; } if (j < 16) { str += "0"; } str += j.toString(16); } return str;}function stringToHex(str) { var val = ""; for (var i = 0; i < str.length; i++) { if (val == "") val = str.charCodeAt(i).toString(16); else val += str.charCodeAt(i).toString(16); } return val}function stringToBytes(str) { var ch, st, re = []; for (var i = 0; i < str.length; i++) { ch = str.charCodeAt(i); st = []; do { st.push(ch & 0xFF); ch = ch >> 8; } while (ch); re = re.concat(st.reverse()); } return re;}//将byte[]转成String的方法function bytesToString(arr) { var str = ''; arr = new Uint8Array(arr); for (var i in arr) { str += String.fromCharCode(arr[i]); } return str;}function bytesToBase64(e) { var r, a, c, h, o, t; for (c = e.length, a = 0, r = ''; a < c;) { if (h = 255 & e[a++], a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4), r += '=='; break } if (o = e[a++], a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2), r += '='; break } t = e[a++], r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), r += base64EncodeChars.charAt(63 & t) } return r}function base64ToBytes(e) { var r, a, c, h, o, t, d; for (t = e.length, o = 0, d = []; o < t;) { do r = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && r == -1); if (r == -1) break; do a = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && a == -1); if (a == -1) break; d.push(r << 2 | (48 & a) >> 4); do { if (c = 255 & e.charCodeAt(o++), 61 == c) return d; c = base64DecodeChars[c] } while (o < t && c == -1); if (c == -1) break; d.push((15 & a) << 4 | (60 & c) >> 2); do { if (h = 255 & e.charCodeAt(o++), 61 == h) return d; h = base64DecodeChars[h] } while (o < t && h == -1); if (h == -1) break; d.push((3 & c) << 6 | h) } return d}//stringToBase64 stringToHex stringToBytes//base64ToString base64ToHex base64ToBytes// hexToBase64 hexToBytes // bytesToBase64 bytesToHex bytesToString

Java.perform(function () { var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec'); secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) { showStacks(); var result = this.$init(a, b); console.log("======================================"); console.log("算法名:" + b + "|str密钥:" + bytesToString(a)); console.log("算法名:" + b + "|Hex密钥:" + bytesToHex(a)); return result; }
var DESKeySpec = Java.use('javax.crypto.spec.DESKeySpec'); DESKeySpec.$init.overload('[B').implementation = function (a) { showStacks(); var result = this.$init(a); console.log("======================================"); var bytes_key_des = this.getKey(); console.log("des密钥 |str " + bytesToString(bytes_key_des)); console.log("des密钥 |hex " + bytesToHex(bytes_key_des)); return result; }
DESKeySpec.$init.overload('[B', 'int').implementation = function (a, b) { showStacks(); var result = this.$init(a, b); console.log("======================================"); var bytes_key_des = this.getKey(); console.log("des密钥 |str " + bytesToString(bytes_key_des)); console.log("des密钥 |hex " + bytesToHex(bytes_key_des)); return result; }
var mac = Java.use('javax.crypto.Mac'); mac.getInstance.overload('java.lang.String').implementation = function (a) { showStacks(); var result = this.getInstance(a); console.log("======================================"); console.log("算法名:" + a); return result; } mac.update.overload('[B').implementation = function (a) { //showStacks(); this.update(a); console.log("======================================"); console.log("update:" + bytesToString(a)) } mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { //showStacks(); this.update(a, b, c) console.log("======================================"); console.log("update:" + bytesToString(a) + "|" + b + "|" + c); } mac.doFinal.overload().implementation = function () { //showStacks(); var result = this.doFinal(); console.log("======================================"); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; } mac.doFinal.overload('[B').implementation = function (a) { //showStacks(); var result = this.doFinal(a); console.log("======================================"); console.log("doFinal参数: |str :" + bytesToString(a)); console.log("doFinal参数: |hex :" + bytesToHex(a)); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; }
var md = Java.use('java.security.MessageDigest'); md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) { //showStacks(); console.log("======================================"); console.log("算法名:" + a); return this.getInstance(a, b); } md.getInstance.overload('java.lang.String').implementation = function (a) { //showStacks(); console.log("======================================"); console.log("算法名:" + a); return this.getInstance(a); } md.update.overload('[B').implementation = function (a) { //showStacks(); console.log("======================================"); console.log("update:" + bytesToString(a)) return this.update(a); } md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { //showStacks(); console.log("======================================"); console.log("update:" + bytesToString(a) + "|" + b + "|" + c); return this.update(a, b, c); } md.digest.overload().implementation = function () { //showStacks(); console.log("======================================"); var result = this.digest(); console.log("digest结果 |hex:" + bytesToHex(result)); console.log("digest结果 |base64:" + bytesToBase64(result)); return result; } md.digest.overload('[B').implementation = function (a) { //showStacks(); console.log("======================================"); console.log("digest参数 |str:" + bytesToString(a)); console.log("digest参数 |hex:" + bytesToHex(a)); var result = this.digest(a); console.log("digest结果: |hex" + bytesToHex(result)); console.log("digest结果: |base64" + bytesToBase64(result)); return result; }
var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec'); ivParameterSpec.$init.overload('[B').implementation = function (a) { //showStacks(); var result = this.$init(a); console.log("======================================"); console.log("iv向量: |str:" + bytesToString(a)); console.log("iv向量: |hex:" + bytesToHex(a)); return result; }
var cipher = Java.use('javax.crypto.Cipher'); cipher.getInstance.overload('java.lang.String').implementation = function (a) { //showStacks(); var result = this.getInstance(a); console.log("======================================"); console.log("模式填充:" + a); return result; } cipher.init.overload('int', 'java.security.Key').implementation = function (a, b) { //showStacks(); var result = this.init(a, b); console.log("======================================"); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); }
var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (a, b) { //showStacks(); var result = this.init(a, b); console.log("======================================"); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); }
return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); console.log("======================================"); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));
return result; } cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); }
var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); }
var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (a, b, c, d) { //showStacks(); var result = this.init(a, b, c, d); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); }
var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (a, b, c, d) { //showStacks(); var result = this.init(a, b, c, d); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); }
var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; }
cipher.update.overload('[B').implementation = function (a) { //showStacks(); var result = this.update(a); console.log("======================================"); console.log("update:" + bytesToString(a)); return result; } cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { //showStacks(); var result = this.update(a, b, c); console.log("======================================"); console.log("update:" + bytesToString(a) + "|" + b + "|" + c); return result; } cipher.doFinal.overload().implementation = function () { //showStacks(); var result = this.doFinal(); console.log("======================================"); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; } cipher.doFinal.overload('[B').implementation = function (a) { //showStacks(); var result = this.doFinal(a); console.log("======================================"); console.log("doFinal参数: |str :" + bytesToString(a)); console.log("doFinal参数: |hex :" + bytesToHex(a)); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; }
var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec'); x509EncodedKeySpec.$init.overload('[B').implementation = function (a) { //showStacks(); var result = this.$init(a); console.log("======================================"); console.log("RSA密钥:" + bytesToBase64(a)); return result; }
var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec'); rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) { //showStacks(); var result = this.$init(a, b); console.log("======================================"); //console.log("RSA密钥:" + bytesToBase64(a)); console.log("RSA密钥N:" + a.toString(16)); console.log("RSA密钥E:" + b.toString(16)); return result; }
var KeyPairGenerator = Java.use('java.security.KeyPairGenerator'); KeyPairGenerator.generateKeyPair.implementation = function () { //showStacks(); var result = this.generateKeyPair(); console.log("======================================"); var str_private = result.getPrivate().getEncoded(); var str_public = result.getPublic().getEncoded(); console.log("公钥 |hex" + bytesToHex(str_public)); console.log("私钥 |hex" + bytesToHex(str_private));
return result; }
KeyPairGenerator.genKeyPair.implementation = function () { //showStacks(); var result = this.genKeyPair(); console.log("======================================");
var str_private = result.getPrivate().getEncoded(); var str_public = result.getPublic().getEncoded(); console.log("公钥 |hex" + bytesToHex(str_public)); console.log("私钥 |hex" + bytesToHex(str_private));
return result; }});
 frida -U -f cn.soulapp.android -l ssl.js  --no-pause


frida -U -f cn.xxxxapp.android -l .\hook_Key.js --no-pause
frida -U -f com.xxxx.app.ui -l C:\Users\xxx\Desktop\333.js --no-pause

②hook网络框架抓包获取密钥

1、首先确定使用的框架,主流框架为okhttp、HttpURLconnection

# android hooking list classes

2、然后搜索过滤类文件中值得怀疑的框架:

.objection # cat objection.log |grep -i volley
.objection # cat objection.log |grep -i okhttp
.objection # cat objection.log |grep -i HttpURLconnection

可以看到当我们在APP上操作时候,经过了okhttp框架

找到APP使用的框架后如okhttp,然后通过frida加载js脚本来进行绕过。同样可以看到数据请求和返回。

3.2.2)批量与绕过思路

①批量hook查看轨迹

②hook强混淆app抓包

3.3)导入证书:

注意点:

如果是p12证书的话 直接上面hook获取密钥

然后直接burp中导入客户端中的证书即可

①导出证书的key

如果证书没有密码的话且存在一个crt证书时

直接导出证书的key
openssl pkcs12 –in client.pfx –nocerts –nodes –out client.key

导出的话 如果有密码的话 必须要输入密码 就需要hook获取密码了

②将crt和上面提取的key合并生产.p12证书

会需要设置密码 随便设置一个密码就好了openssl pkcs12 -export -inkey client.key -in client.crt -out client.p12

③导入证书即可

然后选择第一个

点击next后

然后导入上面合并生成的证书

四.通杀脚本

1.)r0capture通杀脚本

https://github.com/r0ysue/r0capture

r0capture使用方法

https://bbs.pediy.com/thread-272116.htm

缺点:只能结合wireshark分析流量包

不能和burp等抓包软件代理抓包(当然如果未加密可以解密请求包然后给burp重发)

但是可以结合起来挖付费资料文档和视频的思路

即包文档之前返回回来

思路

如果是windows的话 需要安装库

在开启frida后进行输入下面的即可

①抓取app的包为pcap

python r0capture.py -U com.打码.android.servant -v -p ./pkg/001-xxx/001.pcap

②wireshark打开

进行过滤内容http.responsem 获取到其的十六进制

然后十六进制解码即可获取内容

https://cloud.tencent.com/developer/article/1878983

https://www.secpulse.com/archives/177572.html

https://www.cnblogs.com/tomyyyyy/p/15310537.html#tid-dWRAmC

https://www.6b8.me/index.php?m=home&c=article&a=index&id=400

https://ch3nye.top/%E3%80%90%E5%AE%9E%E6%88%98%E3%80%91%E6%9F%90%E4%BA%A4%E5%8F%8Bapp%E7%9A%84%E5%8F%8C%E5%90%91%E8%AE%A4%E8%AF%81crack/#0x1-%E6%A3%80%E6%9F%A5%E8%AF%81%E4%B9%A6%E6%A0%A1%E9%AA%8C%E6%96%B9%E5%BC%8F

https://ch3nye.top/Android-HTTPS%E8%AE%A4%E8%AF%81%E7%9A%84N%E7%A7%8D%E6%96%B9%E5%BC%8F%E5%92%8C%E5%AF%B9%E6%8A%97%E6%96%B9%E6%B3%95%E6%80%BB%E7%BB%93/#0x2-https-%E5%BF%BD%E7%95%A5%E8%AF%81%E4%B9%A6%E9%AA%8C%E8%AF%81


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5NjU3NzE3OQ==&mid=2247488799&idx=2&sn=df942a6acbd86a33aaf9d63eb80cb0cb&chksm=c1226f01c16f10766d35a7343cc21095bec1e493efdb73b206e4ac3d90d2b8987c5fc78b9be6&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh