首页
社区
课程
招聘
[原创] 2019看雪CTF 第二题 变形金钢 WriteUp
2019-3-25 15:53 11199

[原创] 2019看雪CTF 第二题 变形金钢 WriteUp

2019-3-25 15:53
11199
IDA中分析liboo000oo.so,可以看出
.datadiv_decode5009363700628197108在程序开始时对数据解码:

.init_array:00003E78 ; ELF Initialization Function Table
.init_array:00003E78 ; ===========================================================================
.init_array:00003E78
.init_array:00003E78 ; Segment type: Pure data
.init_array:00003E78                 AREA .init_array, DATA
.init_array:00003E78                 ; ORG 0x3E78
.init_array:00003E78                 DCD .datadiv_decode5009363700628197108+1 ; 对数据解码
.init_array:00003E78 ; .init_array   ends
.init_array:00003E78

// 对数据解码
char *datadiv_decode5009363700628197108()
{
  int v0; // r0
  int v1; // r0
  int v2; // r0
  char *result; // r0

  v0 = 0;
  do
  {
    a650f909c721736[v0] ^= 0xA5u;               // 解码串 "650f909c-7217-3647-9331-c82df8b98e98",0
    ++v0;
  }
  while ( v0 != 0x25 );
  v1 = 0;
  do
    aAbcdefghijklmn[v1++] ^= 0xA5u;             // 解码 Base64 编码表:
                                                //  "!:#",0x24,"%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';",0
  while ( v1 != 0x42 );
  v2 = 0;
  do
    aAndroidSupport[v2++] ^= 0x84u;             // 解码串 "android/support/v7/app/AppCompiatActivity",0
  while ( v2 != 0x2A );
  result = &byte_40D0;
  byte_40CA ^= 0xFCu;
  byte_40CB ^= 0xFCu;
  byte_40CC ^= 0xFCu;
  byte_40D0 ^= 0x62u;
  byte_40D1 ^= 0x62u;
  byte_40D2 ^= 0x62u;
  byte_40D3 ^= 0x62u;
  byte_40D4 ^= 0x62u;
  byte_40D5 ^= 0x62u;
  word_40D6 = __PAIR__(HIBYTE(word_40D6), (word_40D6 ^ 0x62)) ^ 0x6200;
  byte_40D8 ^= 0x62u;
  byte_40D9 ^= 0x62u;
  byte_40DA ^= 0x62u;
  byte_40DB ^= 0x62u;
  byte_40DC ^= 0x62u;
  byte_40DD ^= 0x62u;
  byte_40DE ^= 0x62u;
  byte_40DF ^= 0x62u;
  byte_40E0 ^= 0x62u;
  byte_40E1 ^= 0x62u;
  byte_40E2 ^= 0x62u;
  byte_40E3 ^= 0x62u;
  byte_40E4 ^= 0x62u;
  byte_40E5 ^= 0x62u;
  return result;
}

再来看
signed int __fastcall JNI_OnLoad(_JavaVM *a1)
{
  signed int result; // r0
  _JNIEnv *v2; // r5
  int v3; // r6
  _JNIEnv *env; // [sp+0h] [bp-18h]
  int v5; // [sp+4h] [bp-14h]

  env = 0;
  if ( !(a1->functions->GetEnv)() )
    goto LABEL_4;
LABEL_2:
  result = -1;
  while ( _stack_chk_guard != v5 )
  {
LABEL_4:
    v2 = env;
    v3 = (env->functions->FindClass)(env, off_4010);// "android/support/v7/app/AppCompiatActivity"
    dword_4110 = (v2->functions->NewGlobalRef)(v2, v3);
    if ( !v3 || (v2->functions->RegisterNatives)(v2, v3, &JNINativeMethodEq, 1) <= -1 )// 注册 boolean NativeMethod 函数 eq(String)
      goto LABEL_2;
    result = 65542;
  }
  return result;
}

其中:.data:00004014 JNINativeMethodEq JNINativeMethod <aEq, aLjavaLangStrin, eq+1>

可以看到,在函数JNI_OnLoad中注册了一个NativeMethod boolean eq(String),这个就是验证核心

// 注册码核心验证代码
int __fastcall eq(_JNIEnv *a1)
{
  size_t v1; // r10
  unsigned __int8 *v2; // r6
  _BYTE *v3; // r8
  _BYTE *v4; // r11
  int v5; // r0
  size_t v6; // r2
  char *v7; // r1
  int v8; // r3
  int v9; // r1
  unsigned int v10; // r2
  int v11; // r3
  int v12; // r0
  int v13; // r4
  unsigned __int8 v14; // r0
  _BYTE *v15; // r3
  _BYTE *v16; // r5
  char *v17; // r4
  int v18; // r5
  int v19; // r1
  int v20; // r0
  signed int v21; // r1
  int v22; // r2
  size_t v23; // r0
  unsigned int lenSN; // r8
  unsigned int v25; // r5
  _BYTE *buf; // r0
  int v27; // r3
  int v28; // r10
  unsigned int v29; // r2
  int v30; // r12
  bool v31; // zf
  _BYTE *v32; // r1
  bool v33; // zf
  int v34; // r3
  int v35; // r1
  unsigned __int8 v36; // r11
  unsigned int v37; // lr
  char v38; // r1
  char *v39; // r2
  int v40; // t1
  unsigned int v42; // [sp+4h] [bp-234h]
  unsigned int v43; // [sp+8h] [bp-230h]
  unsigned int v44; // [sp+10h] [bp-228h]
  char *SN; // [sp+14h] [bp-224h]
  char v46[256]; // [sp+18h] [bp-220h]
  char v47[256]; // [sp+118h] [bp-120h]
  int v48; // [sp+218h] [bp-20h]

  SN = (a1->functions->GetStringUTFChars)();    // 取SN
  v1 = strlen(a650f909c721736);                 // 用 "650f909c-7217-3647-9331-c82df8b98e98" 初始加密表
                                                // 算法没看太出来,不过不要紧,最后XOR的序列是不变的,只要得到那个序列就可以算出SN
  v2 = malloc(v1);
  v3 = malloc(v1);
  v4 = malloc(v1);
  _aeabi_memclr(v2, v1);
  _aeabi_memclr(v3, v1);
  _aeabi_memclr(v4, v1);
  if ( v1 )
  {
    v5 = 0;
    v6 = v1;
    v7 = a650f909c721736;
    do
    {
      v8 = *v7++;
      if ( v8 != '-' )
        v3[v5++] = v8;
      --v6;
    }
    while ( v6 );
    if ( v5 >= 1 )
    {
      v9 = v5 - 1;
      v10 = -8;
      v11 = 0;
      v12 = 0;
      do
      {
        if ( (v11 | (v10 >> 2)) > 3 )
        {
          v13 = v12;
        }
        else
        {
          v13 = v12 + 1;
          v2[v12] = '-';
        }
        v14 = v3[v9--];
        v11 += 0x40000000;
        v2[v13] = v14;
        ++v10;
        v12 = v13 + 1;
      }
      while ( v9 != -1 );
      if ( v13 >= 0 )
      {
        v15 = v4;
        while ( 1 )
        {
          v16 = *v2;
          if ( (v16 - 97) <= 5u )
            break;
          if ( (v16 - 48) <= 9u )
          {
            v16 = &unk_23DE + v16 - 48;
            goto LABEL_18;
          }
LABEL_19:
          *v15++ = v16;
          --v12;
          ++v2;
          if ( !v12 )
            goto LABEL_20;
        }
        v16 = &unk_23D8 + v16 - 97;
LABEL_18:
        LOBYTE(v16) = *v16;
        goto LABEL_19;
      }
    }
  }
LABEL_20:
  _aeabi_memcpy8(v46, &unk_23E8, 256);
  v17 = v47;
  v18 = 0;
  do
  {
    sub_D20(v18, v1);
    v47[v18++] = v4[v19];
  }
  while ( v18 != 256 );
  v20 = (v47[0] - 41);
  v46[0] = v46[v20];
  v46[v20] = -41;
  v21 = 1;
  do
  {
    v22 = v46[v21];
    v20 = (v20 + v47[v21] + v22) % 256;
    v46[v21++] = v46[v20];
    v46[v20] = v22;
  }
  while ( v21 != 256 );
  v23 = strlen(SN);
  lenSN = v23;
  v25 = v4[3];
  v43 = 8 * (3 - -3 * (v23 / 3));
  v42 = v25 + v43 / 6;
  buf = malloc(v42 + 1);
  if ( lenSN )
  {
    v28 = 0;
    v29 = 0;
    v30 = 0;
    v44 = v25;
    do
    {
      v28 = (v28 + 1) % 256;
      v35 = v46[v28];
      v30 = (v30 + v35) % 256;
      v46[v28] = v46[v30];
      v46[v30] = v35;
      v17 = v46[v28];
      v36 = v46[(v35 + v17)] ^ SN[v29];         // 用加密码表对SN XOR 后 再Base64编码(Base64码表非标准表)再对输出每4个字节一组第0,2个分别XOR 0x7 和 0xf
                                                // 在这儿加密码表序列为一个常数序列,设断点可得到这个序列:
                                                // {0x9b, 0x6b, 0xba, 0x25, 0x73, 0x82, 0xe0, 0x31, 0x86, 0x80, 0xf1, 0xc5, 0xda, 0x82, 0xda, 0x08, 0x38, 0x90, 0x50, 0xa8, 0x31, 0x63, 0x07, 0xa2, ...}
      if ( v29 && (v27 = 0xAAAAAAAB * v29 >> 32, v37 = 3 * (v29 / 3), v37 != v29) )
      {
        v31 = v29 == 1;
        if ( v29 != 1 )
          v31 = v37 + 1 == v29;
        if ( v31 )
        {
          v32 = aAbcdefghijklmn;                // Base64码表  "!:#",0x24,"%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
          buf[v44 + v29] = aAbcdefghijklmn[buf[v44 + v29] | (v36 >> 4)];
          v17 = &buf[v44 + v29];
          v27 = 4 * v36 & 0x3C;
          v17[1] = v27;
          if ( v29 + 1 >= lenSN )
            goto LABEL_53;
        }
        else
        {
          v33 = v29 == 2;
          if ( v29 != 2 )
            v33 = v37 + 2 == v29;
          if ( v33 )
          {
            v17 = (v36 & 0xC0);
            v34 = v44++ + v29;
            buf[v34] = aAbcdefghijklmn[buf[v34] | (v17 >> 6)] ^ 0xF;// 四个一组中的第二个 XOR 0x0f
            v27 = &buf[v34];
            *(v27 + 1) = aAbcdefghijklmn[v36 & 0x3F];
          }
        }
      }
      else
      {
        buf[v44 + v29] = aAbcdefghijklmn[v36 >> 2] ^ 7;// 四个一组中的第0个,XOR 0x07
        v17 = &buf[v44 + v29];
        v27 = 16 * v36 & 0x30;
        v17[1] = v27;
        if ( v29 + 1 >= lenSN )
        {
          v38 = aAbcdefghijklmn[v27];
          *(v17 + 1) = 0x3B3B;
          goto LABEL_43;
        }
      }
      ++v29;
    }
    while ( v29 < lenSN );
  }
  while ( 1 )
  {
    if ( v43 )
    {
      v32 = (&dword_0 + 1);
      v17 = v42;
      v39 = &a98gaLTnFjJG;                      // " {9*8ga*l!Tn?@#fj'j",0x24,"\g;;"
      do
      {
        v27 = buf[v25++];
        v40 = *v39++;                           // 最终的比较
        if ( v40 != v27 )                       // 最后结果要为 " {9*8ga*l!Tn?@#fj'j",0x24,"\g;;"
          v32 = 0;
      }
      while ( v25 < v42 );
    }
    else
    {
      v32 = (&dword_0 + 1);
    }
    buf = (_stack_chk_guard - v48);
    if ( _stack_chk_guard == v48 )
      break;
LABEL_53:
    v38 = v32[v27];
    v17[2] = 52;
LABEL_43:
    v17[1] = v38;
  }
  return v32;
}

由以上分析可以看出验证算法大致是:
BYTE xorKey[] = {0x7, 0, 0xf, 0};
BYTE xorKey2[] = {0x9b, 0x6b, 0xba, 0x25, 0x73, 0x82, 0xe0, 0x31, 0x86, 0x80, 0xf1, 0xc5, 0xda, 0x82, 0xda, 0x08, 0x38, 0x90, 0x50, 0xa8, 0x31, 0x63, 0x07, 0xa2};
char sEnd[] = " {9*8ga*l!Tn?@#fj'j$\\g;;"; //长度24
char SN[24/4*3+1]; //最长18位
char snBase64[25];
int i;
for(i = 0; i < lenSN; i++){
SN[i] ^= xorKey2[i];
}
Base64Encode(SN, snBase64);
for(i = 0; i < 24 - 2; i++){ //最后的';'不计算
snBase64[i] ^= xorKey[i%4];
}
if(!strncmp(snBase64, sEnd, 24)){
//验证通过
}
不过这儿的Base64码表不是标准的"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
而是"!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
如果用标准的Base64算法还需要小小的变换一下。

由验证算法不难得出SN:
LPCSTR p1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
LPCSTR p2 = "!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\\';";
for(i = 0; i < 24; i++){
char c = sEnd[i] ^ (i>=22 ? 0 : xorKey[i%4]); //最后的';'不计算
LPCSTR p = strchr(p2, c); //换成标准的Base64码
snBase64[i] = p1[p-p2];
}
snBase64[i] = 0;
int len = Base64Decode((PBYTE)SN, sizeof(SN), snBase64);
for(i = 0; i < len; i++){
SN[i] ^= xorKey2[i];
}
SN[len] = 0;
OutputDebugString(SN);
最终得到 SN[] = "fu0kzHp2aqtZAuY6";


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞2
打赏
分享
最新回复 (5)
雪    币: 1526
活跃值: (434)
能力值: ( LV4,RANK:52 )
在线值:
发帖
回帖
粉丝
ilanlanya 2019-4-1 21:00
2
0
求教师傅”!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';“这个字符串变成标准的base64是怎么变得
为什么后面直接加上\
雪    币: 12014
活跃值: (3202)
能力值: ( LV15,RANK:1565 )
在线值:
发帖
回帖
粉丝
AloneWolf 3 2019-4-2 09:17
3
0
两个字符表一一对应还不好求吗?
由字符查表得位置:pos=strchr(p1,c1)-p1,再在另一个表由位置得字符:c2=p2[pos]
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
st0ne31 2021-6-14 14:05
4
0
师傅您好,请教一下,v46这个加密序列不是在加密的时候一直在改变吗,为什么可以直接从内存中取出来解密呢?
雪    币: 12014
活跃值: (3202)
能力值: ( LV15,RANK:1565 )
在线值:
发帖
回帖
粉丝
AloneWolf 3 2021-6-15 09:20
5
0
st0ne31 师傅您好,请教一下,v46这个加密序列不是在加密的时候一直在改变吗,为什么可以直接从内存中取出来解密呢?
是在变,但是是一个常数序列{0x9b, 0x6b....},也就是说第一次为0x9b, 第二次为0x6b,....
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
st0ne31 2021-6-15 12:34
6
0
AloneWolf 是在变,但是是一个常数序列{0x9b, 0x6b....},也就是说第一次为0x9b, 第二次为0x6b,....
明白了,感谢师傅解答
游客
登录 | 注册 方可回帖
返回