欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创] 分析一个有趣的so双重壳

[原创] 分析一个有趣的so双重壳

背景

去年四五月份的时候客户反馈过一个so加壳的问题,分析后发现是和某Android应用市场SDK的libYSDK.so冲突。当时该so解壳过程中会操作linker的soinfo结构的部分数据,壳作者把dlopen返回的handle强制转成soinfo结构体指针,这操作个必然有兼容性问题。后来该壳也做了升级,其最新版本有一些挺有趣的技术点,跟大家分享下。

两层壳:

Ida直接打开:


函数列表只有31个sub_**(__mprotect是笔者改名来的), 再看下Section表 :


很多内容被擦掉了,dynsym 和 dynstr在7.0+上linker需要,必须保留,其他保留项对分析没什么用,壳的作者也保留了,这里直接抹掉elf-header中的section表偏移和大小,让ida使用动态链接表解析:


再用IDA打开后函数列表变了:


但是往下看代码区(JNI_OnLoad),代码加密了:


继续往后边拖拽,符号表竟然都在第二个loadsegment:


init解密壳

先考虑第一个loadsegment代码加密的问题,一般在linker的init中执行解密逻辑,init对应的那部分code一定是明文状态,greadelf –d libYSDK.so:


该so编译时的名字是“legu”,后来重命名为“YSDK”的,没有找到init段,只有init_array,跳过去看下:


看来sub_838就是解密函数了,贴上它的伪代码: 

int sub_838()

{

  unsigned int code_segment_end_segment; // ST28_4

  unsigned int data_segment_begin_page; // ST24_4

  unsigned int data_segment_end_page; // ST20_4

  unsigned __int64 v3; // kr00_8

  char v4; // ST13_1

  int result; // r0

  int v6; // [sp+4h] [bp-40h]

  int v7; // [sp+8h] [bp-3Ch]

  int j; // [sp+2Ch] [bp-18h]

  char v9; // [sp+30h] [bp-14h]

  char v10; // [sp+31h] [bp-13h]

  char v11; // [sp+32h] [bp-12h]

  char v12; // [sp+33h] [bp-11h]

  unsigned int v13; // [sp+34h] [bp-10h]

  _DWORD *e_pheaders; // [sp+38h] [bp-Ch]

  unsigned int i; // [sp+3Ch] [bp-8h]

  for ( i = (unsigned int)sub_838 & 0xFFFFF000; *(_DWORD *)i != 0x464C457F; i -= 0x1000 )

    ;

  e_pheaders = (_DWORD *)(*(_DWORD *)(i + 0x1C) + i);// i = loadbase

  v13 = 0;

  while ( *(unsigned __int16 *)(i + 0x2C) > v13 )// phnum > 0

  {

    if ( *e_pheaders != 1 || e_pheaders[6] != 5 )// type != PT_LOAD || flag != Read_Exe

    {

      if ( *e_pheaders == 1 && e_pheaders[6] == 6 )// type == PT_LOAD && flag == Read_Write

      {

        data_segment_begin_page = e_pheaders[2] & 0xFFFFF000;

        data_segment_end_page = (e_pheaders[2] + e_pheaders[4] + 4095) & 0xFFFFF000;

        break;

      }

    }

    else

    {

      code_segment_end_segment = (e_pheaders[2] + e_pheaders[4] + 4095) & 0xFFFFF000;

    }

    ++v13;

    e_pheaders += 8;

  }

  v12 = 0x2B;

  v11 = 0x99u;

  v10 = 32;

  v9 = 21;

  v3 = (unsigned __int64)(unsigned int)dword_4008 << 16;// 0x100028cc << 16

  v7 = (unsigned __int16)dword_4008;

  v6 = (unsigned __int16)dword_4008 - ((unsigned int)dword_4008 >> 16);

  _mprotect(i + ((unsigned int)dword_4008 >> 16), (v6 + 4095) & 0xFFFFF000, 3);// mprotect(loadbase + 0x1000, 0x2000, Read_Write)

  for ( j = HIDWORD(v3); j <= v7; ++j )         // 解密代码区加密部分(0x1000~0x28cc)

  {

    v4 = *(_BYTE *)(i + j);

    *(_BYTE *)(i + j) ^= (unsigned __int8)(((v11 - v10) ^ j) + v9) ^ v12;

    *(_BYTE *)(i + j) += v10 & v9 ^ v11;

    v12 += (v11 + v10 - v9) & v4 & j;

    v11 += (j + v12) ^ v4;

    v10 ^= (v4 - v12) ^ j;

    v9 += j - (v4 + v12);

  }

  _mprotect(i + HIDWORD(v3), (v6 + 4095) & 0xFFFFF000, 5);// mprotect(loadbase + 0x1000, 0x2000, Read_Exe)

  result = __cache_flush(i + HIDWORD(v3), v6);

  dword_4008 = i;            // JNI_OnLoad 中通过该变量获取so的路径

  return result;

}

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功