对某APP的逆向之旅(3)

来源:互联网 发布:javascript什么意思 编辑:程序博客网 时间:2024/05/20 08:22

书接上回,本次对前面解密后的so进行分析
来到start函数
这里写图片描述

进入主函数 sub_9960

首先创建本地socket,用于通信,然后while循环中,不断读取java层的消息,执行对应的功能函数。
创建socket的代码如下:

void __fastcall sub_9960(int a1, void **a2){  ......  ......  port = atoi(&s);  v6 = vars;  socket = sub_A258("127.0.0.1", port);  v8 = vars;  *(_DWORD *)(v6 + 4) = socket;  sub_A064(*(_DWORD *)(v8 + 4) > 0);  pid = getppid();  pid2 = pid;

循环处理的功能子函数如下:

void __fastcall sub_9960(int a1, void **a2){  ......  ......      do      {        v16 = (_BYTE *)*v13;        ++v13;        v28 = *v16;        v17 = (const char **)sub_9F40();        v18 = v17;        switch ( v28 )        {          case 35:            sub_A198(*(_DWORD *)(vars + 4), v29);            break;          default:            break;          case 72:            v28 = *(_DWORD *)(vars + 4);            v24 = hook(vars, v17, v31);            sub_A0B8(v28, (int)v24, 72);            break;          case 73:            v28 = *(_DWORD *)(vars + 4);            v23 = sub_ABF4();            sub_A0B8(v28, v23, 73);            break;          case 98:            v28 = *(_DWORD *)(vars + 4);            v22 = sub_AC84();            sub_A0B8(v28, v22, 98);            break;          case 99:            v28 = *(_DWORD *)(vars + 4);            v21 = sub_AAFC();            sub_A0B8(v28, v21, 99);            break;          case 112:            v28 = *(_DWORD *)(vars + 4);            v20 = sub_AB34();            sub_A0B8(v28, v20, 112);            break;          case 113:            sub_AA90(vars);            return;          case 116:            sub_B144(v17);            break;          case 117:            v28 = *(_DWORD *)(vars + 4);            v19 = sub_9FF0() != 0;            sub_A0B8(v28, v19, 117);            break;        }        ++v12;      }      while ( v12 != v15 );      if ( !v18 )        goto LABEL_3;      free(v18);      v31 = 0;      memset(&buf, 0, 0x200u);      socket2 = *(_DWORD *)(vars + 4);      if ( socket2 > 0 )        goto LABEL_4;    }  }  else  {LABEL_4:    if ( recv(socket2, &buf, 0x200u, 0) > 0 )      goto LABEL_5;  }  sub_AA90(vars);}

函数首先进入的是hook函数,该函数名是我自己修改的。
本次,我们主要分析该so函数的核心,hook的原理及实现。
来到hook函数,如下:

const char *__fastcall hook(int a1, const char **a2, int a3){  int v3; // r4@1  const char *result; // r0@2  int v5; // r0@3  int v6; // r2@3  unsigned int v7; // r3@5  v3 = a1;  if ( a3 )  {    result = *a2;    if ( *a2 )    {      v5 = atoi(result);      v6 = *(_DWORD *)(v3 + 16);      *(_DWORD *)v3 = v5;      if ( v5 == v6 )      {        result = (const char *)1;      }      else      {        result = (const char *)hookmain(v5, (_DWORD *)(v3 + 12), dword_11B08);        if ( result )        {          result = 0;        }        else        {          v7 = *(_DWORD *)(v3 + 12);          if ( v7 > 0x8000 && v7 != -1 )          {            result = (const char *)1;            *(_DWORD *)(v3 + 16) = *(_DWORD *)v3;          }        }      }    }  }  else  {    result = 0;  }  return result;}

继续跟进

int __fastcall hook(int a1, _DWORD *a2, int a3){  _DWORD *v3; // r5@1  int v4; // r6@1  int v5; // r7@1  int v6; // r5@2  char *v7; // r3@4  char v9; // [sp+8h] [bp-80h]@1  __mode_t mode; // [sp+18h] [bp-70h]@3  v3 = a2;  v4 = a1;  v5 = a3;  if ( stat(timescale_path[0], (struct stat *)&v9) )  {    v6 = -1;  }  else  {    *(_DWORD *)_errno() = 0;    chmod(timescale_path[0], (mode | 4) & 0xFFFF);    if ( v5 )      v7 = "1";    else      v7 = "0";    v6 = inject(v4, timescale_path[0], "init", v7, v3);    chmod(timescale_path[0], (unsigned __int16)mode);  }  return v6;}

可以看到,首先会修改文件的权限,然后进行注入。注意里面的函数名都是我分析后,自己修改过的
来到inject函数,代码片段如下:

 v21 = src + 15360;    v22 = get_remote_addr(v10, linker_path[0], (int)&dlopen);    v23 = get_remote_addr(v10, linker_path[0], (int)&dlsym);    v24 = get_remote_addr(v10, linker_path[0], (int)&dlclose);    dlopen_addr_s = v22;    dlsym_addr_s = v23;    dlclose_addr_s = v24;    strcpy((char *)&unk_110DC, v28);    dlopen_param1_s = (char *)&unk_110DC + v21 - (_DWORD)&inject_start_s;    strcpy((char *)&unk_111DC, v5);    dlsym_param2_s = (char *)&unk_111DC + v21 - (_DWORD)&inject_start_s;    saved_hook_addr = 0x4A0 + v21;    saved_cpsr_s = v56;    unk_112DC = *(_DWORD *)&dest;    unk_112E0 = v41;    unk_112E4 = v42;    unk_112E8 = v43;    unk_112EC = v44;    unk_112F0 = v45;    unk_112F4 = v46;    unk_112F8 = v47;    unk_112FC = v48;    unk_11300 = v49;    unk_11304 = v50;    unk_11308 = v51;    unk_1130C = v52;    unk_11310 = v53;    unk_11314 = v54;    unk_11318 = v55;    saved_r0_pc_s = 0x2A0 + v21;    v25 = strlen(s);    memcpy(&unk_113DC, s, v25 + 1);    inject_function_param_s = 0x3A0 + v21;    write_remote_memory_main(v10, v21, &inject_start_s, 0x600u);    memcpy(&src, &dest, 0x48u);    v38 = v21;    v39 = v21;    v26 = sub_B410(v10, &src);    sub_B5EC(v10);

前面为获取dlopen等系统函数的地址,这个没什么可说的,不明白的,建议上网找下hook注入方面的文章看看。
来到修改内存的函数write_remote_memory_main,如下:

signed int __fastcall write_remote_memory_main(int pid, int remote_clock_gettime, void *pMem, unsigned int length){  int remote_clock_gettime1; // r11@1  int pMem1; // r9@1  unsigned int length1; // r4@1  int pid1; // r8@1  unsigned int v9; // r10@3  int v10; // r7@3  int v11; // r4@4  int v12; // r5@4  int v13; // r5@6  int v14; // r3@8  int v15; // [sp+4h] [bp-34h]@3  __int32 dest; // [sp+Ch] [bp-2Ch]@5  remote_clock_gettime1 = remote_clock_gettime;  pMem1 = (int)pMem;  length1 = length;  pid1 = pid;  if ( write_remote_memory1(pid, pMem, length, remote_clock_gettime) == -1 )// 第一种写入内存方式失败,则执行第二种写入内存的方式,采用的是ptrace方式写入  {    v9 = length1 >> 2;                          // v9 = 2    v10 = remote_clock_gettime1;    v15 = length1 & 3;    if ( length1 >> 2 )    {      v11 = remote_clock_gettime1;      v12 = 0;      do      {        memcpy(&dest, (const void *)(v11 + pMem1 - remote_clock_gettime1), 4u);        ++v12;        ptrace(PTRACE_POKETEXT, pid1, v11, dest);// int ptrace(int request, int pid, int addr, int data);                                                // PTRACE_POKETEXT, PTRACE_POKEDATA往内存地址中写入一个字节。内存地址由addr给出。        v11 += 4;      }      while ( v12 != v9 );      v13 = 4 * v12;                            // hook的前8个字节      v10 = remote_clock_gettime1 + v13;      pMem1 += v13;    }    if ( v15 )    {      dest = ptrace(PTRACE_PEEKTEXT, pid1, v10, 0);// 从内存地址中读取一个字节      v14 = 0;      do      {        *((_BYTE *)&dest + v14) = *(_BYTE *)(pMem1 + v14);        ++v14;      }      while ( v14 != v15 );      ptrace(PTRACE_POKETEXT, pid1, v10, dest);    }  }  return 1;}

其中采用了两种方式写入内存,分别为:利用修改/proc/pid/mem实现和利用ptrace实现。
下面看下第一种方式的实现:

ssize_t __fastcall write_remote_memory1(int pid, const void *pMem, size_t length, __off_t remote_clock_gettime){  size_t v4; // r5@1  const void *v5; // r6@1  __off_t v6; // r7@1  int fd; // r0@1  int v8; // r8@1  ssize_t v9; // r5@2  ssize_t result; // r0@4  char s; // [sp+4h] [bp-34h]@1  int v12; // [sp+1Ch] [bp-1Ch]@1  v4 = length;  v5 = pMem;  v6 = remote_clock_gettime;  v12 = _stack_chk_guard;  snprintf(&s, 0x18u, "/proc/%u/mem", pid);  fd = open(&s, 1);  v8 = fd;  if ( fd == -1 )  {    v9 = -1;  }  else  {    lseek(fd, v6, 0);    v9 = write(v8, v5, v4);    close(v8);  }  result = v9;  if ( v12 != _stack_chk_guard )    _stack_chk_fail(v9);  return result;

比较简单,直接修改/proc/pid/mem实现。
第二种方式,是通过inline hook系统函数的前8个字节实现的。
其实,还有第三种方式的,有时间再详细介绍吧

0 0