 声明(打字好累)

本文仅限于技术讨论,不得用于非法途径,后果自负。

 参考资料https://bbs.pediy.com/thread-206293.htm

https://github.com/parkerpeng/DroidAnti

http://blog.csdn.net/cool_way/article/details/22827433

http://blog.csdn.net/myarrow/article/details/7096460

http://blog.csdn.net/guojin08/article/details/9454467

http://blog.sina.com.cn/s/blog_628cc2b70101c8zu.html

https://www.cnblogs.com/skywang12345/p/3624177.html

 编写目的

防内存dump比较笼统,本篇只介绍使用inotify相关实现(以BB为例)。

写在前面

1.1内存dump介绍

关于内存dump相关介绍,请参考如下链接:

1) 讨论android加固防内存dump的技术及vmp壳的防护强度:

https://bbs.pediy.com/thread-206293.htm。

2) android应用反调试以及反内存dump代码收集:

https://github.com/parkerpeng/DroidAnti。

1.2 inotify主要功能

关于inotify相关介绍,请参考如下链接:

1) inotify不生效问题:

http://blog.csdn.net/cool_way/article/details/22827433;

2) Linux inotify功能及实现原理:

http://blog.csdn.net/myarrow/article/details/7096460。

1.3 /proc/pid/mem 与/proc/pid/pagemep

1)使用/proc/pid/mem访问其他进程的内存变量:

http://blog.csdn.net/guojin08/article/details/9454467

2)pagemap的解读:

http://blog.sina.com.cn/s/blog_628cc2b70101c8zu.html

1.4 inotify防内存dump从上面的知识可知,通过对目标进程文件/proc/pid/mem文件操作,可以获得其内存的数据。

而inotify可以监控文件系统的变化,当文件被打开、删除,读写等操作时,同时用户相应变化。

因此可以通过监控/proc/pid/mem 与/proc/pid/pagemep来防止内存dump。

1.5 BB对防内存dump的介绍

我们先看一下BB在防篡改技术的介绍,下图是BB官网上关于防篡改的介绍。(https://www.bangcle.com/products/productindex?product_id=1)

389688bd428bd69cc2f012efc91e6dce.png

#从官网上无法判断其采取何种措施,下面通过实际逆向分析来学习其相关防护策略。 首先介绍下其使用到的数据结构。

2算法与数据结构防内存dump用到了红黑树的数据结构。下面是关于红黑树数据结构的介绍。

1)红黑树(二)之 C语言的实现:

https://www.cnblogs.com/skywang12345/p/3624177.html

2.1自定义的结构变量

2.1.1 FileWatchKey结构

其定义如下:

typedef struct FileWatchKey

{

char* fileName; //监控的文件名

int wd; //inotify_add_watch 返回值

}FileWatchKey;

FileWatchKey结构用于保存监控的文件名以及对应的inotify_add_watch句柄。

2.1.2 RBTree数据结构这个结构用于保存所有监控文件的信息,其定义如下:typedef struct RBTree

{

RBTree* left; //红黑树左节点

RBTree* right; //红黑树右节点

RBTree* parent; //父节点

int color; //红节点 黑节点

FileWatchKey* keybuf; //key

} RBTree;

2.1.3 RBRoot数据结构本结构用于定义头结点以及用于节点比较的函数指针。typedef struct RBRoot

{

void* compareFun; //用于红黑树比较的函数

int const_0; //常量

RBTree* treeHead; //红黑树头结点

}RBRoot;

3 逆向分析流程

3.1 创建线程用于文件监控

该函数位于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为真正的处理函数。

3.2 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;

该函数步骤如下:

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、线程退出。

3.3 inotify_init_function函数

该函数用于初始化文件监控相关信息。本函数经过了混淆,去除如下:

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。

3.3.1 is_same_inotify_wd函数

该函数用于比较红黑树的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在左节点分支。

3.3.2 is_same_inotify_filename函数

该函数用于比较红黑树的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在左节点分支。

3.4 inotify_add_watchByPidinotify_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);

}

该函数做了如下事情:

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 将对应文件纳入到监控中;

3.4.1 DecodeString9函数字符串解密函数,如下: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;

}

3.4.2 inotify_add_watch_insert_node函数1. signed int __fastcall inotify_add_watch_insert_node(int procmemString, uint32_t mask_2)

2. {

3. char *procmemString_1_1; // r6

4. int procmemString_1; // r4

5. const char *v4; // r1

6. int fd; // r0

7. int v6; // r4

8. int v7; // r5

9. uint32_t mask; // [sp+4h] [bp-1Ch]

10.

11. procmemString_1 = procmemString;

12. mask = mask_2;

13. g_fileWatch_errno = 0;

14. for ( g_watchIndex = 0; ; ++g_watchIndex )

15. {

16. v4 = *(const char **)(4 * g_watchIndex + procmemString_1);

17. if ( !v4 )

18. return 1;

19. fd = inotify_add_watch(g_inotify_init, v4, mask);

20. g_fileWatch_fd = fd;

21. if ( fd < 0 )

22. break;

23. if ( !JudeFileIsDir(*(char **)(4 * g_watchIndex + procmemString_1))

24. || (v7 = *(_DWORD *)(4 * g_watchIndex + procmemString_1),

25. *(_BYTE *)(v7 + strlen(*(_DWORD *)(4 * g_watchIndex + procmemString_1)) - 1) == '/') )

26. {

27. procmemString_1_1 = strdup(*(const char **)(4 * g_watchIndex + procmemString_1));// 是文件或者是目录同时最后一个字符是\

28. }

29. inotifyList_user_add_node(g_fileWatch_fd, procmemString_1_1);

30. free(procmemString_1_1);

31. }

32. v6 = 0;

33. if ( fd == -1 )

34. g_fileWatch_errno = *(_DWORD *)_errno(-1);

35. return v6;

36. }

本函数有如下步骤:

1、对输入的字符串数组进行inotify_add_watch;

2、调用JudeFileIsDir判断是否是目录

2、调用inotifyList_user_add_node将wd于文件名写入对应的红黑树中。

3.4.2.1 JudeFileIsDir函数该函数去混淆后如下:bool JudeFileIsDir(char *file)

{

bool result; // r0

if(lstat(file, &g_fileStatStruct)<0)

return false;

return (g_fileStatStruct.st_mode & 0xF000) - 0x4000 <= 0;

}

3.4.2.2 inotifyList_user_add_node函数_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;

}

本函数有如下步骤:

1、调用getinotifyListByWDnode判断对应fd是否已经在红黑树中了;

2、如果在,则直接返回;

3、如果不在则调用insertNewNode插入到对应红黑树中。

下面我们看一getinotifyListByWDnode函数

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函数进行如下操作:

1、遍历二叉树进行查找进行节点查找;

2、如果找到则返回对应节点;

3、如果没找到,并且不创建新节点则返回0;

4、malloc一个新的RBTree;

5、初始化其父节点;

6、初始化新的RBTree;

7、调用 rbtree_left_rotate和rbtree_right_rotate对红黑树进行修正。

上面完成了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函数的工作。

3.5 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);

}

}

}

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。

思考:从上面可以看到线程会持续的对应用的所有线程下的mem与pagemap文件进行监控,是否可以在步骤13直接线程结束?

这样是不行的,如果此时结束,对于后面新创建的线程则不能纳入到本进程中。对于已经被watch的文件再次watch将返回上次的wd,引用次数会加1。

这里面可能有个小问题是:如果线程被删除了则对应的红黑树链表的节点不会被删除,造成内存泄漏。极端情况应用一致持续不断的创建线程然后线程2秒后销毁,运行一段时间后内存会崩溃。

下面看一下inotify_add_watchByTid函数。

3.5.1 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);

}

}

这个函数相对比较简单。

3.6 里程碑至此将目标文件纳入到监控中的相关处理已经分析完了,下面看发生相关事件是的处理流程。

3.7 read_filewatch_event函数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函数步骤如下:

1、调用select函数对inotify初始化句柄进行阻塞。当发生事件时,则线程唤醒;

2、调用ioctl函数获得对应事件的长度;

3、调用read函数将发生的事件信息读取到全局变量中。

4、返回对应的事件BUF。

当发生事件时,就开始进行事件处理流程,首先调用filewatch_Delete清除watch。

3.8 filewatch_Delete函数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函数步骤如下:

1、格式化字符/proc/pid/mem;

2、调用 filewatch_DeleteByFile删除/proc/pid/mem的watch;

3、格式化字符/proc/pid/pagemap;

4、调用 filewatch_DeleteByFile删除/proc/pid/pagemap的watch;

可能存在问题:只删除了进程的对应watch,对于/proc/tid/相关并没有删除。同时task目录下的也没有删除。

下面看一下filewatch_DeleteByFile函数。

3.8.1 filewatch_DeleteByFile函数1. filewatch_DeleteNode(keybuf, g_fileWatch_wd_root);

2. filewatch_DeleteNode(keybuf, g_fileWatch_name_root);

3. filewatch_rm(keybuf)

4. freeKeyBuf(keybuf);

该函数调用步骤如下:

1、调用filewatch_DeleteNode删除wd相关watch;

2、调用filewatch_DeleteNode删除filename相关watch;

3、调用filewatch_rm移除wd;

4、调用freeKeyBuf释放FileWatchKey。

函数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。

igned 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函数进行内存释放工作。

至此删除文件监控分析结束。

下面将开始进行终止线程和父进程相关操作。

3.9 进程线程终止1. pthread_kill(newthread, 10);

2. 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;

}

4 bypass其实 bypass文件监控的方法很多,具有可移植性的方法的是HOOK inotify_add_watch

对于防守方可以监控inotify_add_watch函数是否HOOK。

学习群号:211730239

上传的附件:

梆梆加固之防篡改技术剖析.pdf

(534.83kb,145次下载)

libDexHelper.so

(331.25kb,96次下载)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐