references:
下断点的方法和crackhead一样,通过搜索所有引入的函数,从中找出可以用来下断点的函数
此处为GetDlgItemTextA函数
我们在该函数出现的所有位置下断点,然后在cycle程序的输入框中随便输点东西,触发断点即可
断点触发之后,我们就不需要这些断点了,在函数调用的下一行下断点即可
下面我们开始捋代码
004010A6 |. 6A 11 PUSH 11 ; /Count = 11 (17.)
004010A8 |. 68 71214000 PUSH cycle.00402171 ; |Buffer = cycle.00402171
004010AD |. 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 (1001.)
004010B2 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004010B5 |. E8 0F020000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004010BA |. 0BC0 OR EAX,EAX
004010BC |. 74 61 JE SHORT cycle.0040111F
004010BE |. 6A 11 PUSH 11 ; /Count = 11 (17.)
004010C0 |. 68 60214000 PUSH cycle.00402160 ; |Buffer = cycle.00402160
004010C5 |. 68 E8030000 PUSH 3E8 ; |ControlID = 3E8 (1000.)
004010CA |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004010CD |. E8 F7010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004010D2 |. 0BC0 OR EAX,EAX
004010D4 |. 74 49 JE SHORT cycle.0040111F
GetDlgItemTextA
函数被调用了两次,返回值存储在EAX中,为获取到的字符串的长度,第三个参数为传出参数,用于存储获取到的字符串,两次调用中分别传入了指针0x00402171
和00402160
根据调试结果,第一次调用获取到的是Serial,第二次调用获取到的是Name
只要我们输入不为空(即EAX值不为0),那么上面代码中的JE SHORT cycle.0040111F
就不会执行
接着往下看
004010D6 |. B9 10000000 MOV ECX,10
004010DB |. 2BC8 SUB ECX,EAX
004010DD |. BE 60214000 MOV ESI,cycle.00402160 ; ASCII "1231231231231231"
004010E2 |. 8BFE MOV EDI,ESI
004010E4 |. 03F8 ADD EDI,EAX
004010E6 |. FC CLD
004010E7 |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
这段代码对用户名进行了拷贝,CLD复位DDF标志寄存器,因此在执行REP MOVS时ESI和EDI每次拷贝完成后都会增加,由于此处为REP MOVS BYTE
,因此每次拷贝一个字节,ESI和EDI的增加梯度也为1字节
拷贝的次数由ECX-EAX的结果决定,即(16-用户名长度),其实就是将用户名的长度扩展到了16位
004010E9 |. 33C9 XOR ECX,ECX
004010EB |. BE 71214000 MOV ESI,cycle.00402171 ; ASCII "1234567890000000"
004010F0 |> 41 /INC ECX
004010F1 |. AC |LODS BYTE PTR DS:[ESI]
004010F2 |. 0AC0 |OR AL,AL
004010F4 |. 74 0A |JE SHORT cycle.00401100
004010F6 |. 3C 7E |CMP AL,7E
004010F8 |. 7F 06 |JG SHORT cycle.00401100
004010FA |. 3C 30 |CMP AL,30
004010FC |. 72 02 |JB SHORT cycle.00401100
004010FE |.^EB F0 \JMP SHORT cycle.004010F0
00401100 |> 83F9 11 CMP ECX,11
00401103 |. 75 1A JNZ SHORT cycle.0040111F
清空ECX,然后将ESI指向Serial
下面开始循环,其中有一条指令大家可能没见过
LODS指令,Load String Operand
比如此处的LODS BYTE PTR DS:[ESI]
,会将ESI所指向的内容加载到EAX寄存器中,至于是加载到EAX、AX、AL由指令控制,此处由于我们是LODS BYTE
,因此内存中的内容会被加载到AL中
因此上面代码中的循环就干了一件事,就是检测Serial中的字符是否位于0x30
和0x7E
之间,也就是ASCII码表中的48~126之间
最后判断了一下字符串长度是否为16,因为ECX会多加一次,也就是说比实际长度多1,根据CMP ECX, 11
,我们可以确定Serial的长度必须为16位,因为如果无法满足这个条件,程序就会直接跳到RET指令,那就没得玩儿了,你可以自己试一下,如果Serial的长度不是16位,当你点击Check按钮的时候,程序是没有任何反应的
接着看
00401105 |. E8 E7000000 CALL cycle.004011F1
0040110A |. B9 01FF0000 MOV ECX,0FF01
0040110F |. 51 PUSH ECX
00401110 |. E8 7B000000 CALL cycle.00401190
00401115 |. 83F9 01 CMP ECX,1
00401118 |. 74 06 JE SHORT cycle.00401120
0040111A |> E8 47000000 CALL cycle.00401166
0040111F |> C3 RETN
00401120 |> A1 68214000 MOV EAX,DWORD PTR DS:[402168]
我们需要确保JE SHORT cycle.00401120
执行,因为如果在这里没有跳转的话,那么不可避免的就一定会执行RETN,程序结束,又没得玩了
RETN上面那个CALL只是输出一下错误提示而已
因此我们要保证在前两个CALL执行完毕之后,ECX的值为1
这里我贴一下这两个函数的汇编代码:
cycle.004011f1
004011F1 /$ A1 60214000 MOV EAX,DWORD PTR DS:[402160]
004011F6 |. 8B1D 64214000 MOV EBX,DWORD PTR DS:[402164]
004011FC |. 3305 71214000 XOR EAX,DWORD PTR DS:[402171]
00401202 |. 331D 75214000 XOR EBX,DWORD PTR DS:[402175]
00401208 |. 25 0F1F3F7F AND EAX,7F3F1F0F
0040120D |. 81E3 00010307 AND EBX,7030100
00401213 |. 33C9 XOR ECX,ECX
00401215 |> 8BF0 /MOV ESI,EAX
00401217 |. 8BFB |MOV EDI,EBX
00401219 |. D3E6 |SHL ESI,CL
0040121B |. D3E7 |SHL EDI,CL
0040121D |. 81E6 80808080 |AND ESI,80808080
00401223 |. 81E7 80808080 |AND EDI,80808080
00401229 |. 8BD6 |MOV EDX,ESI
0040122B |. C0EE 07 |SHR DH,7
0040122E |. 66:C1E2 07 |SHL DX,7
00401232 |. C1EA 08 |SHR EDX,8
00401235 |. C0EE 07 |SHR DH,7
00401238 |. 66:C1E2 07 |SHL DX,7
0040123C |. C1EA 08 |SHR EDX,8
0040123F |. C0EE 07 |SHR DH,7
00401242 |. 66:D1EA |SHR DX,1
00401245 |. 8BF2 |MOV ESI,EDX
00401247 |. 8BD7 |MOV EDX,EDI
00401249 |. C0EE 07 |SHR DH,7
0040124C |. 66:C1E2 07 |SHL DX,7
00401250 |. C1EA 08 |SHR EDX,8
00401253 |. C0EE 07 |SHR DH,7
00401256 |. 66:C1E2 07 |SHL DX,7
0040125A |. C1EA 08 |SHR EDX,8
0040125D |. C0EE 07 |SHR DH,7
00401260 |. 66:C1EA 05 |SHR DX,5
00401264 |. 8BFA |MOV EDI,EDX
00401266 |. 33FE |XOR EDI,ESI
00401268 |. 8BD7 |MOV EDX,EDI
0040126A |. 81E2 FF000000 |AND EDX,0FF
00401270 |. 51 |PUSH ECX
00401271 |. 52 |PUSH EDX
00401272 |. BA 08000000 |MOV EDX,8
00401277 |. 91 |XCHG EAX,ECX
00401278 |. 83F8 03 |CMP EAX,3
0040127B |. 7F 0F |JG SHORT cycle.0040128C
0040127D |. F6E2 |MUL DL
0040127F |. 5A |POP EDX
00401280 |. 83C0 08 |ADD EAX,8
00401283 |. 91 |XCHG EAX,ECX
00401284 |. D3C0 |ROL EAX,CL
00401286 |. 33C2 |XOR EAX,EDX
00401288 |. D3C8 |ROR EAX,CL
0040128A |. EB 0D |JMP SHORT cycle.00401299
0040128C |> 83E8 03 |SUB EAX,3
0040128F |. F6E2 |MUL DL
00401291 |. 5A |POP EDX
00401292 |. 91 |XCHG EAX,ECX
00401293 |. D3C3 |ROL EBX,CL
00401295 |. 33DA |XOR EBX,EDX
00401297 |. D3CB |ROR EBX,CL
00401299 |> 59 |POP ECX
0040129A |. 41 |INC ECX
0040129B |. 83F9 08 |CMP ECX,8
0040129E |.^0F85 71FFFFFF \JNZ cycle.00401215
004012A4 \. C3 RETN
cycle.00401190
00401190 /$ 5F POP EDI
00401191 |. 59 POP ECX
00401192 |. 57 PUSH EDI
00401193 |. 81F9 80000000 CMP ECX,80
00401199 |. 7E 55 JLE SHORT cycle.004011F0
0040119B |. 51 PUSH ECX
0040119C |. 8BF1 MOV ESI,ECX
0040119E |. 81E1 FF000000 AND ECX,0FF
004011A4 |. 8BF8 MOV EDI,EAX
004011A6 |. 83F9 08 CMP ECX,8
004011A9 |. 7E 05 JLE SHORT cycle.004011B0
004011AB |. 8BFB MOV EDI,EBX
004011AD |. C1E9 04 SHR ECX,4
004011B0 |> C1C7 08 /ROL EDI,8
004011B3 |. D1E9 |SHR ECX,1
004011B5 |.^75 F9 \JNZ SHORT cycle.004011B0
004011B7 |. C1EE 08 SHR ESI,8
004011BA |. 23FE AND EDI,ESI
004011BC |. 81E7 FF000000 AND EDI,0FF
004011C2 |. 59 POP ECX
004011C3 |> BE 80000000 MOV ESI,80
004011C8 |> 85FE /TEST ESI,EDI
004011CA |. 74 20 |JE SHORT cycle.004011EC
004011CC |. 33FE |XOR EDI,ESI
004011CE |. 57 |PUSH EDI
004011CF |. 81E1 00FF0000 |AND ECX,0FF00
004011D5 |. 87CE |XCHG ESI,ECX
004011D7 |. 32E9 |XOR CH,CL
004011D9 |. 33F1 |XOR ESI,ECX
004011DB |. 87F1 |XCHG ECX,ESI
004011DD |. 51 |PUSH ECX
004011DE |. FF05 82214000 |INC DWORD PTR DS:[402182]
004011E4 |. E8 A7FFFFFF |CALL cycle.00401190
004011E9 |. 5F |POP EDI
004011EA |.^EB D7 |JMP SHORT cycle.004011C3
004011EC |> D1EE |SHR ESI,1
004011EE |.^75 D8 \JNZ SHORT cycle.004011C8
004011F0 \> C3 RETN
指令都认识,但是这两个函数具体做了什么,我是不知道的,肯定是某种算法,但是变成机器码之后,几乎面目全非,根本无法辨识
我也是盯着这两段代码看了好久,什么思路都没有,最后就干脆用C语言把这个代码逻辑给实现了
在这段代码中,我固定了用户名的前8位以及序列号的前4位,然后爆破序列号的4~7位
根据代码逻辑,EBX的初始化过程如下:
ebx = name[4-7] ^ serial[4-7] & 0x07030100
由于很多bit位都在与操作中被丢弃了,所以serial[4-7]
并没有太多可能的值
但是我的这个代码并没有得到合法的Name和Serial,不过我发现了一些规律
在第一个函数cycle.004011f1
运行完毕之后,EAX的值从未变过,EBX的值呈现周期性循环,循环周期为4
经过第二个函数cycle.00401190
的运算之后,ecx的值总是同一个
后来我就突发奇想,如果我们控制EAX的值,使对应掩码0x7F3F1F0F
所有的保留位全部为1,会发生什么?于是得出如下关系式
name[0-3] ^ serial[0-3] = 0x7F3F1F0F
serial[0-3] = name[0-3] ^ 0x7F3F1F0F
上面表达式中的0x7F3F1F0F
可以有多个值,只要每个字节的高4bit的值x
分别满足0<= x <1
、0<= x <2
、0<= x <4
、0<= x <8
即可,具体逻辑可以看代码
但是这样会限制serial[3]
的值不能超过0x50
,后来放松了第一个字节的限制,使其满足0<= x <2
,发现这样也能生成有效的序列号,
然后我就随便找了一组,**JE
、UUUJ
结果发现,居然找出了满足ecx=1
的serial[4-7]
虽然我不知道这究竟是为什么,但事实就是只要我们满足上面那个表达式,那么经过最多64次尝试,就能找到一组合法的用户名和序列号
在通过ecx==1
的检测之后,还有一个检测,但是就比较简单了,具体分析请查看下面的注册机源代码
注册机运行效果:
下面是注册机代码,仍然使用codeblocks编译,你可以取消____DEBUG
宏的注释以观察程序运行过程中各变量的变化:
// Author: 12138
// URL: http://144.34.164.217
// https://144.one
// Date: 2022/05/12 04:00 AM
// Desc: CrackMe.cycle serial generator
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
// #define ____DEBUG
#ifdef ____DEBUG
#define FUCK(_format, _var) printf(_format, _var);
#define CUM(_str) printf(_str);
#else
#define FUCK(_format, _var);
#define CUM(_str);
#endif
typedef unsigned int _ui;
// 这个计数器应该是一个全局变量
_ui _TODO_counter = 0xFEDCBA98;
char generate_random(char lower, char upper) {
return (rand() % (upper - lower + 1)) + lower;
}
_ui string_to_hex(char* _string, int _begin) {
char _24_31[3]={0};
char _16_23[3]={0};
char _8_15[3]={0};
char _0_7[3]={0};
sprintf(_24_31, "%02X", _string[_begin]);
sprintf(_16_23, "%02X", _string[_begin+1]);
sprintf(_8_15, "%02X", _string[_begin+2]);
sprintf(_0_7, "%02X", _string[_begin+3]);
char hex_string[8+1] = {0};
strcpy(hex_string, _0_7);
strcat(hex_string, _8_15);
strcat(hex_string, _16_23);
strcat(hex_string, _24_31);
_ui _ret_val = (_ui)strtoul(hex_string, NULL, 16);
return _ret_val;
}
int hex_to_string(_ui _hex_value, char* _ret_string) {
_ui _s_15 = _hex_value & 0xFF000000;
_s_15 = _s_15 >> 24;
_ui _s_14 = _hex_value & 0x00FF0000;
_s_14 = _s_14 >> 16;
_ui _s_13 = _hex_value & 0x0000FF00;
_s_13 = _s_13 >> 8;
_ui _s_12 = _hex_value & 0x000000FF;
if (_s_12 < 0x30 || _s_12 > 0x7E || _s_13 < 0x30 || _s_13 > 0x7E || _s_14 < 0x30 || _s_14> 0x7E || _s_15 < 0x30 || _s_15 > 0x7E) {
return 1;
}
char hex_string[4+1] = {0};
char _tmp_val[2] = {0};
sprintf(_tmp_val, "%c", _s_12);
strcpy(hex_string, _tmp_val);
sprintf(_tmp_val, "%c", _s_13);
strcat(hex_string, _tmp_val);
sprintf(_tmp_val, "%c", _s_14);
strcat(hex_string, _tmp_val);
sprintf(_tmp_val, "%c", _s_15);
strcat(hex_string, _tmp_val);
strcpy(_ret_string, hex_string);
return 0;
}
_ui _401190(_ui eax, _ui ebx, _ui ecx) {
if(ecx > 0x80) {
_ui _tmp_val = ecx;
_ui esi = ecx;
ecx = ecx & 0xFF;
_ui edi = eax;
if(ecx > 0x08) {
edi = ebx;
ecx = ecx >> 4;
}
while (ecx != 0) {
edi = _rotl(edi, 8);
ecx = ecx >> 1;
}
esi = esi >> 8;
edi = edi & esi;
edi = edi & 0xFF;
ecx = _tmp_val;
esi = 0x80;
while (1) {
if((esi & edi) == 0) {
esi = esi >> 1;
// 只有esi和edi与运算的结果等于0且esi右移1位等于0的时候,这个循环才会被打破
if(esi == 0)
break;
else
continue;
}
edi = edi ^ esi;
_tmp_val = edi;
ecx = ecx & 0xFF00;
_ui _exch_val = ecx;
ecx = esi;
esi = _exch_val;
// 对CH和CL进行异或运算,并将结果反映到ECX上
_ui ch = ecx & 0xFF00;
ch = ch >> 8;
_ui cl = ecx & 0xFF;
ch = ch ^ cl;
ch = ch << 8;
ch = ch & 0xFF00;
ecx = ecx & 0xFFFF00FF;
ecx = ecx | ch;
esi = esi ^ ecx;
_exch_val = ecx;
ecx = esi;
esi = _exch_val;
_TODO_counter += 1;
FUCK("[*] _TODO counter: 0x%x\n", _TODO_counter);
ecx = _401190(eax, ebx, ecx);
edi = _tmp_val;
esi = 0x80;
continue;
}
return ecx;
}
else
return ecx;
}
int register_func(char* extend_user_name, char* serial) {
printf("[*] current Serial: %s\n", serial);
// 获取用户名的0~3位并转换成整型数
_ui eax = string_to_hex(extend_user_name, 0);
FUCK("[*] eax: %x\n", eax);
// 获取用户名的4~7位并转换成整型数
_ui ebx = string_to_hex(extend_user_name, 4);
FUCK("[*] ebx: %x\n", ebx);
// 获取序列号的0~3位并转换成整型数
_ui _xor_eax = string_to_hex(serial, 0);
FUCK("[*] the oprand xor with eax: %x\n", _xor_eax);
// 获取序列号的4~7位并转换成整型数
_ui _xor_ebx = string_to_hex(serial, 4);
FUCK("[*] the oprand xor with ebx: %x\n", _xor_ebx);
eax = eax ^ _xor_eax;
ebx = ebx ^ _xor_ebx;
eax = eax & 0x7F3F1F0F;
ebx = ebx & 0x07030100;
printf("[*] eax is ready for loop: %x\n", eax);
printf("[*] ebx is ready for loop: %x\n",ebx);
// 下面开始循环
int loop_counter = 0, ecx = 0;
while(loop_counter < 8) {
FUCK("\n_________________OO<--%d-->OO_________________\n", loop_counter);
ecx = loop_counter;
_ui esi = eax;
_ui edi = ebx;
esi = esi << ecx;
edi = edi << ecx;
// 这一步其实就是获取到esi和edi每个字节中的最高位
esi = esi & 0x80808080;
edi = edi & 0x80808080;
_ui edx = esi;
// 下面这一堆移位操作其实是把每个字节中的最高位移动到了DL的高四位中
_ui _d = edx & 0x80000000;
_d = _d >> 31;
_ui _c = edx & 0x00800000;
_c = _c >> 23;
_ui _b = edx & 0x00008000;
_b = _b >> 15;
_ui _a = edx & 0x00000080;
_a = _a >> 7;
char _DL_H4_0_3[3] = {0};
sprintf(_DL_H4_0_3, "%X", _d*8+_c*4+_b*2+_a);
char* hex_string = (char*)malloc(2+1);
strcpy(hex_string, _DL_H4_0_3);
edx = (_ui)strtoul(hex_string, NULL, 16);
free(hex_string);
edx = edx << 4;
esi = edx;
FUCK("[*] esi: 0x%x\n", esi);
/*
我们分别用ABCD代表一个32位数中每个字节的最高位
|-----------------| |--------DX-------|
| | |---DH--| |--DL---|
D000 0000 C000 0000 B000 0000 A000 0000 DH >> 7
D000 0000 C000 0000 0000 000B A000 0000 DX << 7
D000 0000 C000 0000 BA00 0000 0000 0000 EDX >> 8
0000 0000 D000 0000 C000 0000 BA00 0000 DH >> 7
0000 0000 D000 0000 0000 000C BA00 0000 DX << 7
0000 0000 D000 0000 CBA0 0000 0000 0000 EDX >> 8
0000 0000 0000 0000 D000 0000 CBA0 0000 DH >> 7
0000 0000 0000 0000 0000 000D CBA0 0000 DX >> 1
0000 0000 0000 0000 0000 0000 DCBA 0000
*/
edx = edi;
// 下面这一堆移位操作其实是把每个字节中的最高位移动到了DL的低四位中
/*
我们分别用ABCD代表一个32位数中每个字节的最高位
|-----------------| |--------DX-------|
| | |---DH--| |--DL---|
D000 0000 C000 0000 B000 0000 A000 0000 DH >> 7
D000 0000 C000 0000 0000 000B A000 0000 DX << 7
D000 0000 C000 0000 BA00 0000 0000 0000 EDX >> 8
0000 0000 D000 0000 C000 0000 BA00 0000 DH >> 7
0000 0000 D000 0000 0000 000C BA00 0000 DX << 7
0000 0000 D000 0000 CBA0 0000 0000 0000 EDX >> 8
0000 0000 0000 0000 D000 0000 CBA0 0000 DH >> 7
0000 0000 0000 0000 0000 000D CBA0 0000 DX >> 5
0000 0000 0000 0000 0000 0000 0000 DCBA
*/
_d = edx & 0x80000000;
_d = _d >> 31;
_c = edx & 0x00800000;
_c = _c >> 23;
_b = edx & 0x00008000;
_b = _b >> 15;
_a = edx & 0x00000080;
_a = _a >> 7;
char _DL_L4_0_3[3] = {0};
sprintf(_DL_L4_0_3, "%02X", _d*8+_c*4+_b*2+_a);
hex_string = (char*)malloc(2+1);
strcpy(hex_string, _DL_L4_0_3);
edx = (_ui)strtoul(hex_string, NULL, 16);
free(hex_string);
edi = edx;
FUCK("[*] edi: 0x%x\n", edi);
edi = edi ^ esi;
edx = edi;
edx = edx & 0xFF;
_ui _tmp_val = ecx;
ecx = eax;
eax = _tmp_val;
FUCK("[*] edx: 0x%x\n", edx);
if (eax > 3) {
eax = eax - 3;
eax = eax * 8;
_tmp_val = eax;
eax = ecx;
ecx = _tmp_val;
ebx = _rotl(ebx, ecx);
ebx = ebx ^ edx;
ebx = _rotr(ebx, ecx);
} else {
eax = eax * 8 + 8;
_tmp_val = eax;
eax = ecx;
ecx = _tmp_val;
eax = _rotl(eax, ecx);
eax = eax ^ edx;
eax = _rotr(eax, ecx);
}
FUCK("[*] eax: 0x%x\n",eax);
FUCK("[*] ebx: 0x%x\n", ebx);
loop_counter += 1;
}
printf("[*] after loop, eax: 0x%X\n", eax);
printf("[*] after loop, ebx: 0x%X\n", ebx);
// 下面是函数cycle.401190
ecx = 0xFF01;
// 这里需要对一个内存(0x402182)的值进行一个加法运算
// 我暂时还不清楚这个内存的值是否固定,因此先留一个TODO,经调试,这里的初始值应该是98BADCFE
// 转换成整型数就是FEDCBA98
// 该函数并不是简单地循环,是一个递归函数,因此需要单独定义
ecx = _401190(eax, ebx, ecx);
FUCK("[*] ecx: 0x%X\n", ecx);
return ecx;
}
int main_call() {
_TODO_counter = 0xFEDCBA98;
char user_name[16+1] = {0};
printf("[*] give me a max-16-len string, 'q' to exit:\n\t");
scanf("%16s", user_name);
FUCK("%s\n", user_name);
if(1==strlen(user_name) && 'q'==user_name[0]) return 'q';
// 将用户名扩充至16位
int user_name_length = strlen(user_name);
printf("[*] user name length: %d\n", user_name_length);
char* extend_user_name = (char*)malloc(16+1);
strcpy(extend_user_name, user_name);
int i = 0;
for(; i<16-user_name_length; i++) {
char _tmp_val[2] = {0};
sprintf(_tmp_val, "%c", user_name[i%user_name_length]);
strcat(extend_user_name, _tmp_val);
}
printf("[*] user name after extended: %s\n", extend_user_name);
// 对于掩码0x7F3F1F0F
// 0x7F和username[3]进行异或运算
// 如果username[3]的1和3bit置位,那么serial[3]的1和3bit一定会复位
// 则serial[3]一定会小于0x30,这样就无法通过字符范围的检测了[0x30, 0x7E]
// if(180 <= extend_user_name[3]) {
// printf("[-] after xor with mask(0x7F), the value would less than 0x30, \n\tusername[3]'s ascii value should not be greater than 0x50\n");
// continue;
// }
// 首先获取到注册码的前4位
// __n_0_3 ^ __s_0_3 == 0x7F3F1F0F
_ui _n_0_3 = string_to_hex(extend_user_name, 0);
FUCK("0x%X\n", _n_0_3);
char _s_0_3[4+1] = {0};
_ui _hex_xor_res = 0;
i=0;
int j = 0;
int k = 0;
int l = 0;
for(; i<8; i++) {
j=0;
for(; j<4; j++) {
k=0;
for(; k<2; k++) {
l=0;
for(; l<2; l++) {
memset(_s_0_3, 0, sizeof(_s_0_3));
char _tmp_char[2+1] = {0};
char hex_string[4+1] = {0};
sprintf(_tmp_char, "%02X", (4*l+3)*16+0x0F);
strcpy(hex_string, _tmp_char);
sprintf(_tmp_char, "%02X", (4*k+3)*16+0x0F);
strcat(hex_string, _tmp_char);
sprintf(_tmp_char, "%02X", (2*j+1)*16+0x0F);
strcat(hex_string, _tmp_char);
sprintf(_tmp_char, "%02X", i*16+0x0F);
strcat(hex_string, _tmp_char);
FUCK("hex_string = %s\n", hex_string);
// _hex_xor_res = 0x0F0F0F0F;
_hex_xor_res = (_ui)strtoul(hex_string, NULL, 16);
FUCK("hex_xor_res = 0x%X\n", _hex_xor_res);
FUCK("_n_0_3----%x\n", _n_0_3 ^ _hex_xor_res);
if(hex_to_string(_n_0_3 ^ _hex_xor_res, _s_0_3)==0) {
FUCK("_s_0_3----%s\n", _s_0_3);
goto __GOTO_ONCE;
}
}
}
}
}
__GOTO_ONCE:
FUCK("%s\n", _s_0_3);
// 为了通过注册码长度检测,这里先给序列号填充为16位,其实后八位暂时是用不着的
char* _s_8_15 = "89abcdef";
// 由于掩码0x07030100最后会和EBX进行与运算
// 所以最后EBX的四个字节,从高到低依次拥有3、2、1、0个有效bit位
// 也就是说,无论_n_4_7和_s_4_7是什么样的,EBX只有2^(3+2+1+0)=64种可能的值
// 下面这个组合可以覆盖到所有的可能性
// 这样一来,我们就可以获取到注册码的4~7位了
char* _s_4 = "@ABCDEFG";
char* _s_5 = "@ABC";
char* _s_6 = "@A";
char* _s_7 = "A";
i=0;
for(; i<8; i++) {
j=0;
for(; j<4;j++) {
k=0;
for(; k<2; k++) {
char* hex_string = (char*)malloc(16+1);
strcpy(hex_string, _s_0_3);
char _s_4_7[4+1] = {0};
char _tmp_val[2] = {0};
sprintf(_tmp_val, "%c", _s_4[i]);
strcpy(_s_4_7, _tmp_val);
sprintf(_tmp_val, "%c", _s_5[j]);
strcat(_s_4_7, _tmp_val);
sprintf(_tmp_val, "%c", _s_6[k]);
strcat(_s_4_7, _tmp_val);
sprintf(_tmp_val, "%c", _s_7[0]);
strcat(_s_4_7, _tmp_val);
strcat(hex_string, _s_4_7);
strcat(hex_string, _s_8_15);
if(register_func(extend_user_name, hex_string) == 1) {
free(hex_string);
printf("[+] half success!!! come on!!!\n\n");
// 实现剩下的运算
char _24_31[3]={0};
char _16_23[3]={0};
char _8_15[3]={0};
char _0_7[3]={0};
// 获取用户名的8~11并转换成整型数
_ui eax = string_to_hex(extend_user_name, 8);
FUCK("[*] eax: %x\n", eax);
// 获取用户名的12~15并转换成整型数
_ui ebx = string_to_hex(extend_user_name, 12);
FUCK("[*] ebx: %x\n", ebx);
eax = eax ^ ebx;
eax = eax ^ _TODO_counter;
eax = eax | 0x40404040;
eax = eax & 0x77777777;
while(1) {
char _eax_bin_format[32+1] = {0};
itoa(eax, _eax_bin_format, 2);
char __eax_bin_format[32+1] = {0};
sprintf(__eax_bin_format, "%032s", _eax_bin_format);
int index=0;
CUM("[+] this is the binary format of serial_8_11 ^ serial_12_15 result:\n\t");
for(; index<32; index++) {
FUCK("%c", __eax_bin_format[index]);
if((index+1)%4 == 0 && (index+1)!=32) {
CUM(" ");
}
if((index+1)%8 == 0 && (index+1)!=32) {
CUM("%% ");
}
}
CUM("\n");
char _s_8_11[4+1] = {0};
sprintf(_tmp_val, "%c", generate_random(0x30, 0x7E));
FUCK("random char %s", _tmp_val);
strcpy(_s_8_11, _tmp_val);
sprintf(_tmp_val, "%c", generate_random(0x30, 0x7E));
FUCK("random char %s", _tmp_val);
strcat(_s_8_11, _tmp_val);
sprintf(_tmp_val, "%c", generate_random(0x30, 0x7E));
FUCK("random char %s", _tmp_val);
strcat(_s_8_11, _tmp_val);
sprintf(_tmp_val, "%c", generate_random(0x30, 0x7E));
FUCK("random char %s", _tmp_val);
strcat(_s_8_11, _tmp_val);
printf("[*] generating random 4-len string: %s\n", _s_8_11);
// char _DEBUG_s_8_11[4+1] = "}3d7";
// strcpy(_s_8_11, _DEBUG_s_8_11);
_ui _s_8_11_hex_value = string_to_hex(_s_8_11, 0);
// 根据汇编代码
// eax = eax ^ s_8_11
// eax = eax ^ s_12_15
// eax == 0必须成立
// 那么可以推导出 eax ^ s_8_11 == s_12_15
FUCK("_s_8_11_hex_value: 0x%X\n", _s_8_11_hex_value);
FUCK("eax: 0x%X\n", eax);
_ui _s_12_15_hex_value = eax ^ _s_8_11_hex_value;
FUCK("_s_12_15_hex_value: 0x%X\n", _s_12_15_hex_value);
hex_string = (char*)malloc(4+1);
if(1==hex_to_string(_s_12_15_hex_value, hex_string)) {
free(hex_string);
continue;
}
char serial_number[16+1] = {0};
strcpy(serial_number, _s_0_3);
strcat(serial_number, _s_4_7);
strcat(serial_number, _s_8_11);
strcat(serial_number, hex_string);
free(hex_string);
printf("[+] Name:\n\t%s\n", user_name);
printf("[+] Serial:\n\t%s\n", serial_number);
char c = 0;
while ((c = getchar()) != EOF && c != '\n');
return 0;
}
}
free(hex_string);
}
}
}
return 0;
}
int main(int argc, char** argv) {
while(1)
if('q' == main_call()) break;
return 0;
}