好久没来学院学习了,先贴篇我的老文吧。这个crackme不算难,但是挺有趣,根据每天日期不同,计算出的注册码也不同。我把这么老而且又没有什么难度的破文放在这里,希望大家见谅,就当是抛砖引玉吧。谢谢。
===================================
拿到idcrkme20后,先用Fi看了一下,好,没有加壳。运行,随便填入Name、Group和Code,点击Check,没有反应,没有跳出什么错误提示的对话框。得,本来以为可以用W32Dasm反编译,找到错误提示的地方,然后下手呢,看来不行了。运行OllyDbg,因为不能找到错误提示,所以只好从对话框或者内存下手,就是找类似memcpy或是GetDlgItemText的地方。随便输入Name、Group、Code为0000、1111、2222。然后查找。
00401A3B . FF15 58B24000 CALL DWORD PTR DS:[<&USER32.GetDlgItemTextA>] ;
看到这里有一个GetDlgItemTextA,注意,从这以下,有几行分别显示了ASCII "2222",ASCII "1111",ASCII "0000",这不就是我们输入的东东吗?就从这附近开始入手。经过仔细查找,发现下列情况。
00401A5B . E8 30F8FFFF CALL IDCRKME2.00401290 (从这个CALL跟进)
004012BA . E8 61020000 CALL IDCRKME2.00401520 (再跟进这个CALL)
00401546 . 390D 88974000 CMP DWORD PTR DS:[409788],ECX (关键比较)
0040154C . 75 63 JNZ SHORT IDCRKME2.004015B1 (关键跳转)
如果是暴破,那么,只要把JNZ这个地方用NOP填满,就可以了。同时,可以看到,DS:[409788] 就是我们填入的Code,那么,看看ECX的内容,也就知道正确的Code了。
可是,现别高兴的太早,试一下就知道了, 这个正确的Code不是固定的,看来是根据填入的Name和/或Group计算而得到的,所以,针对不同的Name和Group,还要找出Code的算法。555……,白高兴了,还得回头。
还是从那个GetDlgItemTextA附近考虑。
00401A46 . E8 250F0000 CALL IDCRKME2.00402970
上面这个CALL,跟进后,发现并没有什么特别,就是把我们填入的Code转化成所对应的数值,存入EAX(比如,填入2222,把它由字符串"2222"转化为十进制的2222,并且以十六进制保存在EAX中。这个CALL还检查填入的Code是否为负值,并做计算,不管那么多,我们只看正值。另外,我不知道对于字符是如何处理的,没有仔细考查。
00401A54 . A3 88974000 MOV DWORD PTR DS:[409788],EAX
看到上面这句了吗?它把EAX的内容复制到 DS:[409788] 中去了,所以,DS:[409788] 中就总是我们填入的Code。接着分析。
00401546 . 390D 88974000 CMP DWORD PTR DS:[409788],ECX (关键比较)
注意上面的关键比较,ECX中应该是正确的Code,那么ECX的值是怎么得来的呢?
004012BA . E8 61020000 CALL IDCRKME2.00401520
这个CALL跟进后,在关键比较之前有几个和ECX相关的操作,如下:
00401523 . 8B0D 70974000 MOV ECX,DWORD PTR DS:[409770]
00401529 . 030D AC974000 ADD ECX,DWORD PTR DS:[4097AC]
00401531 . 81F9 FFFFFF7F CMP ECX,7FFFFFFF
0040153A . 81E9 FFFFFF7F SUB ECX,7FFFFFFF
由此可见,ECX的值就是 DS:[409770] 与 DS:[4097AC] 之和,如果这个和比7FFFFFFF大,则减去7FFFFFFF,最后,ECX中的值就是正确的Code。好了,我们现在就是要看看 DS:[409770] 与 DS:[4097AC] 中的值是怎得来的。
00401A1F . A1 AC974000 MOV EAX,DWORD PTR DS:[4097AC] ;
注意上面这条语句,而且,附近没有对 DS:[4097AC] 值进行更改的操作,随便改变一下Name和Group,发现,DS:[4097AC] 的值不变,注意,不要上当,DS:[4097AC] 的值是根据当天时间计算的,我就开始就没有发现,后来隔了一天才觉察出来的。当然,如果你认真观察,就可以看到下面的语句:
004015CB . FF15 C0B14000 CALL DWORD PTR DS:[<&KERNEL32.GetLocalTime>] ;
GetLocalTime 004015C6 pLocaltime = IDCRKME2.004097D0
这是获得时间的语句,仔细观察,发现得到的时间存在 004097D0 起始的地址内,格式为 Windows 的 SYSTEMTIME 标准格式(关于此时间格式,可以参考Windows相关资料)。下面继续作一个简单的分析。
004015D3 . 66:A1 D2974000 MOV AX,WORD PTR DS:[4097D2]
DS:[4097D2] 其实就是月份,接下来,
004015D9 . 0FBE88 BF704000 MOVSX ECX,BYTE PTR DS:[EAX+4070BF]
DS:[4070A0] 起是一段数据,相当于含有12个元素的数组,根据EAX来决定选择第几个元素,EAX的值已经在上一语句中被赋值成当前月份。
004015F8 . 66:A1 D6974000 MOV AX,WORD PTR DS:[4097D6]
DS:[4097D6] 就是当前日期,同上面类似,
0040161B . 0FBE80 9F704000 MOVSX EAX,BYTE PTR DS:[EAX+40709F]
DS:[407010] 起也是一段数据相当于含有31个元素的数组,根据EAX来决定选择第几个元素,EAX的值已经在上一语句中被赋值成当前日期。
00401622 . 0FAFC1 IMUL EAX,ECX
0040162A . A3 AC974000 MOV DWORD PTR DS:[4097AC],EAX
最后,这两个值相乘,并把积赋值给 DS:[4097AC]。得到了 DS:[4097AC],所以,我们最后的焦点就是 DS:[409770] 的值。仔细观察,发现如下语句:
004019E4 > C705 70974000 00000000 MOV DWORD PTR DS:[409770],0
DS:[409770] 被置0,所以,DS:[409770] 值的更改只可能出现在下面的语句中,而且,据此不远,就是:
00401A3B . FF15 58B24000 CALL DWORD PTR DS:[<&USER32.GetDlgItemTextA>] ;
接下来没几句就是:
00401A5B . E8 30F8FFFF CALL IDCRKME2.00401290 (从这个CALL跟进)
004012BA . E8 61020000 CALL IDCRKME2.00401520 (再跟进这个CALL)
00401546 . 390D 88974000 CMP DWORD PTR DS:[409788],ECX (关键比较)
0040154C . 75 63 JNZ SHORT IDCRKME2.004015B1 (关键跳转)
所以,DS:[409770] 值的更改就在这之间,仔细看看,发现如下两条语句:
004019F3 . E8 E8F9FFFF CALL IDCRKME2.004013E0
004019FC . E8 7FFAFFFF CALL IDCRKME2.00401480
我们先来跟进第一条语句
004019F3 . E8 E8F9FFFF CALL IDCRKME2.004013E0 (跟进)
这个CALL的确是对 DS:[409770] 的值进行操作,注意
00401437 > 0FBEBA F7974000 MOVSX EDI,BYTE PTR DS:[EDX+4097F7]
0040143E . 0FBE82 57704000 MOVSX EAX,BYTE PTR DS:[EDX+407057]
00401448 . 0FBE86 1F704000 MOVSX EAX,BYTE PTR DS:[ESI+40701F]
这里所作的循环就是关键。下面略作解释。 DS:[EDX+4097F7] 就是Name的第EDX个字符, DS:[EDX+407057] 是从 DS:[407058] 起的第EDX个字符, DS:[ESI+40701F] 是从 DS:[407020] 起的第ESI个字符。其中EDX和ESI都是从1开始,每次循环增加1。然后这些字符所对应的值再进行运算。其中,DS:[407058] 起,DS:[407020] 起分别对应两段数据,这两段数据的值是固定的,如果这也是动态计算,那就惨了,还好……
004019F3 . E8 E8F9FFFF CALL IDCRKME2.004013E0
这条CALL操作结束,根据Name计算出一个数值,这里要注意一下,循环的次数是Name的长度-1而不是Name的长度,并且把它保存在 DS:[409770] 中。接下来,是另一个CALL语句。
004019FC . E8 7FFAFFFF CALL IDCRKME2.00401480 (跟进)
和上一个CALL一样,下面所作的循环是关键
004014C9 > 0FBE9E AF974000 MOVSX EBX,BYTE PTR DS:[ESI+4097AF]
004014D0 . 0FBE86 57704000 MOVSX EAX,BYTE PTR DS:[ESI+407057]
004014DA . 0FBE82 1F704000 MOVSX EAX,BYTE PTR DS:[EDX+40701F]
DS:[ESI+407057] 和 DS:[EDX+40701F] 表示的含义也和上一个CALL类似,只不过, DS:[ESI+4097AF] 用来表示Group的第ESI个字符。这个CALL语句根据Group计算出一个数值,同样,循环的次数是Group的长度-1,并且把它和上一个CALL语句根据Name计算出来的保存在 DS:[409770] 中的数值相加,然后仍保存在 DS:[409770] 中,这样, DS:[409770] 的数值就最终确定了。至于 DS:[407058] 起,DS:[407020] 起所分别对的两段数据,就不列在这里了,大家自己看吧。知道了算法,呵呵,我用VC写了一个注册器(好久不用VC了,都差不多忘光了)。
我以上的破解只是针对正数值作为Code的,没有计算负值,也没有考虑字符情况,实在麻烦,而且,我发现,Name和Group的长度必须大于等于3,不然,算出的Code就不对,我也没有仔细看是算法没考虑到还是这个CrackMe软件本身不允许Name和Group的长度太短。算了,累了,不理它了,毕竟这是我第一次仔细研究crack的算法,并写出注册器,希望以后水平会提高。哪位朋友有更好的crack方法,欢迎和我联系交流。
下面,是Immortal Descendants CrackMe 2.0的下载(http://www.caohua.net/computer/crack/MyWorks/idcrkme20/idcrkme20.exe),朋友们有兴趣可以自己回去研究一下,还有我编写的注册器下载(http://www.caohua.net/computer/crack/MyWorks/idcrkme20/idcrkme20keygen.exe)。
最后,是注册器的核心代码,其中FIRST和SECOND两个数组对应的值就是上面提到的两段数据的值。
{
m_NAME=m_GROUP="";
m_CODE=0;
int NameLength, GroupLength,i;
SYSTEMTIME Time;
GetLocalTime(&Time);
long int TIMESUM=0;
long int SUM=0;
long int SUM1=0;
long int SUM2=0;
long int MONTH[]=
{0X39,0X38,0X30,0X33,0X44,0X32,0X46,0X37,0X34,0X30,0X34,0X45};
long int DATE[]=
{0X46,0X33,0X41,0X34,0X42,0X46,0X33,0X42,0X30,0X35,0X30,0X33,
0X46,0X42,0X38,0X42,0X43,0X42,0X34,0X46,0X34,0X39,0X38,0X30,
0X33,0X44,0X35,0X43,0X37,0X34,0X30};
long int FIRST[]=
{0X45,0X74,0X6E,0X35,0X50,0X6E,0X63,0X35,0X41,0X58,0X69,0X31,
0X44,0X46,0X6C,0X6B,0X59,0X71,0X6E,0X75,0X6A,0X73,0X58,0X4E,
0X6D,0X76,0X48,0X64,0X62,0X63,0X72,0X71,0X4F,0X6F,0X54,0X38,
0X61,0X61,0X56,0X35,0X44,0X6B,0X61,0X79,0X6D,0X4D,0X52,0X6B,
0X50,0X6B,0X6F,0X51,0X2E};
long int SECOND[]=
{0X66,0X51,0X4F,0X62,0X56,0X77,0X4E,0X4F,0X61,0X6E,0X6B,0X4A,
0X35,0X73,0X6B,0X71,0X4A,0X76,0X61,0X65,0X33,0X41,0X65,0X35,
0X6A,0X64,0X6F,0X45,0X54,0X75,0X35,0X6E,0X30,0X32,0X4A,0X36,
0X45,0X7A,0X38,0X35,0X34,0X33,0X30,0X50,0X4E,0X53,0X44,0X41,
0X50,0X6A,0X44,0X72,0X59,0X67,0X46,0X61,0X7A,0X65,0X39,0X56,
0X44,0X52,0X6A,0X71};
CDialog::UpdateData(true);
NameLength=m_NAME.GetLength();
GroupLength=m_GROUP.GetLength();
for(i=0;i<NameLength-1;i++)
SUM1+=(static_cast<long int>(m_NAME[i]))*(FIRST[i])*(SECOND[i])*(i+1);
for(i=0;i<GroupLength-1;i++)
SUM2+=(static_cast<long int>(m_GROUP[i]))*(FIRST[i])*(SECOND[i])*(i+1);
TIMESUM=MONTH[static_cast<int>(Time.wMonth)-1]*DATE[static_cast<int> (Time.wDay)-1];
SUM=SUM1+SUM2+TIMESUM;
m_CODE=SUM<0X7FFFFFFF?SUM:(SUM-0X7FFFFFFF);
CDialog::UpdateData(false);
}
===================================
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)