看雪论坛
发新帖

[原创]CrackMe第二题分析记录

谖草 2016-11-6 11:59 836
先Mark:附上序列号的逆运算过程(RC6),当然这是假设已经在内存中已经寻找到该sn的编码后的结果,见下面g_data
此代码中分别附有正向编码和逆向编码过程,剩余分析后续补充:)

// calc_sn.cpp : Defines the entry point for the console application.
//

#include <Windows.h>


typedef struct _context
{
	unsigned int unkonwn1;
	unsigned int *sn;
	int len;
	unsigned int key_a;
	unsigned int key_b;
	unsigned int key_table[32];
	unsigned int key_d;
	unsigned int key_e;
}context;

unsigned int shrl(unsigned int a1, char a2)
{
	return (a1 << a2) | (a1 >> (32 - a2));
}

unsigned int __stdcall inner_log(int a1)
{
	return (unsigned int)(a1 << (32 - (unsigned __int64)(signed __int64)(5))) >> (32- (unsigned __int64)(signed __int64)(5));
}

unsigned int shlr(unsigned int a1, char a2)
{
	return (a1 >> a2) | (a1 << (32 - a2));
}


unsigned char data[] = {
	0xFC, 0x42, 0x43, 0xA4, 0x54, 0x76, 0x72, 0xB2,	0x9F, 0x11, 0xF9, 0xD3, 0x52, 0x4F, 0xF0, 0x8C,
	0xBE, 0x64, 0x65, 0x44, 0x2E, 0x0A, 0xD4, 0xB4,	0x67, 0x64, 0x96, 0x02, 0xA5, 0xBA, 0xF2, 0xA3,
	0x40, 0x30, 0xD9, 0x89, 0x8C, 0x36, 0x4B, 0xDC,	0xAB, 0x2F, 0x4D, 0x45, 0xD7, 0x95, 0x07, 0xC4,
	0x3B, 0xFD, 0x98, 0xE1, 0x02, 0x2F, 0x7D, 0x2F,	0xDB, 0xAA, 0x09, 0x37, 0xD2, 0x2B, 0x88, 0xAC,
	0xF5, 0x9B, 0x55, 0x20, 0xF6, 0x01, 0xB5, 0x69, 0x98, 0x4F, 0xD1, 0xA9, 0x70, 0x40, 0x9E, 0xDC,
	0x2B, 0x7D, 0xB9, 0x1F, 0x1F, 0xB2, 0xA0, 0x14, 0x5F, 0x49, 0xE1, 0xEA, 0x50, 0x1E, 0x41, 0xD8,
	0xEA, 0x22, 0xD6, 0x94, 0xE5, 0x8F, 0x56, 0xCD, 0x36, 0x63, 0x10, 0x32, 0x1F, 0xF0, 0xF7, 0x09,
	0xD9, 0xF6, 0x5C, 0x5E, 0xA0, 0x25, 0x2F, 0xBE, 0x92, 0x63, 0x9C, 0x2E, 0xD1, 0x6D, 0xEA, 0xBB
};

/*
0xF5, 0x9B, 0x55, 0x20, 0xF6, 0x01, 0xB5, 0x69, 0x98, 0x4F, 0xD1, 0xA9,
	0x70, 0x40, 0x9E, 0xDC, 0x2B, 0x7D, 0xB9, 0x1F, 0x1F, 0xB2, 0xA0, 0x14, 0x5F, 0x49, 0xE1, 0xEA,
	0x50, 0x1E, 0x41, 0xD8, 0xEA, 0x22, 0xD6, 0x94, 0xE5, 0x8F, 0x56, 0xCD, 0x36, 0x63, 0x10, 0x32,
	0x1F, 0xF0, 0xF7, 0x09, 0xD9, 0xF6, 0x5C, 0x5E, 0xA0, 0x25, 0x2F, 0xBE, 0x92, 0x63, 0x9C, 0x2E,
	0xD1, 0x6D, 0xEA, 0xBB, 0x6D, 0x07, 0xAF, 0x7F, 0x4C, 0xFA, 0xD7, 0x9B
};
*/

void encrypt(context *ctx, bool encrypt)
{
	int *cur_dword; // esi@2
	unsigned int *tmp_key; // ebx@2
	int fourth_dword; // eax@3
	unsigned int v10; // ST28_4@3
	unsigned int v14; // eax@3
	bool v18; // zf@3
	signed int block_size; // [sp+24h] [bp-Ch]@2
	unsigned int offset = 0;

	if (ctx->len)
	{
		if (encrypt)
		{
			do
			{
				cur_dword = (int *)((unsigned char*)ctx->sn + offset);
				tmp_key = &ctx->key_table[31];
				block_size = 16;
				*cur_dword -= ctx->key_d;
				cur_dword[2] -= ctx->key_e;
				do
				{
					fourth_dword = cur_dword[3];
					cur_dword[3] = cur_dword[2];
					cur_dword[2] = cur_dword[1];
					cur_dword[1] = *cur_dword; 
					*cur_dword = fourth_dword; 

					v10 = shrl(cur_dword[1] * (2 * cur_dword[1] + 1), 5); // 调换前5个比特与后27个比特
					v14 = shrl((DWORD)cur_dword[3] * (2 * (DWORD)(cur_dword[3]) + 1), 5);
					
					*cur_dword = v10 ^ shlr(*cur_dword - *(tmp_key - 1), inner_log(v14));
					cur_dword[2] = ((DWORD)v14) ^ shlr(cur_dword[2] - *tmp_key, inner_log(v10));;

					tmp_key -= 2;
					v18 = block_size-- == 1;					
				} while (!v18);

				offset += 16;
				cur_dword[1] -= ctx->key_a;
				cur_dword[3] -= ctx->key_b;
			} while (offset < ctx->len);
		}
		else
		{
			do
			{
				cur_dword = (int *)((unsigned char*)ctx->sn + offset);
				tmp_key = &ctx->key_table[-1];
				block_size = 16;

				cur_dword[1] += ctx->key_a;
				cur_dword[3] += ctx->key_b;

				do
				{
					tmp_key += 2;

					v10 = shrl(cur_dword[1] * (2 * cur_dword[1] + 1), 5); // 调换前5个比特与后27个比特
					v14 = shrl((DWORD)cur_dword[3] * (2 * (DWORD)(cur_dword[3]) + 1), 5);

					int y = *cur_dword ^v10;
					int x = shrl(y, inner_log(v14));
					*cur_dword = x + *(tmp_key - 1);

					int n = cur_dword[2] ^ ((DWORD)v14);
					int m = shrl(n, inner_log(v10));
					cur_dword[2] = m + *tmp_key;

					fourth_dword = *cur_dword;
					*cur_dword = cur_dword[1];
					cur_dword[1] = cur_dword[2];
					cur_dword[2] = cur_dword[3];
					cur_dword[3] = fourth_dword;

					v18 = block_size-- == 1;
				} while (!v18);

				*cur_dword += ctx->key_d;
				cur_dword[2] += ctx->key_e;

				offset += 16;

			} while (offset < ctx->len);
		}
	}
}

char sn[33] = "11111111111111111111111111111111";

unsigned char g_data[32] = {
	0x4B, 0x7D, 0x6F, 0x22, 0xBD, 0xEA, 0x61, 0xC3, 0x0B, 0xE7, 0xB2, 0xD9, 0x2C, 0x6B, 0x41, 0x88,
	0x5D, 0x71, 0x27, 0x85, 0xBA, 0x71, 0xF0, 0xB9, 0x23, 0x77, 0x28, 0x6C, 0xFC, 0x36, 0xA6, 0xD0
};

int main(int argc, char* argv[])
{
	context ctx = { 0 };
	ctx.sn = (unsigned int*)sn;
	ctx.len = 32;
	ctx.key_a = 0x5bf76637;
	ctx.key_b = 0x4748da7a;
	memcpy(ctx.key_table, data, 4 * 32);
	ctx.key_d = 0x7faf076d;
	ctx.key_e = 0x9bd7fa4c;
	encrypt(&ctx, 1);
	encrypt(&ctx, 0);

	ctx.sn = (unsigned int*)g_data;
	encrypt(&ctx, 0);
	return 0;
}


最新回复 (2)
谖草 2016-11-6 12:07
2
技术点罗列:
1. lua: 加密头格式的lua脚本,可以在内存中搜索“_ENV”等字串来定位脚本文件。
2.RC6:用来加密提示信息,如序列号错误,标题等
3. 加密算法X:用来加密编码后的序列号,流程为:sn---rc6--> sn_x--->X--->compare
其中主要的是lua的逻辑,在回调中分别会设置正确sn的编码后计算的密文值,计算并比较两者
而X算法个人在分析过程中发现了两组类似密钥数据的字节流,算法本身具有RIJNDAEL(AES)的特征,但实际上并不需要对此进行详尽分析,因为有更便捷的处理方法,见下文:

附上内存中的lua字节码,该字节码也是通过rc6加密过,解出来如下:
003C6798  1B 6C 73 11 00 19 93 0D 0A 1A 0A 04 04 04 08 08  ls.?..
003C67A8  78 56 00 00 00 00 00 00 00 00 00 00 00 28 77 40  xV...........(w@
003C67B8  01 0E 40 54 61 73 6B 42 65 67 69 6E 2E 6C 73 00  @TaskBegin.ls.
003C67C8  00 00 00 00 00 00 00 00 02 02 07 00 00 00 08 40  ...........@
003C67D8  40 80 08 C0 40 81 2C 00 00 00 08 00 00 82 2C 40  @€繞?.....?@
003C67E8  00 00 08 00 80 82 26 00 80 00 06 00 00 00 04 0B  ...€?.€....
003C67F8  67 5F 73 74 72 52 65 67 53 6E 04 02 20 04 13 67  g_strRegSn g
003C6808  5F 73 74 72 52 65 67 53 6E 54 6F 56 65 72 69 66  _strRegSnToVerif
003C6818  79 04 01 04 0D 75 73 65 72 52 65 67 69 73 74 65  y.userRegiste
003C6828  72 04 12 67 65 74 52 65 67 53 6E 41 66 74 65 72  rgetRegSnAfter
003C6838  43 61 6C 63 01 00 00 00 01 00 02 00 00 00 00 05  Calc........
003C6848  00 00 00 10 00 00 00 01 00 04 11 00 00 00 41 00  ..........A.
003C6858  00 00 08 00 80 80 86 C0 40 00 A4 80 80 00 08 80  ...€€喞@.€.€
003C6868  00 81 86 00 41 00 C6 40 40 00 A4 80 00 01 08 80  .亞.A.艪@..€
003C6878  80 80 86 40 40 00 C6 80 40 00 1F C0 00 01 1E 00  €€咢@.苺@.?.
003C6888  00 80 41 40 01 00 08 80 41 81 66 00 00 01 26 00  .€A@.€A乫..&.
003C6898  80 00 07 00 00 00 13 FF FF FF FF FF FF FF FF 04  €....
003C68A8  0B 67 5F 73 74 72 52 65 67 53 6E 04 13 67 5F 73  g_strRegSng_s
003C68B8  74 72 52 65 67 53 6E 54 6F 56 65 72 69 66 79 04  trRegSnToVerify
003C68C8  13 66 6E 47 65 74 52 65 67 53 6E 54 6F 56 65 72  fnGetRegSnToVer
003C68D8  69 66 79 04 1D 66 6E 43 61 6C 63 55 73 65 72 49  ifyfnCalcUserI
003C68E8  6E 70 75 74 52 65 67 53 6E 41 66 74 65 72 45 6E  nputRegSnAfterEn
003C68F8  63 13 00 04 00 00 00 00 00 00 04 01 01 00 00 00  c..........
003C6908  00 00 00 00 00 00 11 00 00 00 06 00 00 00 07 00  .............
003C6918  00 00 08 00 00 00 08 00 00 00 08 00 00 00 09 00  .............
003C6928  00 00 09 00 00 00 09 00 00 00 09 00 00 00 0A 00  ................
003C6938  00 00 0A 00 00 00 0A 00 00 00 0A 00 00 00 0B 00  ...............
003C6948  00 00 0D 00 00 00 0F 00 00 00 10 00 00 00 02 00  .............
003C6958  00 00 0B 73 74 72 52 65 67 53 6E 49 6E 00 00 00  ..strRegSnIn...
003C6968  00 11 00 00 00 04 69 52 63 01 00 00 00 11 00 00  ....iRc.....
003C6978  00 01 00 00 00 05 5F 45 4E 56 00 13 00 00 00 15  ...._ENV....
003C6988  00 00 00 01 00 02 03 00 00 00 46 00 40 00 66 00  .......F.@.f.
003C6998  00 01 26 00 80 00 01 00 00 00 04 0B 67 5F 73 74  .&.€....g_st
003C69A8  72 52 65 67 53 6E 01 00 00 00 00 00 00 00 00 00  rRegSn.........
003C69B8  03 00 00 00 14 00 00 00 14 00 00 00 15 00 00 00  ............
003C69C8  01 00 00 00 0B 73 74 72 52 65 67 53 6E 49 6E 00  ...strRegSnIn.
003C69D8  00 00 00 03 00 00 00 01 00 00 00 05 5F 45 4E 56  ........._ENV
003C69E8  07 00 00 00 01 00 00 00 02 00 00 00 10 00 00 00  ............
003C69F8  05 00 00 00 15 00 00 00 13 00 00 00 15 00 00 00  ............
003C6A08  00 00 00 00 01 00 00 00 05 5F 45 4E 56 00 00 00  ......._ENV...


可见lua流的二进制头是被修改过的,改回去就可以直接反编译了,结果如下:
-- Command line: C:\Users\try2crack\Desktop\luadec\lua.bin

-- params : ...
-- function num : 0 , upvalues : _ENV
g_strRegSn = " "
g_strRegSnToVerify = ""
userRegister = function(strRegSnIn)
  -- function num : 0_0 , upvalues : _ENV
  local iRc = -1
  g_strRegSn = strRegSnIn
  g_strRegSnToVerify = fnGetRegSnToVerify()
  g_strRegSn = fnCalcUserInputRegSnAfterEnc(g_strRegSn)
  if g_strRegSn == g_strRegSnToVerify then
    iRc = 1024
  end
  g_strRegSnToVerify = ""
  return iRc
end

getRegSnAfterCalc = function(strRegSnIn)
  -- function num : 0_1 , upvalues : _ENV
  return g_strRegSn
end
之幽灵 2017-3-17 15:21
3
请问这个代码段是怎么解密得到的呢??
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 知道创宇带宽支持 | 微信公众号:ikanxue
Time: 0.010, SQL: 7 / 京ICP备10040895号-17