由于一个特殊的需求,检测应用有没有在联网,于是我尝试写了这个工具。需要声明一点,我这个工具需要自己重新编译ROM。
原理
其实很简单,APP需要访问网络时,需要调用Java层的接口
// 请求的地址
String spec = "http://localhost:9090/formTest";
// 根据地址创建URL对象
URL url = new URL(spec);
// 根据URL对象打开链接
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
所以我们在URL#openConnection()中,添加自己的监控逻辑即可。
可以看到java.net.URL
是在jrt中的,也就是java的运行环境中的,所以我们需要修改jrt的东西。
经过一些查询我知道了Android的libcore和ojluni。
ojluni
ojluni就是OpenJDK, java.lang / java.util / java.net / java.io 的缩写,就是OpenJDK核心库的意思。ojluni在libcore目录下。
有兴趣的同学可以在http://androidxref.com/9.0.0_r3/xref/libcore/
查看所有的源码
具体实现
接下来我们需要修改ojluni下的URL的源码,需要先下载libcore项目,找到URL.java的位置:
libcore/ojluni/src/main/java/java/net/URL.java
在openConnection()方法中添加日志打印,并且将日志保存下来
public URLConnection openConnection() throws java.io.IOException {
java.util.SeempLog.record_str(91, "URL:" + query);
logHttpUrlByLocalSocket(); // 添加此行,记录上层应用访问网络的情况
return handler.openConnection(this);
}
/** 记录访问网络的应用PID/UID/TID/进程名称和调用栈 */
private void logHttpUrlByLocalSocket() {
String url = String.format("%s://%s%s?%s", protocol, authority, path, query);
String lineData = String.format("%s#pid:%s#uid:%s#tid:%s#cmdline:%s#%s\n",
System.currentTimeMillis(), getPid(), getUid(), getTid(), getCmdline(getPid()), url.trim());
String callStack = getCallStack();
File file = new File("sdcard/Download/http.txt");
try {
if (!file.exists()) {
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
try (RandomAccessFile raf = new RandomAccessFile(file, "rwd")) {
raf.seek(file.length());
raf.write(lineData.getBytes());
raf.write(callStack.getBytes());
raf.write("\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
/** 获取pid对应的进程名称 */
public static String getCmdline(int pid) {
FileReader in = null;
BufferedReader reader = null;
String result = null;
try {
File file = new File("/proc/" + pid + "/cmdline");
in = new FileReader(file);
reader = new BufferedReader(in);
String line = reader.readLine();
if (line != null) {
result = line.replace("\0", "");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (reader != null) {
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (result == null) {
return "null";
}
return result;
}
/** 反射获取PID */
private static int getPid() {
try {
Object pid = callStaticObjectMethod(Class.forName("android.os.Process"), "myPid", null);
if (pid != null) {
return (int) pid;
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
/** 反射获取UID */
private static int getUid() {
try {
Object pid = callStaticObjectMethod(Class.forName("android.os.Process"), "myUid", null);
if (pid != null) {
return (int) pid;
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
/** 反射获取TID */
private static int getTid() {
try {
Object pid = callStaticObjectMethod(Class.forName("android.os.Process"), "myTid", null);
if (pid != null) {
return (int) pid;
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
/** 获取调用栈 */
private static String getCallStack() {
StackTraceElement[] stackTrace = new Throwable().fillInStackTrace().getStackTrace();
StringBuffer callStack = new StringBuffer();
callStack.append("callStack:");
for (int i = 0; i < stackTrace.length; i++) {
callStack.append(stackTrace[i].toString());
callStack.append("\n");
}
return callStack.toString();
}
private static Object callObjectMethod(Object target, String method, Class<?>[] parameterTypes, Object... values)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<? extends Object> clazz = target.getClass();
Method declaredMethod = clazz.getDeclaredMethod(method, parameterTypes);
declaredMethod.setAccessible(true);
return declaredMethod.invoke(target, values);
}
public static Object callStaticObjectMethod(Class<?> clazz, String method, Class<?>[] parameterTypes, Object... values)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Method declaredMethod = clazz.getDeclaredMethod(method, parameterTypes);
declaredMethod.setAccessible(true);
return declaredMethod.invoke(null, values);
}
以上就是整个功能。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!