- 目的:自制沙箱进行无感知抓包
- 环境:以下编译前环境准备、刷机都同于AOSP源码编译篇
- 系统产出:无root模式
-
进入源码根目录,运行如下命令
source build/envsetup.sh
mmm development/tools/idegen/
development/tools/idegen/idegen.sh
-
导入Android Studio
打开 Android Studio,选择 Open an existing Android Studio project,找到源码目录,点击 Android.ipr,Open,大约等 6 分钟,导入完毕。
首先要找到对应目标文件在源码哪个目录下,在源码中搜索文件。
adb shell进入手机系统中,系统的证书在/etc/security/cacerts目录下,在aosp源码中搜索cacerts。
可以看到aosp中 /system/ca-certificates/files中的证书和系统中/etc/security/cacerts中的证书一样,只需把charles证书移植到/system/ca-certificates/files即可。
官方安卓系统加载证书的代码在java.security.KeyStore类中,其方法为KeyStore.load,第一个参数为证书文件的io流,第二个参数为证书的password。在源码中搜索java.security.KeyStore
修改内容如下:
package java.security;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.*;
import javax.crypto.SecretKey;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.callback.*;
*/
public final void load(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (password != null) {
String inputPASSWORD = new String(password);
Class logClass = null;
try {
logClass = this.getClass().getClassLoader().loadClass("android.util.Log");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method loge = null;
try {
loge = logClass.getMethod("e", String.class, String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
loge.invoke(null, "r0ysueKeyStoreLoad", "KeyStore load PASSWORD is => " + inputPASSWORD);
Exception e = new Exception("r0ysueKeyStoreLoad");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Date now = new Date();
String currentTime = String.valueOf(now.getTime());
FileOutputStream fos = new FileOutputStream("/sdcard/Download/" + inputPASSWORD + currentTime);
byte[] b = new byte[1024];
int length;
while ((length = stream.read(b)) > 0) {
fos.write(b, 0, length);
}
fos.flush();
fos.close();
}
keyStoreSpi.engineLoad(stream, password);
initialized = true;
}
在源码中搜索java.net.SocketInputStream\java.net.SocketOutputStream
修改内容如下:
// java.net.SocketInputStream
package java.net;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import dalvik.system.BlockGuard;
import sun.net.ConnectionResetException;
private int socketRead(FileDescriptor fd,
byte b[], int off, int len,
int timeout)
throws IOException {
int result = socketRead0(fd, b, off, len, timeout);
if(result>0){
byte[] input = new byte[result];
System.arraycopy(b,off,input,0,result);
String inputString = new String(input);
Class logClass = null;
try {
logClass = this.getClass().getClassLoader().loadClass("android.util.Log");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method loge = null;
try {
loge = logClass.getMethod("e",String.class,String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
loge.invoke(null,"r0ysueSOCKETresponse","Socket is => "+this.socket.toString());
loge.invoke(null,"r0ysueSOCKETresponse","buffer is => "+inputString);
Exception e = new Exception("r0ysueSOCKETresponse");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return result;
}
// java.net.SocketOutputStream
package java.net;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import dalvik.system.BlockGuard;
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);
if(len>0){
byte[] input = new byte[len];
System.arraycopy(b,off,input,0,len);
String inputString = new String(input);
Class logClass = null;
try {
logClass = this.getClass().getClassLoader().loadClass("android.util.Log");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method loge = null;
try {
loge = logClass.getMethod("e",String.class,String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
loge.invoke(null,"r0ysueSOCKETrequest","Socket is => "+this.socket.toString());
loge.invoke(null,"r0ysueSOCKETrequest","buffer is => "+inputString);
Exception e = new Exception("r0ysueSOCKETrequest");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} 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();
}
}
在源码中搜索SslWrapper.java
修改内容如下:
// TODO(nathanmittler): Remove once after we switch to the engine socket.
int read(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
throws IOException {
int result = NativeCrypto.SSL_read(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis) ;
if(result>0){
byte[] input = new byte[result];
System.arraycopy(buf,offset,input,0,result);
String inputString = new String(input);
Class logClass = null;
try {
logClass = this.getClass().getClassLoader().loadClass("android.util.Log");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method loge = null;
try {
loge = logClass.getMethod("e",String.class,String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
loge.invoke(null,"r0ysueSOCKETresponse","SSL is =>"+this.handshakeCallbacks.toString());
loge.invoke(null,"r0ysueSOCKETresponse","buffer is => "+inputString);
Exception e = new Exception("r0ysueSOCKETresponse");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return result;
}
// TODO(nathanmittler): Remove once after we switch to the engine socket.
void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
throws IOException {
if(len>0){
byte[] input = new byte[len];
System.arraycopy(buf,offset,input,0,len);
String inputString = new String(input);
Class logClass = null;
try {
logClass = this.getClass().getClassLoader().loadClass("android.util.Log");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method loge = null;
try {
loge = logClass.getMethod("e",String.class,String.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
loge.invoke(null,"r0ysueSSLrequest","SSL is => "+this.handshakeCallbacks.toString());
loge.invoke(null,"r0ysueSSLrequest","buffer is => "+inputString);
Exception e = new Exception("r0ysueSSLrequest");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
NativeCrypto.SSL_write(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
}
# cd /root/Desktop/COMPILE/aosp/
# export LC_ALL=C
# source build/envsetup.sh
# lunch选择对应的设备,因为要选择无root版,选项中也没有,手动输入lunch aosp_sailfish-user
# make update-api 更新改动
# make -j4
编译成功后刷入手机。