对某APP的逆向之旅(3)

书接上回,本次对前面解密后的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个字节实现的。
其实,还有第三种方式的,有时间再详细介绍吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值