Java层socket抓包与源码分析(下)
创建demo作为分析案例
本章将创建一个简单的udp通信demo,来进行分析并编写Frida Hook脚本
服务端:Python接收器
客户端:App Demo
代码分析
跟上篇一样对接收和发送数据的流程进行hook来获取hook点
调试分析
在receive()
和send()
处下断点进行调试
- 以调试模式运行之后会首先断在
receive()
处
这里继续F7步入
记录下来最后的调用,等下在源码中查找
java.net.DatagramSocket->receive java.net.PlainDatagramSocketImpl->doRecv
继续往下执行断在send()
F7步入
记录下来最后的调用
java.net.DatagramSocket->send java.net.PlainDatagramSocketImpl->send
源码分析
在源码中查找我们刚才记录到的最后的调用
receive
java.net.DatagramSocket->receive java.net.PlainDatagramSocketImpl->doRecv
143 private void doRecv(DatagramPacket p, int flags) throws IOException { // 找到了doRecv
144 if (isClosed()) {
145 throw new SocketException("Socket closed");
146 }
147
148 if (timeout != 0) {
149 IoBridge.poll(fd, POLLIN | POLLERR, timeout);
150 }
151
152 IoBridge.recvfrom(false, fd, p.getData(), p.getOffset(), p.bufLength, flags, p,
153 connected); // 进入IoBridge类找到recvfrom()
154 }
--->>>
606 public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
607 int result;
608 try {
609 InetSocketAddress srcAddress = packet != null ? new InetSocketAddress() : null;
610 result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); // 进入libcore类找到os对象,再找到recvfrom()
611 result = postRecvfrom(isRead, packet, srcAddress, result);
612 } catch (ErrnoException errnoException) {
613 result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
614 }
615 return result;
616 }
--->>>
跟进libcore类
- 到了这里画个图比较清晰,最后传进来的参数是
linux()
对象
跟进去Linux对象找recvfrom()
进来之后找到了
recvfrom()
它最后调用了JNI函数recvfromBytes()
private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
还有一个意外之喜,这里也有
sendtoBytes()
方法,大概率就是我们接下来要找的send()
的java层最终调用
send
java.net.DatagramSocket->send java.net.PlainDatagramSocketImpl->send
跟之前一样
114 protected void send(DatagramPacket p) throws IOException {
115 if (isClosed()) {
116 throw new SocketException("Socket closed");
117 }
118 if (p.getData() == null || p.getAddress() == null) {
119 throw new NullPointerException("null buffer || null address");
120 }
121
122 int port = connected ? 0 : p.getPort();
123 InetAddress address = connected ? null : p.getAddress();
124 IoBridge.sendto(fd, p.getData(), p.getOffset(), p.getLength(), 0, address, port); // 进入IoBridge类找到sendto()
125 }
--->>>
562 public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
563 boolean isDatagram = (inetAddress != null);
564 if (!isDatagram && byteCount <= 0) {
565 return 0;
566 }
567 int result;
568 try {
569 result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); // 这里跟刚才一样进入libcore类找到os对象,再找到recvfrom()
570 } catch (ErrnoException errnoException) {
571 result = maybeThrowAfterSendto(isDatagram, errnoException);
572 }
573 return result;
574 }
进来之后我们把调用的图画出来
最后调用的Java层JNI函数是sendtoBytes()
:这个函数有两个重载,我们调用的是七个参数的重载
private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
写hook代码
直接hook最后的这两个JNI函数,并且把其中传递的buf以及IP等信息打印出来
function hookudp() {
Java.perform(function () {
var LinuxClass = Java.use('libcore.io.Linux')
// 数据接收
// private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
LinuxClass.recvfromBytes.implementation = function (arg0, arg1, arg2, arg3, arg4,arg5) {
var size = this.recvfromBytes(arg0, arg1, arg2, arg3, arg4, arg5)
var byteArray = Java.array('byte', arg1)
var content = ""
for(var i = 0; i < size; i++){
content = content + String.fromCharCode(byteArray[i])
}
console.log("address" + arg5 + "[" + Process.getCurrentThreadId() + "]socketRead0 > size: " + size + "--content: " + content)
printJavaStack('recvfromBytes...')
return size;
}
// 发送数据这里使用的是七个参数的重载
// private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
LinuxClass.sendtoBytes.overload('java.io.FileDescriptor', 'java.lang.Object', 'int', 'int', 'int', 'java.net.InetAddress', 'int').implementation = function (arg0, arg1, arg2, arg3, arg4, arg5, arg6) {
var size = this.sendtoBytes(arg0, arg1, arg2, arg3, arg4, arg5, arg6)
var byteArray = Java.array('byte', arg1)
var content = "";
for(var i=0; i<size; i++){
content = content + String.fromCharCode(byteArray[i])
}
console.log("address" + arg5 + ":" + arg6 + "[" + Process.getCurrentThreadId() + "]sendtoBytes > len: " + size + "--content: " + content)
printJavaStack('sendtoBytes()...')
return size;
}
})
}
打印的结果
把打印结果写入文件方便查看
frida -U -f com.example.okhttp -l hookUDP.js --no-pause -o udp.log
附件:
- 标题: Java层socket抓包与源码分析(下)
- 作者: xiaoeryu
- 创建于 : 2024-06-02 22:29:55
- 更新于 : 2024-06-06 06:05:14
- 链接: https://github.com/xiaoeryu/2024/06/02/Java层socket抓包与源码分析(下)/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。