其他
Android抓包学习的整理和归纳
一 • 抓包方式
二 • 系统层hook抓包
1、java层的抓包
import queue
from simpletcp.tcpserver import TCPServer
def echo(ip, queue, data):
queue.put(bytes("server recv ","UTF-8")+data)
print("echo "+data.decode("utf-8"))
server = TCPServer("192.168.3.8", 5000, echo)
server.run()
public static void GetByHttpURL(final String url) {
new Thread(new Runnable() {
@Override
public void run() {
try {
String resultData="";
URL connUrl=new URL (url);
HttpURLConnection urlConn= (HttpURLConnection)connUrl.openConnection ();
InputStreamReader in=new InputStreamReader (urlConn.getInputStream());
BufferedReader buffer=new BufferedReader (in);
String inputLine=null;
while (((inputLine=buffer.readLine()) !=null)) {
resultData+=inputLine+"\n";
}
in.close();
urlConn.disconnect();
Log.d(TAG, "GetByHttpURL "+resultData);
} catch (Exception e) {
Log.d(TAG, "GetByHttpURL error "+e.getMessage());
e.printStackTrace();
}
}
}).start();
}
public static void GetByOkHttp(String url) {
try {
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以不写
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "GetByOkHttp onResponse: " + response.body().string());
}
});
} catch (Exception e) {
Log.d(TAG, "GetByOkHttp error "+e.getMessage());
e.printStackTrace();
}
}
public static void DoTcp() throws IOException, InterruptedException {
Log.d(TAG, "DoTcp bind tcp");
Socket socket = new Socket("192.168.3.8", 5000);
socket.setSoTimeout(10000);
//发送数据给服务端
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,server".getBytes("UTF-8"));
Thread.sleep(2000);
socket.shutdownOutput();
//读取数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
//打印读取到的数据
Log.d(TAG, "tcp server recv:" + line);
br.close();
socket.close();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Log.d(TAG, "start send http");
GetByOkHttp("http://missking.cc/");
GetByHttpURL("http://10.ip138.com");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
while(true){
DoTcp();
Thread.sleep(3000);
}
} catch (InterruptedException e) {
Log.d(TAG, "DoTcp error: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
Log.d(TAG, "DoTcp error: " + e.getMessage());
e.printStackTrace();
}
}
}).start();
}
//public abstract class OutputStream //这个OutputStream是一个抽象类,我们需要找到这个对象的真实类型
OutputStream outputStream = socket.getOutputStream();
//找到对应的真实类型后,再找到对应的write函数
outputStream.write("hello,server".getBytes("UTF-8"));
//下面打印下input和output的真实类型
Log.d(TAG,"output class:"+outputStream.getClass());
Log.d(TAG,"input class:"+socket.getInputStream().getClass());
output class:class java.net.SocketOutputStream
input class:class java.net.SocketInputStream
//SocketOutputStream.java
//第一步调用到这里
public void write(byte b[]) throws IOException {
socketWrite(b, 0, b.length);
}
//第二步调用到这个
private void socketWrite(byte b[], int off, int len) throws IOException {
if (len <= 0 || off < 0 || len > b.length - off) {
if (len == 0) {
return;
}
throw new ArrayIndexOutOfBoundsException("len == " + len
+ " off == " + off + " buffer length == " + b.length);
}
FileDescriptor fd = impl.acquireFD();
try {
BlockGuard.getThreadPolicy().onNetwork();
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
if (se instanceof sun.net.ConnectionResetException) {
impl.setConnectionResetPending();
se = new SocketException("Connection reset");
}
if (impl.isClosedOrPending()) {
throw new SocketException("Socket closed");
} else {
throw se;
}
} finally {
impl.releaseFD();
}
}
//第三部调用到这里。java部分的就走完了
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
int len) throws IOException;
//先贴是怎么调用的
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
//所以直接看BufferedReader.java的readLine()
public String readLine() throws IOException {
return readLine(false);
}
//然后调用了另外一个重载,这个函数比较大。省略掉非关键位置
String readLine(boolean ignoreLF) throws IOException {
...
fill();
...
}
//然后看看fill的处理
private void fill() throws IOException {
...
in.read(cb, dst, cb.length - dst);
...
}
//上面的in是InputStreamReader这个类型所以InputStreamReader.java中看看read
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
//这个sd是StreamDecoder这个类。继续到里面看看read函数
public int read(char cbuf[], int offset, int length) throws IOException {
...
return n + implRead(cbuf, off, off + len);
...
}
//继续看implRead
int implRead(char[] cbuf, int off, int end) throws IOException {
...
int n = readBytes();
...
}
//继续看readBytes。这里的in就是我们的SocketInputStream。绕了一圈终于到这里了。继续往后看
private int readBytes() throws IOException {
...
int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);
...
}
//下面是SocketInputStream.java中的read函数
public int read(byte b[], int off, int length) throws IOException {
return read(b, off, length, impl.getTimeout());
}
//继续看调用的另外一个重载
int read(byte b[], int off, int length, int timeout) throws IOException {
...
n = socketRead(fd, b, off, length, timeout);
...
}
//继续看socketRead。
private int socketRead(FileDescriptor fd,
byte b[], int off, int len,
int timeout)
throws IOException {
return socketRead0(fd, b, off, len, timeout);
}
//终于到达了最后的native函数。java部分走完了
private native int socketRead0(FileDescriptor fd,
byte b[], int off, int len,
int timeout)throws IOException;
//将数组转换成c++的byte[]。并且hexdump打印结果
function print_bytes(bytes) {
var buf = Memory.alloc(bytes.length);
Memory.writeByteArray(buf, byte_to_ArrayBuffer(bytes));
console.log(hexdump(buf, {offset: 0, length: bytes.length, header: false, ansi: true}));
}
//将java的数组转换成js的数组
function byte_to_ArrayBuffer(bytes) {
var size=bytes.length;
var tmparray = [];
for (var i = 0; i < size; i++) {
var val = bytes[i];
if(val < 0){
val += 256;
}
tmparray[i] = val
}
return tmparray;
}
// java.net.Socket$init(ip,port) 获取ip和端口
// socketWrite0(FileDescriptor fd, byte[] b, int off,int len) 获取发送的数据
// socketRead0(FileDescriptor fd,byte b[], int off, int len,int timeout) 获取接受的数据
function hook_tcp(){
var socketClass= Java.use("java.net.Socket");
socketClass.$init.overload('java.net.InetAddress', 'int').implementation=function(ip,port){
console.log("socket$init ",ip,":",port);
return this.$init(ip,port);
};
var outputClass=Java.use("java.net.SocketOutputStream");
outputClass.socketWrite0.implementation=function(fd,buff,off,len){
console.log("tcp write fd:",fd);
print_bytes(buff);
return this.socketWrite0(fd,buff,off,len);
};
var inputClass=Java.use("java.net.SocketInputStream");
inputClass.socketRead0.implementation=function(fd,buff,off,len,timeout){
var res=this.socketRead0(fd,buff,off,len,timeout);
console.log("tcp read fd:",fd)
print_bytes(buff);
return res;
};
}
function hook_java(){
Java.perform(function(){
hook_tcp();
})
}
function main(){
hook_java();
}
setImmediate(main);
2、jni层抓http包
function getSocketData(fd){
console.log("fd:",fd);
var socketType=Socket.type(fd)
if(socketType!=null){
var res="type:"+socketType+",loadAddress:"+JSON.stringify(Socket.localAddress(fd))+",peerAddress"+JSON.stringify(Socket.peerAddress(fd));
return res;
}else{
return "type:"+socketType;
}
}
function hook_jni_tcp(){
var sendtoPtr=Module.getExportByName("libc.so","sendto");
var recvfromPtr=Module.getExportByName("libc.so","recvfrom");
console.log("sendto:",sendtoPtr,",recvfrom:",recvfromPtr);
//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){
var fd=args[0];
var buff=args[1];
var size=args[2];
var sockdata=getSocketData(fd.toInt32());
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
},
onLeave:function(retval){
}
});
//recvfrom(int fd, void *buf, size_t n, int flags, struct sockaddr *addr, socklen_t *addr_len)
Interceptor.attach(recvfromPtr,{
onEnter:function(args){
this.fd=args[0];
this.buff=args[1];
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()}));
}
});
}
3、java层ssl抓包
GetByOkHttp("https://www.baidu.com/");
GetByHttpURL("https://www.jd.com/");
//最关键的是这个newCall。断点跟踪进去
public static void GetByOkHttp(String url) {
...
Call call = okHttpClient.newCall(request);
...
}
//下面调用了一个静态的初始化RealCall
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
//这里初始化RealCall
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
//到这里初始化好了。这里注意,retryAndFollowUpInterceptor这个实际上是一个拦截器,那么接下来我们应该把断点放在拦截器的intercept函数
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
//拦截器的触发函数。proceed里面的流程较长。里面经过了多次的跳转。
@Override public Response intercept(Chain chain) throws IOException {
...
response = realChain.proceed(request, streamAllocation, null, null);
...
}
//多次跳转后。最后走进了RealConnection.java的connect函数。
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
EventListener eventListener) {
...
//这里判断如果非ssl的处理
if (route.address().sslSocketFactory() == null) {
if (!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication not enabled for client"));
}
String host = route.address().url().host();
if (!Platform.get().isCleartextTrafficPermitted(host)) {
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication to " + host + " not permitted by network security policy"));
}
}
...
//这里面调用的socket.connect()
connectSocket(connectTimeout, readTimeout, call, eventListener);
...
//这里面发送的请求
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
...
}
//先看看socket的连接部分
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
...
//根据不同的type创建不同的socket
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
...
//socket的连接
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
...
}
//然后这里只是单纯的调用下连接。
public void connectSocket(Socket socket, InetSocketAddress address,
int connectTimeout) throws IOException {
socket.connect(address, connectTimeout);
}
//下面继续看发送请求的函数establishProtocol
private void establishProtocol(ConnectionSpecSelector connectionSpecSelector,
int pingIntervalMillis, Call call, EventListener eventListener) throws IOException {
//非ssl的请求直接在这里就返回了
if (route.address().sslSocketFactory() == null) {
protocol = Protocol.HTTP_1_1;
socket = rawSocket;
return;
}
//后面是ssl相关的处理
eventListener.secureConnectStart(call);
//最关键的是这里会创建sslsocket。并且和服务端进行握手交互
connectTls(connectionSpecSelector);
eventListener.secureConnectEnd(call, handshake);
if (protocol == Protocol.HTTP_2) {
socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
http2Connection = new Http2Connection.Builder(true)
.socket(socket, route.address().url().host(), source, sink)
.listener(this)
.pingIntervalMillis(pingIntervalMillis)
.build();
http2Connection.start();
}
}
//最关键的部分。所以我就贴上完整的代码了。里面很多地方有英文注释。我就不自己画蛇添足了。
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
Address address = route.address();
SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
boolean success = false;
SSLSocket sslSocket = null;
try {
// Create the wrapper over the connected socket.
sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
// Configure the socket's ciphers, TLS versions, and extensions.
ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
if (connectionSpec.supportsTlsExtensions()) {
Platform.get().configureTlsExtensions(
sslSocket, address.url().host(), address.protocols());
}
// Force handshake. This can throw!
sslSocket.startHandshake();
// block for session establishment
SSLSession sslSocketSession = sslSocket.getSession();
if (!isValid(sslSocketSession)) {
throw new IOException("a valid ssl session was not established");
}
Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
// Verify that the socket's certificates are acceptable for the target host.
if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
X509Certificate cert = (X509Certificate) unverifiedHandshake.peerCertificates().get(0);
throw new SSLPeerUnverifiedException("Hostname " + address.url().host() + " not verified:"
+ "\n certificate: " + CertificatePinner.pin(cert)
+ "\n DN: " + cert.getSubjectDN().getName()
+ "\n subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
}
//这里是证书的校验
// Check that the certificate pinner is satisfied by the certificates presented.
address.certificatePinner().check(address.url().host(),
unverifiedHandshake.peerCertificates());
// Success! Save the handshake and the ALPN protocol.
String maybeProtocol = connectionSpec.supportsTlsExtensions()
? Platform.get().getSelectedProtocol(sslSocket)
: null;
socket = sslSocket;
source = Okio.buffer(Okio.source(socket));
sink = Okio.buffer(Okio.sink(socket));
handshake = unverifiedHandshake;
protocol = maybeProtocol != null
? Protocol.get(maybeProtocol)
: Protocol.HTTP_1_1;
success = true;
} catch (AssertionError e) {
if (Util.isAndroidGetsocknameError(e)) throw new IOException(e);
throw e;
} finally {
if (sslSocket != null) {
Platform.get().afterHandshake(sslSocket);
}
if (!success) {
closeQuietly(sslSocket);
}
}
}
//上面流程走完。ssl的连接就建立起来了。继续跟踪后。走到下一个关键拦截器CallServerInterceptor.java。我们直接看拦截器函数实现部分
@Override public Response intercept(Chain chain) throws IOException {
...
//前面的代码应该是准备header的数据。我就都省略了。下面这个函数是发送请求的关键函数
httpCodec.finishRequest();
...
}
//继续往后跟踪flush
@Override public void finishRequest() throws IOException {
sink.flush();
}
//继续跟踪sink.write
@Override public void flush() throws IOException {
if (closed) throw new IllegalStateException("closed");
if (buffer.size > 0) {
sink.write(buffer, buffer.size);
}
sink.flush();
}
//继续往后看write
@Override public void write(Buffer source, long byteCount) throws IOException {
checkOffsetAndCount(source.size, 0, byteCount);
...
sink.write(source, toWrite);
...
}
//这个write就到一个关键的位置了。这个out对象就是我们关心的类了。我们查一下out的类型
@Override public void write(Buffer source, long byteCount) throws IOException {
...
//out对象的类型是com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream
out.write(head.data, head.pos, toCopy);
...
}
//接下来我们就可以去找SSLOutputStream的write了。我们在调试中跟踪过去失败了。那么直接去翻源码把。下面是对应的write
public void write(byte[] buf, int offset, int byteCount) throws IOException {
...
ssl.write(Platform.getFileDescriptor(socket), buf, offset, byteCount,writeTimeoutMilliseconds);
...
}
//继续看ssl.write。这个ssl的类型是SslWrapper。找到对应的write如下。
void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
throws IOException {
NativeCrypto.SSL_write(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
}
//继续找NativeCrypto类的SSL_write函数。到这里就结束了。终于到达java调用链的最后一层了。
static native void SSL_write(long sslNativePointer, FileDescriptor fd,
SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis)
throws IOException;
static native int SSL_read(long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc,
byte[] b, int off, int len, int readTimeoutMillis) throws IOException;
function getFullName(name){
Java.perform(function(){
Java.enumerateLoadedClassesSync().forEach(function(className){
if(className.indexOf(name)!=-1){
console.log(className);
}
})
});
}
getFullName("NativeCrypto")
//NativeCrypto SSL_write(long sslNativePointer, FileDescriptor fd,
// SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis)
//NativeCrypto SSL_read(long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc,
// byte[] b, int off, int len, int readTimeoutMillis)
function hook_ssl(){
var NativeCryptoClass= Java.use("com.android.org.conscrypt.NativeCrypto");
NativeCryptoClass.SSL_write.implementation=function(sslPtr,fd,shc,b,off,len,timeout){
console.log("enter SSL_write");
print_bytes(b);
return this.SSL_write(sslPtr,fd,shc,b,off,len,timeout);
};
NativeCryptoClass.SSL_read.implementation=function(sslPtr,fd,shc,b,off,len,timeout){
console.log("enter SSL_read");
var res=this.SSL_read(sslPtr,fd,shc,b,off,len,timeout);
print_bytes(b);
return res;
};
}
function getSocketData2(stream){
var data0=stream.this$0.value;
var sockdata=data0.socket.value;
return sockdata;
}
//SSLOutputStream write(byte[] buf, int offset, int byteCount)
//SSLInputStream read(byte[] buf, int offset, int byteCount)
function hook_ssl2(){
var SSLOutputClass=Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream");
SSLOutputClass.write.overload('[B', 'int', 'int').implementation=function(buf,off,cnt){
console.log(getSocketData2(this));
print_bytes(buf);
return this.write(buf,off,cnt);
};
var SSLInputClass=Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream");
SSLInputClass.read.overload('[B', 'int', 'int').implementation=function(buf,off,cnt){
var res=this.read(buf,off,cnt);
console.log(getSocketData2(this));
print_bytes(buf);
return res;
}
}
4、jni层抓https包
static void NativeCrypto_SSL_write(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject,
jobject shc, jbyteArray b, jint offset, jint len,
jint write_timeout_millis) {
...
ret = sslWrite(env, ssl, fdObject, shc, reinterpret_cast<const char*>(&buf[0]), len,
sslError, write_timeout_millis);
...
}
static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const char* buf, jint len,
OpenSslError& sslError, int write_timeout_millis) {
...
int result = SSL_write(ssl, buf, len);
...
}
int SSL_write(SSL *ssl, const void *buf, int num) {
...
ret = ssl->method->write_app_data(ssl, &needs_handshake,
(const uint8_t *)buf, num);
...
}
int ssl3_write_app_data(SSL *ssl, int *out_needs_handshake, const uint8_t *buf,
int len) {
...
int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw);
...
}
static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) {
...
/* Add any unflushed handshake data as a prefix. This may be a KeyUpdate
* acknowledgment or 0-RTT key change messages. |pending_flight| must be clear
* when data is added to |write_buffer| or it will be written in the wrong
* order. */
if (ssl->s3->pending_flight != NULL) {
OPENSSL_memcpy(
out, ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
flight_len);
BUF_MEM_free(ssl->s3->pending_flight);
ssl->s3->pending_flight = NULL;
ssl->s3->pending_flight_offset = 0;
}
if (!tls_seal_record(ssl, out + flight_len, &ciphertext_len,
max_out - flight_len, type, buf, len)) {
return -1;
}
ssl_write_buffer_set_len(ssl, flight_len + ciphertext_len);
...
/* we now just need to write the buffer */
return ssl3_write_pending(ssl, type, buf, len);
}
static int sock_read(BIO *b, char *out, int outl) {
int ret = 0;
if (out == NULL) {
return 0;
}
bio_clear_socket_error();
#if defined(OPENSSL_WINDOWS)
ret = recv(b->num, out, outl, 0);
#else
ret = read(b->num, out, outl);
#endif
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_fd_should_retry(ret)) {
BIO_set_retry_read(b);
}
}
return ret;
}
static int sock_write(BIO *b, const char *in, int inl) {
int ret;
bio_clear_socket_error();
#if defined(OPENSSL_WINDOWS)
ret = send(b->num, in, inl, 0);
#else
ret = write(b->num, in, inl);
#endif
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_fd_should_retry(ret)) {
BIO_set_retry_write(b);
}
}
return ret;
}
//将数组转换成c++的byte[]。并且hexdump打印结果
function print_bytes(bytes) {
var buf = Memory.alloc(bytes.length);
Memory.writeByteArray(buf, byte_to_ArrayBuffer(bytes));
console.log(hexdump(buf, {offset: 0, length: bytes.length, header: false, ansi: true}));
}
//将java的数组转换成js的数组
function byte_to_ArrayBuffer(bytes) {
var size=bytes.length;
var tmparray = [];
for (var i = 0; i < size; i++) {
var val = bytes[i];
if(val < 0){
val += 256;
}
tmparray[i] = val
}
return tmparray;
}
// java.net.Socket$init(ip,port) 获取ip和端口
// socketWrite0(FileDescriptor fd, byte[] b, int off,int len) 获取发送的数据
// socketRead0(FileDescriptor fd,byte b[], int off, int len,int timeout) 获取接受的数据
function hook_tcp(){
var socketClass= Java.use("java.net.Socket");
socketClass.$init.overload('java.net.InetAddress', 'int').implementation=function(ip,port){
console.log("socket$init ",ip,":",port);
return this.$init(ip,port);
};
var outputClass=Java.use("java.net.SocketOutputStream");
outputClass.socketWrite0.implementation=function(fd,buff,off,len){
console.log("tcp write fd:",fd);
print_bytes(buff);
return this.socketWrite0(fd,buff,off,len);
};
var inputClass=Java.use("java.net.SocketInputStream");
inputClass.socketRead0.implementation=function(fd,buff,off,len,timeout){
var res=this.socketRead0(fd,buff,off,len,timeout);
console.log("tcp read fd:",fd)
print_bytes(buff);
return res;
};
}
//SSL_write(long sslNativePointer, FileDescriptor fd,
// SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis)
//SSL_read(long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc,
// byte[] b, int off, int len, int readTimeoutMillis)
function getSocketData(fd){
console.log("fd:",fd);
var socketType=Socket.type(fd)
if(socketType!=null){
var res="type:"+socketType+",loadAddress:"+JSON.stringify(Socket.localAddress(fd))+",peerAddress"+JSON.stringify(Socket.peerAddress(fd));
return res;
}else{
return "type:"+socketType;
}
}
function getSocketData2(stream){
var data0=stream.this$0.value;
var sockdata=data0.socket.value;
return sockdata;
}
function hook_ssl(){
var NativeCryptoClass= Java.use("com.android.org.conscrypt.NativeCrypto");
NativeCryptoClass.SSL_write.implementation=function(sslPtr,fd,shc,b,off,len,timeout){
console.log("enter SSL_write");
print_bytes(b);
return this.SSL_write(sslPtr,fd,shc,b,off,len,timeout);
};
NativeCryptoClass.SSL_read.implementation=function(sslPtr,fd,shc,b,off,len,timeout){
console.log("enter SSL_read");
var res=this.SSL_read(sslPtr,fd,shc,b,off,len,timeout);
print_bytes(b);
return res;
};
}
//SSLOutputStream write(byte[] buf, int offset, int byteCount)
//SSLInputStream read(byte[] buf, int offset, int byteCount)
function hook_ssl2(){
var SSLOutputClass=Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream");
SSLOutputClass.write.overload('[B', 'int', 'int').implementation=function(buf,off,cnt){
console.log(getSocketData2(this));
print_bytes(buf);
return this.write(buf,off,cnt);
};
var SSLInputClass=Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream");
SSLInputClass.read.overload('[B', 'int', 'int').implementation=function(buf,off,cnt){
var res=this.read(buf,off,cnt);
console.log(getSocketData2(this));
print_bytes(buf);
return res;
}
}
//jni的ssl的加密数据hook
function hook_jni_ssl_enc(){
var writePtr=Module.getExportByName("libc.so","write");
var readPtr=Module.getExportByName("libc.so","read");
console.log("write:",writePtr,",read:",readPtr);
Interceptor.attach(writePtr,{
onEnter:function(args){
var fd=args[0];
var buff=args[1];
var size=args[2];
var sockdata=getSocketData(fd.toInt32());
if(sockdata.indexOf("tcp")){
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
}
},
onLeave:function(retval){
}
});
Interceptor.attach(readPtr,{
onEnter:function(args){
this.fd=args[0];
this.buff=args[1];
this.size=args[2];
},
onLeave:function(retval){
var sockdata=getSocketData(this.fd.toInt32());
if(sockdata.indexOf("tcp")){
console.log(sockdata);
console.log(hexdump(this.buff,{length:this.size.toInt32()}));
}
}
});
}
//jni的ssl明文数据hook
function hook_jni_ssl(){
var sslWritePtr=Module.getExportByName("libssl.so","SSL_write");
var sslReadPtr=Module.getExportByName("libssl.so","SSL_read");
console.log("sslWrite:",sslWritePtr,",sslRead:",sslReadPtr);
var sslGetFdPtr=Module.getExportByName("libssl.so","SSL_get_rfd");
var sslGetFdFunc=new NativeFunction(sslGetFdPtr,'int',['pointer']);
//int SSL_write(SSL *ssl, const void *buf, int num)
Interceptor.attach(sslWritePtr,{
onEnter:function(args){
var sslPtr=args[0];
var buff=args[1];
var size=args[2];
var fd=sslGetFdFunc(sslPtr);
var sockdata=getSocketData(fd);
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
},
onLeave:function(retval){
}
});
//int SSL_read(SSL *ssl, void *buf, int num)
Interceptor.attach(sslReadPtr,{
onEnter:function(args){
this.sslPtr=args[0];
this.buff=args[1];
this.size=args[2];
},
onLeave:function(retval){
var fd=sslGetFdFunc(this.sslPtr);
var sockdata=getSocketData(fd);
console.log(sockdata);
console.log(hexdump(this.buff,{length:this.size.toInt32()}));
}
});
}
function hook_jni_tcp(){
var sendtoPtr=Module.getExportByName("libc.so","sendto");
var recvfromPtr=Module.getExportByName("libc.so","recvfrom");
console.log("sendto:",sendtoPtr,",recvfrom:",recvfromPtr);
//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){
var fd=args[0];
var buff=args[1];
var size=args[2];
var sockdata=getSocketData(fd.toInt32());
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
},
onLeave:function(retval){
}
});
//recvfrom(int fd, void *buf, size_t n, int flags, struct sockaddr *addr, socklen_t *addr_len)
Interceptor.attach(recvfromPtr,{
onEnter:function(args){
this.fd=args[0];
this.buff=args[1];
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()}));
}
});
}
function hook_java(){
Java.perform(function(){
// hook_tcp();
// hook_ssl();
// hook_ssl2();
// hook_jni_tcp();
hook_jni_ssl_enc();
hook_jni_ssl();
})
}
function getFullName(name){
Java.perform(function(){
Java.enumerateLoadedClassesSync().forEach(function(className){
if(className.indexOf(name)!=-1){
console.log(className);
}
})
});
}
function main(){
hook_java();
// getFullName("SSLOutputStream")
}
setImmediate(main);
三 • 中间人抓包
1、服务端验证客户端证书
val resourceStream = resources.openRawResource(R.raw.infinisign_cert)
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(resourceStream, null)
val trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm)
trustManagerFactory.init(keyStore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustManagerFactory.trustManagers, null)
val url = URL("http://infinisign.com/")
val urlConnection = url.openConnection() as HttpsURLConnection
urlConnection.sslSocketFactory = sslContext.socketFactory
public final void load(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
keyStoreSpi.engineLoad(stream, password);
initialized = true;
}
CertificateFactory factory = CertificateFactory.getInstance("X.509");//设置证书类型,X.509是一种格式标准
InputStream stream;
Certificate certificate;//Certificate是证书信息封装的一个bean类
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);//清除默认证书,使用我们自己制定的证书
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getAssets().open("burp.der");
Certificate cert = cf.generateCertificate(caInput);
//设置自己的证书
keyStore.setCertificateEntry("misskings",cert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);//通过keyStore得到信任管理器
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "pwd".toCharArray());//通过keyStore得到密匙管理器
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();//拿到SSLSocketFactory
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
return ;
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
okHttpClient.sslSocketFactory(sslSocketFactory, trustManager)//设置ssl证书
okHttpClient.build();
function hook_keystore(){
var keyManager=Java.use("javax.net.ssl.KeyManagerFactory");
var trustManager=Java.use("javax.net.ssl.TrustManagerFactory");
keyManager.init.overload('java.security.KeyStore', '[C').implementation=function(ks,pwd){
console.log("enter keyManager init");
try{
var savePath="/sdcard/keyManagerFactory_init.p12";
var outStream=Java.use("java.io.FileOutputStream").$new(savePath);
ks.store(outStream,Java.use("java.lang.String").$new(pwd).toCharArray());
console.log("keyManager store success");
}catch(ex){
console.log(ex);
}
return this.init(ks,pwd);
}
trustManager.init.overload('java.security.KeyStore').implementation=function(ks){
console.log("enter trustManager init");
try{
var savePath="/sdcard/TrustManagerFactory_init.p12";
var outStream=Java.use("java.io.FileOutputStream").$new(savePath);
ks.store(outStream);
console.log("trustManager store success");
}catch(ex){
console.log(ex);
}
return this.init(ks);
}
}
trustManager store success
keyManager store success
2、客户端验证服务端证书
xposed解决方案:JustTrustMe
https://github.com/Fuzion24/JustTrustMe
frida解决方案:DroidSSLUnpinning
https://github.com/WooyunDota/DroidSSLUnpinning
看雪ID:misskings
https://bbs.pediy.com/user-home-659397.htm
*本文由看雪论坛 misskings 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!