本文仅限于技术讨论,不得用于非法途径,后果自负。
防内存dump比较笼统,本篇只介绍使用inotify相关实现(以BB为例)。
关于内存dump相关介绍,请参考如下链接:
1) 讨论android加固防内存dump的技术及vmp壳的防护强度:
https://bbs.pediy.com/thread-206293.htm。
2) android应用反调试以及反内存dump代码收集:
https://github.com/parkerpeng/DroidAnti。
关于inotify相关介绍,请参考如下链接:
1) inotify不生效问题:
http://blog.csdn.net/cool_way/article/details/22827433;
2) Linux inotify功能及实现原理:
http://blog.csdn.net/myarrow/article/details/7096460。
1)使用/proc/pid/mem访问其他进程的内存变量:
http://blog.csdn.net/guojin08/article/details/9454467
2)pagemap的解读:
http://blog.sina.com.cn/s/blog_628cc2b70101c8zu.html
我们先看一下BB在防篡改技术的介绍,下图是BB官网上关于防篡改的介绍。(https://www.bangcle.com/products/productindex?product_id=1)
#从官网上无法判断其采取何种措施,下面通过实际逆向分析来学习其相关防护策略。 首先介绍下其使用到的数据结构。
1)红黑树(二)之 C语言的实现:
https://www.cnblogs.com/skywang12345/p/3624177.html
其定义如下:
FileWatchKey结构用于保存监控的文件名以及对应的inotify_add_watch句柄。
该函数位于libdexhelp.so文件中。如下:
从上面的代码可以看到File_notify_threadProc为真正的处理函数。
该函数步骤如下:
1、调用 inotify_init_function函数用于初始化红黑树头信息
2、调用inotify_add_watchByPid(fatherPid)函数,将父进程的/proc/fatherpid/mem与/proc/fatherpid/pagemap纳入到监控中,同时将相应文件名和wd插入到红黑树中。
3、创建线程watchAllTask_threadProc,其将/proc/fatherpid/task/下的所有线程对应的mem与pagemap文件纳入到监控中,同时将 同时将相应文件名和wd插入到红黑树中。
4、调用read_filewatch_event函数对进行监控,如果没发生事件,则阻塞,如果发生事件,则函数返回。
5、调用filewatch_Delete移除监控事件。
6、结束watchAllTask_threadProc线程。
7、结束父进程。
8、线程退出。
该函数用于初始化文件监控相关信息。本函数经过了混淆,去除如下:
从上面可以看到其主要是调用inotifyfile_ini 用于初始化g_fileWatch_wd_root以及g_fileWatch_wd_root,对应的数据结构为RBRoot。
下面看看inotifyfile_ini函数:
从上面可以看到inotifyfile_ini用于malloc一个RBRoot结构,同时将初始化相关成员。其中g_inotify_node_NoValidFlag表示头结点无效。因为目前没有设置任何要监控的文件。
我们看一下2个用于比较的函数。is_same_inotify_wd与is_same_inotify_filename。
该函数用于比较红黑树的key为wd。
本函数主要用于判断新的节点是左节点还是右节点。返回0,是同一个节点,大于0是在右节点分支,小于0在左节点分支。
该函数用于比较红黑树的key为filename。
本函数主要用于判断新的节点是左节点还是右节点。返回0,是同一个节点,大于0是在右节点分支,小于0在左节点分支。
该函数做了如下事情:
1、调用DecodeString9解密字符串“/proc/%ld/mem”;
2、格式化字符串“ /proc/pid/mem”;
3、调用inotify_add_watch_insert_node 将对应文件纳入到监控中;
4、调用DecodeString9解密字符串“/proc/%ld/pagemap”;
5、格式化字符串“ /proc/pid/pagemap”;
6、调用inotify_add_watch_insert_node 将对应文件纳入到监控中;
本函数有如下步骤:
1、对输入的字符串数组进行inotify_add_watch;
2、调用JudeFileIsDir判断是否是目录
2、调用inotifyList_user_add_node将wd于文件名写入对应的红黑树中。
本函数有如下步骤:
下面我们看一getinotifyListByWDnode函数
getinotifyListByWDnode调用query_insert_node来进行查询。
query_insert_node函数进行如下操作:
1、遍历二叉树进行查找进行节点查找;
2、如果找到则返回对应节点;
3、如果没找到,并且不创建新节点则返回0;
4、malloc一个新的RBTree;
5、初始化其父节点;
6、初始化新的RBTree;
7、调用 rbtree_left_rotate和rbtree_right_rotate对红黑树进行修正。
上面完成了getinotifyListByWDnode函数的分析,继续分析insertNewNode。
该函数调用query_insert_node进行新节点的插入操作。
至此函数inotify_add_watchByPid分析完了。
下面看看watchAllTask_threadProc函数的工作。
1、调用DecodeString9解密字符串“/proc/%ld/task/”;
2、格式化字符串“/proc/pid/task/”;
3、调用opendir 打开“/proc/pid/task/”目录;
4、调用readdir读取“/proc/pid/task/”目录;
5、如果返回空,则到步骤11;
6、返回不是空,过滤字符串“ .”与“ ..”;
7、调用DecodeString9解密字符串“/proc/pid/task/tid”;
8、调用inotify_add_watchByPid将tid下的mem与pagemap文件纳入监控中;
9、调用 inotify_add_watchByTid(pfatherPid_1, threadID); 将“/proc/pid/task/tid”中的mem与pagemap纳入到监控中;
10、重复步骤4-步骤9;
11、调用closedir关闭目录;
12、线程睡眠2秒;
13、重复步骤1-12。
这样是不行的,如果此时结束,对于后面新创建的线程则不能纳入到本进程中。对于已经被watch的文件再次watch将返回上次的wd,引用次数会加1。
这里面可能有个小问题是:如果线程被删除了则对应的红黑树链表的节点不会被删除,造成内存泄漏。极端情况应用一致持续不断的创建线程然后线程2秒后销毁,运行一段时间后内存会崩溃。
下面看一下inotify_add_watchByTid函数。
这个函数相对比较简单。
read_filewatch_event函数步骤如下:
1、调用select函数对inotify初始化句柄进行阻塞。当发生事件时,则线程唤醒;
2、调用ioctl函数获得对应事件的长度;
3、调用read函数将发生的事件信息读取到全局变量中。
4、返回对应的事件BUF。
filewatch_Delete函数步骤如下:
1、格式化字符/proc/pid/mem;
2、调用 filewatch_DeleteByFile删除/proc/pid/mem的watch;
3、格式化字符/proc/pid/pagemap;
4、调用 filewatch_DeleteByFile删除/proc/pid/pagemap的watch;
该函数调用步骤如下:
1、调用filewatch_DeleteNode删除wd相关watch;
2、调用filewatch_DeleteNode删除filename相关watch;
3、调用filewatch_rm移除wd;
4、调用freeKeyBuf释放FileWatchKey。
函数filewatch_DeleteNode如下:
filewatch_DeleteNode函数进行节点删除,以及红黑树调整相关操作。下面看一下函数filewatch_rm。
调用inotify_rm_watch进行wd移除。下面看一下freeKeyBuf函数。
freeKeyBuf函数进行内存释放工作。
至此删除文件监控分析结束。
先将监控所有task的线程结束。然后调用killProcess结束父进程。
对于防守方可以监控inotify_add_watch函数是否HOOK。
学习群号:211730239
从上面的知识可知,通过对目标进程文件/proc/pid/mem文件操作,可以获得其内存的数据。
而inotify可以监控文件系统的变化,当文件被打开、删除,读写等操作时,同时用户相应变化。
因此可以通过监控/proc/pid/mem 与/proc/pid/pagemep来防止内存dump。
防内存dump用到了红黑树的数据结构。下面是关于红黑树数据结构的介绍。
typedef struct FileWatchKey
{
char* fileName; //监控的文件名
int wd; //inotify_add_watch 返回值
}FileWatchKey;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!