【文章标题】: Happytown的第18个crackme分析+注册机
【文章作者】: kaien
【作者邮箱】: kkaien@hotmail.com
【软件名称】: CrackMe_0018.exe
【加壳方式】: 无壳
【使用工具】: OllyDbg,VC(写注册机用)
【操作平台】: winxp
本文分两部分,前半部分是算法分析;后半部分是注册机。
其实,本文的前半部分在我的另一个帖子里已经发表过了。
但是我们的happytown发话了,不写出注册机誓不罢休!- -
无奈之下,只好献丑了!
时间仓促,水平有限,错误之处在所难免,还望各位大大们手下留情 ^_^
算法部分代码如下,很简单,看我注释:
0040117E |. 68 C9000000 push 0C9 ; /Count = C9 (201.)
00401183 |. 50 push eax ; |Buffer
00401184 |. 68 E8030000 push 3E8 ; |ControlID = 3E8 (1000.)
00401189 |. 57 push edi ; |hWnd
0040118A |. FFD5 call ebp ; \GetDlgItemTextA
0040118C |. 8BF0 mov esi,eax
0040118E |. 83FE 04 cmp esi,4 ; 用户名长度>=4
00401191 |. 0F8C CA000000 jl CrackMe_.00401261
00401197 |. 8BCE mov ecx,esi
00401199 |. 81E1 01000080 and ecx,80000001
0040119F |. 79 05 jns short CrackMe_.004011A6
004011A1 |. 49 dec ecx
004011A2 |. 83C9 FE or ecx,FFFFFFFE
004011A5 |. 41 inc ecx
004011A6 |> 0F85 B5000000 jnz CrackMe_.00401261 ; 用户名长度必须为偶数,否则就跳了
004011AC |. 8D9424 D8000000 lea edx,dword ptr ss:[esp+D8]
004011B3 |. 56 push esi
004011B4 |. 52 push edx
004011B5 |. E8 B6000000 call CrackMe_.00401270 ; 小写转大写
004011BA |. 83C4 08 add esp,8
004011BD |. 85C0 test eax,eax
004011BF |. 0F84 9C000000 je CrackMe_.00401261
004011C5 |. 8D4424 10 lea eax,dword ptr ss:[esp+10]
004011C9 |. 68 C9000000 push 0C9
004011CE |. 50 push eax
004011CF |. 68 E9030000 push 3E9
004011D4 |. 57 push edi
004011D5 |. FFD5 call ebp
004011D7 |. 3BC6 cmp eax,esi
004011D9 |. 0F85 82000000 jnz CrackMe_.00401261
004011DF |. 8D4C24 10 lea ecx,dword ptr ss:[esp+10]
004011E3 |. 50 push eax
004011E4 |. 51 push ecx
004011E5 |. E8 86000000 call CrackMe_.00401270
004011EA |. 83C4 08 add esp,8
004011ED |. 85C0 test eax,eax
004011EF |. 74 70 je short CrackMe_.00401261
004011F1 |. 8BC6 mov eax,esi
004011F3 |. 33FF xor edi,edi
004011F5 |. 99 cdq
004011F6 |. 2BC2 sub eax,edx
004011F8 |. 8BE8 mov ebp,eax
004011FA |. D1FD sar ebp,1
004011FC |. 3BEB cmp ebp,ebx
004011FE |. 7E 51 jle short CrackMe_.00401251
00401200 |> 8A4C7C 11 /mov cl,byte ptr ss:[esp+edi*2+11] ; 取密码第二位
00401204 |. 8A447C 10 |mov al,byte ptr ss:[esp+edi*2+10] ; 取密码第一位
00401208 |. 80E9 41 |sub cl,41 ; cl= 第二位-41
0040120B |. 2C 41 |sub al,41 ; al= 第一位-41
0040120D |. 0FBEF1 |movsx esi,cl ; esi=cl
00401210 |. 0FBEC8 |movsx ecx,al ; ecx=al
00401213 |. BB 1A000000 |mov ebx,1A ; ebx=1A
00401218 |. 8D14C9 |lea edx,dword ptr ds:[ecx+ecx*8] ; edx=ecx*9=(密码第一位-41)*9
0040121B |. 8D0472 |lea eax,dword ptr ds:[edx+esi*2] ; eax=(密码第一位-41)*9 + (密码第二位-41)*2
0040121E |. 99 |cdq ; 求余数
0040121F |. F7FB |idiv ebx ; eax=eax/ebx=eax/1A 且余数给edx
00401221 |. 0FBE847C D8000000 |movsx eax,byte ptr ss:[esp+edi*2+D8] ; eax=用户名第一位
00401229 |. 83E8 41 |sub eax,41 ; eax=eax-41
0040122C |. 3BD0 |cmp edx,eax ; 比较eax和余数edx
0040122E 75 31 jnz short CrackMe_.00401261
00401230 |. 8D0476 |lea eax,dword ptr ds:[esi+esi*2] ; eax=esi*3=cl*3=(密码第二位-41)*3
00401233 |. 8D0C81 |lea ecx,dword ptr ds:[ecx+eax*4] ; ecx = ecx+eax*4 = al+eax*4 = (密码第一位-41)+(密码第二位-41)*0C
00401236 |. 03C1 |add eax,ecx ; eax=eax+ecx= (密码第一位-41)+(密码第二位-41)*0F
00401238 |. 8BCB |mov ecx,ebx ; ecx=ebx=1A
0040123A |. 99 |cdq
0040123B |. F7F9 |idiv ecx ; eax=eax/1A 余数edx
0040123D |. 0FBE847C D9000000 |movsx eax,byte ptr ss:[esp+edi*2+D9] ; eax=取用户名第二位
00401245 |. 83E8 41 |sub eax,41 ; eax=用户名第二位-41
00401248 |. 3BD0 |cmp edx,eax ; 比较eax和余数edx
0040124A |. 75 15 |jnz short CrackMe_.00401261
0040124C |. 47 |inc edi ; edi++
0040124D |. 3BFD |cmp edi,ebp ; 一组一组的来,每组两个字符,所以共(用户名长度/2)组
0040124F |.^ 7C AF \jl short CrackMe_.00401200
00401251 |> 5F pop edi
00401252 |. 5E pop esi
00401253 |. 5D pop ebp
00401254 |. B8 01000000 mov eax,1
00401259 |. 5B pop ebx
0040125A |. 81C4 90010000 add esp,190
00401260 |. C3 retn
00401261 |> 5F pop edi
00401262 |. 5E pop esi
00401263 |. 5D pop ebp
00401264 |. 33C0 xor eax,eax
00401266 |. 5B pop ebx
00401267 |. 81C4 90010000 add esp,190
0040126D \. C3 retn
总结一下算法:
1。用户名和密码都必须是大小写字母(大写还是小写无关紧要)
2。用户名和密码的位数必须相同且为偶数,位数不能小于4。
算法关键是:
用户名的第一位-41H = [(密码第一位-41H)*9H + (密码第二位-41H)*2H] % 1AH
用户名的第二位-41H = [(密码第一位-41H)+(密码第二位-41H)*0FH] % 1AH
解上面这个方程组就行。(% 就是C里面的取余数运算)
后面的用户名和密码的3、4位;5、6位等等,都可以同样的方法算出。
我们可以看到,如果知道密码的两位来确定用户名比较好计算。因为,连续两位密码可以分别唯一的计算出两位用户名来。
=====================================================================
下面是一个简单的注册机,C写的。
这个注册机将会根据给定的长度随机生成用户名和注册码。
鉴于大小写不是关键,所以我一律按大写算了。大家不要介意
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
void main()
{
char code1,code2,name1,name2;
int length;
char code[256],name[256];
//自己给定密码和用户名长度,两个的长度必须相等且为偶数。
//密码长度=用户名的长度=length*2
//length必须>=2
//如密码长度为10,即length=5
length=5;
//构造随机变量
srand((unsigned)time(NULL));
for(int i=1;i<=length;i++)
{
//随机生成注册码
//code1和2为ASCII的65-90之间的大写字母,即A-Z。取小写字母也无妨
code1=int(((float(rand())/RAND_MAX)*26)+65);
code2=int(((float(rand())/RAND_MAX)*26)+65);
name1=((((code1-0x41)*9)+((code2-0x41)*2))%0x1A)+0x41;
name2=(((code1-0x41)+((code2-0x41)*0xF))%0x1A)+0x41;
code[i*2-2]=code1;
code[i*2-1]=code2;
name[i*2-2]=name1;
name[i*2-1]=name2;
}
code[i*2-2]='\0';
name[i*2-2]='\0';
printf("用户名:\t%s\n",name);
printf("密码:\t%s\n",code);
}
====================================================================
几个随机生成的结果(已验证):
长度4的
用户名: STWY
密码: IZQE
长度6的
用户名: ESKUXI
密码: ISCWXZ
长度8的
用户名: EFDDHCNO
密码: IFNIZVVD
长度10的
用户名: OTDMXGOYTM
密码: OJHJHTCYJV
长度20的
用户名: DGFDZNMXNYSOTFTFVLXU
密码: LRXQVWKNXHUKFAFALAPJ
长度60的
用户名: UVMAQYKJKZHIHDENVGREQISINPMNXIRJEGMRVUTZPOEYCYEQYURSBEXEOPVL
密码: INIWMGSPQLVNHYUDXLNPOKYSDGIJXZBEQIOVFBJIFLEKUCSMUAVFLDRNIXLA
长度100的
用户名: UVUAKPYIECREINMGMLKBBMQFSSBMAOEYVKLZVLUGPCDQWKDXPUIDZBWFORSVQPJTGVEVOMOJJRMEYVMEBLLLQNENMZFVPMLASKIV
密码: INWCOHCQKWNPOTEOSDGRXBQBAWXBIQEKDXVCLASUNBNVIORQBDMPDMUZYDYFSFPCQJGBKOMFZWOICDOIPYNMCZUDATLSPFDFOYAR
这是本人在看雪的第三篇破文,希望大家喜欢。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!