梆梆加固之防内存dump分析

声明

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

参考资料

  1. https://bbs.pediy.com/thread-206293.htm
  2. https://github.com/parkerpeng/DroidAnti
  3. http://blog.csdn.net/cool_way/article/details/22827433
  4. http://blog.csdn.net/myarrow/article/details/7096460
  5. http://blog.csdn.net/guojin08/article/details/9454467
  6. http://blog.sina.com.cn/s/blog_628cc2b70101c8zu.html
  7. https://www.cnblogs.com/skywang12345/p/3624177.html

编写目的

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

写在前面

内存dump介绍

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

  1. 讨论android加固防内存dump的技术及vmp壳的防护强度:
    https://bbs.pediy.com/thread-206293.htm

  2. android应用反调试以及反内存dump代码收集:
    https://github.com/parkerpeng/DroidAnti

inotify主要功能

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

  1. inotify不生效问题:
    http://blog.csdn.net/cool_way/article/details/22827433

  2. Linux inotify功能及实现原理:
    http://blog.csdn.net/myarrow/article/details/7096460

/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

inotify防内存dump

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

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

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

    BB对防内存dump的介绍

    我们先看一下BB在防篡改技术的介绍,下图是BB官网上关于防篡改的介绍。梆梆官网介绍
    这里写图片描述
    从官网上无法判断其采取何种措施,下面通过实际逆向分析来学习其相关防护策略。 首先介绍下其使用到的数据结构。

算法与数据结构

防内存dump用到了红黑树的数据结构。下面是关于红黑树数据结构的介绍。
红黑树之 C语言的实现:
https://www.cnblogs.com/skywang12345/p/3624177.html

自定义的结构变量

FileWatchKey结构

其定义如下:

typedef struct FileWatchKey
{
    char* fileName;                //监控的文件名
    int      wd;                   //inotify_add_watch 返回值
}FileWatchKey;

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

RBTree数据结构

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

typedef struct  RBTree
{
    RBTree*    left;                     //红黑树左节点
    RBTree* right;                       //红黑树右节点
    RBTree* parent;                      //父节点
    int        color;                    //红节点 黑节点
    FileWatchKey* keybuf;                //key
} RBTree;

RBRoot数据结构

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

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

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

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。

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在左节点分支。

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在左节点分支。

inotify_add_watchByPid

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

该函数做了如下事情:

  1. 调用DecodeString9解密字符串“/proc/%ld/mem”;
  2. 格式化字符串“ /proc/pid/mem”;
  3. 调用inotify_add_watch_insert_node 将对应文件纳入到监控中;
  4. 调用DecodeString9解密字符串“/proc/%ld/pagemap”;
  5. 格式化字符串“ /proc/pid/pagemap”;

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

inotify_add_watch_insert_node函数

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

本函数有如下步骤:

  1. 对输入的字符串数组进行inotify_add_watch;
  2. 调用JudeFileIsDir判断是否是目录
  3. 调用inotifyList_user_add_node将wd于文件名写入对应的红黑树中。
JudeFileIsDir函数

该函数去混淆后如下:

bool  JudeFileIsDir(char *file) 
{ 
  bool result; // r0 
  if(lstat(file, &g_fileStatStruct)<0) 
      return false; 
  return (g_fileStatStruct.st_mode & 0xF000) - 0x4000 <= 0;   
}
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. 如果找到则返回对应节点;、如果没找到,并且不创建新节点则返回0;
  3. malloc一个新的RBTree;
  4. 初始化其父节点;
  5. 初始化新的RBTree;
  6. 调用 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函数的工作。

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

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

这个函数相对比较简单。

里程碑

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

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。

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

filewatch_DeleteByFile函数

filewatch_DeleteNode(keybuf, g_fileWatch_wd_root); 
filewatch_DeleteNode(keybuf, g_fileWatch_name_root);
filewatch_rm(keybuf) 
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。

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

其实 bypass文件监控的方法很多,具有可移植性的方法的是HOOK inotify_add_watch
对于防守方可以监控inotify_add_watch函数是否HOOK。
样本下载连接
学习群号:211730239

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值