本帖最后由 〇〇木一 于 2014-5-30 09:04 编辑
【文章作者】: 〇〇木一[OoWoodOne]
【软件名称】: C Program KenGenMe
【下载地址】: http://www.52pojie.cn/thread-261639-1-1.html
【使用工具】: IDA , OD , VC6
分析不出,难受。分析出来了,不发表一下思路也难受。 所以一天时间就没了。
找实习,让我感到未来一片渺茫。大学以来我感觉我都没认真干过什么事,上不上课看心情,总喜欢自己搞一套,到现在绩点也就刚刚毕毕业,还有重修科目在身。找实习也受了不少挫,最后找到了个windows驱动开发的实习岗位,总的来说还算和我胃口。现在就是天天泡图书馆,想想以后要买房子,养老婆...不勤奋不行了。
IDA的使用也是图书馆看的,为了熟悉,我就一直找东西来试试。这个KenGenMe是昨天Blackk大大发的,今天在图书馆试试,结果发现点思路就停不下来了。
下面是正文:
IDA分析KenGenMe的具体流程是:
1.获取输入key,判断key的长度是否为24,不是则失败。 2.对得到的key进行第一步处理,并把处理结果储存在一个全局数组中。 3.对第二步全局数组中的数据进行2次处理,获取数据进行比较验证。
下面是主函数部分: [Asm] 纯文本查看 复制代码 .text:00410240 ; Attributes: bp-based frame
.text:00410240
.text:00410240 _main_0 proc near ; CODE XREF: _mainj
.text:00410240
.text:00410240 anonymous_0 = byte ptr -7Ch
.text:00410240 var_70 = byte ptr -70h
.text:00410240 var_30 = dword ptr -30h
.text:00410240 var_2C = dword ptr -2Ch
.text:00410240 var_28 = dword ptr -28h
.text:00410240 var_24 = dword ptr -24h
.text:00410240 _key_len = dword ptr -20h
.text:00410240 char_table = dword ptr -1Ch
.text:00410240 _key_str = byte ptr -18h
.text:00410240
.text:00410240 push ebp
.text:00410241 mov ebp, esp
.text:00410243 sub esp, 70h
.text:00410246 push ebx
.text:00410247 push esi
.text:00410248 push edi
.text:00410249 lea edi, [ebp+var_70]
.text:0041024C mov ecx, 1Ch
.text:00410251 mov eax, 0CCCCCCCCh
.text:00410256 rep stosd ; ↓获取字符表
.text:00410258 mov [ebp+char_table], offset a123456789abcde ; "123456789abcdefghijklmnpqrstuvwxyz"
.text:0041025F push offset asc_426120 ; "***************************************"...
.text:00410264 call _printf
.text:00410269 add esp, 4
.text:0041026C push offset asc_425A68 ; "\n\n"
.text:00410271 call _printf
.text:00410276 add esp, 4
.text:00410279 push offset aPleaseKeygenMe ; "******** Please KeyGen M"...
.text:0041027E call _printf
.text:00410283 add esp, 4
.text:00410286 push offset asc_425A68 ; "\n\n"
.text:0041028B call _printf
.text:00410290 add esp, 4
.text:00410293 push offset aHaveFun_byBlac ; "******** Have Fun.By Blackk"...
.text:00410298 call _printf
.text:0041029D add esp, 4
.text:004102A0 push offset asc_425A68 ; "\n\n"
.text:004102A5 call _printf
.text:004102AA add esp, 4
.text:004102AD push offset asc_426120 ; "***************************************"...
.text:004102B2 call _printf
.text:004102B7 add esp, 4
.text:004102BA push offset asc_42592C ; "\n"
.text:004102BF call _printf
.text:004102C4 add esp, 4
.text:004102C7 push offset aPleaseEnterPas ; "Please Enter Password:"
.text:004102CC call _printf
.text:004102D1 add esp, 4
.text:004102D4 lea eax, [ebp+_key_str]
.text:004102D7 push eax ; ↓获取key
.text:004102D8 push offset aS ; "%s"
.text:004102DD call _scanf
.text:004102E2 add esp, 8 ; ↓获取key串长度
.text:004102E5 lea ecx, [ebp+_key_str]
.text:004102E8 push ecx ; Str
.text:004102E9 call _strlen
.text:004102EE add esp, 4
.text:004102F1 mov [ebp+_key_len], eax
.text:004102F4 cmp [ebp+_key_len], 18h
.text:004102F8 jz loc_4103A6 ; 长度不为24就失败↓
.text:004102FE push offset aBadWork_please ; "Bad Work.Please try again!"
.text:00410303 call _printf
.text:00410308 add esp, 4
.text:0041030B mov edx, File._cnt
.text:00410311 sub edx, 1
.text:00410314 mov File._cnt, edx
.text:0041031A cmp File._cnt, 0
.text:00410321 jl short loc_410345
.text:00410323 mov eax, File._ptr
.text:00410328 movsx ecx, byte ptr [eax]
.text:0041032B and ecx, 0FFh
.text:00410331 mov [ebp+var_24], ecx
.text:00410334 mov edx, File._ptr
.text:0041033A add edx, 1
.text:0041033D mov File._ptr, edx
.text:00410343 jmp short loc_410355
.text:00410345 ; ---------------------------------------------------------------------------
.text:00410345
.
.
.
.text:0041038C ; ---------------------------------------------------------------------------
.text:0041038C
.text:0041038C loc_41038C: ; CODE XREF: _main_0+129j
.text:0041038C push offset File ; File
.text:00410391 call __filbuf
.text:00410396 add esp, 4
.text:00410399 mov [ebp+var_28], eax
.text:0041039C
.text:0041039C loc_41039C: ; CODE XREF: _main_0+14Aj
.text:0041039C push 0 ; Code
.text:0041039E call _exit
.text:004103A3 ; ---------------------------------------------------------------------------
.text:004103A3 add esp, 4
.text:004103A6
.text:004103A6 loc_4103A6: ; CODE XREF: _main_0+B8j
.text:004103A6 lea ecx, [ebp+_key_str]
.text:004103A9 push ecx ; key_str
.text:004103AA mov edx, [ebp+char_table]
.text:004103AD push edx ; char_table
.text:004103AE mov eax, [ebp+_key_len]
.text:004103B1 push eax ; key_len
.text:004103B2 call _DealKey ; 首先处理key
.text:004103B7 add esp, 0Ch
.text:004103BA push offset _GlobalArray_0
.text:004103BF movsx ecx, _GlobalArray_1
.text:004103C6 push ecx
.text:004103C7 call _VerifyKey ; 再验证
.text:004103CC add esp, 8
.text:004103CF mov edx, File._cnt
.text:004103D5 sub edx, 1
.text:004103D8 mov File._cnt, edx
.text:004103DE cmp File._cnt, 0
.text:004103E5 jl short loc_410409
.text:004103E7 mov eax, File._ptr
.text:004103EC movsx ecx, byte ptr [eax]
.text:004103EF and ecx, 0FFh
.text:004103F5 mov [ebp+var_2C], ecx
.text:004103F8 mov edx, File._ptr
.text:004103FE add edx, 1
.text:00410401 mov File._ptr, edx
.text:00410407 jmp short loc_410419
.text:00410409 ; ---------------------------------------------------------------------------
.text:00410409
.
.
.
.text:00410450 ; ---------------------------------------------------------------------------
.text:00410450
.text:00410450 loc_410450: ; CODE XREF: _main_0+1EDj
.text:00410450 push offset File ; File
.text:00410455 call __filbuf
.text:0041045A add esp, 4
.text:0041045D mov [ebp+var_30], eax
.text:00410460
.text:00410460 loc_410460: ; CODE XREF: _main_0+20Ej
.text:00410460 pop edi
.text:00410461 pop esi
.text:00410462 pop ebx
.text:00410463 add esp, 70h
.text:00410466 cmp ebp, esp
.text:00410468 call __chkesp
.text:0041046D mov esp, ebp
.text:0041046F pop ebp
.text:00410470 retn
.text:00410470 _main_0 endp
搞清楚各种变量并修改后,使用IDA的f5功能可以一目了然(几乎和源代码一样)
[C] 纯文本查看 复制代码
int __cdecl main_0()
{
int v0; // eax@13
char v2; // [sp+0h] [bp-7Ch]@0
char v3; // [sp+Ch] [bp-70h]@1
int v4; // [sp+4Ch] [bp-30h]@13
int v5; // [sp+50h] [bp-2Ch]@10
int v6; // [sp+54h] [bp-28h]@6
int v7; // [sp+58h] [bp-24h]@3
size_t _key_len; // [sp+5Ch] [bp-20h]@1
char *char_table; // [sp+60h] [bp-1Ch]@1
char _key_str[24]; // [sp+64h] [bp-18h]@1
char_table = "123456789abcdefghijklmnpqrstuvwxyz";// 字符表
printf("**************************************************************");
printf("\n\n");
printf("******** Please KeyGen Me. ********");
printf("\n\n");
printf("******** Have Fun.By Blackk[PYG] ********");
printf("\n\n");
printf("**************************************************************");
printf("\n");
printf("Please Enter Password:");
scanf("%s", _key_str);
_key_len = strlen(_key_str);
if ( _key_len != 24 ) // 判断长度
{
printf("Bad Work.Please try again!");
...
exit(0);
}
DealKey(24, char_table, _key_str); // 处理函数(key长度,字符表,key)
VerifyKey(GlobalArray_1[0], GlobalArray_0); // 验证函数(全局变量1首字符,全局变量2)
...
return _chkesp(1, v0, v2);
}
这里有部分的代码省略了,都是些关于调试或者控制台有关的代码,无关紧要。
要搞清楚算法既验证原理就要逆行而上,先来看看验证部分代码
[Asm] 纯文本查看 复制代码
int __cdecl VerifyKey0(int gArr1Char0, int globalArray0)
.text:004017A0 _VerifyKey0 proc near ; CODE XREF: _VerifyKeyj
.text:004017A0
.text:004017A0 anonymous_0 = byte ptr -5Ch
.text:004017A0 var_50 = byte ptr -50h
.text:004017A0 Dst = byte ptr -0Ch
.text:004017A0 changeG1 = word ptr -8
.text:004017A0 var_5 = byte ptr -5
.text:004017A0 var_4 = byte ptr -4
.text:004017A0 gArr1Char0 = dword ptr 8
.text:004017A0 globalArray0 = dword ptr 0Ch
.text:004017A0
.text:004017A0 push ebp
.text:004017A1 mov ebp, esp
.text:004017A3 sub esp, 50h
.text:004017A6 push ebx
.text:004017A7 push esi
.text:004017A8 push edi
.text:004017A9 lea edi, [ebp+var_50]
.text:004017AC mov ecx, 14h
.text:004017B1 mov eax, 0CCCCCCCCh
.text:004017B6 rep stosd
.text:004017B8 mov [ebp+var_4], 1
.text:004017BC mov eax, [ebp+gArr1Char0] ; 取首字符运算后(changeG1)判断是否符合要求↓
.text:004017BF and eax, 0F0h
.text:004017C4 sar eax, 5
.text:004017C7 mov [ebp+changeG1], ax
.text:004017CB movsx ecx, [ebp+changeG1]
.text:004017CF cmp ecx, 1
.text:004017D2 jnz short loc_4017EB
.text:004017D4 push 1 ; int
.text:004017D6 push 0Eh ; int
.text:004017D8 mov edx, [ebp+globalArray0]
.text:004017DB push edx ; keyStr
.text:004017DC push (offset _GlobalArray_1+1) ; globalArr1pchar1
.text:004017E1 call j__DealKey1 ; changeG1为1时,第二次处理函数程序崩溃
.text:004017E6 add esp, 10h
.text:004017E9 jmp short loc_4017F8
.text:004017EB ; ---------------------------------------------------------------------------
.text:004017EB
.text:004017EB loc_4017EB: ; CODE XREF: _VerifyKey0+32j
.text:004017EB movsx eax, [ebp+changeG1]
.text:004017EF cmp eax, 2
.text:004017F2 jz short loc_4017F8
.text:004017F4 mov [ebp+var_4], 0 ; changeG1不为2时失败
.text:004017F8
.text:004017F8 loc_4017F8: ; CODE XREF: _VerifyKey0+49j
.text:004017F8 ; _VerifyKey0+52j
.text:004017F8 movsx ecx, [ebp+var_4]
.text:004017FC test ecx, ecx
.text:004017FE jz short loc_401873 ; 只用changeG1为2时,才能真正运行到验证
.text:00401800 push 1 ; int
.text:00401802 push 0Eh ; int
.text:00401804 push offset keyStr ; "9QX6K882ISS5M"
.text:00401809 push (offset _GlobalArray_1+1) ; globalArr1pchar1
.text:0040180E call j__DealKey1 ; 处理函数2
.text:00401813 add esp, 10h
.text:00401816 push 3 ; Size
.text:00401818 push (offset _GlobalArray_1+1) ; Src
.text:0040181D lea edx, [ebp+Dst]
.text:00401820 push edx ; Dst
.text:00401821 call _memcpy ; 最后取值1
.text:00401826 add esp, 0Ch
.text:00401829 push 4 ; Size
.text:0040182B push (offset _GlobalArray_1+0Ah) ; Src
.text:00401830 lea eax, [ebp+Dst+3] ; 最后取值2
.text:00401833 push eax ; Dst
.text:00401834 call _memcpy
.text:00401839 add esp, 0Ch
.text:0040183C mov [ebp+var_5], 0
.text:00401840 push offset Str2 ; "FB40"
.text:00401845 lea ecx, [ebp+Dst]
.text:00401848 push ecx ; Str1
.text:00401849 call _strcmp ; 比较
.text:0040184E add esp, 8
.text:00401851 test eax, eax
.text:00401853 jnz short loc_401864
.text:00401855 push offset Format ; "Good Job!"
.text:0040185A call _printf
.text:0040185F add esp, 4
.text:00401862 jmp short loc_401871
.text:00401864 ; ---------------------------------------------------------------------------
.text:00401864
.text:00401864 loc_401864: ; CODE XREF: _VerifyKey0+B3j
.text:00401864 push offset aBadWork_please ; "Bad Work.Please try again!"
.text:00401869 call _printf
.text:0040186E add esp, 4
.text:00401871
.text:00401871 loc_401871: ; CODE XREF: _VerifyKey0+C2j
.text:00401871 jmp short loc_401880
.text:00401873 ; ---------------------------------------------------------------------------
.text:00401873
.text:00401873 loc_401873: ; CODE XREF: _VerifyKey0+5Ej
.text:00401873 push offset aBadWork_please ; "Bad Work.Please try again!"
.text:00401878 call _printf
.text:0040187D add esp, 4
.text:00401880
.text:00401880 loc_401880: ; CODE XREF: _VerifyKey0:loc_401871j
.text:00401880 pop edi
.text:00401881 pop esi
.text:00401882 pop ebx
.text:00401883 add esp, 50h
.text:00401886 cmp ebp, esp
.text:00401888 call __chkesp
.text:0040188D mov esp, ebp
.text:0040188F pop ebp
.text:00401890 retn
.text:00401890 _VerifyKey0 endp
F5 之后[C] 纯文本查看 复制代码
int __cdecl VerifyKey0(int gArr1Char0, int globalArray0)
{
int v2; // eax@7
char v4; // [sp+0h] [bp-5Ch]@0
char v5; // [sp+Ch] [bp-50h]@1
char Dst[4]; // [sp+50h] [bp-Ch]@6
__int16 changeG1; // [sp+54h] [bp-8h]@1
char v8; // [sp+57h] [bp-5h]@6
char v9; // [sp+58h] [bp-4h]@1
memset(&v5, -858993460, 0x50u);
v9 = 1;
changeG1 = (gArr1Char0 & 0xF0) >> 5; // changeG1的值必为2,为1时DealKey引入空值报错
if ( (signed __int16)((gArr1Char0 & 0xF0) >> 5) == 1 )// gArr1Char0的大小必须在0x40-0x5F
{
j__DealKey1(&GlobalArray_1[1], (char *)globalArray0, 14, 1);// 陷阱程序崩溃
}
else
{
if ( changeG1 != 2 ) // changeG1不为2时失败
v9 = 0;
}
if ( v9 )
{
j__DealKey1(&GlobalArray_1[1], "9QX6K882ISS5M", 14, 1);// 第2次处理
memcpy(Dst, &GlobalArray_1[1], 3u); // 从全局数组中第2位开始取3个
memcpy(&Dst[3], &GlobalArray_1[10], 4u); // 再从第10位开始取4个
v8 = 0;
if ( strcmp(Dst, "FB40") ) // 最后判断是否字串与"FB40"相等
v2 = printf("Bad Work.Please try again!");
else
v2 = printf("Good Job!");
}
else
{
v2 = printf("Bad Work.Please try again!");
}
return _chkesp(1, v2, v4);
}
从上述代码中可以看出GloabalArray_1[0]用来判断是否真正进入验证: changeG1=(GloabalArray_1[0]&0xF0)>>5; 而changeG1必须等于2才能进入真正的验证,所以GloabalArray_1[0]的值的二进制形式必须是[010?????B],其中问号表示未知(1 or 0),也就是GloabalArray_1[0]的范围必须在0x40~0x5F之间。 最后的验证为取GloabalArray_1[1]开始的3个字符,和GloabalArray_1[10]开始的4个字符。但是其实GloabalArray_1[10]之后取得只有前两个字符有用,因为验证最后是与字符串”FB40”比较是否相等所以要使验证通过最后肯定是这样的: GloabalArray_1[1]==’F’; GloabalArray_1[2]==’B’; GloabalArray_1[3]==’4’; GloabalArray_1[10]==’0’; GloabalArray_1[11]==’\0’;
再来看一下第二个处理函数,篇幅问题就直接看下F5的结果吧 [C] 纯文本查看 复制代码 int __cdecl DealKey1(char *globalArr1pchar1, char *keyStr, int gaLen, int status)
{
char v5; // [sp+0h] [bp-64h]@0
char v6; // [sp+Ch] [bp-58h]@1
int keyStrLen; // [sp+4Ch] [bp-18h]@1
int i; // [sp+50h] [bp-14h]@2
int v9; // [sp+54h] [bp-10h]@6
int v10; // [sp+58h] [bp-Ch]@6
unsigned int j; // [sp+5Ch] [bp-8h]@6
int kslTMP; // [sp+60h] [bp-4h]@1
memset(&v6, -858993460, 0x58u);
kslTMP = strlen(keyStr);
keyStrLen = kslTMP;
if ( status == 1 ) // 传进来的status均为1,仅此if范围内代码有用
{
kslTMP = 0;
for ( i = 0; i < gaLen; ++i )
{
kslTMP = i;
globalArr1pchar1 ^= keyStr[i % keyStrLen];// 与传进的keyStr对应下标位置值异或
}
}
if ( status == 2 ) // 无用
{
v9 = 0;
v10 = 0;
for ( j = 0; ; ++j )
{
kslTMP = j__max0(gaLen, keyStrLen);
if ( kslTMP <= j )
break;
globalArr1pchar1[v9] ^= keyStr[v10++];
++v9;
if ( v9 == gaLen )
v9 = 0;
if ( v10 == keyStrLen )
v10 = 0;
}
}
strcmp(GlobalArray_0, globalArr1pchar1);
return _chkesp(1, kslTMP, v5);
}
对此函数调用的地方仅仅有两处: 可以看出两处调用第4个参数status均为1
第一处是个陷阱会导致程序崩溃 j__DealKey1(&GlobalArray_1[1], (char *)globalArray0, 14, 1);// 陷阱程序崩溃 他的第二个参数是调用某个全局字串,通过分析可以发现,这个全局数组其实一直会是什么都没有,这就导致处理函数在获取字串长为0, globalArr1pchar1 ^= keyStr[i % keyStrLen];// 与传进的keyStr对应下标位置值异或 keyStrLen为0,已0为除数(取余会有除操作)导致系统错误,程序崩溃。
第2处的调用传入keyStr="9QX6K882ISS5M", j__DealKey1(&GlobalArray_1[1], "9QX6K882ISS5M", 14, 1);// 第2次处理
传进的GlobalArray_1数组中的值是与keyStr对应下标的值进行异或,这样就可以得出: 我把第一次处理key后的全局变量GloabalArray_1[?] 写成<?>(为了方便). <1> ^ ’9’==’F’; <2> ^ ‘Q’==’B’; <3> ^ ‘X’==’4’; <10> ^ ‘S’==’0’; <11> ^ ‘S’==’\0’; 计算后再加上方面对GloabalArray_1[0]的分析可以得出: <0> = 0x40~0x5F (010?????B) <1> = 0x7F (01111111B) <2> = 0x13 (00010011B) <3> = 0x6C (01101100B) <10> = 0x63 (01100011B) <11> = 0x53 (01010011B)
有了这些数据接下来就去看一下第一次处理也是最主要的处理key的部分: 标注一些变量后F5能够很清晰的了解算法 [C] 纯文本查看 复制代码 int __cdecl DealKey0(int key_len, char *char_table, char *key_str)
{
char v4; // [sp+0h] [bp-180h]@0
char v5; // [sp+Ch] [bp-174h]@1
int res; // [sp+4Ch] [bp-134h]@14
char vResult; // [sp+50h] [bp-130h]@11
int vCmp5; // [sp+54h] [bp-12Ch]@9
int vCmp8; // [sp+58h] [bp-128h]@11
int minC58; // [sp+5Ch] [bp-124h]@14
int _index; // [sp+60h] [bp-120h]@9
int _gArrIndex; // [sp+64h] [bp-11Ch]@9
int v13; // [sp+68h] [bp-118h]@14
int v14; // [sp+6Ch] [bp-114h]@14
char c; // [sp+70h] [bp-110h]@3
int i; // [sp+74h] [bp-10Ch]@1
unsigned int j; // [sp+78h] [bp-108h]@3
char _arr[256]; // [sp+7Ch] [bp-104h]@6
size_t char_table_len; // [sp+17Ch] [bp-4h]@4
memset(&v5, 0xCCCCCCCCu, 0x174u);
for ( i = 0; i < key_len; ++i ) // 将key转为字符表对应下标
{
c = key_str;
for ( j = 0; ; ++j )
{
char_table_len = strlen(char_table);
if ( char_table_len <= j )
break;
if ( char_table[j] == c )
{
_arr = j;
break;
}
}
}
vCmp5 = 0;
_index = 0;
_gArrIndex = 0;
while ( _index < key_len )
{
vCmp8 = 0;
vResult = 0;
while ( vCmp8 < 8 && _index < key_len )
{
LOBYTE(res) = _arr[_index];
v14 = 8 - vCmp8;
v13 = 5 - vCmp5;
minC58 = min(5 - vCmp5, 8 - vCmp8);
if ( vCmp5 <= vCmp8 )
LOBYTE(res) = (_BYTE)res << vCmp8;
else
LOBYTE(res) = (signed int)(unsigned __int8)res >> vCmp5;
vResult |= res;
vCmp8 += minC58;
vCmp5 += minC58;
if ( vCmp5 >= 5 )
{
++_index;
vCmp5 = 0;
}
}
GlobalArray_1[_gArrIndex++] = vResult;
}
GlobalArray_1[_gArrIndex] = 0;
return _chkesp(1, 5 * key_len / 8, v4);
}
上面就是关键算法的实现部分了,首先它根据输入key将每个字符在字符表中的下标存入_arr[]中(为了方便我将把_arr[?]直接写成[?],与上面<?>对应),然后再去[?]中的值进行运算,将每个运算结果写入到对应的<?>中。 [?]的值只能是在字符表的范围内,在这里字符表为”123456789abcdefghiklmnpqrstuvwxyz”共34个字符(它里面竟然不包含’o’,简直坑爹,害的我很苦),所以[?]值得范围是0~33(00000000B~00100001B). 这个算法的分析没什么好讲的,因为这已经是源代码了。下面主要看看每个<?>对应的生成方式,每个<?>会用多个[?]来生成。改变一下这段代码就能获取每个<?>的生成方式。 改变后的c代码: [C] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace std;
#define min(x,y) (((x)<(y)) ? (x):(y))
string toStr(int a)
{
char b[8];
itoa(a,b,10);
return b;
}
int main()
{
printf("\n_arr:<?>\nGloabalArray_1:[?]\n\n");
int key_len=24;
int vCmp5 = 0 , vCmp8 = 0;
int _index = 0;
int _gArrIndex = 0;
string vResult;
int minC58;
string res;
while ( _index < key_len )
{
vCmp8 = 0;
vResult = "<";
vResult += toStr(_gArrIndex);
vResult += "> = ";
bool p=false;
while ( vCmp8 < 8 && _index < key_len )
{
res = "([";
res += toStr(_index);
res += "]";
minC58 = min(5 - vCmp5, 8 - vCmp8);
if ( vCmp5 <= vCmp8 )
res = res + "<<" + toStr(vCmp8);
else
res = res + ">>" + toStr(vCmp5);
res+=")";
if(p)
vResult +="|";
vResult +=res;
p=true;
vCmp8 += minC58;
vCmp5 += minC58;
if ( vCmp5 >= 5 )
{
++_index;
vCmp5 = 0;
}
}
_gArrIndex++;
vResult +="\n";
printf(vResult.c_str());
}
printf("\n");
system("pause");
return 0;
}
运行后就能看到每个<?>的生成方式了。 其中只有<0>,<1>,<2>,<3>,<10>,<11>是我们需要的
下面就是最主要关键的部分了,根据已知的东西来分析获取key: 第一个有用的[?]的范围: [?] = (00000000B~00100001B); 先定义[?]为未知 Def [?] = 00??????B(黄底的部分说明原本是在[?]中的部分,下同) 一步步来 ①: <0> = ([0]<<0) | ([1]<<5) )= 010?????B ↓ 00??????B| ???00000B = 010?????B ↓ 000?????B| 01000000B = 010?????B 所以①可以得出: [0] = 000?????B , [1] = 00???010B ② <1> = ([1]>>3) | ([2]<<2) | ([3]<<7) = 01111111B ↓ 00000???B | ??????00B | ?0000000B = 01111111B ↓ 00000?11B | 01111?00B | 00000000B = 01111111B [1] = 00?11010B 要小于00100001B 所以 由②可得: [1] = 00011010B , [2] = 00011111B , [3] = 00?????0B ③ <2> = ([3]>>1) | ([4]<<4) = 00010011B ↓ 000?????B | ????0000B = 00010011B ↓ 000?00111B | 000?0000B = 00010011B [3] = 00?001110B , 要小于00100001B 所以 由③可得: [3] = 00000110B , [4] = 00??0001B ④ <3> = ([4]>>4) | ([5]<<1) | ([6]<<6) = 01101100B ↓ 000000??B | 0??????0B | ??000000B = 01101100B ↓ 00000000B | 0?101100B | 0?000000B = 01101100B [5] = 00?10110B,要小于00100001B 所以 有④可得: [4] = 00000001B , [5] = 00010110B , [6] = 00????01B ⑤ <10> = ([16]<<0) | ([17]<<5) = 01100011B ↓ 00??????B | ???00000B = 01100011B ↓ 00000011B |01100000B = 01100011B 所以 由⑤可得: [16] = 00000011B , [17] = 00???011B ⑥ <11> = ([17] >> 3) | ([18]<<2) | ([19]<<7) =01010011B ↓ 00000???B | ??????00B | ?0000000B = 01010011B ↓ 00000011B | 0101000B | 00000000B = 01010011B 所以 由⑥可得: [17] = 0001101?B , [18] = 00010100B , [19] = 00?????0B 最后综上可得:
[0] = 000?????B = (0~31) [1] = 00011010B= 26 [2] = 00011111B =31 [3] = 00000110B = 6 [4] = 00000001B = 1 [5] = 00010110B = 22 [6] = 00????01B = {1,5,9,13,17,21,25,29,33} [16] = 00000011B = 3 [17] = 00011011B = 27 [18] = 00010100B = 20 [19] = 00?????0B = {2,4,6,8...30,32}
最后通过对应字符表可得最后key字符串的24位字符的范围: [C] 纯文本查看 复制代码
charTable = '123456789abcdefghijklnpqrstuvwxyz';
key[0] = {'1' ~ 'w'};
key[1] = 's';
key[2] = 'x';
key[3] = '7';
key[4] = '2';
key[5] = 'n';
key[6] = {'2','6','a','e','i','m','r','v','z'};
key[7] ~ key[15] = 任意;
key[16] = '4';
key[17] = 't';
key[18] = 'l';
key[19] = {'1','3','5','7','9','b','d','f','h','j','l','n','q','s','u','w','y'};
key[20] ~ key[24] = 任意;
//这里的任意是charTable中的任意字符(很坑爹的不包含字母'o');
随便测试几个key:
分析完了,明天继续图书馆。 |