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