|
|
发表于
2024-11-18 19:21:25
|
显示全部楼层
来自 福建
3.native 层 http 发包


`Hook实现`
- js
- // 获取 libc.so 库中的 sendto 和 recvfrom 函数的指针
- var sendtoPtr = Module.getExportByName("libc.so", "sendto");
- var recvfromPtr = Module.getExportByName("libc.so", "recvfrom");
- console.log("sendto:", sendtoPtr, ", recvfrom:", recvfromPtr);
- // 拦截 sendto 函数
- // sendto(int fd, const void *buf, size_t n, int flags, const struct sockaddr *addr, socklen_t addr_len)
- Interceptor.attach(sendtoPtr, {
- onEnter: function(args) {
- // 获取文件描述符 fd
- var fd = args[0];
- // 获取要发送的缓冲区指针 buff
- var buff = args[1];
- // 获取数据大小 size
- var size = args[2];
-
- // 获取套接字的相关信息
- var sockdata = getSocketData(fd.toInt32());
- console.log(sockdata);
-
- // 打印缓冲区的十六进制内容
- console.log(hexdump(buff, { length: size.toInt32() }));
- },
- onLeave: function(retval) {
- // 离开 sendto 函数时不做额外处理
- }
- });
- // 拦截 recvfrom 函数
- // recvfrom(int fd, void *buf, size_t n, int flags, struct sockaddr *addr, socklen_t *addr_len)
- Interceptor.attach(recvfromPtr, {
- onEnter: function(args) {
- // 获取文件描述符 fd
- this.fd = args[0];
- // 获取缓冲区指针 buff
- this.buff = args[1];
- // 获取数据大小 size
- this.size = args[2];
- },
- onLeave: function(retval) {
- // 获取套接字的相关信息
- var sockdata = getSocketData(this.fd.toInt32());
- console.log(sockdata);
-
- // 打印接收到的缓冲区的十六进制内容
- console.log(hexdump(this.buff, { length: this.size.toInt32() }));
- }
- });
复制代码
拦截 `sendto` 和 `recvfrom` 函数,捕获发送和接收的数据包。`onEnter` 钩子函数用于在函数调用前处理参数,获取文件描述符和缓冲区地址,调用 `hexdump` 打印缓冲区内容以便查看实际发送或接收的数据
4.native 层 https 发包

`Hook实现`
- js
- // 获取 libc.so 库中的 write 和 read 函数的指针
- var writePtr = Module.getExportByName("libc.so", "write");
- var readPtr = Module.getExportByName("libc.so", "read");
- console.log("write:", writePtr, ", read:", readPtr);
- // 拦截 write 函数
- // write(int fd, const void *buf, size_t count)
- Interceptor.attach(writePtr, {
- onEnter: function(args) {
- // 获取文件描述符 fd
- var fd = args[0];
- // 获取写入的数据缓冲区指针 buff
- var buff = args[1];
- // 获取数据大小 size
- var size = args[2];
- // 获取套接字信息(假设 getSocketData 是自定义函数)
- var sockdata = getSocketData(fd.toInt32());
- // 如果套接字是 TCP 类型,打印相关数据
- if (sockdata.indexOf("tcp") !== -1) {
- console.log(sockdata);
- console.log(hexdump(buff, { length: size.toInt32() }));
- }
- },
- onLeave: function(retval) {
- // 离开 write 函数时不做额外处理
- }
- });
- // 拦截 read 函数
- // read(int fd, void *buf, size_t count)
- Interceptor.attach(readPtr, {
- onEnter: function(args) {
- // 获取文件描述符 fd
- this.fd = args[0];
- // 获取读取的缓冲区指针 buff
- this.buff = args[1];
- // 获取数据大小 size
- this.size = args[2];
- },
- onLeave: function(retval) {
- // 获取套接字信息
- var sockdata = getSocketData(this.fd.toInt32());
- // 如果套接字是 TCP 类型,打印相关数据
- if (sockdata.indexOf("tcp") !== -1) {
- console.log(sockdata);
- console.log(hexdump(this.buff, { length: this.size.toInt32() }));
- }
- }
- });
- // 获取 libssl.so 中的 SSL_write、SSL_read 和 SSL_get_rfd 函数的指针
- var sslWritePtr = Module.getExportByName("libssl.so", "SSL_write");
- var sslReadPtr = Module.getExportByName("libssl.so", "SSL_read");
- console.log("sslWrite:", sslWritePtr, ", sslRead:", sslReadPtr);
- // 获取 SSL_get_rfd 函数的指针,用于从 SSL 结构体中获取文件描述符
- var sslGetFdPtr = Module.getExportByName("libssl.so", "SSL_get_rfd");
- // 使用 NativeFunction 创建对 SSL_get_rfd 函数的调用
- var sslGetFdFunc = new NativeFunction(sslGetFdPtr, 'int', ['pointer']);
- // 拦截 SSL_write 函数
- // int SSL_write(SSL *ssl, const void *buf, int num)
- Interceptor.attach(sslWritePtr, {
- onEnter: function(args) {
- // 获取 SSL 对象指针
- var sslPtr = args[0];
- // 获取要发送的缓冲区指针
- var buff = args[1];
- // 获取数据大小
- var size = args[2];
-
- // 使用 SSL_get_rfd 获取文件描述符
- var fd = sslGetFdFunc(sslPtr);
- // 获取套接字的数据(假设 getSocketData 是自定义函数)
- var sockdata = getSocketData(fd);
-
- // 打印套接字数据和发送数据的十六进制内容
- console.log(sockdata);
- console.log(hexdump(buff, { length: size.toInt32() }));
- },
- onLeave: function(retval) {
- // 离开 SSL_write 函数时不做额外处理
- }
- });
- // 拦截 SSL_read 函数
- // int SSL_read(SSL *ssl, void *buf, int num)
- Interceptor.attach(sslReadPtr, {
- onEnter: function(args) {
- // 获取 SSL 对象指针
- this.sslPtr = args[0];
- // 获取接收缓冲区指针
- this.buff = args[1];
- // 获取接收数据的大小
- this.size = args[2];
- },
- onLeave: function(retval) {
- // 使用 SSL_get_rfd 获取文件描述符
- var fd = sslGetFdFunc(this.sslPtr);
- // 获取套接字的数据
- var sockdata = getSocketData(fd);
-
- // 打印套接字数据和接收到的十六进制数据
- console.log(sockdata);
- console.log(hexdump(this.buff, { length: this.size.toInt32() }));
- }
- });
复制代码
`r0capture简介`
- 仅限安卓平台,测试安卓7-14 可用 ;
- 无视所有证书校验或绑定;
- 通杀TCP/IP四层模型中的应用层中的全部协议;
- 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本;
- 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等;
- 无视加固
局限:部分开发实力过强的大厂或框架,采用的是自身的SSL框架,比如WebView、部分融合App、小程序或Flutter,这部分目前暂未支持。
- python3 r0capture.py -U wuaipojie -v -p test.pcap
复制代码
wireshark下载地址
3.ebpf抓包实战&ecapture
1.什么是ebpf
what-is-ebpf
eBPF是一个运行在 Linux 内核里面的虚拟机组件,它可以在无需改变内核代码或者加载内核模块的情况下,安全而又高效地拓展内核的功能。

2.ebpf的功能之网络抓包

ecapture
官方案例
eCapture主要利用了eBPF和HOOK技术:
- **eBPF加载机制**:利用eBPF技术进行数据包的捕获和处理,eBPF程序是事件驱动的,当内核或应用程序通过某个挂钩点时运行。预定义的钩子包括系统调用、函数入口/出口、内核跟踪点、网络事件和其他几个;
- **HOOK机制**:使用eBPF uprobe相关函数进行用户态函数的HOOK,支持对不同编程语言实现的加密库进行HOOK,如OpenSSL、GnuTLS、NSS/NSPR。
eCapture 的工作原理涉及到用户态和内核态。用户态就是运行应用程序的地方,比如各种 App。在这个区域中,eCapture 通过一个共享的模块(Shared Object)获取应用程序的网络数据。然后,它将这些数据传递给内核态的 eBPF 程序进行分析和处理。
在内核空间,eCapture 通过 eBPF 插件捕捉网络层的数据流,比如数据包是从哪里来的、发到了哪里去。这一过程不需要修改应用程序本身,所以对系统性能影响很小。

安卓设备的内核版本只有在5.10版本上才可以进行无任何修改的开箱抓包操作(如果你的设备是安卓13,应该可以正常使用ecapture。低于13的安卓设备,如果内核是5.10,理论也是可行的。 因为安卓使用的linux内核的ebpf环境受内核版本号的影响,而工作良好的ebpf接口是在内核5.5版本时才全部使能。)
可通过adb命令查看自己的设备的内核版本
- adb shell cat /proc/version
- 或者adb shell uname -a
复制代码
下载地址
- adb push ecapture /data/local/tmp/
- adb shell chmod 777 /data/local/tmp/ecapture
复制代码
`使用说明`
- NAME:
- eCapture - 通过eBPF捕获SSL/TLS明文数据,无需安装CA证书。支持Linux/Android内核,适用于amd64/arm64架构。
- USAGE:
- eCapture [flags]
- VERSION:
- androidgki_arm64:v0.8.9:6.5.0-1025-azure
- COMMANDS:
- bash 捕获bash命令的执行信息
- gotls 捕获使用TLS/HTTPS加密的Golang程序的明文通信
- help 获取有关任何命令的帮助信息
- tls 用于捕获TLS/SSL明文内容,无需CA证书。支持OpenSSL 1.0.x/1.1.x/3.x或更新版本。
- DESCRIPTION:
- eCapture(旁观者)是一个可以捕获如HTTPS和TLS等明文数据包的工具,且不需要安装CA证书。
- 它还可以捕获bash命令,适用于安全审计场景,比如mysqld数据库审计等(在Android中禁用)。
- 支持Linux(Android)系统,内核版本为X86_64 4.18或aarch64 5.5及更高版本。
- 项目仓库:[url]https://github.com/gojue/ecapture[/url]
- 官方主页:[url]https://ecapture.cc[/url]
- 使用方法:
- ecapture tls -h
- ecapture bash -h
- Docker使用示例:
- docker pull gojue/ecapture:latest
- docker run --rm --privileged=true --net=host -v ${HOST_PATH}:${CONTAINER_PATH} gojue/ecapture -h
- NAME:
- tls - 用于捕获TLS/SSL明文内容,无需CA证书。支持OpenSSL 1.0.x/1.1.x/3.x及更新版本。
- USAGE:
- eCapture tls [flags]
- DESCRIPTION:
- 使用eBPF uprobe/TC捕获进程事件数据和网络数据。还支持pcap-NG格式。
- 示例:
- ecapture tls -m [text|keylog|pcap] [flags] [pcap过滤表达式(用于pcap模式)]
- ecapture tls -m pcap -i wlan0 -w save.pcapng host 192.168.1.1 and tcp port 443
- ecapture tls -l save.log --pid=3423
- ecapture tls --libssl=/lib/x86_64-linux-gnu/libssl.so.1.1
- ecapture tls -m keylog --pcapfile save_3_0_5.pcapng --ssl_version="openssl 3.0.5" --libssl=/lib/x86_64-linux-gnu/libssl.so.3
- ecapture tls -m pcap --pcapfile save_android.pcapng -i wlan0 --libssl=/apex/com.android.conscrypt/lib64/libssl.so --ssl_version="boringssl 1.1.1" tcp port 443
- Docker使用示例:
- docker pull gojue/ecapture
- docker run --rm --privileged=true --net=host -v /etc:/etc -v /usr:/usr -v ${PWD}:/output gojue/ecapture tls -m pcap -i wlp3s0 --pcapfile=/output/ecapture.pcapng tcp port 443
- OPTIONS:
- --cgroup_path="/sys/fs/cgroup" 设置cgroup路径,默认值:/sys/fs/cgroup。
- -h, --help[=false] 获取tls命令的帮助信息
- -i, --ifname="" (TC Classifier) 要附加探针的网络接口名称
- -k, --keylogfile="ecapture_openssl_key.og" 存储SSL/TLS密钥的文件,eCapture捕获加密通信中的密钥并将其保存到该文件
- --libssl="" 指定libssl.so文件路径,默认从curl中自动查找
- -m, --model="text" 捕获模型,可以是:text(明文内容),pcap/pcapng(原始数据包格式),key/keylog(SSL/TLS密钥)
- -w, --pcapfile="save.pcapng" 将原始数据包以pcapng格式写入文件
- --ssl_version="" 指定OpenSSL/BoringSSL版本,例如:--ssl_version="openssl 1.1.1g" 或 --ssl_version="boringssl 1.1.1"
- GLOBAL OPTIONS:
- -b, --btf=0 启用BTF模式(0:自动选择;1:核心模式;2:非核心模式)
- -d, --debug[=false] 启用调试日志
- --eventaddr="" 设置接收捕获事件的服务器地址。默认值与logaddr相同(例如:tcp://127.0.0.1:8090)
- --hex[=false] 以十六进制字符串打印字节数据
- --listen="localhost:28256" 设置HTTP服务器的监听地址,默认值:127.0.0.1:28256
- -l, --logaddr="" 设置日志服务器的地址。例如:-l /tmp/ecapture.log 或 -l tcp://127.0.0.1:8080
- --mapsize=1024 设置每个CPU的eBPF映射大小(事件缓冲区)。默认值:1024 * PAGESIZE(单位:KB)
- -p, --pid=0 设置目标进程ID。如果为0,则目标为所有进程
- -u, --uid=0 设置目标用户ID。如果为0,则目标为所有用户
复制代码
- adb shell ps | findstr 应用包名(获取进程pid)
- ./ecapture tls -p pid -m text
复制代码 |
|