关于抓包的总体的思路和方法. 2025年1月14号
- iOS破解
- 2025-01-14
- 57热度
- 0评论
1, 抓包的单向证书, 通过实际的例子去解读这个问题.
, 服务端证书, 校验原理. --- 本质
---> 后续只要发送, 就能发送过去. 接收到请求, 才能, 返回数据. 才能集成证书.
通过离线的代码python 和 加上证书, 脱离app 做操作.
如果有服务端证书校验[app自带的凭证还要交给--> 服务器校验!!! ], 你的charles 没有证书,使用抓包工具自带证书, 你也不能做到. ----> 找到
服务端可以绕过证书校验,就可以返回值. 使用 Charles 可以愉快的进行抓包了.
绕过: 1, 找到集成在手机上的证书.p12 . 或者 .bks 后缀而存在的.
2, 需要找到证书的密码, (导入证书需要)
手机集成客户端校验, piner, 集成 sha256 , 集成服务端,校验 + p12 的证书, + 密码,
举例子的代码:
KeyStore.getInstance("BKS") ===>
KeyStore.getInstance("PKCS12") ==> 这两种不同的格式的证书.
===> 使用了, openRawResource 在 Raw 这个目录下存在的
===> 使用额, getAssets().open("TLS"); 就是在 Assets这个目录下存在.
Request 请求的证书, 需要把 Request 发送过去.
hook密码
Java.perform(function () {
var KeyStore = Java.use("java.security.KeyStore");
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (v1, v2) {
var pwd = Java.use("java.lang.String").$new(v2); // string的构造方法,传递一个字符数组参数,转换为, 放在hook脚本里面, java.use, 实例化一个字符串,如果有参数,就把对象传递进去,那么恰好就是字符数组.
console.log('\n------------') // 把一个字符数组,转换为字符串,
console.log("类型:" + this.getType()); // 把他的类型加入了进来, 传入 PKCS12,
console.log("密码:" + pwd);
console.log(JSON.stringify(v1)); //JSON.stringify 就可以将他到底是哪个类, 就可以把他给显示出来, 就可以知道她到底在哪个目录, 本质文件在哪里, 还需要给他拿到,
//console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); // 这里输出调用栈,可以直接,
var res = this.load(v1, v2); // 当我们hook keyLoad 时候,可以知道当前, 当前的读取的格式到底是什么. V1 是第一个参数输入流执行的参数, v2 是第二个参数, 只有内存地址.,
return res;
};
});
// frida -U -f com.paopaotalk.im -l 1.hook_password.js
// 111111
那么 泡泡聊天,就能够测试,这个脚本了.
找到 ,
类型:BKS
密码:111111
"<instance: java.io.InputStream, $className: android.content.res.AssetManager$AssetInputStream>"
找到 Asset目录,有没有BKS 文件, 如果有,那么很有可能就是证书.
尝试解压 泡泡聊天 apk, 找到 Asset 目录和 后缀为 bks结尾的文件.
===> 如果有多个 bks 结尾的文件, 丢到 搜索引擎里面可以看到, 是否我们想要的.
-----> 如果想要直接确定, hook时候直接使用,转存方式,在参数中直接实现写入到本地目录 . 只要内容拿到了, 直接写入到我自定的文件即可.
===> 通用的导出证书的脚本.
===> 导出证书时候, 一定要把 相关的 sd卡的 读写, 手机存储, 一定要开启.
Java.perform(function () {
var KeyStore = Java.use("java.security.KeyStore");
var String = Java.use("java.lang.String");
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (inputStream, v2) {
var pwd = String.$new(v2);
console.log('\n------------')
console.log("密码:" + pwd, this.getType());
// 我们这里写了一个判断, 因为如果这个文件, 等于一个 bks, 那么就让他走这里的代码,
if (this.getType() === "BKS") { // 这段代码,本质, 读取到java中的内容,并且写入到文件当中. ======== 如果是其他类型, 直接照着修改. p12或其他
var myArray = new Array(1024); //================================> 而且这个字节数组默认是 1024个字节.
for (var i = 0; i < myArray.length; i++) {
myArray[i] = 0x0; // 默认里面放置的是 0.
}
var buffer = Java.array('byte', myArray); // 这里我们创建一个 缓冲区, buffer. 里面是字节数组. ,大小默认是1024个字节. 表示我创建一个1024的字节.
// 核心的代码看这里, 通过 这里实例化 java中的一个file对象, 创建一个文件, 目标在于 远程安卓手机上的目录和远程内存. 并且加上 时间戳的 bks 后缀结尾.
var file = Java.use("java.io.File").$new("/sdcard/Download/paopao-" + new Date().getTime() + ".bks"); // ======== 如果是其他类型, bks直接照着修改. p12或其他
var out = Java.use("java.io.FileOutputStream").$new(file); // 创建一个输出流, 根据这个文件进行关联.
var r;
while ((r = inputStream.read(buffer)) > 0) { // 通过 这个 while 将输出流写入到, 这个文件. 最多是去读取 1024 字节.[37.6--泡泡hook导出证书,mp4==> 08:48 的位置.]
out.write(buffer, 0, r); // 中间这里是写入到输出流.
}
console.log("save success!")
out.close() // 通过 out.close 代表写完了.
}
var res = this.load(inputStream, v2); // 最后, 本质上你需要到 input 里面去 一点点儿的 读取, 然后一点儿一点儿写入到我们的文件.
return res;
};
});
// frida -U -f com.paopaotalk.im -l 2.hook_save.js
==> 证书 拿到 + 密码, 也可以通过 charles 携带也可以.
===> 总结:
1, hook KeyStore 的load 方法, 获取调用栈. 谁传入了这个,就是 inputstream 对象.
java.lang.Throwable
at java.security.KeyStore.load(Native Method) // 这里是最后调用的--> 调用的结束位置
at com.base.httpnet.https.HttpsUtils.prepareKeyManager(HttpsUtils.java:2) //---> 调用逐渐向上寻找.
at com.base.httpnet.https.HttpsUtils.getSslSocketFactoryBase(HttpsUtils.java:2)
at com.base.httpnet.https.HttpsUtils.getSslSocketFactory(HttpsUtils.java:4)
at com.vvchat.vcapp.QYXApplication.initOkHttpData(QYXApplication.java:3)
at com.vvchat.vcapp.QYXApplication.inital(QYXApplication.java:12)
at com.vvchat.vcapp.QYXApplication.onCreate(QYXApplication.java:5)
at com.stub.StubApp.onCreate(Unknown Source:47)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1283)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6880)
2 ,你知道他是怎么来的,就知道他是哪个对象了.
2, 反编译 apk + 脱壳
使用 frida-dexdump出来以后,直接, 全部托进去, 发现为空, 那么 , 一个个尝试, 把为空的删除,就能够找到了.
(直接使用1-5 ,或者 1-10 , 大段去找,谁中断他们,删除谁. )
3, 分析, 根据调用栈一步步向上去找. ------>
根据这个" instance.load(inputStream, str.toCharArray());"--> 前面是 两个参数, 前面是证书, 后面是密码, 这里的 str 即应是密码.
inputStream 应该是他的输入流.
找到他的上一步,----> java.security.KeyStore.load(Native Method)
public static void com.vvchat.vcapp.QYXApplication.initOkHttpData() {
try {
if (!RxHttpNet.isInit()) {
HttpsUtils.SSLParams sslSocketFactory = HttpsUtils.getSslSocketFactory(c.getAssets().open("client.bks"), "111111", new InputStream[0]); // 打开这里,获取他的密码. 通过这里代码的回溯,找到这里的, 证明我们这里的分析是正确的.如果分析以下,你还想要证明, 我们是否还需要一个hook.
a0 f2 = f(sslSocketFactory.trustManager, sslSocketFactory.sSLSocketFactory);
RxHttpNet.getInstance().setOkHttpClient(f2);
setOkHttpDataHeaders();
RxHttpNet.getInstance().init(getAppContext());
Glide.get(c).getRegistry().replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(f2));
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
------ > 奇怪的提示--- > 找不到相关的类和方法和 包.
---> 明明脱壳出来,反编译后发现有, 他却提示没有?,,,=====> 这在远程的安卓机器上, 说的就是壳导致的,就是手机里面对你的dex文件进行了壳, 启动的一瞬间, dex 先要 loading 到 memory, 之后,才能 运行相对应的功能, 如果你都没有 loading finished, 的话, 那么, 就会报错,
就可以用一个 setTime out的延迟, 延时,
尤其对于第三方的包, 加入一个 settime out 的延迟,对应地去 修改.
bks 证书, 必须 --[portecle-1.11/证书转换程序java jar ]-to ---> p12 证书--> 导入 charlles ,就可以使用了.
charles的设置-- > ssl proxy, 客户端证书, 找到 Client_Certificates, add--->

案例: 美之图app ,3,5,3.
通过测试在 android 13的位置上没有办法使用, frida 16.1.3 可以在安卓10(小米8)上能够跑起来.
类型:pkcs12
密码:uX39!dd$#rr_XIyb%
"<instance: java.io.InputStream, $className: android.content.res.AssetManager$AssetInputStream>"
java.lang.Throwable
at java.security.KeyStore.load(Native Method)
at com.deepe.c.j.c.b.b(Unknown Source:32)
at com.deepe.c.j.c.b.a(Unknown Source:39)
at com.deepe.c.j.e.d.getSslSocketFactory(Unknown Source:20)
at com.deepe.c.j.d.g.a(Unknown Source:42)
at com.deepe.c.j.d.g.a(Unknown Source:120)
at com.deepe.c.j.d.a.a(Unknown Source:33)
at com.deepe.c.j.h.run(Unknown Source:69)
// frida -D 10.10.10.200:5555 -f com.mmzztt.app -l "/home/calleng/p9/Mikrom2.0/JS_editor/com_paopaotalk_im_HOOK证书密码__防止截图黑屏.js"
通过上面的可以得到, 证书使用 pkcs12 处理, 所以我们可以 解压后,看看有没有 p12 结尾的, Asset目录里面确认以下.看看.
成功取得数据的证书绑定验证的脚本
from requests_pkcs12 import get
# 定义请求头
headers = {
"Accept": "*/*",
"User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190828.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 mztapk",
"Referer": "https://app.mmzztt.com",
"Charset": "UTF-8",
"Host": "api.iimzt.com"
}
# 定义 URL
url = "https://api.iimzt.com/app/post/p?id=91452"
# 如果需要 PKCS12 证书
pkcs12_filename = "/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download/妹子图-1737123511881.pkcs12" # 替换为你的证书路径
pkcs12_password = "uX39!dd$#rr_XIyb%" # 替换为你的证书密码
# 发送 GET 请求
response = get(
url,
headers=headers,
pkcs12_filename=pkcs12_filename, # 如果需要客户端证书
pkcs12_password=pkcs12_password, # 如果需要客户端证书
verify=False # 验证服务器证书
)
# 输出响应内容
print(response.status_code)
print(response.text)
关于证书转换的问题, 以前默认是 p12 证书, 我要使用 request 去请求,那么我使用, Portecle 这个 java工具也可以转换.
通过request的模式访问
import requests
# 客户端证书和私钥文件路径
cert_file = '/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download/certificate.pem' # 证书文件路径
key_file = '/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download//private_key.pem' # 私钥文件路径
# 请求的 URL
url = "https://api.iimzt.com/app/post/p?id=91452"
# 请求头
headers = {
"Accept": "*/*",
"User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190828.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 mztapk",
"Referer": "https://app.mmzztt.com",
"Charset": "UTF-8",
"Host": "api.iimzt.com"
}
# 发送请求
try:
response = requests.get(
url,
headers=headers,
cert=(cert_file, key_file), # 使用客户端证书和私钥
verify=False # 不验证服务器证书
)
# 输出响应内容
print("Status Code:", response.status_code)
print("Response Body:", response.text)
except requests.exceptions.RequestException as e:
print("Request failed:", e)
# ==============检测 文件是否存在.====================
import os
print("Certificate file exists:", os.path.exists(cert_file))
print("Key file exists:", os.path.exists(key_file))
with open(cert_file, 'r') as f:
print("Certificate content:", f.read())
with open(key_file, 'r') as f:
print("Key content:", f.read())
# ==============检测 文件是否存在.====================
# 类型:pkcs12
# 密码:uX39!dd$#rr_XIyb%
# "<instance: java.io.InputStream, $className: android.content.res.AssetManager$AssetInputStream>"
# curl -H "Accept: */*" -H "User-Agent: Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190828.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 mztapk" -H "Referer: https://app.mmzztt.com" -H "Charset: UTF-8" -H "Host: api.iimzt.com" --compressed "https://api.iimzt.com/app/list/posts?type=post&order=best&page=2"
# 默认requests不支持直接使用p12格式的证书,所以需要将p12转换成pem才可以,
# 注意1, request 需要 cert_file 证书文件(certificate.pem)
# 注意2, request 需要 私钥文件(private_key.pem):
# 注意3, 目前的高版本的 openssl 3.x , 不再兼容老旧的算法.
# 在 Python 中使用分离的证书和私钥 ===> 将生成的 certificate.pem 和 private_key.pem 文件路径替换到 Python 代码中:
# openssl pkcs12 -in meizhitu_2025.p12 -out certificate.pem -nokeys -password 'pass:uX39!dd$#rr_XIyb%' -legacy
# openssl pkcs12 -in meizhitu_2025.p12 -out private_key.pem -nocerts -nodes -password 'pass:uX39!dd$#rr_XIyb%' -legacy
通过curl 的模式访问, 证书加密的数据
第一种如果不成功
curl -v -X GET \
-H "Accept: */*" \
-H "User-Agent: Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190828.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 mztapk" \
-H "Referer: https://app.mmzztt.com" \
-H "Charset: UTF-8" \
-H "Host: api.iimzt.com" \
--cert "/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download/certificate.pem" \
--key "/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download/private_key.pem" \
--insecure \
"https://api.iimzt.com/app/post/p?id=91452"
使用第二种
curl -v -X GET \
-H "Accept: */*" \
-H "User-Agent: Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190828.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 mztapk" \
-H "Referer: https://app.mmzztt.com" \
-H "Charset: UTF-8" \
-H "Host: api.iimzt.com" \
--cert "/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download/certificate.pem" \
--key "/home/calleng/p9/Mikrom2.0/season5/day36 抓包-服务端证书/Download/private_key.pem" \
--insecure "https://api.iimzt.com/app/post/p?id=91452"
