本文仅限于技术讨论,不得用于非法途径,后果自负。
防内存dump比较笼统,本篇只介绍使用inotify相关实现(以BB为例)。
关于内存dump相关介绍,请参考如下链接:
讨论android加固防内存dump的技术及vmp壳的防护强度:
https://bbs.pediy.com/thread-206293.htm
android应用反调试以及反内存dump代码收集:
https://github.com/parkerpeng/DroidAnti
关于inotify相关介绍,请参考如下链接:
inotify不生效问题:
http://blog.csdn.net/cool_way/article/details/22827433
Linux inotify功能及实现原理:
http://blog.csdn.net/myarrow/article/details/7096460
从上面的知识可知,通过对目标进程文件/proc/pid/mem文件操作,可以获得其内存的数据。
而inotify可以监控文件系统的变化,当文件被打开、删除,读写等操作时,同时用户相应变化。
因此可以通过监控/proc/pid/mem 与/proc/pid/pagemep来防止内存dump。
我们先看一下BB在防篡改技术的介绍,下图是BB官网上关于防篡改的介绍。梆梆官网介绍
从官网上无法判断其采取何种措施,下面通过实际逆向分析来学习其相关防护策略。 首先介绍下其使用到的数据结构。
防内存dump用到了红黑树的数据结构。下面是关于红黑树数据结构的介绍。
红黑树之 C语言的实现:
https://www.cnblogs.com/skywang12345/p/3624177.html
其定义如下:
typedef struct FileWatchKey
{
char* fileName; //监控的文件名
int wd; //inotify_add_watch 返回值
}FileWatchKey;
FileWatchKey结构用于保存监控的文件名以及对应的inotify_add_watch句柄。
这个结构用于保存所有监控文件的信息,其定义如下:
typedef struct RBTree
{
RBTree* left; //红黑树左节点
RBTree* right; //红黑树右节点
RBTree* parent; //父节点
int color; //红节点 黑节点
FileWatchKey* keybuf; //key
} RBTree;
本结构用于定义头结点以及用于节点比较的函数指针。
typedef struct RBRoot
{
void* compareFun; //用于红黑树比较的函数
int const_0; //常量
RBTree* treeHead; //红黑树头结点
}RBRoot;
该函数位于libdexhelp.so文件中。如下:
void __fastcall createFileWatch_thread(int fatherPid, pthread_t a2)
{
int v2; // r4
signed int v3; // r5
_DWORD *pfatherPid; // r6
pthread_t newthread; // [sp+4h] [bp-14h]
newthread = a2;
v2 = fatherPid;
v3 = 31;
pfatherPid = malloc(4u);
*pfatherPid = v2;
while ( pthread_create(&newthread, 0, (void *(*)(void *))File_notify_threadProc, pfatherPid) )
{
if ( !--v3 )
break;
sleep(1u);
}
}
从上面的代码可以看到File_notify_threadProc为真正的处理函数。
signed int __fastcall File_notify_threadProc(__pid_t *pfatherPid)
int fatherPid; // r5
unsigned int result; // r0
signed int v3; // r6
_DWORD *v4; // r7
struct inotify_event *inotifyeventStr; // r0
pthread_t newthread; // [sp+4h] [bp-1Ch]
fatherPid = *pfatherPid;
free(pfatherPid);
result = inotify_init_function();
if ( result )
{
inotify_add_watchByPid(fatherPid);
v3 = 0x1F;
v4 = malloc(4u);
*v4 = fatherPid;
while ( pthread_create(&newthread, 0, (void *(*)(void *))watchAllTask_threadProc, v4) )
{
if ( !--v3 )
break;
sleep(1u);
}
inotifyeventStr = (struct inotify_event *)read_filewatch_event(-1);
if ( inotifyeventStr )
GetFileWatchRBTreeKeyName(inotifyeventStr->wd);
filewatch_Delete(fatherPid);
pthread_kill(newthread, 10);
result = killProcess(fatherPid, 9);
}
return result;
该函数步骤如下:
该函数用于初始化文件监控相关信息。本函数经过了混淆,去除如下:
signed int __fastcall inotify_init_function()
{
signed int result; // r0
//如果初始化过,则直接返回。
if ( g_inotify_init_finshFlag )
return 1;
g_fileWatch_errno = 0;
//初始化文件监控
g_inotify_init = inotify_init();
if ( g_inotify_init < 0 )
{
result = 0;
g_fileWatch_errno = g_inotify_init;
return result;
}
//置位初始化完成标志
g_inotify_init_finshFlag = 1;
//初始化以监控句柄比较的红黑树RBRoot 结构
g_fileWatch_wd_root = (struct RBRoot *)inotifyfile_ini((int)is_same_inotify_wd, 0);
//初始化以监控文件名比较的红黑树RBRoot 结构
g_fileWatch_name_root = (struct RBRoot *)inotifyfile_ini((int)is_same_inotify_filename, 0);
return 1;
}
从上面可以看到其主要是调用inotifyfile_ini 用于初始化g_fileWatch_wd_root以及g_fileWatch_wd_root,对应的数据结构为RBRoot。
下面看看inotifyfile_ini函数:
truct RBRoot *__fastcall inotifyfile_ini(void *comparefun, int const_0)
{
void *comparefun_1; // r5
int const_0_1; // r4
struct RBRoot *result; // r0
comparefun_1 = comparefun;
const_0_1 = const_0;
result = (struct RBRoot *)malloc(0xCu);
if ( result )
{
result->fun = comparefun_1;
result->const_0 = const_0_1;
result->root = (struct RBTree *)&g_inotify_node_NoValidFlag;
}
return result;
}
从上面可以看到inotifyfile_ini用于malloc一个RBRoot结构,同时将初始化相关成员。其中g_inotify_node_NoValidFlag表示头结点无效。因为目前没有设置任何要监控的文件。
我们看一下2个用于比较的函数。is_same_inotify_wd与is_same_inotify_filename。
该函数用于比较红黑树的key为wd。
struct FileWatchKey *__fastcall is_same_inotify_wd(struct FileWatchKey *node, int a2)
{
struct FileWatchKey *result; // r0
if ( node && a2 )
result = (struct FileWatchKey *)(node->wd - *(_DWORD *)(a2 + 4));
else
result = (struct FileWatchKey *)((char *)node - a2);
return result;
}
本函数主要用于判断新的节点是左节点还是右节点。返回0,是同一个节点,大于0是在右节点分支,小于0在左节点分支。
该函数用于比较红黑树的key为filename。
int __fastcall is_same_inotify_filename(struct FileWatchKey *node, const char *a2)
{
int result; // r0
if ( node && a2 )
result = strcmp(node->name, *(const char **)a2);
else
result = (char *)node - a2;
return result;
}
本函数主要用于判断新的节点是左节点还是右节点。返回0,是同一个节点,大于0是在右节点分支,小于0在左节点分支。
inotify_add_watchByPid函数将对应进程的mem与pagemap文件纳入到监控中,同时创建红黑树节点并插入到相应红黑树中。
int __fastcall inotify_add_watchByPid(int fatherPid)
{
int fatherPid_1; // r7
int v3; // [sp+0h] [bp-140h]
char v4; // [sp+4h] [bp-13Ch]
char v5; // [sp+5h] [bp-13Bh]
char v6; // [sp+6h] [bp-13Ah]
char v7; // [sp+7h] [bp-139h]
char v8; // [sp+8h] [bp-138h]
char v9; // [sp+9h] [bp-137h]
char v10; // [sp+Ah] [bp-136h]
char v11; // [sp+Bh] [bp-135h]
char v12; // [sp+Ch] [bp-134h]
char v13; // [sp+Dh] [bp-133h]
char v14; // [sp+Eh] [bp-132h]
char v15; // [sp+10h] [bp-130h]
char v16; // [sp+11h] [bp-12Fh]
char v17; // [sp+12h] [bp-12Eh]
char v18; // [sp+13h] [bp-12Dh]
char v19; // [sp+14h] [bp-12Ch]
char v20; // [sp+15h] [bp-12Bh]
char v21; // [sp+16h] [bp-12Ah]
char v22; // [sp+17h] [bp-129h]
char v23; // [sp+18h] [bp-128h]
char v24; // [sp+19h] [bp-127h]
char v25; // [sp+1Ah] [bp-126h]
char v26; // [sp+1Bh] [bp-125h]
char v27; // [sp+1Ch] [bp-124h]
char v28; // [sp+1Dh] [bp-123h]
char v29; // [sp+1Eh] [bp-122h]
char v30; // [sp+1Fh] [bp-121h]
char v31; // [sp+20h] [bp-120h]
char v32; // [sp+21h] [bp-11Fh]
char v33; // [sp+22h] [bp-11Eh]
char procmemString; // [sp+24h] [bp-11Ch]
fatherPid_1 = fatherPid;
memset(&v3, 0, 0x10u);
v4 = -34;
v5 = -61;
v6 = -49;
v8 = -119;
v9 = -64;
BYTE1(v3) = 126;
v10 = -56;
HIWORD(v3) = -9085;
v7 = -125;
v11 = -125;
v13 = -55;
v12 = -63;
v14 = -63;
((void (__fastcall *)(int *, signed int, signed int))DecodeString9)(&v3, 13, 0xD2);// /proc/%ld/mem
sprintf(&procmemString, (const char *)&v3, fatherPid_1);
inotify_add_watch_insert_node((int)&procmemString, 0xFFFu);
memset(&v15, 0, 0x14u);
v19 = -43;
v20 = -56;
v21 = -60;
v23 = -126;
v24 = -53;
v25 = -61;
v29 = -64;
v17 = -120;
v22 = -120;
v26 = -120;
v30 = -62;
v16 = 85;
v28 = -58;
v31 = -54;
v32 = -58;
v18 = -41;
v27 = -41;
v33 = -41;
((void (__fastcall *)(char *, signed int, signed int))DecodeString9)(&v15, 17, 242);// /proc/%ld/pagemap
sprintf(&procmemString, &v15, fatherPid_1);
return inotify_add_watch_insert_node((int)&procmemString, 0xFFFu);
}
该函数做了如下事情:
字符串解密函数,如下:
char * DecodeString9(char *buf, int len, char key)
{
int i; // r4
for(i = 0; i < len; i++)
{
buf[i]= buf[i + 2] ^key ^ buf[i+1];
}
buf[i]=0;
return buf;
}
signed int __fastcall inotify_add_watch_insert_node(int procmemString, uint32_t mask_2)
{
char *procmemString_1_1; // r6
int procmemString_1; // r4
const char *v4; // r1
int fd; // r0
int v6; // r4
int v7; // r5
uint32_t mask; // [sp+4h] [bp-1Ch]
procmemString_1 = procmemString;
mask = mask_2;
g_fileWatch_errno = 0;
for ( g_watchIndex = 0; ; ++g_watchIndex )
{
v4 = *(const char **)(4 * g_watchIndex + procmemString_1);
if ( !v4 )
return 1;
fd = inotify_add_watch(g_inotify_init, v4, mask);
g_fileWatch_fd = fd;
if ( fd < 0 )
break;
if ( !JudeFileIsDir(*(char **)(4 * g_watchIndex + procmemString_1))
|| (v7 = *(_DWORD *)(4 * g_watchIndex + procmemString_1),
*(_BYTE *)(v7 + strlen(*(_DWORD *)(4 * g_watchIndex + procmemString_1)) - 1) == '/') )
{
// 是文件或者是目录同时最后一个字符是\
procmemString_1_1 = strdup(*(const char **)(4 * g_watchIndex + procmemString_1));
}
inotifyList_user_add_node(g_fileWatch_fd, procmemString_1_1);
free(procmemString_1_1);
}
v6 = 0;
if ( fd == -1 )
g_fileWatch_errno = *(_DWORD *)_errno(-1);
return v6;
}
本函数有如下步骤:
该函数去混淆后如下:
bool JudeFileIsDir(char *file)
{
bool result; // r0
if(lstat(file, &g_fileStatStruct)<0)
return false;
return (g_fileStatStruct.st_mode & 0xF000) - 0x4000 <= 0;
}
_DWORD *__fastcall inotifyList_user_add_node(int fd, _DWORD *procmemString)
{
_DWORD *inotifyfileKeyBuf; // r4
const char *v3; // r5
int v4; // r6
inotifyfileKeyBuf = 0;
if ( fd > 0 )
{
inotifyfileKeyBuf = procmemString;
if ( procmemString )
{
v3 = (const char *)procmemString;
v4 = fd;
inotifyfileKeyBuf = (_DWORD *)getinotifyListByWDnode(fd);
if ( !inotifyfileKeyBuf )
{
inotifyfileKeyBuf = calloc(1u, 0x40u);
inotifyfileKeyBuf[1] = v4;
*inotifyfileKeyBuf = strdup(v3);
insertNewNode((int)inotifyfileKeyBuf, (int)g_fileWatch_wd_root);
insertNewNode((int)inotifyfileKeyBuf, (int)g_fileWatch_name_root);
}
}
}
return inotifyfileKeyBuf;
}
本函数有如下步骤:
int __fastcall getinotifyListByWDnode(int node_wd, struct RBRoot *rbroot)
{
int result; // r0
int v3; // r0
if ( rbroot
&& (void **)rbroot->root != &g_inotify_node_NoValidFlag
&& (query_insert_node(0, node_wd, rbroot), (void **)v3 != &g_inotify_node_NoValidFlag) )
{
result = *(_DWORD *)(v3 + 0x10);
}
else
{
result = 0;
}
return result;
}
getinotifyListByWDnode调用query_insert_node来进行查询。
void __fastcall query_insert_node(int createFlag, int inotifyfileKeyBuf, struct RBRoot *rbroot)
{
struct RBRoot *rbroot_1; // r7
signed int find; // r5
struct RBTree *curNode; // r4
void **fatherNode; // r6
int result; // r0
struct RBTree *tempNode; // r3
struct RBTree *newNode; // r5
struct RBTree *newNode_1; // r4
struct RBTree *rootHead; // r3
struct RBTree *parent; // r6
struct RBTree *gparent; // r1
struct RBTree *v14; // r2
struct RBTree *v15; // r1
struct RBTree *v16; // [sp+4h] [bp-24h]
struct RBTree *root; // [sp+4h] [bp-24h]
int inotifyfileKeyBuf_1; // [sp+8h] [bp-20h]
int createFlag_1; // [sp+Ch] [bp-1Ch]
int v20; // [sp+18h] [bp-10h]
rbroot_1 = rbroot;
find = 0;
curNode = rbroot->root;
fatherNode = &g_inotify_node_NoValidFlag;
createFlag_1 = createFlag;
inotifyfileKeyBuf_1 = inotifyfileKeyBuf;
while ( curNode != (struct RBTree *)&g_inotify_node_NoValidFlag )
{
if ( find )
goto LABEL_35;
result = ((int (__fastcall *)(int, struct FileWatchKey *, int))rbroot_1->fun)(
inotifyfileKeyBuf_1,
curNode->keybuf,
rbroot_1->const_0);
if ( result >= 0 )
{
if ( result )
{
tempNode = curNode->right; // 没找到,目标节点比当前节点大
}
else
{
tempNode = curNode; // 找到了
find = 1;
}
}
else
{
tempNode = curNode->left; // 没找到,比当前节点小
}
fatherNode = (void **)&curNode->left;
curNode = tempNode;
}
if ( find || !createFlag_1 || (newNode = (struct RBTree *)malloc(0x14u)) == 0 )
LABEL_35:
JUMPOUT(__CS__, v20); // 找到了或没找到但是不创建新节点,则返回
newNode->parent = (struct RBTree *)fatherNode;// 创建新节点,初始化
newNode->keybuf = (struct FileWatchKey *)inotifyfileKeyBuf_1;
if ( fatherNode == &g_inotify_node_NoValidFlag )
{
rbroot_1->root = newNode; // 更新根节点
}
else if ( ((int (__fastcall *)(int, void *, int))rbroot_1->fun)(inotifyfileKeyBuf_1, fatherNode[4], rbroot_1->const_0) >= 0 )
{
fatherNode[1] = newNode; // 比父节点大 更新父节点右节点
}
else
{
*fatherNode = newNode; // 比父节点小更新父节点的右节点
}
newNode_1 = newNode;
newNode->left = (struct RBTree *)&g_inotify_node_NoValidFlag;// 初始化新节点
newNode->right = (struct RBTree *)&g_inotify_node_NoValidFlag;
newNode->color = 1; // 先设置为红色
while ( 1 )
{
while ( 1 )
{
rootHead = rbroot_1->root;
if ( newNode_1 == rootHead || (parent = newNode_1->parent, parent->color != 1) )
{
rootHead->color = 0;// 新节点是跟节点 或者新节点的父节点颜色不是红色,则将当前节点设置成黑色并退出
goto LABEL_35;
}
gparent = parent->parent;
v14 = gparent->left;
if ( parent == gparent->left )
break;
if ( v14->color == 1 )
{
parent->color = 0;
v14->color = 0;
newNode_1->parent->parent->color = 1;
LABEL_31:
newNode_1 = newNode_1->parent->parent;
}
else
{
root = (struct RBTree *)&rbroot_1->root;
if ( newNode_1 == parent->left )
{
rbtree_left_rotate(root, parent);
newNode_1 = parent;
}
newNode_1->parent->color = 0;
newNode_1->parent->parent->color = 1;
rbtree_right_rotate(root, newNode_1->parent->parent);
}
}
v15 = gparent->right;
if ( v15->color == 1 )
{
parent->color = 0;
v15->color = 0;
newNode_1->parent->parent->color = 1;
goto LABEL_31;
}
v16 = (struct RBTree *)&rbroot_1->root;
if ( newNode_1 == parent->right )
{
rbtree_right_rotate(v16, parent);
newNode_1 = parent;
}
newNode_1->parent->color = 0;
newNode_1->parent->parent->color = 1;
rbtree_left_rotate(v16, newNode_1->parent->parent);
}
}
}
query_insert_node函数进行如下操作:
上面完成了getinotifyListByWDnode函数的分析,继续分析insertNewNode。
int __fastcall insertNewNode(int inotifyfileKeyBuf, int a2)
{
int result; // r0
int v3; // r0
if ( a2 && (query_insert_node(1, inotifyfileKeyBuf, (struct RBRoot *)a2), (void **)v3 != &g_inotify_node_NoValidFlag) )
result = *(_DWORD *)(v3 + 16);
else
result = 0;
return result;
}
该函数调用query_insert_node进行新节点的插入操作。
至此函数inotify_add_watchByPid分析完了。
下面看看watchAllTask_threadProc函数的工作。
void __fastcall __noreturn watchAllTask_threadProc(int *pfatherPid)
{
struct dirent *v1; // r5
const char *v2; // r5
int v3; // r0
int threadID; // r0
DIR *dirp; // [sp+4h] [bp-2CCh]
int pfatherPid_1; // [sp+Ch] [bp-2C4h]
int dot; // [sp+14h] [bp-2BCh]
int dotdot; // [sp+18h] [bp-2B8h]
char v9; // [sp+1Ch] [bp-2B4h]
char v10; // [sp+20h] [bp-2B0h]
char v11; // [sp+21h] [bp-2AFh]
char v12; // [sp+22h] [bp-2AEh]
char v13; // [sp+23h] [bp-2ADh]
char v14; // [sp+24h] [bp-2ACh]
char v15; // [sp+25h] [bp-2ABh]
void (__noreturn *pthread_exit)(); // [sp+28h] [bp-2A8h]
char v17; // [sp+38h] [bp-298h]
__int16 v18; // [sp+48h] [bp-288h]
char v19; // [sp+A0h] [bp-230h]
char v20; // [sp+A1h] [bp-22Fh]
char v21; // [sp+A2h] [bp-22Eh]
char v22; // [sp+A3h] [bp-22Dh]
char v23; // [sp+A4h] [bp-22Ch]
char v24; // [sp+A5h] [bp-22Bh]
char v25; // [sp+A6h] [bp-22Ah]
char v26; // [sp+A7h] [bp-229h]
char v27; // [sp+A8h] [bp-228h]
char v28; // [sp+A9h] [bp-227h]
char v29; // [sp+AAh] [bp-226h]
char v30; // [sp+ABh] [bp-225h]
char v31; // [sp+ACh] [bp-224h]
char v32; // [sp+ADh] [bp-223h]
char v33; // [sp+AEh] [bp-222h]
char v34; // [sp+AFh] [bp-221h]
char v35; // [sp+B0h] [bp-220h]
char proctaskString; // [sp+B4h] [bp-21Ch]
char v37; // [sp+1B4h] [bp-11Ch]
pfatherPid_1 = *pfatherPid;
free(pfatherPid);
memset(&pthread_exit, 0, 0x10u);
pthread_exit = pthread_exit_0;
sigaction(10, (const struct sigaction *)&pthread_exit, 0);
memset(&v19, 0, 0x12u);
v22 = 30;
v23 = 28;
v24 = 1;
v25 = 13;
v27 = 75;
v28 = 2;
v29 = 10;
v31 = 26;
v33 = 29;
v20 = -88;
v34 = 5;
v21 = 65;
v26 = 65;
v30 = 65;
v32 = 15;
v35 = 65;
((void (__fastcall *)(char *))DecodeString9)(&v19);// /proc/%ld/task/
sprintf(&proctaskString, &v19, pfatherPid_1);
while ( 1 )
{
do
dirp = opendir(&proctaskString);
while ( !dirp );
while ( 1 )
{
v1 = readdir(dirp);
if ( !v1 )
break;
dot = 0;
*(_WORD *)((char *)&dot + 1) = -25275;
((void (__fastcall *)(int *, signed int, signed int))DecodeString9)(&dot, 1, 246);// .
dotdot = 0;
*(_WORD *)((char *)&dotdot + 1) = -21672;
v2 = &v1->d_name[8];
v9 = 0;
HIBYTE(dotdot) = -85;
((void (__fastcall *)(int *, signed int, signed int))DecodeString9)(&dotdot, 2, 221);// ..
if ( strcmp(v2, (const char *)&dot) )
{
if ( strcmp(v2, (const char *)&dotdot) )
{
memset(&v37, 0, 0x100u);
memset(&v10, 0, 7u);
v11 = 23;
v12 = -127;
v14 = -127;
v13 = -41;
v15 = -41;
((void (__fastcall *)(char *, signed int, signed int))DecodeString9)(&v10, 4, 179);// %s%s
sprintf(&v37, &v10, &proctaskString, v2);// /proc/15557/task/15557
if ( lstat(&v37, (struct stat *)&v17) != -1 && (v18 & 0xF000) == 0x4000 )
{
v3 = atoi(v2);
inotify_add_watchByPid(v3);
threadID = atoi(v2);
inotify_add_watchByTid(pfatherPid_1, threadID);
}
}
}
}
closedir(dirp);
sleep(2u);
}
}
}
思考:从上面可以看到线程会持续的对应用的所有线程下的mem与pagemap文件进行监控,是否可以在步骤13直接线程结束?
这样是不行的,如果此时结束,对于后面新创建的线程则不能纳入到本进程中。对于已经被watch的文件再次watch将返回上次的wd,引用次数会加1。
这里面可能有个小问题是:如果线程被删除了则对应的红黑树链表的节点不会被删除,造成内存泄漏。极端情况应用一致持续不断的创建线程然后线程2秒后销毁,运行一段时间后内存会崩溃。
下面看一下inotify_add_watchByTid函数。
int __fastcall inotify_add_watchByTid(int fatherpid, int tid)
{
int fatherpid_1; // ST00_4
int tid_1; // ST04_4
int v4; // ST00_4
int v5; // ST04_4
char s; // [sp+8h] [bp-158h]
char v8; // [sp+9h] [bp-157h]
char v9; // [sp+Ah] [bp-156h]
char v10; // [sp+Bh] [bp-155h]
char v11; // [sp+Ch] [bp-154h]
char v12; // [sp+Dh] [bp-153h]
char v13; // [sp+Eh] [bp-152h]
char v14; // [sp+Fh] [bp-151h]
char v15; // [sp+10h] [bp-150h]
char v16; // [sp+11h] [bp-14Fh]
char v17; // [sp+12h] [bp-14Eh]
char v18; // [sp+13h] [bp-14Dh]
char v19; // [sp+14h] [bp-14Ch]
char v20; // [sp+15h] [bp-14Bh]
char v21; // [sp+16h] [bp-14Ah]
char v22; // [sp+17h] [bp-149h]
char v23; // [sp+18h] [bp-148h]
char v24; // [sp+19h] [bp-147h]
char v25; // [sp+1Ah] [bp-146h]
char v26; // [sp+1Bh] [bp-145h]
char v27; // [sp+1Ch] [bp-144h]
char v28; // [sp+1Dh] [bp-143h]
char v29; // [sp+1Eh] [bp-142h]
char v30; // [sp+1Fh] [bp-141h]
char v31; // [sp+24h] [bp-13Ch]
char v32; // [sp+25h] [bp-13Bh]
char v33; // [sp+26h] [bp-13Ah]
char v34; // [sp+27h] [bp-139h]
char v35; // [sp+28h] [bp-138h]
char v36; // [sp+29h] [bp-137h]
char v37; // [sp+2Ah] [bp-136h]
char v38; // [sp+2Bh] [bp-135h]
char v39; // [sp+2Ch] [bp-134h]
char v40; // [sp+2Dh] [bp-133h]
char v41; // [sp+2Eh] [bp-132h]
char v42; // [sp+2Fh] [bp-131h]
char v43; // [sp+30h] [bp-130h]
char v44; // [sp+31h] [bp-12Fh]
char v45; // [sp+32h] [bp-12Eh]
char v46; // [sp+33h] [bp-12Dh]
char v47; // [sp+34h] [bp-12Ch]
char v48; // [sp+35h] [bp-12Bh]
char v49; // [sp+36h] [bp-12Ah]
char v50; // [sp+37h] [bp-129h]
char v51; // [sp+38h] [bp-128h]
char v52; // [sp+39h] [bp-127h]
char v53; // [sp+3Ah] [bp-126h]
char v54; // [sp+3Bh] [bp-125h]
char v55; // [sp+3Ch] [bp-124h]
char v56; // [sp+3Dh] [bp-123h]
char v57; // [sp+3Eh] [bp-122h]
char v58; // [sp+3Fh] [bp-121h]
char v59; // [sp+44h] [bp-11Ch]
fatherpid_1 = fatherpid;
tid_1 = tid;
memset(&s, 0, 0x19u);
v10 = 5;
v11 = 7;
v19 = 1;
v12 = 26;
v8 = -4;
v20 = 20;
v17 = 17;
v26 = 17;
v9 = 90;
v14 = 90;
v18 = 90;
v21 = 6;
v23 = 90;
v27 = 90;
v15 = 80;
v16 = 25;
v24 = 80;
v25 = 25;
v29 = 16;
v13 = 22;
v22 = 30;
v28 = 24;
v30 = 24;
((void (__fastcall *)(char *))DecodeString9)(&s);
sprintf(&v59, &s, fatherpid_1, tid_1, fatherpid_1, tid_1);// /proc/16709/task/16709/mem
inotify_add_watch_insert_node((int)&v59, 0xFFFu);
memset(&v31, 0, 0x1Du);
v35 = -9;
v36 = -22;
v37 = -26;
v32 = 99;
v40 = -23;
v45 = -10;
v33 = -86;
v38 = -86;
v42 = -86;
v47 = -86;
v51 = -86;
v41 = -31;
v46 = -18;
v54 = -30;
v43 = -15;
v49 = -23;
v55 = -32;
v34 = -11;
v44 = -28;
v50 = -31;
v52 = -11;
v53 = -28;
v57 = -28;
v58 = -11;
v56 = -24;
v39 = -96;
v48 = -96;
((void (__fastcall *)(char *, signed int, signed int))DecodeString9)(&v31, 26, 230);
sprintf(&v59, &v31, v4, v5);
return inotify_add_watch_insert_node((int)&v59, 0xFFFu);
}
}
这个函数相对比较简单。
至此将目标文件纳入到监控中的相关处理已经分析完了,下面看发生相关事件是的处理流程。
int __fastcall read_filewatch_event1(int a1_FF, int const_1)
{
char *v2; // r1
char *v3; // r3
struct timeval *timeout; // r4
int inotify_init; // r0
int v7; // r0
int v8; // r3
int a1_FF_1; // [sp+8h] [bp-20h]
int const_1_1; // [sp+Ch] [bp-1Ch]
if ( const_1 <= 0 )
return 0;
a1_FF_1 = a1_FF;
const_1_1 = const_1;
setjmp((struct __jmp_buf_tag *)&unk_53D04);
g_fileWatch_errno = 0;
if ( dword_53E04 )
{
if ( dword_53E04 <= dword_53E08 - 16 )
{
v2 = (char *)&dword_53E0C + dword_53E04;
dword_63E0C = (int)&dword_53E0C + dword_53E04;
v3 = (char *)&(*(struct inotify_event **)((char *)&dword_53E0C + dword_53E04 + 12))[1] + dword_53E04;
dword_53E04 = (int)v3;
if ( v3 == (char *)dword_53E08 )
{
dword_53E04 = 0;
}
else if ( (signed int)v3 > dword_53E08 )
{
dword_53E08 = (char *)&dword_53E0C + dword_53E08 - v2;
memcpy(&dword_53E0C, v2, dword_53E08);
return read_filewatch_event1(a1_FF_1, const_1_1);
}
if ( g_inotify_init_flag1 )
p9E01CAAA70B3E111F16A18AB1BC2AB55((int *)v2);
return dword_63E0C;
}
}
else
{
dword_53E08 = dword_53E04;
}
g_FF = a1_FF_1;
timeout = (struct timeval *)&g_FF;
dword_53D00 = 0;
if ( a1_FF_1 <= 0 )
timeout = 0;
dword_63E10 = (int)timeout;
memset(&g_fds, 0, 0x80u);
inotify_init = g_inotify_init;
*((_DWORD *)&unk_63DA8 + (g_inotify_init >> 5) + 0x1B) = 1 << (g_inotify_init & 0x1F);
v7 = select(inotify_init + 1, (fd_set *)&g_fds, 0, 0, timeout);// //监控fd的事件。当有事件发生时,返回值>0
g_slect_returnValue = v7;
if ( v7 < 0 )
goto LABEL_21;
if ( !v7 )
return 0;
while ( 1 )
{
v7 = ioctl(g_inotify_init, 0x541Bu, &g_fileWatch_event_MaxLen);
g_slect_returnValue = v7;
if ( v7 )
break;
if ( g_fileWatch_event_MaxLen >= (unsigned int)(16 * const_1_1) )
goto LABEL_20;
}
if ( v7 == -1 )
goto LABEL_21;
LABEL_20:
v7 = read(g_inotify_init, &(&dword_53E0C)[4 * dword_53E08], 0x10000 - dword_53E08);
g_fileWatch_event_readLen = v7;
if ( v7 < 0 )
{
LABEL_21:
g_fileWatch_errno = *(_DWORD *)_errno(v7);
return 0;
}
if ( !v7 )
return 0;
dword_53E08 += v7;
dword_63E0C = (int)&dword_53E0C;
v8 = dword_53E18 + 0x10;
if ( dword_53E18 + 0x10 == dword_53E08 )
v8 = 0;
dword_53E04 = v8;
if ( g_inotify_init_flag1 )
p9E01CAAA70B3E111F16A18AB1BC2AB55((int *)&dword_53E0C);
return dword_63E0C;
}
read_filewatch_event函数步骤如下:
int __fastcall filewatch_Delete(int fatherPid)
{
int fatherPid_1; // r7
int v3; // [sp+0h] [bp-140h]
char v4; // [sp+4h] [bp-13Ch]
char v5; // [sp+5h] [bp-13Bh]
char v6; // [sp+6h] [bp-13Ah]
char v7; // [sp+7h] [bp-139h]
char v8; // [sp+8h] [bp-138h]
char v9; // [sp+9h] [bp-137h]
char v10; // [sp+Ah] [bp-136h]
char v11; // [sp+Bh] [bp-135h]
char v12; // [sp+Ch] [bp-134h]
char v13; // [sp+Dh] [bp-133h]
char v14; // [sp+Eh] [bp-132h]
char v15; // [sp+10h] [bp-130h]
char v16; // [sp+11h] [bp-12Fh]
char v17; // [sp+12h] [bp-12Eh]
char v18; // [sp+13h] [bp-12Dh]
char v19; // [sp+14h] [bp-12Ch]
char v20; // [sp+15h] [bp-12Bh]
char v21; // [sp+16h] [bp-12Ah]
char v22; // [sp+17h] [bp-129h]
char v23; // [sp+18h] [bp-128h]
char v24; // [sp+19h] [bp-127h]
char v25; // [sp+1Ah] [bp-126h]
char v26; // [sp+1Bh] [bp-125h]
char v27; // [sp+1Ch] [bp-124h]
char v28; // [sp+1Dh] [bp-123h]
char v29; // [sp+1Eh] [bp-122h]
char v30; // [sp+1Fh] [bp-121h]
char v31; // [sp+20h] [bp-120h]
char v32; // [sp+21h] [bp-11Fh]
char v33; // [sp+22h] [bp-11Eh]
char v34; // [sp+24h] [bp-11Ch]
fatherPid_1 = fatherPid;
memset(&v3, 0, 0x10u);
v4 = -75;
v5 = -88;
v6 = -92;
v8 = -30;
v9 = -85;
BYTE1(v3) = 62;
v10 = -93;
HIWORD(v3) = -18456;
v7 = -24;
v11 = -24;
v13 = -94;
v12 = -86;
v14 = -86;
((void (__fastcall *)(int *, signed int, signed int))DecodeString9)(&v3, 13, 249);
sprintf(&v34, (const char *)&v3, fatherPid_1);// /proc/1340/mem
filewatch_DeleteByFile((int)&v34);
memset(&v15, 0, 0x14u);
v19 = 10;
v20 = 23;
v21 = 27;
v23 = 93;
v24 = 20;
v25 = 28;
v29 = 31;
v17 = 87;
v22 = 87;
v26 = 87;
v30 = 29;
v16 = -111;
v28 = 25;
v31 = 21;
v32 = 25;
v18 = 8;
v27 = 8;
v33 = 8;
((void (__fastcall *)(char *, signed int, signed int))DecodeString9)(&v15, 17, 233);
sprintf(&v34, &v15, fatherPid_1);
return filewatch_DeleteByFile((int)&v34);
}
filewatch_Delete函数步骤如下:
filewatch_DeleteNode(keybuf, g_fileWatch_wd_root);
filewatch_DeleteNode(keybuf, g_fileWatch_name_root);
filewatch_rm(keybuf)
freeKeyBuf(keybuf);
该函数调用步骤如下:
函数filewatch_DeleteNode如下:
nt __fastcall filewatch_DeleteNode(struct FileWatchKey *keybuf, struct RBRoot *rbroot)
struct RBRoot *v3; // r6
int v4; // r0
int v5; // r7
void **v6; // r5
void **v7; // r4
void **v8; // r3
struct RBTree **v9; // r2
struct RBTree *v10; // r1
struct RBTree *v11; // r2
struct RBTree *v12; // r3
struct RBTree *v13; // r2
int v14; // [sp+4h] [bp-1Ch]
if ( !rbroot )
return 0;
v3 = rbroot;
query_insert_node(0, (int)keybuf, rbroot);
v5 = v4;
if ( (void **)v4 == &g_inotify_node_NoValidFlag )
return 0;
v6 = (void **)v4;
v14 = *(_DWORD *)(v4 + 16);
if ( *(void ***)v4 != &g_inotify_node_NoValidFlag && *(void ***)(v4 + 4) != &g_inotify_node_NoValidFlag )
v6 = sub_28F7C((void **)v4);
v7 = (void **)*v6;
if ( *v6 == &g_inotify_node_NoValidFlag )
v7 = (void **)v6[1];
v7[2] = v6[2];
v8 = (void **)v6[2];
if ( v8 == &g_inotify_node_NoValidFlag )
{
v3->root = (struct RBTree *)v7;
}
else if ( v6 == *v8 )
{
*v8 = v7;
}
else
{
v8[1] = v7;
}
if ( v6 != (void **)v5 )
*(_DWORD *)(v5 + 16) = v6[4];
if ( !v6[3] )
{
while ( 1 )
{
if ( v7 == (void **)v3->root || v7[3] )
{
v7[3] = 0;
break;
}
v9 = (struct RBTree **)v7[2];
v10 = *v9;
if ( v7 == (void **)*v9 )
{
v10 = v9[1];
if ( v10->color == 1 )
{
v10->color = 0;
*((_DWORD *)v7[2] + 3) = 1;
rbtree_right_rotate((struct RBTree *)&v3->root, (struct RBTree *)v7[2]);
v10 = (struct RBTree *)*((_DWORD *)v7[2] + 1);
}
v11 = v10->right;
if ( v10->left->color || v11->color )
{
if ( !v11->color )
{
v10->left->color = 0;
v10->color = 1;
rbtree_left_rotate((struct RBTree *)&v3->root, v10);
v10 = (struct RBTree *)*((_DWORD *)v7[2] + 1);
}
v10->color = *((_DWORD *)v7[2] + 3);
*((_DWORD *)v7[2] + 3) = 0;
v10->right->color = 0;
rbtree_right_rotate((struct RBTree *)&v3->root, (struct RBTree *)v7[2]);
goto LABEL_35;
}
ABEL_31:
v10->color = 1;
v7 = (void **)v7[2];
}
else
{
if ( v10->color == 1 )
{
v10->color = 0;
*((_DWORD *)v7[2] + 3) = 1;
rbtree_left_rotate((struct RBTree *)&v3->root, (struct RBTree *)v7[2]);
v10 = *(struct RBTree **)v7[2];
}
v12 = v10->right;
v13 = v10->left;
if ( !v12->color && !v13->color )
goto LABEL_31;
if ( !v13->color )
{
v12->color = 0;
v10->color = 1;
rbtree_right_rotate((struct RBTree *)&v3->root, v10);
v10 = *(struct RBTree **)v7[2];
}
v10->color = *((_DWORD *)v7[2] + 3);
*((_DWORD *)v7[2] + 3) = 0;
v10->left->color = 0;
rbtree_left_rotate((struct RBTree *)&v3->root, (struct RBTree *)v7[2]);
LABEL_35:
v7 = (void **)&v3->root->left;
}
}
}
free(v6);
return v14;
}
filewatch_DeleteNode函数进行节点删除,以及红黑树调整相关操作。下面看一下函数filewatch_rm。
unsigned int __fastcall filewatch_rm(struct FileWatchKey *keybuf)
int v1; // r1
int v2; // r0
signed int v3; // r3
v1 = keybuf->wd;
g_fileWatch_errno = 0;
v2 = inotify_rm_watch(g_inotify_init, v1);
v3 = 1;
if ( v2 < 0 )
{
v3 = 0;
g_fileWatch_errno = v2;
}
return v3;
调用inotify_rm_watch进行wd移除。下面看一下freeKeyBuf函数。
void __fastcall freeKeyBuf(void *ptr)
{
void *v1; // r4
void *v2; // r0
v1 = ptr;
v2 = *(void **)ptr;
if ( v2 )
free(v2);
free(v1);
}
freeKeyBuf函数进行内存释放工作。
至此删除文件监控分析结束。
下面将开始进行终止线程和父进程相关操作。
pthread_kill(newthread, 10);
killProcess(fatherPid, 9);
先将监控所有task的线程结束。然后调用killProcess结束父进程。
unsigned int __fastcall killProcess(__pid_t a1, int a2)
{
unsigned int result; // r0
result = linux_eabi_syscall(__NR_kill, a1, a2);
return result;
}
其实 bypass文件监控的方法很多,具有可移植性的方法的是HOOK inotify_add_watch
对于防守方可以监控inotify_add_watch函数是否HOOK。
样本下载连接
学习群号:211730239