Android FileObserver 实现原理(inotify)
0x0前言
之前在分析某个Android平台加固壳子的时候就碰到过inotify,被用来监控/proc 文件系统,防止gdb调试器的附加,以达到反调试的目的。inotify机制是从linux kernel 2.6.13开始引入,Android 1.5对应的linux内核已经是2.6.26了。因此完全可以在Android上利用inotify达到反调试的目的。而且Android将inotify直接封装成了FileObserver类,可以直接在Java代码中使用。当然在jni中自己调用inotify也是很容易的。
0x01 FileObserver 使用实例
想要在Java层使用FileObserver,必须先继承FileObserver类,实现其onEvent()函数。作为目标文件发生变化时调用的方法。
private class SingleFileObserver extends FileObserver{
//......
@Override
public void onEvent(int i, String s) {
//.....
}
}
通过startWatching()开始监视,stopWatching()停止监视。
考虑到FileObserver不支持子目录的递归,将FileObserver封装了一层,以达到可以递归监视的目的。
public class RecursiveFileObserver{
private ArrayList<SingleFileObserver> mSingleObservers = new ArrayList<SingleFileObserver>();
public RecursiveFileObserver(String path){
//解析子目录
Stack<String> pathStack = new Stack<String>();
pathStack.push(path);
while (!pathStack.isEmpty()){
String parentPath = pathStack.pop();
if (mSingleObservers.add(new SingleFileObserver(parentPath))){
Log.d("C&C","add observer success"+parentPath);
}
File parent = new File(parentPath);
if (parent.isDirectory()){
File[] files = parent.listFiles();
for (int i =0;i<files.length;i++){
File f = files[i];
if (f.isDirectory() &&
(f.getName().equals(".") || f.getName().equals(".."))){
//跳过 "." ".." 目录
}else {
pathStack.push(f.toString());
//pathStack.push(f.getAbsolutePath());
Log.d("C&C","file list:"+f.toString());
}
}
}
}
}
public void startWatching() {
for (int i = 0;i<mSingleObservers.size();i++){
mSingleObservers.get(i).startWatching();
}
}
public void stopWatching() {
for (int i = 0;i<mSingleObservers.size();i++){
mSingleObservers.get(i).stopWatching();
}
}
private class SingleFileObserver extends FileObserver{
protected String mPath ;
protected int mMask;
public static final int DEFAULT_MASK = CREATE | MODIFY | DELETE;
public SingleFileObserver(String path){
this(path , DEFAULT_MASK);
}
public SingleFileObserver(String path , int mask){
super(path , mask);
mPath = path;
mMask = mask;
}
@Override
public void onEvent(int i, String s) {
int event = i&FileObserver.ALL_EVENTS;
switch (event){
case MODIFY:
//查看是否被调试
if (isDebugged(s)){
Log.d("C&C","is debugged");
}
}
}
}
}
0x02 FileObserver 实现原理
Android已经将linux下的inotify机制封装成了FileObserver抽象类,必须继承FileObserver类才能使用。
android.os.FileObserver
Monitors files (using inotify) to fire an event after files are accessed or changed by by any process on the device (including this one). FileObserver is an abstract class; subclasses must implement the event handler onEvent(int, String).
Each FileObserver instance monitors a single file or directory. If a directory is monitored, events will be triggered for all files and subdirectories inside the monitored directory.
An event mask is used to specify which changes or actions to report. Event type constants are used to describe the possible changes in the event mask as well as what actually happened in event callbacks.
Android sdk的官方文档说的是监视一个目录,则该目录下所有的文件和子目录的改变都会触发监听的事件。经过测试,其实对于监听目录的子目录的文件改动,FileObserver对象是无法接收到事件回调的。
FileObserver可以监听的类型:
- ACCESS 访问文件
- MODIFY 修改文件
- ATTRIB 修改文件属性,例如chmod 、chown等
- CLOSE_WRITE以可写属性打开的文件被关闭
- CLOSE_NOWRITE 以不可写属性被打开的文件被关闭
- OPEN 文件被打开
- MOVED_FROM 文件被移走,例如mv
- MOVED_TO 移入新文件,例如mv cp
- CREATE 创建新文件
- DELETE 删除文件,例如rm
- DELETE_SELF 自删除,一个文件在执行时删除自己
- MOVE_SELF 自移动,一个可执行文件在执行时移动自己
- CLOSE 关闭文件 = (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
- ALL_EVENTS 上面所有的事件
要先继承FileObserver抽象类,实现其中的OnEvent()方法
public abstract void onEvent(int event, String path);
在FileObserver类中封装有一个static 的线程类
private static class ObserverThread extends Thread {
......
}
- inotify初始化
在ObserverThread类的构造方法中:
public ObserverThread() {
super("FileObserver");
m_fd = init();
}
这里的init是native方法:
private native int init();
在/frameworks/base/core/jni/android_util_FileObserver.cpp中
static jint android_os_fileobserver_init(JNIEnv* env, jobject object)
{
return (jint)inotify_init();
}
很明显,只是调用了inotify_init(),初始化inotify。
- 开始监控
使用要先调用FileObserver.startWatching()--->ObserverThread.startWatching()--->linux( inotify_add_watch() )
public int startWatching(String path, int mask, FileObserver observer) {
int wfd = startWatching(m_fd, path, ma
Integer i = new Integer(wfd);
if (wfd >= 0) {
synchronized (m_observers) {
m_observers.put(i, new WeakReference(observer));
}
}
return i;
}
static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask)
{
int res = -1;
if (fd >= 0)
{
const char* path = env->GetStringUTFChars(pathString, NULL);
res = inotify_add_watch(fd, path, mask); //返回监视器描述符
env->ReleaseStringUTFChars(pathString, path);
}
return res;
}
- 监控过程
在 ObserverThread 线程运行的run()方法中:
public void run() {
observe(m_fd);
}
static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd)
{
char event_buf[512];
struct inotify_event* event;
while (1)
{
int event_pos = 0;
int num_bytes = read(fd, event_buf, sizeof(event_buf)); //读取事件
if (num_bytes < (int)sizeof(*event))
{
if (errno == EINTR)
continue;
ALOGE("***** ERROR! android_os_fileobserver_observe() got a short event!");
return;
}
while (num_bytes >= (int)sizeof(*event))
{
int event_size;
event = (struct inotify_event *)(event_buf + event_pos);
jstring path = NULL;
if (event->len > 0)
{
path = env->NewStringUTF(event->name);
}
//调用java层ObserverThread类的OnEvent方法
env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path);
if (env->ExceptionCheck()) { //异常处理
env->ExceptionDescribe();
env->ExceptionClear();
}
if (path != NULL)
{
env->DeleteLocalRef(path);
}
//指向下一个inotify_event结构
event_size = sizeof(*event) + event->len;
num_bytes -= event_size;
event_pos += event_size;
}
}
}
inotify_event的结构如下:
struct inotify_event {
__s32 wd; /* watch descriptor */
__u32 mask; /* watch mask */
__u32 cookie; /* cookie to synchronize two events */
__u32 len; /* length (including nulls) of name */
char name[0]; /* stub for possible name */
};
- wd: 被监视目标的 watch 描述符
- mask : 事件掩码
- name: 被监视目标的路径名,文件名被0填充,使得下一个事件结构能够以4字节对齐
- len : name字符串的长度
调用的onEvent()方法:
public void onEvent(int wfd, int mask, String path) {
// look up our observer, fixing up the map if necessary...
FileObserver observer = null;
synchronized (m_observers) { //同步代码块
WeakReference weak = m_observers.get(wfd);
if (weak != null) { // can happen with lots of events from a dead wfd
observer = (FileObserver) weak.get();
if (observer == null) {
m_observers.remove(wfd);
}
}
}
// ...then call out to the observer without the sync lock held
if (observer != null) { //为什么不使用同步代码块???
try {
observer.onEvent(mask, path); //调用FileObserver抽象类的OnEvent()方法,也就是自己实现的OnEvent()方法
} catch (Throwable throwable) {
Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
}
}
}
- 停止监控
FileObserver.stopWatching() --> ObserverThread.stopWatching()---> linux( inotify_rm_watch() )
static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object, jint fd, jint wfd)
{
inotify_rm_watch((int)fd, (uint32_t)wfd);
}
0x04 完整Demo下载地址
https://github.com/ChengChengCC/Android-demo/tree/master/FileObserver
Android FileObserver 实现原理(inotify)的更多相关文章
- Android Touch事件原理加实例分析
Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. ...
- android handler工作原理
android handler工作原理 作用 便于在子线程中更新主UI线程中的控件 这里涉及到了UI主线程和子线程 UI主线程 它很特别.通常我们会认为UI主线程将页面绘制完成,就结束了.但是它没有. ...
- android MultiDex multidex原理原理下遇见的N个深坑(二)
android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multi ...
- android MultiDex multiDex原理(一)
android MultiDex 原理(一) Android分包MultiDex原理详解 转载请注明:http://blog.csdn.net/djy1992/article/details/5116 ...
- 解析 Android Things 技术原理
2012 年 6 月,由 IoT-GSI(Global Standards Initiative on Internet of Things)发布的白皮书“ITU-T Y.4000/Y.2060”[1 ...
- Android View绘制原理分析
推荐两篇分析view绘制原理比较好的文章,感谢作者的分享. <Android应用层View绘制流程与源码分析> <View 绘制流程>
- Android屏幕适配原理
几个概念: 1) 屏幕密度(dpi) :dot per inch,即每英寸像素数. ldpi(120),mdpi(160),hdpi(240),xhdpi(320) 计算方法: 以480x854,4. ...
- [Android实例] Scroll原理-附ScrollView源码分析
想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内 ...
- (转)Android之ListView原理学习与优化总结
转自: http://jishu.zol.com.cn/12893.html 在整理前几篇文章的时候有朋友提出写一下ListView的性能优化方面的东西,这个问题也是小马在面试过程中被别人问到的….. ...
随机推荐
- concurrent.futures模块(进程池/线程池)
需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...
- apache禁止IP访问网站
参考资料: http://www.cnblogs.com/zhuangge/archive/2011/04/13/2014892.html 先引用一下上面资料的内容: 用apache搭建的WEB服务器 ...
- django by example 第四章 dashboard处html无法渲染问题
描述: 实现django by example 代码时,第四章 dashboard处html无法渲染问题. 此时报错,NoReverseMatch at /account/login/, Error ...
- Java集合:LinkedList源码解析
Java集合---LinkedList源码解析 一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据re ...
- IPv6下网络编程socket, TCP和UDP例子,以及兼容IPV4和IPV6的类
一.TCP socket ipv6与ipv4的区别 服务器端源代码如下: #include <stdio.h> #include <stdlib.h> #include < ...
- jquery中siblings方法配合什么方法一起使用
siblings() 获得匹配集合中每个元素的同胞,通过选择器进行筛选是可选的.接下来通过本文给大家介绍jQuery siblings()用法实例详解,需要的朋友参考下吧 siblings() 获得匹 ...
- SignalR 服务器系统配置要求
SignalR 所支持的服务器版本..NET Framework 版本.IIS和其他组件. SignalR操作系统要求 SignalR组件能够运行在下面的服务器和客户端操作系统.需要注意的是使用Web ...
- Struts2学习第四天——拦截器及文件上传
1.概述 Struts2的很多核心功能都是由拦截器完成的. 拦截器很好的实现了AOP的编程思想,在动作的执行之前和结果的返回之后,做拦截处理. 2.struts2的默认拦截器栈 3.自定义拦截器 St ...
- 实战C++对象模型之成员函数调用
先说结论:C++的类成员函数和C函数实质是一样的,只是C++类成员函数多了隐藏参数this. 通过本文的演示,可以看见这背后的一切,完全可C函数方式调用C++类普通成员函数和C++类虚拟成员函数. 为 ...
- @WebFilter怎么控制多个filter的执行顺序
转自:http://blog.csdn.net/liming_0820/article/details/53332070 之前我们控制多个filter的执行顺序是通过web.xml中控制filter的 ...