暑假里迷起了irc,所以拿到了mIRC就顺便把它keygen了。我用的是mIRC 6.02版。此软件采用的是用户名/注册码的保护,在软件的帮助菜单里有注册选项。
首先我们看看基本的注册情况。填好相关的注册码和用户名之后,点注册会发现提示错误。之后自动清除了输入的内容,但是还是可以继续注册。了解这些就够了。
现在开始我们的破解之旅。
输入好注册码和用户名之后,进入trw,下断点,bpx GetDlgItemTextA,bpx GetWindowTextA。
因为这两个是常用的获取EDIT控件文本内容的函数啦。
点注册,发现没有拦截下来。
那么就有两种可能性了:第一,程序采用了其他的方法获得文本内容,比如除了刚才的两个断点之外,还可以向EDIT控件发送WM_GETTEXT消息来获得文本;第二,程序是用delphi编写的,有自己独立的获取方式而不依靠windows提供的函数。
无论是哪种可能性,都可以用一种方法来解决,那就是下断点bpx hmemcpy。这个号称是9x平台下的万能断点,只要是输入注册码的,都可以用这个断点来拦截。
下断点,点注册。终于拦截下来了。拦下后,下命令pmodule,回到mIRC程序里,发现我们在call SendDlgItemMessageA的下面,原来它是用这个函数给对话框里的EDIT控件发送WM_GETTEXT消息。
016F:004C688D 68A8A85700 PUSH DWORD 0057A8A8 <==============以后名字存放的地点
016F:004C6892 68E7030000 PUSH DWORD 03E7
016F:004C6897 6A0D PUSH BYTE +0D <====================WM_GETTEXT的值
016F:004C6899 6883000000 PUSH DWORD 83
016F:004C689E FF7508 PUSH DWORD [EBP+08]
016F:004C68A1 E8284B0900 CALL `USER32!SendDlgItemMessageA`
016F:004C68A6 688FAC5700 PUSH DWORD 0057AC8F <=======+=======pmodule后我们就在这里了
016F:004C68AB 68E7030000 PUSH DWORD 03E7 |_______以后我们输入的注册码
016F:004C68B0 6A0D PUSH BYTE +0D 存放的地点
016F:004C68B2 6882000000 PUSH DWORD 82
016F:004C68B7 FF7508 PUSH DWORD [EBP+08]
016F:004C68BA E80F4B0900 CALL `USER32!SendDlgItemMessageA`
016F:004C68BF 688FAC5700 PUSH DWORD 0057AC8F <==============注册码作为参数二
016F:004C68C4 68A8A85700 PUSH DWORD 0057A8A8 <==============姓名作为参数一
016F:004C68C9 E88FFBFFFF CALL 004C645D <====================调用函数(关键的函数)!!
016F:004C68CE 85C0 TEST EAX,EAX <====================测试函数返回值
016F:004C68D0 0F84B7000000 JZ NEAR 004C698D <===============返回值如果等于0
就跳走(出错的地方)
从我们看到的代码来看,004c645d处函数肯定是产生注册码并且进行比较的函数.因此我们应当跟进去,走到004C68C9的时候,F8跟进去.看到:
016F:004C645D 55 PUSH EBP
016F:004C645E 8BEC MOV EBP,ESP
016F:004C6460 53 PUSH EBX
016F:004C6461 56 PUSH ESI
016F:004C6462 57 PUSH EDI
016F:004C6463 8B750C MOV ESI,[EBP+0C]
016F:004C6466 8B5D08 MOV EBX,[EBP+08]
016F:004C6469 BF440E5800 MOV EDI,00580E44
016F:004C646E 6804010000 PUSH DWORD 0104
016F:004C6473 6A00 PUSH BYTE +00
016F:004C6475 57 PUSH EDI
016F:004C6476 E8D55C0800 CALL 0054C150
016F:004C647B 83C40C ADD ESP,BYTE +0C
016F:004C647E 6804010000 PUSH DWORD 0104
016F:004C6483 6A00 PUSH BYTE +00
016F:004C6485 68480F5800 PUSH DWORD 00580F48
016F:004C648A E8C15C0800 CALL 0054C150
016F:004C648F 83C40C ADD ESP,BYTE +0C
016F:004C6492 56 PUSH ESI
016F:004C6493 57 PUSH EDI
016F:004C6494 8BF7 MOV ESI,EDI
016F:004C6496 8BFB MOV EDI,EBX
016F:004C6498 33C0 XOR EAX,EAX
016F:004C649A 83C9FF OR ECX,BYTE -01
016F:004C649D F2AE REPNE SCASB
016F:004C649F F7D1 NOT ECX
016F:004C64A1 2BF9 SUB EDI,ECX
016F:004C64A3 87F7 XCHG ESI,EDI
016F:004C64A5 8BC7 MOV EAX,EDI
016F:004C64A7 8BD1 MOV EDX,ECX
016F:004C64A9 C1E902 SHR ECX,02
016F:004C64AC F3A5 REP MOVSD
016F:004C64AE 8BCA MOV ECX,EDX
016F:004C64B0 83E103 AND ECX,BYTE +03
016F:004C64B3 F3A4 REP MOVSB
016F:004C64B5 5F POP EDI
016F:004C64B6 5E POP ESI
016F:004C64B7 56 PUSH ESI
016F:004C64B8 57 PUSH EDI
016F:004C64B9 8BFE MOV EDI,ESI
016F:004C64BB BE480F5800 MOV ESI,00580F48
016F:004C64C0 33C0 XOR EAX,EAX
016F:004C64C2 83C9FF OR ECX,BYTE -01
016F:004C64C5 F2AE REPNE SCASB
016F:004C64C7 F7D1 NOT ECX
016F:004C64C9 2BF9 SUB EDI,ECX
016F:004C64CB 87F7 XCHG ESI,EDI
016F:004C64CD 8BC7 MOV EAX,EDI
016F:004C64CF 8BD1 MOV EDX,ECX
016F:004C64D1 C1E902 SHR ECX,02
016F:004C64D4 F3A5 REP MOVSD
016F:004C64D6 8BCA MOV ECX,EDX
016F:004C64D8 83E103 AND ECX,BYTE +03
016F:004C64DB F3A4 REP MOVSB
016F:004C64DD 5F POP EDI
016F:004C64DE 5E POP ESI
016F:004C64DF 68480F5800 PUSH DWORD 00580F48
016F:004C64E4 57 PUSH EDI
016F:004C64E5 E880FEFFFF CALL 004C636A <==================call 后面紧跟test和跳转的
016F:004C64EA 85C0 TEST EAX,EAX 就要留意了
016F:004C64EC 740A JZ 004C64F8 <===eax=0就跳到失败,所以在函数返回前不能
让eax=0
016F:004C64EE B801000000 MOV EAX,01 <========令eax=1,然后跳转,不远处就返回了,
016F:004C64F3 E991000000 JMP 004C6589 回到上面004c68ce的地方,上面要求
eax不等于0就注册成功,这里是1正好
满足,所以这个004664E5大有文章.
前面的一大票代码看起来很难分析,其实暂时可以不管,因为这还是我们第一遍跟踪.第一遍只要大概掌握代码的情况就可以了.我们注意到004C64E5 处的call和后面的test jz又构成了一个调用函数然后比较返回值的情况.而且它后面有mov eax,1 jmp 004c6589.这就可以肯定是判断注册码是否合法的函数了.看看在call前面push 入栈的是哪些东西.在执行到0x4c64e4的时候,下命令d 580f48 可以看到注册码,d edi 看到名字.所以,这更加肯定了我们的推测,004c636a处的函数就是核心.在跟进去之前要记住,不能让eax=0,不然就是注册失败的标志.好了跟进来.
016F:004C636A 55 PUSH EBP
016F:004C636B 8BEC MOV EBP,ESP
016F:004C636D 83C4F4 ADD ESP,BYTE -0C
016F:004C6370 53 PUSH EBX
016F:004C6371 56 PUSH ESI
016F:004C6372 57 PUSH EDI
016F:004C6373 8B750C MOV ESI,[EBP+0C]
016F:004C6376 FF7508 PUSH DWORD [EBP+08] <==========d *(ebp+8)发现这是名字
016F:004C6379 E8525F0800 CALL 0054C2D0 <====这个函数是干什么的?
016F:004C637E 59 POP ECX <=====执行完函数到了这里,看看eax里是什么?是名字的
016F:004C637F 83F805 CMP EAX,BYTE +05 <==比较名字是不是大于5个字母 长度.
016F:004C6382 7307 JNC 004C638B
016F:004C6384 33C0 XOR EAX,EAX <==小于5就把eax清零,返回,就注册失败了.
016F:004C6386 E9C9000000 JMP 004C6454
016F:004C638B 6A2D PUSH BYTE +2D <==0x2d就是字符"-"
016F:004C638D 56 PUSH ESI <==注册码
016F:004C638E E89D5E0800 CALL 0054C230 <==这个函数跟踪后发现是返回"-"在注册码中
016F:004C6393 83C408 ADD ESP,BYTE +08 的位置.如果你输入的注册码不含"-"就失败
016F:004C6396 8BD8 MOV EBX,EAX
016F:004C6398 85DB TEST EBX,EBX
016F:004C639A 7507 JNZ 004C63A3
016F:004C639C 33C0 XOR EAX,EAX
016F:004C639E E9B1000000 JMP 004C6454
016F:004C63A3 C60300 MOV BYTE [EBX],00 <===把"-"替换成0,就是说把我们输入的
016F:004C63A6 56 PUSH ESI 注册码变成两个字符串
016F:004C63A7 E878ED0800 CALL 00555124 <===把注册码第一部分转化成数值
(原来输入的是字符)
016F:004C63AC 59 POP ECX
016F:004C63AD 8945FC MOV [EBP-04],EAX <===把第一部分的数值放入这个地方
016F:004C63B0 C6032D MOV BYTE [EBX],2D <===把注册码恢复成员来的样子
016F:004C63B3 43 INC EBX
016F:004C63B4 803B00 CMP BYTE [EBX],00
016F:004C63B7 7507 JNZ 004C63C0
016F:004C63B9 33C0 XOR EAX,EAX
016F:004C63BB E994000000 JMP 004C6454
016F:004C63C0 53 PUSH EBX
016F:004C63C1 E85EED0800 CALL 00555124 <===把注册码第二部分转化成数值
016F:004C63C6 59 POP ECX
016F:004C63C7 8945F8 MOV [EBP-08],EAX <===存好
016F:004C63CA FF7508 PUSH DWORD [EBP+08] <===姓名
016F:004C63CD E8FE5E0800 CALL 0054C2D0 <===取得姓名长度
016F:004C63D2 59 POP ECX
016F:004C63D3 8945F4 MOV [EBP-0C],EAX <===把长度存好
016F:004C63D6 33C0 XOR EAX,EAX <===开始计算了
016F:004C63D8 33DB XOR EBX,EBX
016F:004C63DA BA03000000 MOV EDX,03
016F:004C63DF 8B4D08 MOV ECX,[EBP+08]
016F:004C63E2 83C103 ADD ECX,BYTE +03 <===从姓名第三个字开始算
016F:004C63E5 3B55F4 CMP EDX,[EBP-0C]
016F:004C63E8 7D1C JNL 004C6406
016F:004C63EA 0FB631 MOVZX ESI,BYTE [ECX] <===取姓名里的一个字符
016F:004C63ED 0FAF3485B8835600 IMUL ESI,[EAX*4+005683B8] <==与一个数相乘保存在esi中
016F:004C63F5 03DE ADD EBX,ESI <=累加起来 (这个数具体是什么后面讲)
016F:004C63F7 40 INC EAX
016F:004C63F8 83F826 CMP EAX,BYTE +26
016F:004C63FB 7E02 JNG 004C63FF
016F:004C63FD 33C0 XOR EAX,EAX
016F:004C63FF 42 INC EDX
016F:004C6400 41 INC ECX
016F:004C6401 3B55F4 CMP EDX,[EBP-0C]
016F:004C6404 7CE4 JL 004C63EA 循环完成循环计算后,ebx里面就是计算的结果
016F:004C6406 3B5DFC CMP EBX,[EBP-04] 比较ebx和开始获得的注册码第一部分数值
016F:004C6409 7404 JZ 004C640F 相等就继续计算第二部分
016F:004C640B 33C0 XOR EAX,EAX 不然就清0 eax滚蛋
016F:004C640D EB45 JMP SHORT 004C6454
016F:004C640F 33C0 XOR EAX,EAX <==第二部分开始了
016F:004C6411 33DB XOR EBX,EBX
016F:004C6413 BA03000000 MOV EDX,03
016F:004C6418 8B4D08 MOV ECX,[EBP+08]
016F:004C641B 83C103 ADD ECX,BYTE +03 <=还是从姓名的第3个字开始算
016F:004C641E 3B55F4 CMP EDX,[EBP-0C]
016F:004C6421 7D23 JNL 004C6446
016F:004C6423 0FB631 MOVZX ESI,BYTE [ECX] <=取姓名里的一个字
016F:004C6426 0FB679FF MOVZX EDI,BYTE [ECX-01] <=取前一个字
016F:004C642A 0FAFF7 IMUL ESI,EDI <=相乘
016F:004C642D 0FAF3485B8835600 IMUL ESI,[EAX*4+005683B8] <=再与另一个数相乘
016F:004C6435 03DE ADD EBX,ESI <=累加 (和上面的那个一样后面讲)
016F:004C6437 40 INC EAX
016F:004C6438 83F826 CMP EAX,BYTE +26
016F:004C643B 7E02 JNG 004C643F
016F:004C643D 33C0 XOR EAX,EAX
016F:004C643F 42 INC EDX
016F:004C6440 41 INC ECX
016F:004C6441 3B55F4 CMP EDX,[EBP-0C]
016F:004C6444 7CDD JL 004C6423 循环计算
016F:004C6446 3B5DF8 CMP EBX,[EBP-08] 比较计算结果,和注册码第二部分数值
016F:004C6449 7404 JZ 004C644F 相同则跳到成功标志
016F:004C644B 33C0 XOR EAX,EAX
016F:004C644D EB05 JMP SHORT 004C6454
016F:004C644F B801000000 MOV EAX,01 设立注册码比较成功标志
016F:004C6454 5F POP EDI
016F:004C6455 5E POP ESI
016F:004C6456 5B POP EBX
016F:004C6457 8BE5 MOV ESP,EBP
016F:004C6459 5D POP EBP
016F:004C645A C20800 RET 08
好了,现在讲一讲上面两次出现的[EAX*4+005683B8]是什么意思.
在跟踪到那个乘法的时候,下d 005683B8,会看到数据栏里是这个样子
0B000000 06000000 11000000 0C000000
0C000000 0E000000 05000000 0C000000
10000000 0A000000 0B000000 06000000
0E000000 0E000000 04000000 0B000000
06000000 0E000000 0E000000 04000000
0B000000 09000000 0C000000 0B000000
0A000000 08000000 0A000000 0A000000
10000000 08000000 04000000 06000000
0A000000 0C000000 10000000 08000000
0A000000 04000000 10000000 00000000
发现数据都是每4字节出现一次,这就是为什么用eax*4了.其实005683B8这个地址里出现的数据是固定的,就是说这是个字典,每次计算注册码的时候到里面来查一个数字然后跟名字的一个字母做乘法运算.那么我怎么才能知道这个字典是多大呢?那就看
016F:004C63F8 83F826 CMP EAX,BYTE +26
和
016F:004C6438 83F826 CMP EAX,BYTE +26
都是把eax和0x26,也就是38来比较,所以一共有39个数据.就是除了0之外的那些.
至此,我们完全摸清了,mIRC的注册码的计算情况.当我们执行到016F:004C6406和016F:004C6446的时候各做一次? ebx显示出十进制值,然后把两个值用"-"链接起来就是我们输入名字所对应的注册码了,比如我的lllaaa[BCG]对应7591-674568.
好了,既然搞清楚了算法,写注册机也就容易了.下面我贴出注册机的c语言源代码
#include
#include
#include
int main(int argc,char *argv[])
{
char dict[39]=
{
0x0b,0x06,0x11,0x0c,0x0c,0x0e,0x05,0x0c,0x10,0x0a,
0x0b,0x06,0x0e,0x0e,0x04,0x0b,0x06,0x0e,0x0e,0x04,
0x0b,0x09,0x0c,0x0b,0x0a,0x08,0x0a,0x0a,0x10,0x08,
0x04,0x06,0x0a,0x0c,0x10,0x08,0x0a,0x04,0x10
};
char name[255];
printf("Input your name please:");
gets(name);
int length=strlen(name);
if (length>=39)
{
printf("\nYour name is longer than 39 chars,please choose another one.");
return 1;
}
int i=0;
long serial1=0,serial2=0;
for (i=0;i{
serial1+=name[i+3]*dict[i];
serial2+=name[i+3]*name[i+2]*dict[i];
}
printf("The serial number is:%d-%d",serial1,serial2);
return 1;
}
本来源程序是支持名字超过39字节的,但是我为了编程的简单,就做了限制.
相关视频
相关阅读 Windows错误代码大全 Windows错误代码查询激活windows有什么用Mac QQ和Windows QQ聊天记录怎么合并 Mac QQ和Windows QQ聊天记录Windows 10自动更新怎么关闭 如何关闭Windows 10自动更新windows 10 rs4快速预览版17017下载错误问题Win10秋季创意者更新16291更新了什么 win10 16291更新内容windows10秋季创意者更新时间 windows10秋季创意者更新内容kb3150513补丁更新了什么 Windows 10补丁kb3150513是什么
热门文章 去除winrar注册框方法
最新文章
比特币病毒怎么破解 比去除winrar注册框方法
华为无线路由器HG522-C破解教程(附超级密码JEB格式文件京东电子书下载和阅读限制破解教UltraISO注册码全集(最新)通过Access破解MSSQL获得数据
人气排行 华为无线路由器HG522-C破解教程(附超级密码JEB格式文件京东电子书下载和阅读限制破解教UltraISO注册码全集(最新)qq相册密码破解方法去除winrar注册框方法(适应任何版本)怎么用手机破解收费游戏华为无线猫HG522破解如何给软件脱壳基础教程
查看所有0条评论>>