Anonymous、 发表于 2018-11-12 15:38

两道简单的CTF

本帖最后由 Anonymous、 于 2018-11-12 20:55 编辑

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

二、第一个CTF
这一题比较简单,先打开app看下



java层代码很简单,就是调用native层的一个校验函数
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层函数

.text:00000E54               PUSH            {R4,R6,R7,LR}
.text:00000E56               ADD             R7, SP, #8
.text:00000E58               MOV             R1, R2
.text:00000E5A               LDR             R2,
.text:00000E5C               LDR.W         R3,
.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, ; "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,这个函数看下



.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, ; "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这个函数

// 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; //
int v28; //
size_t v29; //
_BYTE v30; //
char v31; //
char v32; //
char v33; //
char v34; //
int v35; //
int v36; //
int v37; //
int v38; //
int v39; //
int v40; //
int v41; //
int v42; //
int v43; //

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 = v12;
      if ( v16 == 4 )
      {
      v11 -= 4;
      v18 = 0;
      v14 = v28 + 4;
      while ( v18 != 4 )
      {
          for ( i = 0; i <= 0x3F; ++i )
          {
            if ( v30 == byte_4455 )
            {
            v30 = i;
            break;
            }
          }
          ++v18;
      }
      v34 = v31 + (v30 << 6);
      v32 = (v30 >> 4) & 3 | 4 * v30;
      v12 = v29;
      v33 = (v30 >> 2) & 0xF | 16 * v30;
      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 = 0;
      for ( l = 0; l != 4; ++l )
      {
      for ( m = 0; m <= 0x3F; ++m )
      {
          if ( v30 == byte_4455 )
          {
            v30 = m;
            break;
          }
      }
      }
      v34 = v31 + (v30 << 6);
      v32 = (v30 >> 4) & 3 | 4 * v30;
      v33 = (v30 >> 2) & 0xF | 16 * v30;
      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,并没有什么可疑的地方

.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, ; __stack_chk_guard
.text:00000E90               LDR             R1,
.text:00000E92               STR             R1,
.text:00000E94               MOVS            R1, #0
.text:00000E96               STR             R1,
.text:00000E98               LDR             R1,
.text:00000E9A               LDR             R3,
.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,
.text:00000EB2               LDR             R2,
.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

.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

.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, ; __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

.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,
.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

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 = v12;
    }
    v11 = &v3;
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;
    v6 = *v8;
}
v9 = 16;
LABEL_16:
v13 = malloc(v9);
v14 = 0;
for ( k = 0; k < v9 / 16; ++k )
{
    sub_F24(&v6, v2, &v13);
    v14 += 16;
}
v16 = sub_156C(v13, v9);
free(v6);
free(v13);
return v16;
}

再看sub_F24

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; //
_BYTE *v28; //

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;
    v17 = v28;
    v18 = v28;
    v19 = v28;
    v20 = v28;
    v21 = v28;
    v28 = v28;
    v22 = v28;
    v28 = v19;
    v23 = v28;
    v28 = v16;
    v24 = v28;
    v28 = v20;
    v28 = v21;
    v28 = v17;
    v28 = v23;
    v28 = v22;
    v28 = v24;
    v15 = v15;
    v15 = v15;
    v25 = j;
    v28 = v18;
    if ( j > 9u )
      break;
    v5 = 0;
    v27 = j;
    while ( v5 != 4 )
    {
      v6 = &v28;
      v7 = v28;
      v8 = v6;
      v9 = v6;
      v10 = v6;
      v11 = v8 ^ v7 ^ v9;
      v28 = v7 ^ 2 * (v8 ^ v7) ^ ((v8 ^ v7) >> 7) & 0x1B ^ v11 ^ v10;
      v6 = v8 ^ v7 ^ v10 ^ 2 * (v10 ^ v9) ^ ((v10 ^ v9) >> 7) & 0x1B;
      v6 = ((v10 ^ v7) >> 7) & 0x1B ^ v11 ^ 2 * (v10 ^ v7);
      v6 = 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"



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



三、第二个CTF

先打开app


拉入jeb分析下

    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层函数,简单分析如下

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; //
int v17; //
int v18; //
const char *v19; //
int v20; //
int v21; //
char *v22; //
int v23; //

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;
    if ( !(v17 & 1) )
      v10 = &v17 + 1;
    v10 = 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呢?动态调试粉线内存存储的是这样的



那就对上了,与我们的key进行亦或,记录16次,v11的值,分别是 0x57,0x65,0x6c,0x63,0x6f,0x6d,0x65,0x5f,0x74,0x6f,0x5f,0x73,0x64,0x6e,0x69,0x73
但是到此为止,我们进行不下去了,16位的key无法穷举,所以只能从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 = 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 = 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))
.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文件格式入手



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

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

for (int i = 0; i < 16; i++)
      {
                printf("%d,", decrypt(data, png));
                key = decrypt(data, png);
      }
      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), fp);
                }
      }
      fclose(p);
      fclose(fp);

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



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

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

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

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



贴个完整版的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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


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, png));
                key = decrypt(data, png);
      }
      printf("\n");
      
      for (int i = 0; i < 16; i++)
      {
                printf("%c", key ^ table);
      }
      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), fp);
                }

      }
      fclose(p);
      fclose(fp);

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

第二个也是样本,太大了,分开打包的




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

很不错,支持楼主一个~
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 两道简单的CTF