Java层文件系统监控简单实现
在阅读android 10的aosp源码 framework/base/core/java/android/app/ActivityThread.java时候,看到了AndroidOs这个新东西,发现是一个IO回调,挺有意思
aosp/libcore回调机制
比如File.delete函数
public boolean delete() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
if (isInvalid()) {
return false;
}
return fs.delete(this);
}
最终会调用至fs.delete
看看fs是啥玩意
public class File
implements Serializable, Comparable<File>
{
/**
* The FileSystem object representing the platform's local file system.
*/
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
调试后发现fs其实是UniFileSystem
源码中也解释了这一点
class DefaultFileSystem {
/**
* Return the FileSystem object for Unix-based platform.
*/
public static FileSystem getFileSystem() {
return new UnixFileSystem();
}
}
继续追踪UnixFileSystem.delete
public boolean delete(File f) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
// (i.e., only remove/update affected entries) but probably
// not worth it since these entries expire after 30 seconds
// anyway.
cache.clear();
javaHomePrefixCache.clear();
// BEGIN Android-changed: Access files through common interface.
try {
Libcore.os.remove(f.getPath());
return true;
} catch (ErrnoException e) {
return false;
}
// END Android-changed: Access files through common interface.
}
调用了Libcore.os.remove(File)
看看Libcore.java中的os赋值
/** @hide */
public final class Libcore {
private Libcore() { }
/**
* Direct access to syscalls. Code should strongly prefer using {@link #os}
* unless it has a strong reason to bypass the helpful checks/guards that it
* provides.
*/
public static final Os rawOs = new Linux();
/**
* Access to syscalls with helpful checks/guards.
* For read access only; the only supported way to update this field is via
* {@link #compareAndSetOs}.
*/
@UnsupportedAppUsage
public static volatile Os os = new BlockGuardOs(rawOs);
public static Os getOs() {
return os;
}
/**
* Updates {@link #os} if {@code os == expect}. The update is atomic with
* respect to other invocations of this method.
*/
public static boolean compareAndSetOs(Os expect, Os update) {
Objects.requireNonNull(update);
if (os != expect) {
return false;
}
synchronized (Libcore.class) {
boolean result = (os == expect);
if (result) {
os = update;
}
return result;
}
}
}
赋值有两个地方。
1.class静态初始化时:public static volatile Os os = new BlockGuardOs(rawOs);
2.调用compareAndSetOs函数,将第二个参数设置为期望的os回调。从os = update就可以看出。
安卓系统就是调用了compareAndSetOs函数,设置了os回调。
所以AndroidOs最终通过ActivityThread->AndroidOs进行IO监控.
ActivityThread.java调用compareAndSetOs函数
//ActivityThread.java
public static void main(String[] args) {
........
........
AndroidOs.install();//调用了AndroidOs.install
........
........
}
private static class AndroidOs extends ForwardingOs {
public static void install() {
.....
.....
do {
def = Os.getDefault();
//调用了上述所说的关键os赋值
} while (!Os.compareAndSetDefault(def, new AndroidOs(def)));
}
private AndroidOs(Os os) {
super(os);
}
......
......
......
}
ActivityThread.main函数是通过zygote进程起来后调用的第一个函数。从函数名public static void main就可以看出来。感兴趣的可以分析一下,细节不讲了. 提示大家参考 ActivityThrad.java作为函数入口点的代码,可以参考ActivityManagerService->boolean startProcessLocked函数.
boolean startProcessLocked(... ) {
....
....
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
final String entryPoint = "android.app.ActivityThread";
....
....
}
通过动态代理AndroidOs,调用Os.compareAndSetDefault进行替换
AndroidOs 继承了ForwardingOs, 该类在aosp/libcore源码下面
/**
* Subclass this if you want to override some {@link Os} methods but otherwise delegate.
*
* @hide
*/
@libcore.api.CorePlatformApi
public class ForwardingOs implements Os {
}
该类实现了libcore.io.Os.java接口,
/** @hide */
@libcore.api.CorePlatformApi
public interface Os {
....
....
public boolean access(String path, int mode) throws ErrnoException;
public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public void chmod(String path, int mode) throws ErrnoException;
public void chown(String path, int uid, int gid) throws ErrnoException;
public String readlink(String path) throws ErrnoException;
public String realpath(String path) throws ErrnoException;
public void remove(String path) throws ErrnoException;
}
我们要实现的IO监控刚好是Os接口声明的,所以可以直接代理AndroidOs, 实现对open access remove等函数监控
如何拿到AndroidOs实例,并将代理设置为本进程的IO回调呢?
同样参考ActivityThread.java,
private static class AndroidOs extends ForwardingOs {
public static void install() {
.....
.....
do {
def = Os.getDefault();
//调用了上述所说的关键os赋值
} while (!Os.compareAndSetDefault(def, new AndroidOs(def)));
}
private AndroidOs(Os os) {
super(os);
}
......
......
......
}
1.反射调用Os.getDefault拿到AndroidOs实例,直接创建一个proxy
defObj = LbcoreOsGetDef.invoke(null, new Object[0]);
List<Class<?>> interfaces = getAllInterfaces(Class.forName("libcore.io.Linux"));
if (interfaces != null && interfaces.size() > 0) {
Class[] ifs = interfaces.toArray(new Class[interfaces.size()]);
newObj = Proxy.newProxyInstance(defObj.getClass().getClassLoader(),
ifs, new ForwardingOsProxy(defObj));
}
private static class ForwardingOsProxy implements InvocationHandler {
private Object mOrigin;
ForwardingOsProxy(Object origin) {
mOrigin = origin;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d(TAG, String.format("ForwardingOsProxy call %s %s",
method.getName(), Arrays.toString(args)));
return method.invoke(mOrigin, args);
}
}
2.反射调用compareAndSetDefault函数,设置代理os
RefUtils.MethodRef<Boolean> compareAndSetDefaultMethod =
new RefUtils.MethodRef<Boolean>("libcore.io.Os",
true, "compareAndSetDefault",
new Class[]{OsClass, OsClass});
do {
Log.d(TAG, "installOs try...");
} while (!compareAndSetDefaultMethod.invoke(null, new Object[]{defObj, newObj}));
Log.d(TAG, "installOs success!");
然后写一个demo测试,点击按钮,删除/data/data/pkg/test.txt
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new File(getApplicationInfo().dataDir, "test.txt").delete();
}
});
看一下log:
04-24 16:38:55.179 5182 5182 D Hook: ForwardingOsProxy call getuid null
04-24 16:38:55.180 5182 5182 D Hook: ForwardingOsProxy call getuid null
04-24 16:38:55.182 5182 5182 D Hook: ForwardingOsProxy call remove [/data/user/0/com.example.demo/test.txt]