看雪论坛
发新帖
9

[原创]看雪2016 第二十九题 CrackMe逆向分析

HighHand 2016-12-31 22:47 1562
crackme采用多线程,分步骤解码方式,最终需要解密一段代码再运行并弹出提示消息框。反调试为CRC校验,具体逆向步骤如下:
IDA 打开crackerme,发现 GetDlgItemTextA,查找参考并来到 sub_4012E7

  v3 = 0;
  v4 = 8;
  v5 = &sn;
  v6 = (unsigned __int8)sn;
  while ( v4 )
  {
    v3 += v6;
    LOBYTE(v6) = *++v5;
    --v4;
  }
  if ( (_BYTE)v6 )
    result = (char *)MessageBoxA(hWnd, err, err, 0);
  else
    result = sub_401B0A(v3);

输入sn必须为8字符


char *__stdcall sub_401B0A(int a1)
{
  char *result; // eax@2

  if ( dword_403037 )
  {
    ++byte_403C99;
    switch ( a1 )
    {
      case 0x566:
        result = (char *)sub_401DA0();
        break;
      case 0x79A:
        result = (char *)sub_401DC1();
        break;
      case 0x86B:
        result = sub_401E16();
        break;
      case 0x5D5:
        result = (char *)sub_401DE2();
        break;
      case 0x325:
        result = (char *)sub_401DE2();
        break;
      default:
        byte_403C99 = 0;
        result = (char *)MessageBoxA(0, err, err, 0);
        break;
    }
  }
  else
  {
    result = (char *)MessageBoxA(0, err, err, 0);
  }
  return result;
}

每个sn求和必须为325,另一个线程可验证该求和结果。

下面分析线程 4016F0
      a3 = 8;
      while ( a3 )
      {
        v8 += v11;
        *v10++ = v11 ^ 0x66;
        ++v9;
        --a3;
        LOBYTE(v11) = *v9;
      }

sn与66异或,
    if ( v8 == 0x353 )
    {
      v12 = 12;
      goto LABEL_18;
    }
    if ( v8 == 0x325 )
    {
      v12 = 4;
      goto LABEL_18;
    }
    if ( v8 == 0x29B )
      break;
    if ( v8 == 0x363 )
    {
      v12 = 20;
LABEL_18:
      v13 = (char *)&dword_4032F1 + v12;
      LOWORD(v12) = *(_WORD *)((char *)&unk_403BEA + 7);
      a3 = v12 << 16;
      LOWORD(a3) = *(_WORD *)((char *)&unk_403BEA + 9) + a3;
      ((void (__stdcall *)(_DWORD))a3)(*(_DWORD *)v13);
    }

求和与主线程线程相对应325,LABEL_18:处为SetEvent唤醒另一线程进行处理。

下面分析线程 004017F9
    v8 = sub_401A02;
    while ( (unsigned int)sub_401A02 >= (unsigned int)v7 )
    {
      LOBYTE(v6) = *(_BYTE *)v7;
      v5 += v6;
      v7 = (void (__noreturn *)())((char *)v7 + 1);
    }
    if ( v5 != dword_403C81 )
      v5 = v0;

crc校验,OD调试这里时会有异常,可直接跳过

     v18 = dword_403C85[0];
      i = (char *)&dword_403C85[1];
      v17 = dword_403C85[1];
      if ( dword_403C85[0] + v17 == 0x32113442 )
      {
        if ( v11 )
        {
          for ( i = (char *)10; i; --i )
          {
            *v11 = v18 ^ (v17 + *v12);
            ++v12;
            ++v11;
          }
          ((void (__thiscall *)(_DWORD *))v13)(v12);  // 执行解码后的代码
        }
        byte_403C99 = 0;
      }

地址 403C85 是 sn^66后的结果,这里分成2个DWORD进行解码,且v0 + v1 = 32113442,解码公式为 y = (x + v1) ^ v0,
密文为:
0040331D  69 92 14 09 58 1A 65 79 60 13 CC D1 28 34 0A 2C  i?.Xey`萄(4.,
0040332D  0F 40 0B 5C 83 08 4B F4 30 85 39 34 18 40 0B CD  @\?K??4@?
0040333D  E9 6F CA 8C 1F 0F 4B F4 


密钥未知,明文未知,估计又要穷举的节奏。关于明文的猜测
1. 最后几个字节为 retn 即C3 或 C2 xx,悲剧了,没穷举出来。
2. 在err的下面发现了, .data:004032B5 aGood           db 'Good!',0,故穷举该地址,得出sn。

基于以上发现,理一下思路。
1. sn字节求和为325
2. sn需要异或 66,记v
3. dword,v0 + v1 = 32113442
4. v作为key解码,y = (x + v1) ^ v0,解码的明文中含有 Good那个地址。

穷举需要一个技巧,不然会浪费大量时间,由于sn^66后相加等于 32113442,说明每个字节的最高位为0,这样就可以按字节进行穷举了。

#include "stdafx.h"
#include <string.h>

const char *cch = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

int test2(char buf[62*62], unsigned char vv)
{
	int k = 0;
	char sn[4] = { 0 };
	for (int i = 0; i < 62; i++)
	{
		sn[0] = (unsigned char)cch[i];
		for (int j = 0; j < 62; j++)
		{
			sn[1] = (unsigned char)cch[j];
			if (((sn[0] ^ 0x66) + (sn[1] ^ 0x66)) == vv)
			{
				buf[k++] = sn[0];
				buf[k++] = sn[1];
			}
		}
	}
	return k;
}

unsigned char cc[] = {
	0x69, 0x92, 0x14, 0x09, 0x58, 0x1A, 0x65, 0x79, 0x60, 0x13, 0xCC, 0xD1, 0x28, 0x34, 0x0A, 0x2C,
	0x0F, 0x40, 0x0B, 0x5C, 0x83, 0x08, 0x4B, 0xF4, 0x30, 0x85, 0x39, 0x34, 0x18, 0x40, 0x0B, 0xCD,
	0xE9, 0x6F, 0xCA, 0x8C, 0x1F, 0x0F, 0x4B, 0xF4,
};

int test3(char *sn)
{
	char buf[16] = { 0 };
	for (int i = 0; i < 8; i++)
	{
		buf[i] = sn[i] ^ 0x66;
	}

	unsigned long *vv = (unsigned long *)buf;
	int s = vv[0] + vv[1];
	if (s != 0x32113442)
		return 0;

	unsigned long mm[0x0a];
	unsigned long *c = (unsigned long*)cc;
	for (int i = 0; i < 0x0a; i++)
	{
		int x = c[i];
		x += vv[1];
		x ^= vv[0];
		mm[i] = x;
	}
	
	int f = 0;
	for (int i = 0; i < 0x28; i++)
	{
		if (*(((unsigned char*)mm)+ i) == 0xb5)
		{
			if (*(((unsigned char*)mm) + i+1) == 0x32)
			{
				if (*(((unsigned char*)mm) + i + 2) == 0x40)
				{
					if (*(((unsigned char*)mm) + i + 3) == 0x00)
					{
						f = 1;
					}
				}
			}
		}
	}
	//004032B5
	if (f == 1)
	{
		printf("sn:%s\n", sn);
	}
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	char buf[16] = { 0 };

	unsigned char a0[62 * 62] = { 0 };
	int k0 = test2((char*)a0, 0x42);
	unsigned char a1[62 * 62] = { 0 };
	int k1 = test2((char*)a1, 0x34);
	unsigned char a2[62 * 62] = { 0 };
	int k2 = test2((char*)a2, 0x11);
	unsigned char a3[62 * 62] = { 0 };
	int k3 = test2((char*)a3, 0x32);

	for (int i = 0; i < k0; i+=2)
	{
		int x0 = a0[i] + a0[i + 1];
		for (int j = 0; j < k1; j += 2)
		{
			int x1 = a1[j] + a1[j + 1];
			for (int m = 0; m < k2; m += 2)
			{
				int x2 = a2[m] + a2[m + 1];
				for (int n = 0; n < k3; n += 2)
				{
					int x3 = a3[n] + a3[n + 1];
					if ((x0 + x1 + x2 + x3) == 0x325)
					{
						buf[0] = a0[i]; buf[4] = a0[i+1];
						buf[1] = a1[j]; buf[5] = a1[j+1];
						buf[2] = a2[m]; buf[6] = a2[m+1];
						buf[3] = a3[n]; buf[7] = a3[n+1];
						
						test3(buf);
					}
				}
			}
		}
	}

	return 0;
}



穷举结果如下,还好不多,逐个试验得出sn为:KAhuskey
sn:kahuSKey
sn:kchuSIey
sn:kAhuSkey
sn:kChuSiey
sn:KahusKey
sn:KajusKcy
sn:KchusIey
sn:KAhuskey <--
sn:KAjuskcy
sn:KChusiey
本主题帖已收到 0 次赞赏,累计¥0.00
最新回复 (0)
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 微信公众号:ikanxue
Time: 0.010, SQL: 8 / 京ICP备10040895号-17