首页
社区
课程
招聘
[原创]看雪 CTF 的一道题目
发表于: 2020-11-26 19:56 5385

[原创]看雪 CTF 的一道题目

2020-11-26 19:56
5385

感觉做完之后能对 Android 逆向有更进一步的理解,特地码一下,也拜读了几位大佬的文章。


1、看一下androidmanifest.xml

就一个活动进去看看


2、Mainactivity

    看起来只要把用户名跟密码 作为 颠倒的字符串就行

    然而并不是,再仔细观察就能发现 public class MainActivity extends AppCompiatActivity

   开始以为是android 原生的AppCompiatActivity ,然而是改了一个相似的名字不仔细看,很容易忽略,这个操作挺骚的

懒得截图,直接用了某大佬的图


3、AppCompaitActivity 

    真正的校验逻辑在 AppCompaitActivity 这个类的 public native boolean eq(String str); 函数内

    这个函数是一个 native 函数,那么具体的校验逻辑必然在 oo000oo 对应的 so 文件中


4、发现so文件中并没有eq该函数,所以判断在JNI_LOAD或者在init_array里面做了手脚。


5、跟踪.init_array

    init_array函数执行了datadiv_decode5009363700628197108字符串解密函数



    650f909c-7217-3647-9331-c82df8b98e98


!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';


android/support/v7/app/AppCompiatActivity

Eq  (Ljava/lang/String;)Z

    总结:


#解码36长度字符串byte_4020:

byte_4020 =650f909c-7217-3647-9331-c82df8b98e98+0x00(结束符)

 

#解码Base64为的64+1个编码字符byte_4050= 

base64Chars = byte_4050 =!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';+0x00(结束符)

 

#app伪装java类的名称byte_40A0:

className_Sign= android/support/v7/app/AppCompiatActivity +0x00(结束符)


6、JNI_OnLoad



7、动态注册

8、关键函数

app_password = (char *)(*(int (**)(void))(*(_DWORD *)a1 + 676))();
  rc4_key_len = strlen(rc4_raw_key);            // rc4_raw_key = 650f909c-7217-3647-9331-c82df8b98e98
  rc4_key3 = (unsigned __int8 *)malloc(rc4_key_len);// rc4_raw_key 被删除"-"符号,再经过特殊处理,同时有添加回新的“-”符号
                                                // 新的结果:89e89b8f-d28c-1339-7463-7127c909f056
  rc4_key2 = malloc(rc4_key_len);               // rc4_raw_key的内容删除“-” :650f909c721736479331c82df8b98e98
  rc4_key4 = malloc(rc4_key_len);
  _aeabi_memclr(rc4_key3, rc4_key_len);         // RC4密钥生成
  _aeabi_memclr(rc4_key2, rc4_key_len);         // aeabi_memclr(memset的地址和长度是常量)
  _aeabi_memclr(rc4_key4, rc4_key_len);
  if ( rc4_key_len )                            // key长度大于0
  {
    rc4_key2_len = 0;
    v6 = rc4_key_len;
    v7 = rc4_raw_key;
    do
    {
      v8 = (unsigned __int8)*v7++;
      if ( v8 != 45 )                           // 45(-)减号/破折号
        rc4_key2[rc4_key2_len++] = v8;
      --v6;
    }
    while ( v6 );                               // 删除(rc4_raw_key中的"-") =650f909c721736479331c82df8b98e98
    if ( rc4_key2_len >= 1 )
    {
      rc4_key2_pos = rc4_key2_len - 1;
      v10 = -8;
      v11 = 0;
      rc4_key3_len = 0;
      do
      {
        if ( (v11 | (v10 >> 2)) > 3 )           // // v10>>2 = -2?后第一次=0x3FFFFFFE
        {
          rc4_key3_pos = rc4_key3_len;
        }
        else
        {
          rc4_key3_pos = rc4_key3_len + 1;
          rc4_key3[rc4_key3_len] = 45;
        }
        v14 = rc4_key2[rc4_key2_pos--];
        v11 += 0x40000000;
        rc4_key3[rc4_key3_pos] = v14;
        ++v10;
        rc4_key3_len = rc4_key3_pos + 1;
      }
      while ( rc4_key2_pos != -1 );
      if ( rc4_key3_pos >= 0 )
      {
        v15 = rc4_key4;
        while ( 1 )
        {
          v16 = (_BYTE *)*rc4_key3;
          if ( (unsigned __int8)((_BYTE)v16 - 97) <= 5u )
            break;
          if ( (unsigned __int8)((_BYTE)v16 - 48) <= 9u )
          {
            v16 = (char *)&unk_23DE + (_DWORD)v16 - 48;
            goto LABEL_18;
          }
LABEL_19:
          *v15++ = (_BYTE)v16;
          --rc4_key3_len;
          ++rc4_key3;
          if ( !rc4_key3_len )
            goto LABEL_20;
        }
        v16 = (char *)&unk_23D8 + (_DWORD)v16 - 97;
LABEL_18:
        LOBYTE(v16) = *v16;
        goto LABEL_19;
      }
    }
  }
LABEL_20:                                       // sbox 生成
  _aeabi_memcpy8(sbox, &unk_23E8, 256);
  Rc4_T = tbox;
  tbox_pos = 0;
  do
  {
    sub_D20(tbox_pos, rc4_key_len);             // 需要跟进去看看
    tbox[tbox_pos++] = rc4_key4[v19];
  }
  while ( tbox_pos != 256 );
  ix = (unsigned __int8)(tbox[0] - 41);
  sbox[0] = sbox[ix];
  sbox[ix] = -41;                               // d7
  v21 = 1;
  do
  {
    v22 = (unsigned __int8)sbox[v21];
    ix = (ix + (unsigned __int8)tbox[v21] + v22) % 256;
    sbox[v21++] = sbox[ix];
    sbox[ix] = v22;
  }
  while ( v21 != 256 );                         // sbox初始化
  app_password_length = strlen(app_password);   // 就是解密数组密码的字符
  app_password_length_loc = app_password_length;
  new_base64_index = (unsigned __int8)rc4_key4[3];
  pwd_b64_bitlen = 8 * (3 - -3 * (app_password_length / 3));
  new_base64_length = new_base64_index + pwd_b64_bitlen / 6;
  new_base64 = malloc(new_base64_length + 1);   // 分配新的base64需要使用的字节。
  if ( app_password_length_loc )                // app输入的密码长度
  {
    txi = 0;
    pwd_idx = 0;                                // 循环体index
    txj = 0;
    v44 = new_base64_index;
    do
    {
      txi = (txi + 1) % 256;                    //  RC4 算法的产生密钥流循环体开始
      tx = (unsigned __int8)sbox[txi];
      txj = (txj + tx) % 256;
      sbox[txi] = sbox[txj];
      sbox[txj] = tx;
      Rc4_T = (char *)(unsigned __int8)sbox[txi];
      needTodoBase64 = sbox[(unsigned __int8)(tx + (_BYTE)Rc4_T)] ^ app_password[pwd_idx];// Base64内嵌在RC4的内部
                                                // 先线程一个RC4加密后的字符
                                                // 后面用这个字符进行Base64的解码操作
                                                // Base64魔改的算法:
                                                // 1:Base64 字典被替换
                                                // 2:Base最后以为“=”会被替换成“;”
                                                // 3: 对特定字符进行异或操作.每4个字符中第0个与0x6异或,第0个与0xF异或
                                                // switch (i%4){
                                                //     case 0:
                                                //         base64char = (char) (iAscii ^0x07);
                                                //         break;
                                                //     case 2:
                                                //         base64char = (char) (iAscii ^0xF);
                                                //         break;
                                                //     default:
                                                //         base64char =encodeChars[i];
                                                // }
      if ( pwd_idx && (v27 = 2863311531u * (unsigned __int64)pwd_idx >> 32, v37 = 3 * (pwd_idx / 3), v37 != pwd_idx) )// 逗号运算符是指在C语言中,多个表达式可以用逗号分开,
                                                // 其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
                                                // 
                                                // 影响结果:
                                                // false:
                                                // 其实就是index/3(取摸)!=0
                                                // 
                                                // true: 
                                                // index/3 =0
                                                // v37:index可以是3的最大倍数,0,3,6,9,12...
                                                // index=3,则v37=3;index =6,则v37=6;index=7,则v37=6
      {
        v31 = pwd_idx == 1;                     // 是否是appPassword[1]字符
        if ( pwd_idx != 1 )
          v31 = v37 + 1 == pwd_idx;
        if ( v31 )                              // 判断index%3=1的情况 ,即1,4,7,10
        {
          retn = base64_table;                  // v34第一次=0x33(51);
                                                // base64_table:存放的是base64的table字典字符64+1个
                                                // base64_table:(字符长度是64+1):!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';
          new_base64[v44 + pwd_idx] = base64_table[(unsigned __int8)new_base64[v44 + pwd_idx] | ((unsigned int)needTodoBase64 >> 4)];
          Rc4_T = &new_base64[v44 + pwd_idx];
          v27 = 4 * needTodoBase64 & 0x3C;      // 0x3C =60 ,即111100二进制
          Rc4_T[1] = v27;
          if ( pwd_idx + 1 >= app_password_length_loc )
            goto LABEL_53;                      // 跳转到程序的倒数第2个Lable
        }
        else
        {
          v33 = pwd_idx == 2;                   // index%3=2的情况 2,5,8,11....
          if ( pwd_idx != 2 )
            v33 = v37 + 2 == pwd_idx;
          if ( v33 )
          {
            Rc4_T = (char *)(needTodoBase64 & 0xC0);// 0xC0 =192即11000000二进制,保留最高2bit
            v34 = v44++ + pwd_idx;
            new_base64[v34] = base64_table[(unsigned __int8)new_base64[v34] | ((unsigned int)Rc4_T >> 6)] ^ 0xF;// 0xf=15 ,即二进制1111
                                                // 这里异或好像是对Base64模拟的多余操作
            v27 = (int)&new_base64[v34];
            *(_BYTE *)(v27 + 1) = base64_table[needTodoBase64 & 0x3F];// 0x3f=63 即111111二进制
          }
        }
      }
      else
      {
        new_base64[v44 + pwd_idx] = base64_table[(unsigned int)needTodoBase64 >> 2] ^ 7;// 7的二进制四111,这里对base64进行了魔改操作
        Rc4_T = &new_base64[v44 + pwd_idx];
        v27 = 16 * needTodoBase64 & 0x30;       // 0x30=48,即二进制110000bit
        Rc4_T[1] = v27;
        if ( pwd_idx + 1 >= app_password_length_loc )// 最后一位
        {
          v38 = base64_table[v27];
          *((_WORD *)Rc4_T + 1) = 15163;
          goto LABEL_43;
        }
      }
      ++pwd_idx;
    }                                           // end for if ( v26_app_password_length ) 内部的do
    while ( pwd_idx < app_password_length_loc );
  }                                             // end if ( v26_strText_length ) 输入密码lenght>0
                                                // 这是时RC4-04部分的算法“产生密钥流”
                                                // v28_new_base64:同时结合if部分逐个对RC4生成的结果做魔改的Base64运算
  while ( 1 )
  {
    if ( pwd_b64_bitlen )
    {                                           // v45 是根据输入密码长度计算,比如密码:12345678;v45=0x48u(即78)
      retn = (_BYTE *)(&dword_0 + 1);           // retn =1
      Rc4_T = (char *)new_base64_length;
      v39 = &byte_24E8;                         // v41指向的char* = 0x20(即空格)+"{9*8ga*l!Tn?@#fj'j$\g;;"
      do
      {
        v27 = (unsigned __int8)new_base64[new_base64_index++];
        v40 = (unsigned __int8)*v39++;
        if ( v40 != v27 )
          retn = 0;
      }
      while ( new_base64_index < new_base64_length );// v44 =0x3Fu
    }
    else
    {
      retn = (_BYTE *)(&dword_0 + 1);
    }
    new_base64 = (_BYTE *)(_stack_chk_guard - v48);
    if ( _stack_chk_guard == v48 )
      break;
LABEL_53:
    v38 = retn[v27];
    Rc4_T[2] = 52;                              // 输入密码:12345678,这里是:wk4.
LABEL_43:
    Rc4_T[1] = v38;
  }
  return (unsigned __int8)retn;                 // sub_784 方法返回只有这么一个位置。我们侧重分析如何让该方法返回真即可


    接下来我分段分析,我把每一段都分别用python写了一个单独的函数


8.1、RC4密钥去掉-

# RC4密钥去掉-
 # return 650f909c721736479331c82df8b98e98
 def getRc4Key2():
     rc4key2 = ""
     for i in range(len(rc4key)):
         value = rc4key[i]
         if ( ord(value) != 0x2d): # 字符串 转 ascii码
             rc4key2 += value
     return rc4key2


8.2、

# return 89e89b8f-d28c-1339-7463-7127c909f056
 def getRc4Key3(key):
     keylen = len(key)
     rc4key2value1 = 0xFFFFFFF8
     rc4key2value2 = 0x0
     rc4key3 = ""
 
     while keylen > 0:
         temp = rc4key2value1 >> 2
         temp2 = rc4key2value2 | temp
         if temp2 <= 3:
             rc4key3 += chr(0x2d)# ascii码 转  字符串
 
         keylen -= 1
         rc4key3 += key[keylen]
 
         rc4key2value2 += 0x40000000
         rc4key2value2 = fixedint.UInt32(rc4key2value2)
         rc4key2value1 += 1
         rc4key2value1 = fixedint.UInt32(rc4key2value1)
 
     return rc4key3


8.3、

#     算法参数01:unk_23DE(10位长度) =2409715836
#     算法参数02:unk_23D8(16位长度)=dbeafc2409715836
# return 36f36b3c-a03e-4996-8759-8408e626c215
def getRc4Key4(key):
    keylen = len(key)
    keypos = 0
    retnkey = ""
    unk_23DE = "2409715836"
    unk_23D8 = "dbeafc2409715836"

    while keylen > 0:
        value = key[keypos]
        valueascii = ord(value)
        value97 = valueascii - 97
        value48 = valueascii - 48
        if (value97 <= 5) and (value97 >= 0):
            value = unk_23D8[value97]
        elif (value48 <= 9) and (value48 >= 0):
            value = unk_23DE[value48]

        retnkey += value
        keylen -= 1
        keypos += 1

    return retnkey


8.4、根据密钥生成临时的数据key


# 根据密钥生成临时的数据key
 # 计算RC4 的临时256自己T向量,公式:iK[i]=(byte)aKey.charAt((i % aKey.length()));
 def getRc4Key5(key):
     tbox = ""
     pos = 0
     keylen = len(key)
 
     while pos < 256:
         tbox += key[pos % keylen]
         pos += 1
 
     return tbox


8.5、sbox初始化

def getSboxK(key):
    Sbox = [0xD7, 0xDF, 0x02, 0xD4, 0xFE, 0x6F, 0x53, 0x3C, 0x25, 0x6C, 0x99, 0x97, 0x06, 0x56, 0x8F, 0xDE, 0x40, 0x11,
            0x64, 0x07, 0x36, 0x15, 0x70, 0xCA, 0x18, 0x17, 0x7D, 0x6A, 0xDB, 0x13, 0x30, 0x37, 0x29, 0x60, 0xE1, 0x23,
            0x28, 0x8A, 0x50, 0x8C, 0xAC, 0x2F, 0x88, 0x20, 0x27, 0x0F, 0x7C, 0x52, 0xA2, 0xAB, 0xFC, 0xA1, 0xCC, 0x21,
            0x14, 0x1F, 0xC2, 0xB2, 0x8B, 0x2C, 0xB0, 0x3A, 0x66, 0x46, 0x3D, 0xBB, 0x42, 0xA5, 0x0C, 0x75, 0x22, 0xD8,
            0xC3, 0x76, 0x1E, 0x83, 0x74, 0xF0, 0xF6, 0x1C, 0x26, 0xD1, 0x4F, 0x0B, 0xFF, 0x4C, 0x4D, 0xC1, 0x87, 0x03,
            0x5A, 0xEE, 0xA4, 0x5D, 0x9E, 0xF4, 0xC8, 0x0D, 0x62, 0x63, 0x3E, 0x44, 0x7B, 0xA3, 0x68, 0x32, 0x1B, 0xAA,
            0x2D, 0x05, 0xF3, 0xF7, 0x16, 0x61, 0x94, 0xE0, 0xD0, 0xD3, 0x98, 0x69, 0x78, 0xE9, 0x0A, 0x65, 0x91, 0x8E,
            0x35, 0x85, 0x7A, 0x51, 0x86, 0x10, 0x3F, 0x7F, 0x82, 0xDD, 0xB5, 0x1A, 0x95, 0xE7, 0x43, 0xFD, 0x9B, 0x24,
            0x45, 0xEF, 0x92, 0x5C, 0xE4, 0x96, 0xA9, 0x9C, 0x55, 0x89, 0x9A, 0xEA, 0xF9, 0x90, 0x5F, 0xB8, 0x04, 0x84,
            0xCF, 0x67, 0x93, 0x00, 0xA6, 0x39, 0xA8, 0x4E, 0x59, 0x31, 0x6B, 0xAD, 0x5E, 0x5B, 0x77, 0xB1, 0x54, 0xDC,
            0x38, 0x41, 0xB6, 0x47, 0x9F, 0x73, 0xBA, 0xF8, 0xAE, 0xC4, 0xBE, 0x34, 0x01, 0x4B, 0x2A, 0x8D, 0xBD, 0xC5,
            0xC6, 0xE8, 0xAF, 0xC9, 0xF5, 0xCB, 0xFB, 0xCD, 0x79, 0xCE, 0x12, 0x71, 0xD2, 0xFA, 0x09, 0xD5, 0xBC, 0x58,
            0x19, 0x80, 0xDA, 0x49, 0x1D, 0xE6, 0x2E, 0xE3, 0x7E, 0xB7, 0x3B, 0xB3, 0xA0, 0xB9, 0xE5, 0x57, 0x6E, 0xD9,
            0x08, 0xEB, 0xC7, 0xED, 0x81, 0xF1, 0xF2, 0xBF, 0xC0, 0xA7, 0x4A, 0xD6, 0x2B, 0xB4, 0x72, 0x9D, 0x0E, 0x6D,
            0xEC, 0x48, 0xE2, 0x33]

    ix = ord(key[0]) - 0x29
    Sbox[0] = Sbox[ix]
    Sbox[ix] = 0xd7
    sboxpos = 1

    while sboxpos < 256:
        value = Sbox[sboxpos]
        ix = (ix + ord(key[sboxpos]) + value)%256
        Sbox[sboxpos] = Sbox[ix]
        Sbox[ix] = value
        sboxpos += 1

    return Sbox


8.6、接下来就是 RC4 + base64混合加密的地方了

def getNewBase64(password,tbox,rc4key4):
    password_length = len(password)
    b64_salt_size = ord(rc4key4[3])

    # 空数组
    new_base64 = []

    if password_length <= 0:
        return

    txi = 0
    pwd_idx = 0
    txj = 0
    new_base64_tmpValue = 0

    while pwd_idx < password_length:
        txi = (txi + 1) % 256
        tx = tbox[txi]
        txj = (txj + tx) % 256
        tbox[txi] = tbox[txj]
        tbox[txj] = tx
        Rc4_T = tbox[txi]
        tboxIndex = byteToInteger(tx + Rc4_T)
        needTodoBase64 = byteToInteger(tbox[tboxIndex] ^ ord(password[pwd_idx]))

        pwdtem = pwd_idx % 3
        if pwdtem == 0:
            base64table_index = needTodoBase64 >> 2
            base64table_value = ord(base64table[base64table_index])
            base64table_tmp = base64table_value ^ 7
            new_base64.append(base64table_tmp)
            needbaser64_tmp = 16 * needTodoBase64
            new_base64_tmpValue = needbaser64_tmp & 0x30
        elif pwdtem == 1:
            base64table_index_temp = needTodoBase64 >> 4
            base64table_index = base64table_index_temp + new_base64_tmpValue
            new_base64.append(ord(base64table[base64table_index]))
            needbaser64_tmp = needTodoBase64 << 2
            new_base64_tmpValue = needbaser64_tmp & 0x3C
            pwd_idx_tem = pwd_idx + 1
            if pwd_idx_tem == password_length:
                new_base64.append(ord(base64table[new_base64_tmpValue]))
                new_base64.append(0x34)
        elif pwdtem == 2:
            needbaser64_tmp2 = needTodoBase64 & 0xC0
            b64_salt_size = b64_salt_size + 1
            base64table_index_temp = needbaser64_tmp2 >> 6
            base64table_index = new_base64_tmpValue + base64table_index_temp
            base64table_value = ord(base64table[base64table_index])
            base64table_tmp = base64table_value ^ 0xf
            new_base64.append(base64table_tmp)
            needbaser64_tmp2 = needTodoBase64 & 0x3F
            new_base64_tmpValue = ord(base64table[needbaser64_tmp2])
            new_base64.append(new_base64_tmpValue)
        pwd_idx = pwd_idx + 1
    return new_base64


9、分析文档及代码地址:https://github.com/dadaoshizhong/rc4-base64-/upload


10、参考

https://bbs.pediy.com/thread-262472.htm

https://bbs.pediy.com/thread-250348-1.htm

http://blog.syang.xyz/2019/04/kanxue-transformer/

https://bbs.pediy.com/thread-250413.htm

https://xz.aliyun.com/t/4614



[课程]Android-CTF解题方法汇总!

收藏
免费 2
支持
分享
最新回复 (5)
雪    币: 145
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
很好。
2020-11-27 11:38
0
雪    币: 2922
活跃值: (4916)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
3
原题呢
2020-11-27 11:42
0
雪    币: 1726
活跃值: (1085)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
这个冷饭看过四五次了。。
2020-11-27 14:17
0
雪    币: 1636
活跃值: (653)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
wx_Dispa1r 这个冷饭看过四五次了。。
以前没有搞过so层,只能借鉴别人搞过的东西,这是我的学习笔记
2020-11-29 17:34
0
雪    币: 582
活跃值: (317)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
请问这种题是哪里找到的 我也想拿来练练手
2020-11-30 15:38
0
游客
登录 | 注册 方可回帖
返回
//