首页
社区
课程
招聘
[原创]看雪CTF2017 第十二题分析
2017-6-25 12:06 4491

[原创]看雪CTF2017 第十二题分析

2017-6-25 12:06
4491

先说一下如何过反调试,调试环境为ida和android模拟器,ida卡到不行啊,羡慕有真机的。

模拟器启动程序后使用ida附加,注意先不要点check按钮,不然so后启动。

ida在exit处下断点,继续执行程序,点check按钮加载so,不出意外ida会断在exit函数处。

在ida的threads窗口找到当前线程,将其挂起即可,去除反调试功能。

在check函数中依然有反调试检查,由于程序有cache检查的bug,可继续调试,真机还不确定。


使用工具dex2jar和jd-gui反编译java代码,主要函数如下:

      public void onClick(View paramAnonymousView)
      {
        paramAnonymousView = Main.this.editText.getText().toString().trim();
        if (ua.check(ca.a(paramAnonymousView) + ba.a(paramAnonymousView) + ab.a(paramAnonymousView)) == 1)
        {
          bc.showresult(Main.this.tv, true);
          return;
        }
        bc.showresult(Main.this.tv, false);
      }

首先,对输入sn进行编码,分别为ca.a,ba.a,ab.a完成后组成字符串传给ua.check,并且返回1

ua.check是个so库,通过ida进行反编译,其check入口为:

    v6 = &s;
    memset(&s, 0, 0x25u);
    v7 = v5;
    do
    {
      v8 = *(_DWORD *)v7;
      v7 += 8;
      v9 = *((_DWORD *)v7 - 1);
      *(_DWORD *)v6 = v8;
      *((_DWORD *)v6 + 1) = v9;
      v10 = v6 + 8;
      v6 += 8;
    }
    while ( v7 != v5 + 32 );
    *v10 = *(_DWORD *)v7;
    ((void (__fastcall *)(_JNIEnv *, _JavaVM *, const char *))v3->functions->ReleaseStringUTFChars)(v3, v4, v5);
    v11 = strlen(&s);
    result = do_check((int *)&s, v11) != 0;

继续跟踪do_check函数

  if ( len == 36 )
  {
    sn_2 = j_j_malloc(0x28u);
    sn_3 = (int)sn_2;
    if ( sn_2 )
    {
      j_j_memcpy(sn_2, sn_1, v4);
      *(_BYTE *)(sn_3 + 36) = 4;
      *(_BYTE *)(sn_3 + 37) = 4;
      *(_BYTE *)(sn_3 + 38) = 4;
      *(_BYTE *)(sn_3 + 39) = 4;

输入sn长度必须为36,且后面补4个4

      do
      {
        v8 = byte_5D24[v2];                     // des key
        v9 = 0;
        do
        {
          *(&key_bits[8 * v2] + v9) = (v8 >> (7 - v9)) & 1;
          ++v9;
        }
        while ( v9 != 8 );
        ++v2;
      }
      while ( v2 != 8 );
      sub_1650((int)key_bits);

上面是设置des密钥,这个过程被作者修改过,我们可以把扩展后key直接拿出来用。

      do
      {
        v11 = (void *)(sn_3 + v10);
        j_j_memcpy(&dest, (const void *)(sn_3 + v10), 8u);
        v15 = 0;
        v16 = 0;
        des_encrypt((int)&dest, (int)&v15);
        v10 += 8;
        j_j_memcpy(v11, &v15, 8u);
      }
      while ( v10 != 40 );

对sn进行des加密

      update_key2((int)&key2, (int)&v15);
      rc6_encrypt(sn_3, 0x20u, (char *)&key2, 16);
      v12 = 0;
      while ( *(_BYTE *)(sn_3 + v12) == byte_5D3D[v12] )
      {
        if ( ++v12 == 32 )
        {
          result = 1;
          goto LABEL_14;
        }
      }

上面是更新rc6的key(替换key后4字节为des加密结果+32处4字节),并且进行rc6加密。加密结果与必须为:

42 D3 C3 C2 F1 2A E9 2D  66 C9 28 22 2C EB 54 0E
94 07 E5 77 4A 92 B7 92  2E 5D FD F0 F3 54 9F C6

rc6扩展key也被修改过,如下:

  *a1 = 0xB7E15163;
  v3 = a1;
  v4 = a1 + 43;
  v5 = a2;
  do
  {
    v3[1] = *v3 + 0x61C88647;
    ++v3;
  }
  while ( v3 != v4 );
  
// 下面是标准p,q
// const unsigned int p = 0xb7e15163;
// const unsigned int q = 0x9e3779b9 // <- 标准

逆向方法如下:

1. 作者给出了前8个字符为kxuectf{,猜测最后一个字符为}。前8个字符正好是des块长度,也就是说,我们知道了rc6的部分明文和密文,可以通过rc6解密穷举出rc6后4位key和全部rc6的明文。

	unsigned char buf11[32] = {
		0x42, 0xD3, 0xC3, 0xC2, 0xF1, 0x2A, 0xE9, 0x2D, 
		0x66, 0xC9, 0x28, 0x22, 0x2C, 0xEB, 0x54, 0x0E, 
		0x94, 0x07, 0xE5, 0x77, 0x4A, 0x92, 0xB7, 0x92,
		0x2E, 0x5D, 0xFD, 0xF0, 0xF3, 0x54, 0x9F, 0xC6
	};
	unsigned char buf12[32] = { 0};
	unsigned char buf99[32] = { 0x4C, 0xD9, 0xA3, 0xE6, 0xED, 0xFE, 0xD1, 0x05, 0xC8, 0x97, 0x65, 0xD4, 0x9A, 0x53, 0xF4, 0xD1 };
	unsigned char key2[32] = {
		0x65, 0x48, 0x32, 0xEF, 0xBA, 0xCD, 0x56, 0x4E, 0x0F, 0x9B, 0x1D, 0x27, 
		0xff, 
		0xff,
		0xff,
		0xff, 0x00
	};
	
	int *i = (int *)&key2[12];
	while (*i)
	{
		if (((*i) & 0xFFFFFF) == 0)
			printf("%08X\n", (*i));

		ss_decrypt(key2, buf11, buf12, 16);

		if (memcmp(buf99, buf12, 8) == 0)
		{
			printf("key:\n");
			printhex((char *)key2, 16);
			ss_decrypt(key2, buf11, buf12, 32);

			for (int j = 0; j < 32; j+= 8)
				des.decrypt((char *)buf12+j, (char *)buf13+j);
			printhex((char *)buf13, 32);
		}

		(*i)--;
	}
	//key
	//65 48 32 EF BA CD 56 4E 0F 9B 1D 27 13 6A 7E 1F
	//78 6B 68 72 70 67 73 7D 51 34 70 65 6C 63 67 72
	//71 36 66 49 34 65 6C 79 61 67 72 65 72 32 67 76

作者的rc6算法效率真的很低啊。穷举过程非常漫长。。。。

获取rc6 key之后,下面穷举sn中的后4字节:其中0x7B为已知 } 编码得来

		unsigned char buf15[8] = { 0xFF, 0xFF, 0xFF, 0x7B, 4, 4, 4, 4 }; 
		int *i = (int *)&buf15[0];
		while (*i)
		{
			if (((*i) & 0xFFFF) == 0)
				printf("%08X\n", (*i));

			des.encrypt((char *)buf15, (char *)buf13);
			if (*(int*)buf13 == 0x1F7E6A13)
			{
				printhex((char *)buf15, 8);
			}

			(*i)--;
		}
		return 0;
		// sn 后4字节为 61 3E 36 7B

将得到完全36字节进行反向编码,依然穷举:

		unsigned char buf4[36] = {
			0x78, 0x6B, 0x68, 0x72, 0x70, 0x67, 0x73, 0x7D, 0x51, 0x34, 0x70, 0x65, 0x6C, 0x63, 0x67, 0x72,
			0x71, 0x36, 0x66, 0x49, 0x34, 0x65, 0x6C, 0x79, 0x61, 0x67, 0x72, 0x65, 0x72, 0x32, 0x67, 0x76,
			0x61, 0x3E, 0x36, 0x7B
		};

		for (int i = 0; i < 36; i++)
		{
			printf("%02X:", buf4[i]);
			for (int j = 0x00; j < 0xff; j++)
			{
				unsigned char ch = encrypt_abch(j);
				if (ch == buf4[i])
				{
					printf("%c ", j);
				}
			}
			printf("\n");
		}

注意要使用ab.a中的编码算法,得出:

sn=kxuectf{D3crypted1sV3rylntere5tin91}



[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回