首页
社区
课程
招聘
[旧帖] [原创]某网络设备管理软件算法分析 0.00雪花
发表于: 2014-11-12 19:58 2133

[旧帖] [原创]某网络设备管理软件算法分析 0.00雪花

2014-11-12 19:58
2133
安装程序下载地址
http://pan.baidu.com/s/1c08JWVQ
软件有服务器端和客户端,服务器端是一些网络设备,
临时会员版块不注册好像看不到,所以我就说一下,这是某国企的内部软件,详情见百度
http://wenku.baidu.com/view/0ed456f2dd3383c4ba4cd216.html
客户端不注册也能用,但是连接服务器的时候会失败,软件没有输入注册码的地方,登陆的时候会获取服务器上的一个文件,ip/vb.htm

一个正确的key如下:
OK gethwinfo=00:80:c8:27:e4:31^1000c879-9ef1f899^01^.^00.00.00.48.58.30.30 ^01.00.00.01^00000001
其中1000c879-9ef1f899为序列号。
没有序列号也能运行程序,但是连接设备的时候就连不上了,一直显示登录中

注册码验证是在一个dll里面,完整程序太大上传不了,我就只上传了DLL。
IDA打开这个dll,可以看到其中有一个XXC_Login函数比较可疑,call    sub_1000E510后面有一个条件跳转,明显就是关键跳了

直接从这里爆破程序没有功能,同样是一直显示连接中,
先看一下有没有什么已知的算法,IDA的插件扫出来的有MD4,MD5,CRC32,
看了一下,CRC32是在XXC_GetUpdateFileVersion里面调用的,MD5也不在刚才那个关键CALL里面,这个先不管它。

直接去看那个call,IDA F5:
 int __userpurge sub_1000E510<eax>(int a1<ecx>, int a2<ebx>, int a3)
{
  int v3; // edi@1
  char *v4; // eax@3
  int *v5; // esi@3
  int v6; // ST24_4@3
  u_long *v7; // edi@3
  char v9; // [sp+0h] [bp-F24h]@0
  char v10; // [sp+Ch] [bp-F18h]@1
  char v11; // [sp+10h] [bp-F14h]@3
  char v12; // [sp+14h] [bp-F10h]@3
  char v13; // [sp+18h] [bp-F0Ch]@3
  char v14; // [sp+1Ch] [bp-F08h]@3
  char v15; // [sp+20h] [bp-F04h]@3
  char v16; // [sp+24h] [bp-F00h]@1
  char v17; // [sp+314h] [bp-C10h]@2
  unsigned int v18; // [sp+F20h] [bp-4h]@1

  v18 = (unsigned int)&v10 ^ __security_cookie;
  v3 = a1;
  sub_10001000(555);
  sub_10001060(0, v9);
  sub_1000F170(&v16);
  if ( sub_100010C0(&v16) == -1 || strstr(&v17, "notwrite") )
    return -1;
  v4 = strchr(&v17, 61);
  v5 = (int *)(v3 + 708);
  v6 = v3 + 708;
  v7 = (u_long *)(v3 + 704);
  if ( sscanf(v4 + 1, "%02x:%02x:%02x:%02x:%02x:%02x^%x-%x", &v10, &v11, &v12, &v13, &v14, &v15, v7, v6) != 8 )
  {
    *v5 = -1;
    *v7 = -1;
    return -1;
  }
  *(_DWORD *)a2 = *v7;
  *(_DWORD *)a3 = *v5;
  return ((unsigned __int8)sub_1000E490(*v7, *v5) != 0) - 1;
}

可以看出返回值是1000E490决定的,分析一下输入的参数
 mov     eax, [edi]
.text:1000E5E3                 mov     [ebx], eax
.text:1000E5E5                 mov     ecx, [esi]
.text:1000E5E7                 mov     [ebp+0], ecx
.text:1000E5EA                 mov     edx, [esi]
.text:1000E5EC                 mov     eax, [edi]
.text:1000E5EE                 push    edx             ; int
.text:1000E5EF                 push    eax             ; netlong
.text:1000E5F0                 call    sub_1000E490    ; eax为序列号前8位,edx为后8位
.text:1000E5F5                 movzx   eax, al


跟进去,首先是ntohl转化了字节顺序,然后序列号运算后输出了16位字符串,但是只是取了其中2位进行验证了。
.text:1000E490                 sub     esp, 0Ch
.text:1000E493                 mov     eax, ___security_cookie
.text:1000E498                 xor     eax, esp
.text:1000E49A                 mov     [esp+0Ch+var_4], eax
.text:1000E49E                 xor     eax, eax
.text:1000E4A0                 push    esi
.text:1000E4A1                 mov     esi, ds:ntohl
.text:1000E4A7                 mov     dword ptr [esp+10h+var_C+1], eax
.text:1000E4AB                 mov     word ptr [esp+10h+var_C+5], ax
.text:1000E4B0                 mov     byte ptr [esp+10h+var_C+7], al
.text:1000E4B4                 mov     eax, [esp+10h+netlong]
.text:1000E4B8                 push    eax             ; netlong
.text:1000E4B9                 call    esi ; ntohl
.text:1000E4BB                 mov     ecx, [esp+10h+arg_4]
.text:1000E4BF                 push    ecx             ; netlong
.text:1000E4C0                 mov     [esp+8], eax
.text:1000E4C4                 call    esi ; ntohl
.text:1000E4C6                 mov     dword ptr [esp+10h+var_C+4], eax
.text:1000E4CA                 lea     eax, [esp+10h+var_C]
.text:1000E4CE                 call    sub_100344D0    ; //加密
.text:1000E4D3                 cmp     byte ptr [esp+10h+var_C+6], 56h//第一处验证
.text:1000E4D8                 pop     esi
.text:1000E4D9                 jnz     short loc_1000E4F3
.text:1000E4DB                 cmp     byte ptr [esp+0Ch+var_C+7], 5Ah//第二处验证
.text:1000E4E0                 jnz     short loc_1000E4F3
.text:1000E4E2                 mov     al, 1
.text:1000E4E4                 mov     ecx, [esp+0Ch+var_4]


再看加密算法,初始化了几个常数,然后call    sub_10034530
.text:100344D0 sub_100344D0    proc near               ; CODE XREF: sub_1000E490+3Ep
.text:100344D0                 cmp     dword_108E340C, 0
.text:100344D7                 jnz     short loc_1003451F
.text:100344D9                 mov     dword_108E3410, 0A91h
.text:100344E3                 mov     dword_108E3414, 6Ch
.text:100344ED                 mov     dword_108E3418, 2C6h
.text:100344F7                 mov     dword_108E3420, 10Dh
.text:10034501                 mov     dword_108E3428, 0DEh
.text:1003450B                 mov     dword_108E3430, 1BAh
.text:10034515                 mov     dword_108E340C, 1
.text:1003451F
.text:1003451F loc_1003451F:                           ; CODE XREF: sub_100344D0+7j
.text:1003451F                 push    eax
.text:10034520                 call    sub_10034530
.text:10034525                 pop     ecx
.text:10034526                 retn
.text:10034526 sub_100344D0    endp


最终算法我就F5看了一下
int __cdecl sub_10034530(int a1)
{
  int v1; // edx@1
  int v2; // eax@1
  int v3; // ecx@1
  int v4; // edx@1
  int v5; // ecx@1
  int v6; // eax@1
  int v7; // edx@1
  int v8; // eax@1
  int v9; // ecx@1
  int v10; // edx@1
  int v11; // ecx@1
  int v12; // eax@1
  int v13; // edx@1
  unsigned int v14; // ebx@1
  unsigned int v15; // edi@1
  signed int v16; // esi@1
  int v17; // edx@2
  int v18; // eax@3
  unsigned int v19; // edx@3
  int v20; // eax@3
  int v21; // ecx@3
  int v22; // edx@3
  int v23; // ecx@3
  int v24; // eax@3
  int v25; // edx@3
  int v26; // eax@3
  int v27; // ecx@3
  int v28; // edx@3
  int v29; // ecx@3
  int v30; // eax@3
  int v31; // edx@3
  int v32; // esi@3
  int result; // eax@3

  v1 = (*(_DWORD *)a1 ^ (*(_DWORD *)(a1 + 4) >> 4)) & 0xF0F0F0F;
  v2 = v1 ^ *(_DWORD *)a1;
  v3 = 16 * v1 ^ *(_DWORD *)(a1 + 4);
  v4 = (unsigned __int16)(v3 ^ (((unsigned int)v1 ^ *(_DWORD *)a1) >> 16));
  v5 = v4 ^ v3;
  v6 = (v4 << 16) ^ v2;
  v7 = (v6 ^ ((unsigned int)v5 >> 2)) & 0x33333333;
  v8 = v7 ^ v6;
  v9 = 4 * v7 ^ v5;
  v10 = v9 ^ ((unsigned int)v8 >> 8);
  v10 &= 0xFF00FFu;
  v11 = v10 ^ v9;
  v12 = (v10 << 8) ^ v8;
  v13 = (v12 ^ ((unsigned int)v11 >> 1)) & 0x55555555;
  v14 = ((v13 ^ (unsigned int)v12) >> 29) + 8 * (v13 ^ v12);
  v15 = ((2 * v13 ^ (unsigned int)v11) >> 29) + 8 * (2 * v13 ^ v11);
  v16 = 28;
  do
  {
    v15 ^= dword_100875A8[(v14 ^ dword_108E3418[v16]) >> 26] ^ dword_100876A8[(((v14 ^ dword_108E341C[v16]) >> 4)
                                                                             + ((v14 ^ dword_108E341C[v16]) << 28)) >> 26] ^ dword_10086FA8[((v14 ^ dword_108E3418[v16]) >> 2) & 0x3F] ^ dword_100871A8[(unsigned __int8)((unsigned __int16)(v14 ^ LOWORD(dword_108E3418[v16])) >> 8) >> 2] ^ dword_100873A8[((v14 ^ dword_108E3418[v16]) >> 18) & 0x3F] ^ dword_100870A8[((((v14 ^ dword_108E341C[v16]) >> 4) + ((v14 ^ dword_108E341C[v16]) << 28)) >> 2) & 0x3F] ^ dword_100872A8[((((v14 ^ dword_108E341C[v16]) >> 4) + ((v14 ^ dword_108E341C[v16]) << 28)) >> 10) & 0x3F] ^ dword_100874A8[((((v14 ^ dword_108E341C[v16]) >> 4) + ((v14 ^ dword_108E341C[v16]) << 28)) >> 18) & 0x3F];
    v17 = dword_100875A8[(v15 ^ dword_108E3410[v16]) >> 26] ^ dword_100876A8[(((v15 ^ dword_108E3414[v16]) >> 4)
                                                                            + ((v15 ^ dword_108E3414[v16]) << 28)) >> 26] ^ dword_10086FA8[((v15 ^ dword_108E3410[v16]) >> 2) & 0x3F] ^ dword_100871A8[(unsigned __int8)((unsigned __int16)(v15 ^ LOWORD(dword_108E3410[v16])) >> 8) >> 2] ^ dword_100873A8[((v15 ^ dword_108E3410[v16]) >> 18) & 0x3F] ^ dword_100870A8[((((v15 ^ dword_108E3414[v16]) >> 4) + ((v15 ^ dword_108E3414[v16]) << 28)) >> 2) & 0x3F] ^ dword_100872A8[((((v15 ^ dword_108E3414[v16]) >> 4) + ((v15 ^ dword_108E3414[v16]) << 28)) >> 10) & 0x3F] ^ dword_100874A8[((((v15 ^ dword_108E3414[v16]) >> 4) + ((v15 ^ dword_108E3414[v16]) << 28)) >> 18) & 0x3F];
    v16 -= 4;
    v14 ^= v17;
  }
  while ( v16 > -2 );
  v18 = (v15 >> 3) + (v15 << 29);
  v19 = (v18 ^ (((v14 >> 3) + (v14 << 29)) >> 1)) & 0x55555555;
  v20 = v19 ^ v18;
  v21 = 2 * v19 ^ ((v14 >> 3) + (v14 << 29));
  v22 = v21 ^ ((unsigned int)v20 >> 8);
  v22 &= 0xFF00FFu;
  v23 = v22 ^ v21;
  v24 = (v22 << 8) ^ v20;
  v25 = (v24 ^ ((unsigned int)v23 >> 2)) & 0x33333333;
  v26 = v25 ^ v24;
  v27 = 4 * v25 ^ v23;
  v28 = (unsigned __int16)(v27 ^ HIWORD(v26));
  v29 = v28 ^ v27;
  v30 = (v28 << 16) ^ v26;
  v31 = (v30 ^ ((unsigned int)v29 >> 4)) & 0xF0F0F0F;
  v32 = v30 ^ (v30 ^ ((unsigned int)v29 >> 4)) & 0xF0F0F0F;
  result = a1;
  *(_DWORD *)a1 = v32;
  *(_DWORD *)(a1 + 4) = v29 ^ 16 * v31;
  return result;
}

已经是C代码了,关键分析一下每个常量,改好就能用了。用IIS架了个服务器,放上了那个vb.htm,用OD调了一下,把常量都分析出来了。

结果如下:
bool CSearchDlg::Check(unsigned int a1, unsigned int a2)
{
	bool bOK=false;
	if(a1<10000000 || a2<10000000)
		return false;
	unsigned int b1,b2,result;
	int v16;
	unsigned int v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,
		v17,v18,v19,v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,v30,v31,v32;

	unsigned int dword_10086FA8[100]={0x002080800,0x000080000,0x002000002,0x002080802,0x002000000,0x000080802,0x000080002,0x002000002,0x000080802,0x002080800,0x002080000,0x000000802,0x002000802,0x002000000,0x000000000,0x000080002,
		0x000080000,0x000000002,0x002000800,0x000080800,0x002080802,0x002080000,0x000000802,0x002000800,0x000000002,0x000000800,0x000080800,0x002080002,0x000000800,0x002000802,0x002080002,0x000000000,
		0x000000000,0x002080802,0x002000800,0x000080002,0x002080800,0x000080000,0x000000802,0x002000800,0x002080002,0x000000800,0x000080800,0x002000002,0x000080802,0x000000002,0x002000002,0x002080000,
		0x002080802,0x000080800,0x002080000,0x002000802,0x002000000,0x000000802,0x000080002,0x000000000,0x000080000,0x002000000,0x002000802,0x002080800,0x000000002,0x002080002,0x000000800,0x000080802},
dword_100870A8[100]={0x040108010,0x000000000,0x000108000,0x040100000,0x040000010,0x000008010,0x040008000,0x000108000,0x000008000,0x040100010,0x000000010,0x040008000,0x000100010,0x040108000,0x040100000,0x000000010,
		0x000100000,0x040008010,0x040100010,0x000008000,0x000108010,0x040000000,0x000000000,0x000100010,0x040008010,0x000108010,0x040108000,0x040000010,0x040000000,0x000100000,0x000008010,0x040108010,
		0x000100010,0x040108000,0x040008000,0x000108010,0x040108010,0x000100010,0x040000010,0x000000000,0x040000000,0x000008010,0x000100000,0x040100010,0x000008000,0x040000000,0x000108010,0x040008010,
		0x040108000,0x000008000,0x000000000,0x040000010,0x000000010,0x040108010,0x000108000,0x040100000,0x040100010,0x000100000,0x000008010,0x040008000,0x040008010,0x000000010,0x040100000,0x000108000},
dword_100871A8[100]={0x004000001,0x004040100,0x000000100,0x004000101,0x000040001,0x004000000,0x004000101,0x000040100,0x004000100,0x000040000,0x004040000,0x000000001,0x004040101,0x000000101,0x000000001,0x004040001,
		0x000000000,0x000040001,0x004040100,0x000000100,0x000000101,0x004040101,0x000040000,0x004000001,0x004040001,0x004000100,0x000040101,0x004040000,0x000040100,0x000000000,0x004000000,0x000040101,
		0x004040100,0x000000100,0x000000001,0x000040000,0x000000101,0x000040001,0x004040000,0x004000101,0x000000000,0x004040100,0x000040100,0x004040001,0x000040001,0x004000000,0x004040101,0x000000001,
		0x000040101,0x004000001,0x004000000,0x004040101,0x000040000,0x004000100,0x004000101,0x000040100,0x004000100,0x000000000,0x004040001,0x000000101,0x004000001,0x000040101,0x000000100,0x004040000},
dword_100872A8[100]={0x000401008,0x010001000,0x000000008,0x010401008,0x000000000,0x010400000,0x010001008,0x000400008,0x010401000,0x010000008,0x010000000,0x000001008,0x010000008,0x000401008,0x000400000,0x010000000,
		0x010400008,0x000401000,0x000001000,0x000000008,0x000401000,0x010001008,0x010400000,0x000001000,0x000001008,0x000000000,0x000400008,0x010401000,0x010001000,0x010400008,0x010401008,0x000400000,
		0x010400008,0x000001008,0x000400000,0x010000008,0x000401000,0x010001000,0x000000008,0x010400000,0x010001008,0x000000000,0x000001000,0x000400008,0x000000000,0x010400008,0x010401000,0x000001000,
		0x010000000,0x010401008,0x000401008,0x000400000,0x010401008,0x000000008,0x010001000,0x000401008,0x000400008,0x000401000,0x010400000,0x010001008,0x000001008,0x010000000,0x010000008,0x010401000},
dword_100873A8[100]={0x008000000,0x000010000,0x000000400,0x008010420,0x008010020,0x008000400,0x000010420,0x008010000,0x000010000,0x000000020,0x008000020,0x000010400,0x008000420,0x008010020,0x008010400,0x000000000,
		0x000010400,0x008000000,0x000010020,0x000000420,0x008000400,0x000010420,0x000000000,0x008000020,0x000000020,0x008000420,0x008010420,0x000010020,0x008010000,0x000000400,0x000000420,0x008010400,
		0x008010400,0x008000420,0x000010020,0x008010000,0x000010000,0x000000020,0x008000020,0x008000400,0x008000000,0x000010400,0x008010420,0x000000000,0x000010420,0x008000000,0x000000400,0x000010020,
		0x008000420,0x000000400,0x000000000,0x008010420,0x008010020,0x008010400,0x000000420,0x000010000,0x000010400,0x008010020,0x008000400,0x000000420,0x000000020,0x000010420,0x008010000,0x008000020},
dword_100874A8[100]={0x080000040,0x000200040,0x000000000,0x080202000,0x000200040,0x000002000,0x080002040,0x000200000,0x000002040,0x080202040,0x000202000,0x080000000,0x080002000,0x080000040,0x080200000,0x000202040,
		0x000200000,0x080002040,0x080200040,0x000000000,0x000002000,0x000000040,0x080202000,0x080200040,0x080202040,0x080200000,0x080000000,0x000002040,0x000000040,0x000202000,0x000202040,0x080002000,
		0x000002040,0x080000000,0x080002000,0x000202040,0x080202000,0x000200040,0x000000000,0x080002000,0x080000000,0x000002000,0x080200040,0x000200000,0x000200040,0x080202040,0x000202000,0x000000040,
		0x080202040,0x000202000,0x000200000,0x080002040,0x080000040,0x080200000,0x000202040,0x000000000,0x000002000,0x080000040,0x080002040,0x080202000,0x080200000,0x000002040,0x000000040,0x080200040},
dword_100875A8[100]={0x000004000,0x000000200,0x001000200,0x001000004,0x001004204,0x000004004,0x000004200,0x000000000,0x001000000,0x001000204,0x000000204,0x001004000,0x000000004,0x001004200,0x001004000,0x000000204,
		0x001000204,0x000004000,0x000004004,0x001004204,0x000000000,0x001000200,0x001000004,0x000004200,0x001004004,0x000004204,0x001004200,0x000000004,0x000004204,0x001004004,0x000000200,0x001000000,
		0x000004204,0x001004000,0x001004004,0x000000204,0x000004000,0x000000200,0x001000000,0x001004004,0x001000204,0x000004204,0x000004200,0x000000000,0x000000200,0x001000004,0x000000004,0x001000200,
		0x000000000,0x001000204,0x001000200,0x000004200,0x000000204,0x000004000,0x001004204,0x001000000,0x001004200,0x000000004,0x000004004,0x001004204,0x001000004,0x001004200,0x001004000,0x000004004},
dword_100876A8[100]={0x020800080,0x020820000,0x000020080,0x000000000,0x020020000,0x000800080,0x020800000,0x020820080,0x000000080,0x020000000,0x000820000,0x000020080,0x000820080,0x020020080,0x020000080,0x020800000,
		0x000020000,0x000820080,0x000800080,0x020020000,0x020820080,0x020000080,0x000000000,0x000820000,0x020000000,0x000800000,0x020020080,0x020800080,0x000800000,0x000020000,0x020820000,0x000000080,
		0x000800000,0x000020000,0x020000080,0x020820080,0x000020080,0x020000000,0x000000000,0x000820000,0x020800080,0x020020080,0x020020000,0x000800080,0x020820000,0x000000080,0x000800080,0x020020000,
		0x020820080,0x000800000,0x020800000,0x020000080,0x000820000,0x000020080,0x020020080,0x020800000,0x000000080,0x020820000,0x000820080,0x000000000,0x020000000,0x020800080,0x000020000,0x000820080},
dword_108E3410[100]={1,0x000000a91,0x00000006c,0x0000002c6,0x000000000,0x00000010d,0x000000000,0x0000000de,0x000000000,0x0000001ba,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,
		0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,
		0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000000000,0x000237de8,0x0fffffffe,0x000000001,0x000006768,0x000000000,0x0000007d0,0x000237e10,0x0ffffffff,0x000000000,0x000000000,
		0x000000000,0x0000007d0,0x000000e10,0x000000000,0x00000003c,0x000000000,0x000015180,0x000000000,0x000000e10,0x000000000,0x00000003c,0x000000000,0x000015180,0x000000000,0x000000e10,0x000000000};
	
	//测试样例a1=0x177d75fa,a2=0x23dc9ac5
	b1=ntohl(a2);//转化字节序
	b2=ntohl(a1);

	v1=(b2^(b1>>4))&0x0f0f0f0f;
	v2=v1^b2;
	v3 = 16 * v1 ^ b1;
	v4 =(v3 ^ ((v1 ^ b2) >> 16))&0xffff;
	v5 = v4 ^ v3;
	v6 = (v4 << 16) ^ v2;
	v7 = (v6 ^ (v5 >> 2)) & 0x33333333;
	v8 = v7 ^ v6;
	v9 = 4 * v7 ^ v5;
	v10 = v9 ^ (v8 >> 8);
	v10 &= 0xFF00FFu;
	v11 = v10 ^ v9;
	v12 = (v10 << 8) ^ v8;
	v13 = (v12 ^ (v11 >> 1)) & 0x55555555;
	v14 = ((v13 ^ v12) >> 29) + 8 * (v13 ^ v12);
	v15 = ((2 * v13 ^ v11) >> 29) + 8 * (2 * v13 ^ v11);
	v16 = 28;
	unsigned int t1,t2,t3,t5,t6,t7,t8;
 do
  {
	t1=v14 ^ dword_108E3410[v16+4];//0xef73ac5c
	t2=(t1 >> 4) + (t1 << 28);//0xcef73ac5
	t3=v14 ^ dword_108E3410[v16+3];
	//t2>>0x12&0x3f;//0x0000003d
	v4=dword_100874A8[(t2 >> 18) & 0x3F];
	v4^=dword_100872A8[(t2 >> 10)& 0x3F];
	v4^=dword_100870A8[(t2 >> 2) & 0x3F];
	v4^=dword_100873A8[(t3 >> 18) & 0x3F];
	v4^=dword_100871A8[(t3>>0xA)&0x3F];
	v4^=dword_10086FA8[(t3 >> 2) & 0x3F];
	v4^=dword_100876A8[t2 >> 26] ;
    v4^= dword_100875A8[t3 >> 26];
	v15^=v4;
    t5=v15 ^ dword_108E3410[v16+2];//0x3fc2f242
	t6=(t5 >> 4) + (t5 << 28);//0x23fc2f24
	t7=v15 ^ dword_108E3410[v16+1];
	t8=dword_100874A8[(t6 >> 18) & 0x3F];
	t8^=dword_100872A8[(t6 >> 10) & 0x3F];
	t8^=dword_100870A8[(t6 >> 2) & 0x3F] ;
	t8^=dword_100873A8[(t7 >> 18) & 0x3F];
	t8^=dword_100871A8[(t7>>0xA)&0x3F];
	t8^=dword_10086FA8[(t7 >> 2) & 0x3F];
	t8^=dword_100876A8[t6 >> 26];
	t8^=dword_100875A8[t7 >> 26];
	v17=t8;
    v16 -= 4;
    v14 ^= v17;
  }
  while ( v16 >-2  );
  v18 = (v14 >> 3) + (v14 << 29);
  v19 = (((v15>>3)+(v15<<29)^(v18 >> 1))) & 0x55555555;
  v20 = v19 ^ ((v15>>3)+(v15<<29));
  v21 = 2 * v19 ^ (v18);
  v22 = v21 ^ ((unsigned int)v20 >> 8);
  v22 &= 0xFF00FFu;
  v23 = v22 ^ v21;
  v24 = (v22 << 8) ^ v20;
  v25 = (v24 ^ ((unsigned int)v23 >> 2)) & 0x33333333;
  v26 = v25 ^ v24;
  v27 = 4 * v25 ^ v23;
  v28 = (unsigned __int16)(v27 ^ HIWORD(v26));
  v29 = v28 ^ v27;
  v30 = (v28 << 16) ^ v26;
  v31 = (v30 ^ ((unsigned int)v29 >> 4)) & 0xF0F0F0F;
  v32 = v30 ^ v31;
  //result = b2;
  //b2 = v32;
  b1 = v29 ^ 16 * v31;
  result=b1>>16;
	b2=result/0x100;
	b1=result%0x100;
	if((b1==0x56)&&(b2==0x5A))//验证2个字节
		bOK=true;
	else
		bOK=false;
	return bOK;
}


算法是还原出来了,可是怎么样才能验证成功呢?要么得把算法倒推回去,从最后2个字节做逆运算,要么就穷举。厂家提供序列号的时候肯定要用这个算法的逆算法,所以这个算法肯定是可逆的,这时我忽然想到一个这么复杂还要可逆,肯定不是自己发明的,这个算法是不是什么知名算法,我搜了一下算法里面的常数0x33333333,在论坛里面找到了2个类似的:http://bbs.pediy.com/showthread.php?t=136534和http://bbs.pediy.com/showthread.php?t=26409
从这里可以断定这个是DES算法,不过我看我这个的SPBOX跟他们的不一样。
DES逆运算要知道KEY,而要知道KEY就得找到DESSetkey的函数,那还得回头去分析算法,要找发现是DES可能还会去分析,现在我都还原到C了,花了整整一下午时间,也就没兴趣再弄了,还是先试试穷举看速度能不能接受。

DWORD WINAPI SearchThread(LPVOID lParam)
{
	CSearchDlg* pDlg=(CSearchDlg*)lParam;
	unsigned int a1,a2;
	char str1[50]={0},,str2[50],;
	pDlg->GetDlgItemText(IDC_EDIT1,str1,50);
	a1=strtoul(str1,NULL,16);
	if(a1<0x10000000)
	{
		a1=0x10000000;
		_ui64toa(a1,str1,16);
		pDlg->SetDlgItemText(IDC_EDIT1,str1);
	}
	pDlg->GetDlgItemText(IDC_EDIT2,str2,50);
	a2=strtoul(str2,NULL,16);
	if(a2<0x10000000)
	{
		a2=0x10000000;
		_ui64toa(a2,str2,16);
		pDlg->SetDlgItemText(IDC_EDIT2,str2);
	}
	pDlg->bContinue=true;
	while(pDlg->bContinue)
	{
		_ui64toa(a2,str2,16);
		if(pDlg->Check2(str1,str2)&&pDlg->Check(a1,a2))
		{
				pDlg->MessageBox("找到一组序列号");	
				pDlg->SetDlgItemText(IDC_EDIT2,str2);
				break;
		}
		if(0==(a2%0x20))
		{
			_ui64toa(a2,str2,16);
			pDlg->SetDlgItemText(IDC_EDIT2,str2);
		}
		a2++;
	}
	return 0;
}

结果基本上不到一秒钟就能穷举出来一个。然后用这个key去测试,关键跳正确跳转了。
这就算成功了吧,但是我觉得这个有点奇怪,加密的算法是:
8字节-8字节   des加密----->       8字节-8字节,
程序只验证了2个字节,剩下14字节都没用上。根据高中的排列组合知识不难知道穷举能碰对的几率:
一共0x10的16次方个数,符合条件的密文有0x10的14次方个,也就是0x10000分之一的几率,des是可逆的,反推回去同样明文也就是0x10000分之一的几率能通过验证,也就是6万分之一,即使不知道算法,直接穷举碰对的概率也是相当大的,这样的条件作为序列号验证有点不合理。
/////////////////////////////////////////////////////////////////////////////////////////
过了2天后朋友跟我说算出来的序列号不行,虽然能连上,但是再执行一段时间后就断开了。
他经过测试得到的结论是程序连上去之后还有一处验证,算法他已经分析好了(写这篇文章时我去找了这个算法,找了半天也找不到,IDA里面就没有2040f这个常数,因为没有设备,本地架的服务器不能完全连上,所以不能继续后面的测试,大家要不信的话我也没办法了)。
比如序列号9b393966-8a2a99b2,那么验证过程是:
先把9b393966-8a2a99b2   分别转成ASIIC码,如下(都是16进制数据,我就省略0x了),
   39 62 33 39 33 39 36 36 -- 38 61 32 61  39 39 62 32

把前8位相加 +3962 +3339+  3339 +3636    =  D60A
然后把后8位,先调一下位置,  把最后一位调到最前面,得到如下 3238 61 32 61   39 39 62  
最后,把3238+6132+6139+3962 = 12E05

d60a+12e05=2040f,就为总和。

总和为2040ff时,验证通过。
接下来我们把这个算法写成C,首先做一下调整
每一位拆开
(39+33+33+36)*100+(62+39+39+36)=  D60A
(32+61+61+39)*100+(38+32+39+62)= 12E05
2个算式合并
(39+33+33+36+32+61+61+39)*100+(62+39+39+36+38+32+39+62)=2040f
最后代码这样的
//第二个验证算法
bool CSearchDlg::Check2(char *stra,char *strb)
{
	
	byte arr1[10]={0},arr2[10]={0};
	unsigned int sum1,sum2;
	sum1=sum2=0;
	int i;
	for(i=0;i<8;i++)
	{
		arr1[i]=stra[i];
	}
	arr2[0]=strb[7];
	for(i=0;i<7;i++)
	{
		arr2[i+1]=strb[i];
	}
	sum1=(arr1[0]+arr1[2]+arr1[4]+arr1[6])*0x100+arr1[1]+arr1[3]+arr1[5]+arr1[7];
	sum2=(arr2[0]+arr2[2]+arr2[4]+arr2[6])*0x100+arr2[1]+arr2[3]+arr2[5]+arr2[7];
	if((sum1+sum2)==0x2040f)
		return true;
	return false;
}

2个条件,前面的线程函数对应改成
if(pDlg->Check2(str1,str2)&&pDlg->Check(a1,a2))
这下子速度慢了,我改成了批量生成,把结果写入文件,2个月之后朋友又来找我,说挂了那么多天才跑出来几十个号,刚开始几天还行,到后面一天可能都出不来一个号。
那么我们接着分析一下第2个算法,
第二个算法是求和,和是常数,我们没有必要每一位都穷举,穷举其中几位,其它几位就唯一确定了,
比如说2个2位数相加等于100,
如果把2个数都从0-99穷举,就要10000次
如果穷举一个,另外一个用100去减,就是穷举100次。
接着原算式:
(39+33+33+36+32+61+61+39)*100+(62+39+39+36+38+32+39+62)=2040f
换成符号来表示
(a1+a2+a3+a4+a5+a6+a7+a8)*100+(a9+a10+a11+a12+a13+a14+a15+a16)=2040f
记为m*100+n=s
其中ai为0-9的数字或者A-F的字母对应的ASCII码,
其中30<=ai<=39  或者61<=ai<=66。
根据ai的范围,8个ai相加最小值为30*8=180,最大值为66*8=330,
但是ai不能取39-61之间的值,也就是不一定180-330之间的每个值都能取得。
我这就不做数学推导了,直接让电脑算最精确了。
//计算8个1-f的数字对应ascii码和的范围
int _tmain(int argc, _TCHAR* argv[])
{
	int i,j,sum,c=0;
	int v0,v1,v2,v3,v4,v5,v6,v7;
	char ch[5]={0};
	int a[0x10]={0};
	int r[0x400]={0};
	int t;
	for(i=0;i<0x10;i++)
	{
		_itoa(i,ch,16);
		a[i]=ch[0];
	}
	for(v0=0;v0<16;v0++)
	{
		printf("v0=%x ",v0);//程序运行比较慢,加个进度条。。
		for(v1=0;v1<16;v1++)
			for(v2=0;v2<16;v2++)
				for(v3=0;v3<16;v3++)
					for(v4=0;v4<16;v4++)
						for(v5=0;v5<16;v5++)
							for(v6=0;v6<16;v6++)
								for(v7=0;v7<16;v7++)
								{
									sum=a[v0]+a[v1]+a[v2]+a[v3]+a[v4]+a[v5]+a[v6]+a[v7];
									r[sum]=1;
								}
	}
	for(i=0;i<=0x330;i++)
	{
		if(r[i])
		{
			if(r[i-1]==0)
				printf("<<<");
			printf("%x  ",i);
			c++;
			if(r[i+1]==0)
				printf(">>>");
			if(i%5==0)
				printf("\n");
		}
	}
	return 0;
}


运行的结果303  >>><<<308,说明304-307的和是取不到的。
也就是说ai和的范围为  180~303 和 308~330。
再回头看m*100+n=s
其中s=2040f为常数,
因为m*100以00结尾,又s结尾为0f
所以n结尾为0f。
又n在180~303 和 308~330这个范围内,
n的取值只能为:20f或者30f。
s减去n得到m的取值为202或者201。
原问题归结为2个方程组
方程组1:
a1+a2+a3+a4+a5+a6+a7+a8=201
a9+a10+a11+a12+a13+a14+a15+a16=30f
方程组2:
a1+a2+a3+a4+a5+a6+a7+a8=202
a9+a10+a11+a12+a13+a14+a15+a16=20f
其中30<=ai<=39  或者61<=ai<=66。
这样好像缩小到了一个很小的范围了,这几个方程组到底有多少组解呢,毕竟解完方程后还要通过第一个验证,而第一个验证只有65536分之一的几率能过,我们现在需要1千个序列号,大约要这个方程产生6千万组解,有可能一共都有效key都没这么多。
到底能不能到这么多号,随手写了个程序统计一下方程组的解。

//求4个8元不定方程解的数量
int _tmain(int argc, _TCHAR* argv[])
{
	int i,sum,c=0;
	int v0,v1,v2,v3,v4,v5,v6,v7;
	int a[0x10]={0};
	char ch[5]={0};
	int r[0x400]={0};
	char line[100]={0};
		for(i=0;i<0x10;i++)
	{
		_itoa(i,ch,16);
		a[i]=ch[0];
	}
	for(v0=0;v0<16;v0++)
	{
		printf("v0=%x ",v0);
		for(v1=0;v1<16;v1++)
			for(v2=0;v2<16;v2++)
				for(v3=0;v3<16;v3++)
					for(v4=0;v4<16;v4++)
						for(v5=0;v5<16;v5++)
							for(v6=0;v6<16;v6++)
								for(v7=0;v7<16;v7++)
								{
									sum=a[v0]+a[v1]+a[v2]+a[v3]+a[v4]+a[v5]+a[v6]+a[v7];
									r[sum]++;
									
								}
	}
	sprintf(line,"0x201:%d个解\n0x202:%d个解\n0x20f:%d个解\n0x30f:%d个解\n",
		r[0x201],r[0x202],r[0x20f],r[0x30f]);
	printf(line);
	return 0;
}

运行结果:
0x201:52456096个解
0x202:52892616个解
0x20f:12291776个解
0x30f:3368个解
很多组解,看来之前的担忧是多余的,很多解。
方程组有2个方程,都穷举的话比较慢,我们先解其中一个方程求得500个解,存入一个文件,然后再解另外一个方程。
//计算和为20f的500组解
i
nt _tmain(int argc, _TCHAR* argv[])
{
	int i,j,sum,c=0;
	int v0,v1,v2,v3,v4,v5,v6,v7;
	char ch[5]={0};
	int a[0x10]={0};
	int r[0x400]={0};
	int t;
	for(i=0;i<0x10;i++)
	{
		_itoa(i,ch,16);
		a[i]=ch[0];
	}
	FILE* file,*f20f;
	file=fopen("result.txt","a+");
	f20f=fopen("20f.txt","a+");
	char line[100]={0};
	for(v0=0;v0<16;v0++)
	{
		printf("v0=%x ",v0);
		for(v1=0;v1<16;v1++)
			for(v2=0;v2<16;v2++)
				for(v3=0;v3<16;v3++)
					for(v4=0;v4<16;v4++)
						for(v5=0;v5<16;v5++)
							for(v6=0;v6<16;v6++)
								for(v7=0;v7<16;v7++)
								{
									sum=a[v0]+a[v1]+a[v2]+a[v3]+a[v4]+a[v5]+a[v6]+a[v7];
									if(sum==0x20f)
									{
										r[sum]++;
										//求前500个解
										if(r[sum]<=500)
										{
											sprintf(line,"%c%c%c%c%c%c%c%c\n",
											a[v0],a[v1],a[v2],a[v3],a[v4],a[v5],a[v6],a[v7]);
											fwrite(line,sizeof(char),9,f20f);
										}
										else
											goto end;
									}
									
								}
	}
end:
	fclose(file);
	fclose(f20f);
	printf(line);
	return 0;
}

解出来
008999ff
00899f9f
00899ff9
0089f99f
0089f9f9
0089ff99
008f999f
008f99f9
008f9f99
008ff999
009899ff
00989f9f
00989ff9
0098f99f
0098f9f9
0098ff99
009989ff
00998f9f
00998ff9
009998ff
009999ef
......
......
然后收尾工作:
DWORD WINAPI NewSearchThread(LPVOID lParam)
{
	CSearchDlg* pDlg=(SearchDlg*)lParam;
	unsigned int a1,a2;//,an;
	unsigned int i,c=0;//个数
	char ch[5]={0};
	char str1[50]={0},str2[50]={0},strn[50]={0},tmpstr[50]={0};
	pDlg->GetDlgItemText(IDC_EDIT4,strn,50);//IDC_EDIT4为20f那个方程的解任取一个手动填入作为常数
	//初始化已知字符
	str1[1]=strn[0];
	str1[3]=strn[1];
	str1[5]=strn[2];
	str1[7]=strn[3];
	str2[0]=strn[4];
	str2[2]=strn[5];
	str2[4]=strn[6];
	str2[6]=strn[7];
	int a[0x10]={0};
	for(i=0;i<0x10;i++)
	{
		_itoa(i,ch,16);
		a[i]=ch[0];//初始化ASCII码
	}
	pDlg->bContinue=true;
	FILE *f;
	f=fopen("result.txt","a+");
	SYSTEMTIME sys; 
	GetLocalTime( &sys );
	sprintf(tmpstr,"start time:%02u-%02u %02u:%02u:%02u:%03u  \n",sys.wMonth,sys.wDay,
		sys.wHour,sys.wMinute,sys.wSecond,sys.wMilliseconds);
	fwrite(tmpstr,1,strlen(tmpstr),f);
	int v0,v1,v2,v3,v4,v5,v6,v7;
	for(v0=0;v0<16;v0++)
	{
		printf("v0=%x ",v0);
		for(v1=0;v1<16;v1++)
			for(v2=0;v2<16;v2++)
				for(v3=0;v3<16;v3++)
					for(v4=0;v4<16;v4++)
						for(v5=0;v5<16;v5++)
							for(v6=0;v6<16;v6++)
								for(v7=0;v7<16;v7++)
								{
									if(!pDlg->bContinue)
										goto end;
									if(a[v0]+a[v1]+a[v2]+a[v3]+a[v4]+a[v5]+a[v6]+a[v7]==0x202)
									{
										str1[0]=a[v0];
										str1[2]=a[v1];
										str1[4]=a[v2];
										str1[6]=a[v3];
										str2[7]=a[v4];
										str2[1]=a[v5];
										str2[3]=a[v6];
										str2[5]=a[v7];
										a1=strtoul(str1,NULL,16);
										a2=strtoul(str2,NULL,16);
										if(pDlg->Check(a1,a2))
										{
											sprintf(tmpstr,"%s----%s\n",str1,str2);
											fwrite(tmpstr,sizeof(char),strlen(tmpstr),f);
											c++;
											if(c%10==9)
												fflush(f);
											pDlg->SetDlgItemInt(IDC_EDIT3,c);
										}
									}
								}
	}

		
end:
	fclose(f);
	return 0;
}

运行一下,速度还行,平均一秒钟可能算出来几个号,说明算法优化还是很重要的,原先一天一个号,现在一秒钟几个。
start time:11-12 10:49:15:244  
00a07859----99f1f99b
00b09889----95fcf295
00c02879----9df4f698
00c028f9----96f2f897
00c088e9----92f2f797
00d09899----91f5ff90
00e03899----93f5fd95
00e09809----99faf694
00f018e9----99f9f094
00f03889----90f8fa98
00f088a9----95f7f295
1000c879----9ef1f899
1000f889----9cf7f396
10204869----98f8fa9d
102078a9----94f6f99d
10307819----94f8ff9d
103088e9----9cf0f698
1030f8a9----93f9f398
10401849----9df9fd97
104018d9----95f7ff96
xxsdk.rar

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 32
活跃值: (34)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
楼主,图挂了,全挂了。顶你哦
2014-11-16 23:44
0
游客
登录 | 注册 方可回帖
返回
//