吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 25313|回复: 98
收起左侧

[Android CTF] 两道简单的CTF

  [复制链接]
Anonymous、 发表于 2018-11-12 15:38
本帖最后由 Anonymous、 于 2018-11-12 20:55 编辑

一、说明
两个CTF是一个群友前两天给我的,比较简单,自己比较菜,有什么不对的地方,还请各位师傅指点.

二、第一个CTF
这一题比较简单,先打开app看下
QQ截图20181112130924.bmp


java层代码很简单,就是调用native层的一个校验函数
[Java] 纯文本查看 复制代码
 protected void onCreate(Bundle arg2) {
        super.onCreate(arg2);
        this.setContentView(0x7F09001C);
        this.findViewById(0x7F070022).setOnClickListener(new View$OnClickListener() {
            public void onClick(View arg3) {
                if(check.checkflag(MainActivity.this.findViewById(0x7F070036).getText().toString())) {
                    Toast.makeText(MainActivity.this, "you are right~!", 1).show();
                }
                else {
                    Toast.makeText(MainActivity.this, "wrong!", 1).show();
                }
            }
        });
    }


public class check {
    static {
        System.loadLibrary("checkso");
    }

    public check() {
        super();
    }

public static native boolean checkflag(String arg0) {
    }
}


看下 native层函数

[Asm] 纯文本查看 复制代码
.text:00000E54                 PUSH            {R4,R6,R7,LR}
.text:00000E56                 ADD             R7, SP, #8
.text:00000E58                 MOV             R1, R2
.text:00000E5A                 LDR             R2, [R0]
.text:00000E5C                 LDR.W           R3, [R2,#0x2A4]
.text:00000E60                 MOVS            R2, #0
.text:00000E62                 BLX             R3      ; 获取输入的值
.text:00000E64                 MOV             R4, R0  ; R4 = R0 = input
.text:00000E66                 BL              sub_E08 ; 未传入参数,无返回值,暂时不分析
.text:00000E6A                 LDR             R0, =(off_6004 - 0xE70)
.text:00000E6C                 ADD             R0, PC  ; off_6004
.text:00000E6E                 LDR             R1, [R0] ; "c2RuaXNjc2RuaXNjYWJjZA=="
.text:00000E70                 MOV             R0, R4  ; s1
.text:00000E72                 BLX             strcmp  ; 比较输入的值和R1 R1 = "c2RuaXNjc2RuaXNjYWJjZA=="
.text:00000E76                 CLZ.W           R0, R0
.text:00000E7A                 LSRS            R0, R0, #5
.text:00000E7C                 POP             {R4,R6,R7,PC}


这么简单????那么我们输入c2RuaXNjc2RuaXNjYWJjZA==,试下,wrong~~~~~~~~~,要you are right~!才算正确~~~进去sub_E08,这个函数看下

132925ds39vw544fwal3r1.jpg

[Asm] 纯文本查看 复制代码
.text:00000E08                                         ; Java_com_example_p7xxtmx_1g_fakefunc_check_checkflag+12↓p
.text:00000E08 ; __unwind {
.text:00000E08                 PUSH            {R4,R6,R7,LR}
.text:00000E0A                 ADD             R7, SP, #8
.text:00000E0C                 LDR             R0, =(off_6004 - 0xE12)
.text:00000E0E                 ADD             R0, PC  ; off_6004
.text:00000E10                 LDR             R4, [R0] ; "c2RuaXNjc2RuaXNjYWJjZA=="
.text:00000E12                 MOV             R0, R4  ; s
.text:00000E14                 BLX             strlen  ; 去c2RuaXNjc2RuaXNjYWJjZA==的长度
.text:00000E18                 MOV             R1, R0
.text:00000E1A                 MOV             R0, R4
.text:00000E1C                 POP.W           {R4,R6,R7,LR}
.text:00000E20                 B.W             sub_16D8 ; 将c2RuaXNjc2RuaXNjYWJjZA==和其长度传入


再看sub_16d8这个函数

[Asm] 纯文本查看 复制代码
// write access to const memory has been detected, the output may be wrong!
int __fastcall sub_16D8(int a1, int a2)
{
  int v2; // r4
  int v3; // r5
  int v4; // r6
  int v5; // r7
  int v6; // r8
  int v7; // r9
  int v8; // r10
  int v9; // r11
  int v10; // r10
  int v11; // r6
  int v12; // r5
  void *v13; // r9
  int v14; // r11
  int v15; // r11
  unsigned int v16; // r10
  bool v17; // zf
  int v18; // r0
  unsigned int i; // r1
  int j; // r1
  unsigned int k; // r1
  int l; // r0
  unsigned int m; // r2
  size_t v24; // r6
  int n; // r2
  int result; // r0
  int v27; // [sp+4h] [bp-44h]
  int v28; // [sp+8h] [bp-40h]
  size_t v29; // [sp+14h] [bp-34h]
  _BYTE v30[3]; // [sp+19h] [bp-2Fh]
  char v31; // [sp+1Ch] [bp-2Ch]
  char v32; // [sp+1Dh] [bp-2Bh]
  char v33; // [sp+1Eh] [bp-2Ah]
  char v34; // [sp+1Fh] [bp-29h]
  int v35; // [sp+20h] [bp-28h]
  int v36; // [sp+24h] [bp-24h]
  int v37; // [sp+28h] [bp-20h]
  int v38; // [sp+2Ch] [bp-1Ch]
  int v39; // [sp+30h] [bp-18h]
  int v40; // [sp+34h] [bp-14h]
  int v41; // [sp+38h] [bp-10h]
  int v42; // [sp+3Ch] [bp-Ch]
  int v43; // [sp+40h] [bp-8h]

  v40 = v2;
  v41 = v3;
  v42 = v4;
  v43 = v5;
  v36 = v6;
  v37 = v7;
  v38 = v8;
  v39 = v9;
  v10 = a1;
  v11 = a2;
  v12 = 0;
  v13 = malloc(0);
  if ( v13 )
  {
    v14 = 0;
    v27 = v10;
    v29 = 0;
LABEL_3:
    v28 = v14;
    v15 = v14 + v10;
    v16 = 0;
    while ( 1 )
    {
      v17 = v11 == v16;
      if ( v11 != v16 )
      {
        v12 = *(v15 + v16);
        v17 = v12 == 61;
      }
      if ( v17 || !isalnum(v12) && (v12 | 4) != 47 )
        break;
      v30[v16++] = v12;
      if ( v16 == 4 )
      {
        v11 -= 4;
        v18 = 0;
        v14 = v28 + 4;
        while ( v18 != 4 )
        {
          for ( i = 0; i <= 0x3F; ++i )
          {
            if ( v30[v18] == byte_4455[i] )
            {
              v30[v18] = i;
              break;
            }
          }
          ++v18;
        }
        v34 = v31 + (v30[2] << 6);
        v32 = (v30[1] >> 4) & 3 | 4 * v30[0];
        v12 = v29;
        v33 = (v30[2] >> 2) & 0xF | 16 * v30[1];
        v13 = realloc(v13, v29 + 3);
        for ( j = 0; j != 3; ++j )
          *(v13 + v29 + j) = *(&v32 + j);
        v29 += 3;
        v10 = v27;
        goto LABEL_3;
      }
    }
    if ( v16 )
    {
      for ( k = v16; k < 4; ++k )
        v30[k] = 0;
      for ( l = 0; l != 4; ++l )
      {
        for ( m = 0; m <= 0x3F; ++m )
        {
          if ( v30[l] == byte_4455[m] )
          {
            v30[l] = m;
            break;
          }
        }
      }
      v34 = v31 + (v30[2] << 6);
      v32 = (v30[1] >> 4) & 3 | 4 * v30[0];
      v33 = (v30[2] >> 2) & 0xF | 16 * v30[1];
      v24 = v29 + v16 - 1;
      v13 = realloc(v13, v24);
      for ( n = 0; v16 - 1 != n; ++n )
        *(v13 + v29 + n) = *(&v32 + n);
    }
    else
    {
      v24 = v29;
    }
    v12 = realloc(v13, v24 + 1);
    *(v12 + v24) = 0;
  }
  result = _stack_chk_guard - v35;
  if ( _stack_chk_guard == v35 )
    result = v12;
  return result;
}


是不是很长 ,是不是很怕~,这个函数只是进行Base64解密,结果sdniscsdniscabcd,但是这个结果,上层函数并没有使用,所以分析了半天,这个函数居然没用~~~~~
这个时候我们就要想下是不是JNI_OnLoad或者.init_array是否被做了手脚了,先看JNI_OnLoad,并没有什么可疑的地方

[Asm] 纯文本查看 复制代码
.text:00000E84 ; signed int __fastcall JNI_OnLoad(_JNIEnv *a1, int a2, int a3, int a4)
.text:00000E84                 EXPORT JNI_OnLoad
.text:00000E84 JNI_OnLoad                              ; DATA XREF: LOAD:00000220↑o
.text:00000E84
.text:00000E84 var_10          = -0x10
.text:00000E84 var_C           = -0xC
.text:00000E84
.text:00000E84 ; __unwind {
.text:00000E84                 PUSH            {R2-R4,R6,R7,LR}
.text:00000E86                 ADD             R7, SP, #0x10
.text:00000E88                 LDR             R1, =(__stack_chk_guard_ptr - 0xE90)
.text:00000E8A                 LDR             R2, =0x10006
.text:00000E8C                 ADD             R1, PC  ; __stack_chk_guard_ptr
.text:00000E8E                 LDR             R4, [R1] ; __stack_chk_guard
.text:00000E90                 LDR             R1, [R4]
.text:00000E92                 STR             R1, [SP,#0x10+var_C]
.text:00000E94                 MOVS            R1, #0
.text:00000E96                 STR             R1, [SP,#0x10+var_10]
.text:00000E98                 LDR             R1, [R0]
.text:00000E9A                 LDR             R3, [R1,#0x18]
.text:00000E9C                 MOV             R1, SP
.text:00000E9E                 BLX             R3      ; FindClass
.text:00000EA0                 MOV             R1, R0
.text:00000EA2                 MOV.W           R0, #0xFFFFFFFF
.text:00000EA6                 CMP             R1, #0
.text:00000EA8                 ITT EQ
.text:00000EAA                 MOVEQ           R0, #6
.text:00000EAC                 MOVTEQ.W        R0, #1
.text:00000EB0                 LDR             R1, [SP,#0x10+var_C]
.text:00000EB2                 LDR             R2, [R4]
.text:00000EB4                 SUBS            R1, R2, R1
.text:00000EB6                 ITT EQ
.text:00000EB8                 ADDEQ           SP, SP, #8
.text:00000EBA                 POPEQ           {R4,R6,R7,PC}
.text:00000EBC                 BLX             __stack_chk_fail


再看.init_array,调用sub_F20

[Asm] 纯文本查看 复制代码
.text:00000F20 sub_F20                                 ; DATA XREF: .init_array:00005E00↓o
.text:00000F20 ; __unwind {
.text:00000F20                 B.W             sub_EC8
.text:00000F20 ; } // starts at F20


再调用sub_EC8

[Asm] 纯文本查看 复制代码
.text:00000EC8 sub_EC8                                 ; CODE XREF: sub_F20↓j
.text:00000EC8 ; __unwind {
.text:00000EC8                 PUSH            {R4,R6,R7,LR}
.text:00000ECA                 ADD             R7, SP, #8
.text:00000ECC                 LDR             R0, =(strcmp_ptr - 0xED4)
.text:00000ECE                 LDR             R1, =(sub_E28+1 - 0xED8)
.text:00000ED0                 ADD             R0, PC  ; strcmp_ptr
.text:00000ED2                 LDR             R2, =(dword_6008 - 0xEDC)
.text:00000ED4                 ADD             R1, PC  ; sub_E28
.text:00000ED6                 LDR             R4, [R0] ; __imp_strcmp
.text:00000ED8                 ADD             R2, PC  ; dword_6008
.text:00000EDA                 MOV             R0, R4
.text:00000EDC                 BLX             j_registerInlineHook ; 将strcmp替换为sub_E28,将dword_6008替换为strcmp
.text:00000EE0                 CBZ             R0, loc_EE8
.text:00000EE2                 MOV.W           R0, #0xFFFFFFFF.text:00000EE6                 POP             {R4,R6,R7,PC}


查看sub_E28

[Asm] 纯文本查看 复制代码
.text:00000E28                 PUSH            {R4,R6,R7,LR}
.text:00000E2A                 ADD             R7, SP, #8
.text:00000E2C                 MOV             R4, R0  ; R4 = R0 = input
.text:00000E2E                 BL              sub_E08 ; 之前分析过了,Base64解密,返回值为sdniscsdniscabcd
.text:00000E32                 MOV             R1, R0  ; R1 = RO = "sdniscsdniscabcd"
.text:00000E34                 MOV             R0, R4  ; R0 = R4 = input
.text:00000E36                 BL              sub_1388 ; sub_1388(input,"sdniscsdniscabcd")
.text:00000E3A                 LDR             R2, =(dword_6008 - 0xE42)
.text:00000E3C                 LDR             R1, =(aK47Faihmk9Weml - 0xE44)
.text:00000E3E                 ADD             R2, PC  ; dword_6008
.text:00000E40                 ADD             R1, PC  ; "K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8="
.text:00000E42                 LDR             R2, [R2]
.text:00000E44                 POP.W           {R4,R6,R7,LR}
.text:00000E48                 BX              R2      ; 执行dword_6008,其实是strcmp


所以这个函数的主要意思就是sub_1388(input,"sdniscsdniscabcd"),然后和"K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8="比较,我们继续看sub_1388

[C] 纯文本查看 复制代码
int __fastcall sub_1388(const char *a1, int a2)
{
  int v2; // r9
  const char *v3; // r6
  signed int v4; // r0
  signed int v5; // r4
  _BYTE *v6; // r11
  signed int j; // r0
  const char *v8; // r1
  signed int v9; // r8
  signed int i; // r1
  char *v11; // r2
  char v12; // r2
  char *v13; // r10
  int v14; // r5
  int k; // r4
  int v16; // r6

  v2 = a2;
  v3 = a1;
  v4 = strlen(a1);
  v5 = v4;
  if ( v4 > 15 )
  {
    v9 = (v4 + 16) & 0xFFFFFFF0;
    v6 = malloc(v9);
    for ( i = 0; ; ++i )
    {
      if ( i >= v9 )
        goto LABEL_16;
      if ( i < v5 )
        break;
      v11 = &unk_4146 + v9 - v5;
      if ( v5 & 0xF )
        goto LABEL_13;
      v12 = 16;
LABEL_14:
      v6[i] = v12;
    }
    v11 = &v3[i];
LABEL_13:
    v12 = *v11;
    goto LABEL_14;
  }
  v6 = malloc(0x10u);
  for ( j = 0; j != 16; ++j )
  {
    v8 = &unk_4146 + 16 - v5;
    if ( j < v5 )
      v8 = &v3[j];
    v6[j] = *v8;
  }
  v9 = 16;
LABEL_16:
  v13 = malloc(v9);
  v14 = 0;
  for ( k = 0; k < v9 / 16; ++k )
  {
    sub_F24(&v6[v14], v2, &v13[v14]);
    v14 += 16;
  }
  v16 = sub_156C(v13, v9);
  free(v6);
  free(v13);
  return v16;
}


再看sub_F24

[C] 纯文本查看 复制代码
int __fastcall sub_F24(int a1, int a2, int a3)
{
  int i; // r3
  int j; // r1
  int v5; // r2
  unsigned __int8 *v6; // r3
  int v7; // r5
  int v8; // r12
  int v9; // lr
  unsigned __int8 v10; // r1
  int v11; // r9
  int v12; // r2
  int v13; // r0
  int k; // r3
  _BYTE *v15; // r6
  char v16; // r0
  char v17; // lr
  char v18; // r12
  char v19; // r4
  char v20; // r3
  char v21; // r2
  char v22; // r5
  char v23; // r4
  char v24; // r0
  int v25; // r0
  int v27; // [sp+4h] [bp-24h]
  _BYTE *v28; // [sp+8h] [bp-20h]

  for ( i = 0; i != 16; ++i )
    *(a3 + i) = *(a1 + i);
  dword_60BC = a3;
  dword_60C0 = a2;
  sub_105C();
  sub_1524(0);
  for ( j = 1; ; j = v27 + 1 )
  {
    v12 = dword_60BC;
    v13 = 0;
    v28 = dword_60BC;
    while ( v13 != 4 )
    {
      for ( k = 0; k != 4; ++k )
        *(v12 + 4 * k) = RijnDael_AES_LONG_4255[*(v12 + 4 * k)];
      ++v12;
      ++v13;
    }
    v15 = v28;
    v16 = v28[1];
    v17 = v28[2];
    v18 = v28[3];
    v19 = v28[9];
    v20 = v28[13];
    v21 = v28[10];
    v28[1] = v28[5];
    v22 = v28[6];
    v28[5] = v19;
    v23 = v28[14];
    v28[13] = v16;
    v24 = v28[15];
    v28[9] = v20;
    v28[2] = v21;
    v28[10] = v17;
    v28[6] = v23;
    v28[14] = v22;
    v28[3] = v24;
    v15[15] = v15[11];
    v15[11] = v15[7];
    v25 = j;
    v28[7] = v18;
    if ( j > 9u )
      break;
    v5 = 0;
    v27 = j;
    while ( v5 != 4 )
    {
      v6 = &v28[4 * v5];
      v7 = v28[4 * v5];
      v8 = v6[1];
      v9 = v6[2];
      v10 = v6[3];
      v11 = v8 ^ v7 ^ v9;
      v28[4 * v5++] = v7 ^ 2 * (v8 ^ v7) ^ ((v8 ^ v7) >> 7) & 0x1B ^ v11 ^ v10;
      v6[2] = v8 ^ v7 ^ v10 ^ 2 * (v10 ^ v9) ^ ((v10 ^ v9) >> 7) & 0x1B;
      v6[3] = ((v10 ^ v7) >> 7) & 0x1B ^ v11 ^ 2 * (v10 ^ v7);
      v6[1] = v11 ^ v10 ^ v8 ^ 2 * (v9 ^ v8) ^ ((v9 ^ v8) >> 7) & 0x1B;
    }
    sub_1524(v25);
  }
  return sub_1524(10);
}


中间很多分支函数,一个个的看,找到关键点AES_LONG_4255,猜测是标准的AES加密,但是那种类型的就不知道,
这时候可疑用一个小工具了,"K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8="这是结果,key是"sdniscsdniscabcd"

QQ截图20181112142321.jpg

结果出来了,为flag{fake_func_3nfxvs},其实这题还有其他坑的地方,就是当你用手机打开的时候,你会发现找不到check按钮(上面的是模拟器运行的),
还要自己修改修改布局文件才行,有兴趣的可疑自己试一试,后面会上传apk

QQ截图20181112142543.jpg

三、第二个CTF

先打开app


拉入jeb分析下

[Java] 纯文本查看 复制代码
    public native String getKey(String arg1) {
    }

    protected void onCreate(Bundle arg4) {
        super.onCreate(arg4);
        this.setContentView(0x7F09001B);
        View v4 = this.findViewById(0x7F070062);
        ((TextView)v4).setText(this.stringFromJNI());
        this.findViewById(0x7F070031).setOnClickListener(new View$OnClickListener() {
            public void onClick(View arg5) {
                arg5 = MainActivity.this.findViewById(0x7F070062);
                String input = MainActivity.this.findViewById(0x7F070003).getText().toString();
                if(input.length() == 16) {  // 判断是否为16位
                    input = MainActivity.this.getKey(input);  // 调用native方法,从输入的字符串得到key
                    View v1 = MainActivity.this.findViewById(0x7F070046);
                    Bitmap v0_1 = MagicImageUtils.readMagicImage(MainActivity.this, "png/encrypt_png.dat", input);  // 用key解密图片,然后显示
                    if(v0_1 != null) {
                        ((TextView)arg5).setText("Congratulations!");
                        MainActivity.this.showMsgToast("Congratulations!");
                        ((ImageView)v1).setImageBitmap(v0_1);
                    }
                    else {
                        MainActivity.this.showMsgToast("Something wrong!");
                    }
                }
                else {
                    MainActivity.this.showMsgToast("Something wrong!");
                }
            }
        });
        View v0 = this.findViewById(0x7F070046);
        Bitmap v1 = MagicImageUtils.readImage(((Context)this), "png/bg.png");
        if(v1 != null) {
            ((TextView)v4).setText(" ");
            ((ImageView)v0).setImageBitmap(v1);
        }
        else {
            this.showMsgToast("Something wrong!");
        }
    }


看下native层函数,简单分析如下

[C] 纯文本查看 复制代码
jstring __fastcall Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey(int a1, int a2, int a3)
{
  _JNIEnv *v3; // r5
  int input; // r8
  int input_len; // r10
  int input_key; // r9
  signed int i; // r5
  char *v8; // r1
  int v9; // r0
  const char *v10; // r6
  char v11; // r0
  unsigned int v12; // r0
  const char *v13; // r1
  jstring v14; // r4
  jstring result; // r0
  _JNIEnv *v16; // [sp+4h] [bp-44h]
  int v17; // [sp+8h] [bp-40h]
  int v18; // [sp+Ch] [bp-3Ch]
  const char *v19; // [sp+10h] [bp-38h]
  int v20; // [sp+18h] [bp-30h]
  int v21; // [sp+1Ch] [bp-2Ch]
  char *v22; // [sp+20h] [bp-28h]
  int v23; // [sp+28h] [bp-20h]

  v3 = a1;
  input = a3;
  v22 = 0;
  v20 = 0;
  v21 = 0;
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::__init(
    &v20,
    "Welcome_to_sdnisc_2018_By.Zero",
    30);
  v19 = 0;
  v17 = 0;
  v18 = 0;
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::__init(
    &v17,
    "                ",
    16);
  input_len = (v3->functions->GetStringLength)(v3, input);
  v16 = v3;
  input_key = (v3->functions->GetStringChars)(v3, input, 0);
  for ( i = 0; ; ++i )
  {
    v12 = sub_75A4D5AC(i, 0x1Eu);
    if ( i >= input_len )                       // 前面已经分析key的长度只能是16,所以就是循环16次
      break;
    v8 = v22;
    v9 = -30 * v12;
    v10 = v19;
    if ( !(v20 & 1) )
      v8 = &v20 + 1;
    v11 = v8[v9 + i];
    if ( !(v17 & 1) )
      v10 = &v17 + 1;
    v10[i] = v11 ^ *(input_key + 2 * i);        // 前面的和key都无关系,只需确认16次v11,里分别是多少
  }
  v13 = v19;
  if ( !(v17 & 1) )
    v13 = &v17 + 1;
  v14 = v16->functions->NewStringUTF(&v16->functions, v13);
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v17);
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v20);
  result = (_stack_chk_guard - v23);
  if ( _stack_chk_guard == v23 )
    result = v14;
  return result;
}


这里v11 ^ *(input_key + 2 * i),是关键部分,但是为什么是input_key + 2 * i呢?动态调试粉线内存存储的是这样的

QQ图片20181112150923.png

那就对上了,与我们的key进行亦或,记录16次,v11的值,分别是 0x57,0x65,0x6c,0x63,0x6f,0x6d,0x65,0x5f,0x74,0x6f,0x5f,0x73,0x64,0x6e,0x69,0x73
但是到此为止,我们进行不下去了,16位的key无法穷举,所以只能从java层进行入手了,回到java层

[Java] 纯文本查看 复制代码
 public static native int decrypt(int arg0, char arg1) {
    }

    public static Bitmap readImage(Context arg6, String arg7) {
        Bitmap v6_3;
        ArrayList v0 = new ArrayList();
        Bitmap v1 = null;
        try {
            InputStream v6_1 = arg6.getAssets().open(arg7);
            while(true) {
                int v7 = v6_1.read();
                if(v7 <= -1) {
                    break;
                }

                ((List)v0).add(Byte.valueOf(((byte)v7)));
            }

            byte[] v6_2 = new byte[((List)v0).size()];
            Iterator v7_1 = ((List)v0).iterator();
            int v3;
            for(v3 = 0; v7_1.hasNext(); ++v3) {
                v6_2[v3] = v7_1.next().byteValue();
            }

            v6_3 = BitmapFactory.decodeByteArray(v6_2, 0, ((List)v0).size());
        }
        catch(IOException v6) {
            goto label_35;
        }

        try {
            System.out.println(v6_3);
            return v6_3;
        }
        catch(IOException v7_2) {
            v1 = v6_3;
            v6 = v7_2;
        }

    label_35:
        v6.printStackTrace();
        return v1;
    }

    public static Bitmap readMagicImage(Context arg5, String arg6, String arg7) {
        Bitmap v5_3;
        ArrayList v0 = new ArrayList();
        Bitmap v1 = null;
        try {
            InputStream v5_1 = arg5.getAssets().open(arg6);
            int v2;
            for(v2 = 0; true; ++v2) {
                int v3 = v5_1.read();
                if(v3 <= -1) {
                    break;
                }

                ((List)v0).add(Byte.valueOf(((byte)MagicImageUtils.decrypt(v3, arg7.charAt(v2 % 16)))));
            }

            byte[] v5_2 = new byte[((List)v0).size()];
            Iterator v7 = ((List)v0).iterator();
            for(v2 = 0; v7.hasNext(); ++v2) {
                v5_2[v2] = v7.next().byteValue();
            }

            v5_3 = BitmapFactory.decodeByteArray(v5_2, 0, ((List)v0).size());
        }
        catch(IOException v5) {
            goto label_40;
        }

        try {
            System.out.println(v5_3);
            return v5_3;
        }
        catch(IOException v6) {
            v1 = v5_3;
            v5 = v6;
        }

    label_40:
        v5.printStackTrace();
        return v1;
    }

readImage,读取正常格式的图片,readMagicImage读取加密的格式,中间的区别只有一个,MagicImageUtils.decrypt(v3, arg7.charAt(v2 % 16))
[Asm] 纯文本查看 复制代码
.text:75A42C30 Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt
.text:75A42C30 ;   __unwind {
.text:75A42C30                 SUBS            R0, R2, #1
.text:75A42C32                 EORS            R0, R3
.text:75A42C34                 EOR.W           R0, R0, #0x61
.text:75A42C38                 BX              LR
.text:75A42C38 ;   } // starts at 75A42C30


很简单,就是进行下亦或解密,由于就多了一步每个字节亦或解密,这时候我们就可以从PNG文件格式入手

QQ图片20181112152139.png

三个圈,分别是PNG文件头,chunk长度,chunk标识,三个基本都是不会变得,所以我们用两个头文件前16位进行亦或,拿到key,顺便解密加密的png文件

[C] 纯文本查看 复制代码
int png[16] = { 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52 };
int data[16] = { 0xe8,0x36,0x07,0x77,0x2a,0x35,0x3e,0x75,0x35,0x80,0x69,0x42,0x3b,0x6d,0x14,0x6f };
int table[16] = { 0x57,0x65,0x6c,0x63,0x6f,0x6d,0x65,0x5f,0x74,0x6f,0x5f,0x73,0x64,0x6e,0x69,0x73 };
int key[16];

 for (int i = 0; i < 16; i++)
        {
                printf("%d,", decrypt(data[i], png[i]));
                key[i] = decrypt(data[i], png[i]);
        }
        printf("\n");
        FILE *p = fopen("c:\\users\\administrator\\desktop\\encrypt_png.dat", "rb");
        FILE *fp = fopen("c:\\users\\administrator\\desktop\\dat.png", "wb");

        while (!feof(p))
        {
                for (int i = 0; i < 16; i++)
                {
                        fputc(decrypt(fgetc(p) , key[i]), fp);
                }
        }
        fclose(p);
        fclose(fp);


下面是解密的png文件,成功拿到flag

dat.png

如果这是比赛,到此即可结束了,但是我们出于学习目的,所以继续,拿到安卓端的key才行,前面分析已经拿到了table(就是前面那个16次v11)
所以我们用key再和table亦或下即可

[C] 纯文本查看 复制代码
int png[16] = { 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52 };
int data[16] = { 0xe8,0x36,0x07,0x77,0x2a,0x35,0x3e,0x75,0x35,0x80,0x69,0x42,0x3b,0x6d,0x14,0x6f };
int table[16] = { 0x57,0x65,0x6c,0x63,0x6f,0x6d,0x65,0x5f,0x74,0x6f,0x5f,0x73,0x64,0x6e,0x69,0x73 };
int key[16];

        for (int i = 0; i < 16; i++)
        {
                printf("%d,", decrypt(data[i], png[i]));
                key[i] = decrypt(data[i], png[i]);
        }
        printf("\n");
        
        for (int i = 0; i < 16; i++)
        {
                printf("%c", key[i] ^ table[i]);
        }
        printf("\n");


成功拿到key,XaE3*2#@!qV^v+_.  输入看下结果

QQ截图20181112153224.jpg

贴个完整版的代码
[C] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int png[16] = { 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52 };
int data[16] = { 0xe8,0x36,0x07,0x77,0x2a,0x35,0x3e,0x75,0x35,0x80,0x69,0x42,0x3b,0x6d,0x14,0x6f };
int table[16] = { 0x57,0x65,0x6c,0x63,0x6f,0x6d,0x65,0x5f,0x74,0x6f,0x5f,0x73,0x64,0x6e,0x69,0x73 };
int key[16];


int decrypt(int v3, int a4)
{
        return (v3 - 1) ^ a4 ^ 0x61;
}


int main(void)
{

        for (int i = 0; i < 16; i++)
        {
                printf("%d,", decrypt(data[i], png[i]));
                key[i] = decrypt(data[i], png[i]);
        }
        printf("\n");
        
        for (int i = 0; i < 16; i++)
        {
                printf("%c", key[i] ^ table[i]);
        }
        printf("\n");

        FILE *p = fopen("c:\\users\\administrator\\desktop\\encrypt_png.dat", "rb");
        FILE *fp = fopen("c:\\users\\administrator\\desktop\\dat.png", "wb");

        while (!feof(p))
        {
                for (int i = 0; i < 16; i++)
                {
                        fputc(decrypt(fgetc(p) , key[i]), fp);
                }

        }
        fclose(p);
        fclose(fp);

        printf("\n");
        system("pause");
        return 0;
}


第二个也是样本,太大了,分开打包的
样本.zip (2.7 MB, 下载次数: 232) MagicImageViewer.zip (2.31 MB, 下载次数: 178)



点评

感谢分享  发表于 2019-3-11 15:48

免费评分

参与人数 24吾爱币 +22 热心值 +21 收起 理由
kkf123 + 1 热心回复!
K_K + 1 + 1 我很赞同!
NM$L + 1 + 1 谢谢@Thanks!
初心wo + 1 + 1 谢谢@Thanks!
一筐萝卜 + 1 + 1 我很赞同!
siuhoapdou + 1 + 1 谢谢@Thanks!
YCAPTAIN + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
丶咖啡猫丶 + 1 + 1 我很赞同!
QinBeast + 1 + 1 用心讨论,共获提升!
静叶流云 + 1 + 1 用心讨论,共获提升!
XhyEax + 2 + 1 我很赞同!
zzp98756 + 1 + 1 用心讨论,共获提升!
喜之郎果冻 + 1 用心讨论,共获提升!
hack123 + 1 热心回复!
这里是废物 + 1 谢谢@Thanks!
bb8820 + 1 用心讨论,共获提升!
usermargin + 1 + 1 谢谢@Thanks!
Dispa1r + 1 + 1 谢谢@Thanks!
笙若 + 1 谢谢@Thanks!
voyage1969 + 1 + 1 仰望大佬
Crdsa + 1 谢谢@Thanks!
onmiuncai + 1 + 1 谢谢@Thanks!
二逼159 + 1 + 1 热心回复!
wmsuper + 2 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

UC大法好 发表于 2018-11-12 18:43
很厉害啊,可惜我还不会
feifei_shen 发表于 2018-12-13 15:19
膜拜大佬!!!  为什么我在反汇编sub_F24后出现的是Byte_4255而不是RijnDael_AES_LONG_4255,大佬是怎么操作的,还是用的什么插件吗?
我也有相同的问题。是安装了findcrypt3么?还是自己看出来之后自己改的名字?
t0m1tu 发表于 2018-11-12 17:53
对比我只能做出来找规律的题而言,已经很厉害了
now88888 发表于 2018-11-12 18:04
很厉害了!、!!
头像被屏蔽
闪电游戏 发表于 2018-11-12 18:19
还不错嘛!!
ANDCHUNG 发表于 2018-11-12 18:22
学到了,多谢了
crakergod 发表于 2018-11-12 18:50
学习了!!
壹万叁 发表于 2018-11-12 19:05
谢谢分享,学习了~
Yuniova 发表于 2018-11-12 20:01
下载下来尝试一下 代码看起来挺复杂
尕可 发表于 2018-11-12 20:17
很不错,支持楼主一个~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 14:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表