首页
社区
课程
招聘
[原创]bxm 第8个 crackme 破文
发表于: 2008-4-13 14:16 4947

[原创]bxm 第8个 crackme 破文

2008-4-13 14:16
4947
【文章标题】: bxm 第8个 crackme 破文
【文章作者】: aneasystone
【作者邮箱】: aneasystone@163.com
【作者主页】: http://www.aneasystone.googlepages.com
【作者QQ号】: 492318837
【软件名称】: bxm的第8个crackme
【软件大小】: 88.7kb
【下载地址】: 自己搜索下载
【加壳方式】: 无
【编写语言】: Visual C++6.0
【使用工具】: PEID OllyICE
【操作平台】: Windows XP
【软件介绍】:
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】

这是我第一次写破文 写的不好 各位看官还请海涵...
bxm的这个crackme通过在程序中调用11次GetSystemTime这个API获得相隔两次的间隔时间
如果在中间设了断点 间隔时间必不为0 crackme会生成错误序列号来干扰反调试

前些天玩这个crackme时 就是陷入了这个圈套 每次调试时获得序列号都不一样
刚开始还以为 序列号每分钟变化一次 后来经bxm的提示
才知道里面另有蹊跷

于是乎 把反调试,反跟踪的知识恶补了几天
今天再把bxm的那个crackme拿出来又看了看 才知道这个crackme的玄机所在
下面是分析过程

---------------------------lined by aneasyston-----------------------------------

od载入 查找--所有参考文本字串--找到“重试?” 双击进入
我们来到1BF6处
00401BB2   > /8A10          mov     dl, [eax]
00401BB4   . |8A1E          mov     bl, [esi]
00401BB6   . |8ACA          mov     cl, dl
00401BB8   . |3AD3          cmp     dl, bl
00401BBA   . |75 1E         jnz     short 00401BDA
00401BBC   . |84C9          test    cl, cl
00401BBE   . |74 16         je      short 00401BD6
00401BC0   . |8A50 01       mov     dl, [eax+1]
00401BC3   . |8A5E 01       mov     bl, [esi+1]
00401BC6   . |8ACA          mov     cl, dl
00401BC8   . |3AD3          cmp     dl, bl
00401BCA   . |75 0E         jnz     short 00401BDA
00401BCC   . |83C0 02       add     eax, 2
00401BCF   . |83C6 02       add     esi, 2
00401BD2   . |84C9          test    cl, cl
00401BD4   .^\75 DC         jnz     short 00401BB2
00401BD6   >  33C0          xor     eax, eax
00401BD8   .  EB 05         jmp     short 00401BDF
00401BDA   >  1BC0          sbb     eax, eax
00401BDC   .  83D8 FF       sbb     eax, -1
00401BDF   >  85C0          test    eax, eax
00401BE1   .  6A 00         push    0
00401BE3   .  75 0C         jnz     short 00401BF1<--------------- 修改这里即可爆破
00401BE5   .  68 54B14200   push    0042B154
00401BEA   .  68 48B14200   push    0042B148
00401BEF   .  EB 0A         jmp     short 00401BFB
00401BF1   >  68 3CB14200   push    0042B13C
00401BF6   .  68 34B14200   push    0042B134                         ;  ASCII "重试?"
00401BFB   >  8BCD          mov     ecx, ebp
00401BFD   .  E8 65700100   call    00418C67
00401C02   .  6A 00         push    0
00401C04   .  8BCD          mov     ecx, ebp
00401C06   .  E8 B8770100   call    004193C3
00401C0B   .  5F            pop     edi
00401C0C   .  5E            pop     esi
00401C0D   .  5D            pop     ebp
00401C0E   .  5B            pop     ebx
00401C0F   .  83C4 50       add     esp, 50
00401C12   .  C3            retn

向上面看了看 是1BE3处跳到此处 如果你只想学习爆破 75变74即可
如果你想继续深入 就请继续看下去

我们再向上面看看 发现上面代码都十分相似
00401700 004016e0 esi 这几个函数调用多次
一直往上翻 翻到这里
004017F9   > \8B35 74224200 mov     esi, [<&KERNEL32.GetSystemTime>] ;  kernel32.GetSystemTime

现在我们知道esi是上面函数了 就是我们担心的那个GetSystemTime函数
从004017F9 到 00401BAE 这个函数调用了共11次
我们要注意的是 在这段代码中我们不能设置断点
我们先把断点设在00401BAE处
ctrl+F2 重新载入程序 F9运行 到我们的断点处停下
00401BAE   .  8D4424 20     lea     eax, [esp+20]                    ;  此处就是序列号

在CPU窗口下面 我们就已经看到我们所要的序列号了
如果你只想知道自己用户名所对应的序列号的话 也就到此为止了
如果还要继续研究这个crackme 看看他到底干了些什么 研究研究它的算法
写一个自己的keygen
OK,follow me,let's go on.... HOHO~~

现在我们的问题是从004017F9 到 00401BAE 这里不能放断点
而这个crackme的基本算法就在这段代码里面 555~~ 该怎么办呢????
这里我有两种方法
1. 把这段代码拷到记事本里 不用单步跟踪 自己认真研究下代码
   其实也不难 很多代码几乎都是重复的 只要研究出来一个 剩下的都简单的
   当然这对那些汇编刚入门的朋友来说 无非是在打击他的自信心
   所以 我们这里用第二种方法
2.  既然它不让我设断点 我就偏偏设个断点 看它能把我怎么样?? 呵呵
   这就叫明知山有虎,偏向虎山行...
下面我们在第一个call esi 和 第二个call esi上设断点
00401804      FFD6          call    esi <-----------------------第一次call
00401806   .  8B7C24 1A     mov     edi, [esp+1A]
0040180A   .  8B5C24 1C     mov     ebx, [esp+1C]
0040180E   .  8D4C24 10     lea     ecx, [esp+10]
00401812   .  81E7 FFFF0000 and     edi, 0FFFF
00401818      51            push    ecx
00401819   .  81E3 FFFF0000 and     ebx, 0FFFF                      
0040181F      FFD6          call    esi <----------------------第二次call
00401821   .  8B5424 1C     mov     edx, [esp+1C]
00401825   .  8B4424 1A     mov     eax, [esp+1A]
00401829   .  81E2 FFFF0000 and     edx, 0FFFF
0040182F   .  25 FFFF0000   and     eax, 0FFFF
00401834   .  0FAFD7        imul    edx, edi
00401837   .  0FAFC3        imul    eax, ebx
0040183A   .  52            push    edx
0040183B   .  50            push    eax
0040183C      E8 9FFEFFFF   call    004016E0                         ; 计算两次时间间隔
00401841   .  0FBE4C24 43   movsx   ecx, byte ptr [esp+43]           ;
00401846   .  8B7C24 22     mov     edi, [esp+22]
0040184A   .  8B5C24 24     mov     ebx, [esp+24]
0040184E   .  8D8408 8C0000>lea     eax, [eax+ecx+8C]    ; EAX = ECX+8C(确保EAX初值为0)
00401855   .  81E7 FFFF0000 and     edi, 0FFFF
0040185B   .  50            push    eax
0040185C   .  81E3 FFFF0000 and     ebx, 0FFFF
00401862      E8 99FEFFFF   call    00401700                         ; 算法所在处
00401867   .  A1 E0D04200   mov     eax, [42D0E0]


第一次call完后 我们要记下ECX和EDX的值还有[ESP+1C]处的值
这些是第一次调用GetSystemTime时的系统时间
一直跟到第二次call处
这里我们把ECX EDX 还有[ESP+1C]修改成第一次的系统时间
呵呵 聪明的读者应该明白了吧 我们给它造成一个假象 我们伪造了系统时间 HOHO~
我们继续跟到下面的call    004016E0 发现EAX = 0;
你可以跟进去看看 EAX 就是把第二次系统时间减去第一次系统时间

这样我们摆脱了可恶的反调试 接下来研究算法就水到渠成了
在00401862处按F7步入跟踪

00401700  /$  8B4C24 04     mov     ecx, [esp+4]
00401704  |.  33D2          xor     edx, edx
00401706  |>  8BC1          /mov     eax, ecx
00401708  |.  83E0 0F       |and     eax, 0F
0040170B  |.  C1E9 04       |shr     ecx, 4
0040170E  |.  83F8 09       |cmp     eax, 9
00401711  |.  77 05         |ja      short 00401718
00401713  |.  83C0 30       |add     eax, 30                             ; 小于9就加30
00401716  |.  EB 03         |jmp     short 0040171B
00401718  |>  83C0 57       |add     eax, 57                           ; 大于9就加57
0040171B  |>  884414 04     |mov     [esp+edx+4], al
0040171F  |.  42            |inc     edx
00401720  |.  85C9          |test    ecx, ecx
00401722  |.^ 75 E2         \jnz     short 00401706
00401724  |.  8D4414 04     lea     eax, [esp+edx+4]
00401728  |.  56            push    esi
00401729  |.  57            push    edi
0040172A  |.  8D7C24 0C     lea     edi, [esp+C]
0040172E  |.  8808          mov     [eax], cl
00401730  |.  8A50 FF       mov     dl, [eax-1]
00401733  |.  8A4C24 0C     mov     cl, [esp+C]
00401737  |.  885424 0C     mov     [esp+C], dl
0040173B  |.  8848 FF       mov     [eax-1], cl
0040173E  |.  83C9 FF       or      ecx, FFFFFFFF
00401741  |.  33C0          xor     eax, eax
00401743  |.  F2:AE         repne   scas byte ptr es:[edi]
00401745  |.  F7D1          not     ecx
00401747  |.  2BF9          sub     edi, ecx
00401749  |.  8BC1          mov     eax, ecx
0040174B  |.  8BF7          mov     esi, edi
0040174D  |.  BF E0D04200   mov     edi, 0042D0E0                    ; 这里存放计算结果
00401752  |.  C1E9 02       shr     ecx, 2
00401755  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>
00401757  |.  8BC8          mov     ecx, eax
00401759  |.  33C0          xor     eax, eax
0040175B  |.  83E1 03       and     ecx, 3
0040175E  |.  F3:A4         rep     movs byte ptr es:[edi], byte ptr>
00401760  |.  5F            pop     edi
00401761  |.  5E            pop     esi
00401762  \.  C3            retn


这里就比较简单了 附上我写的keygen 你应该很容易理解的
Keygen写的很烂 各位见笑了~~

-------------------------------lined by aneasystone----------------------------

#include <stdio.h>
#include <string.h>

int main()
{
	char username[20] = {0};
	char sn[25] = {0};
	int numbers[10] = {0x8c,0x42,0x23,0x3e,0x4a,0x4f,0x81,0x71,0x39,0x5d};

	printf("plz input your name(length must greater than 5)\n>>");
	gets(username);

	if(strlen(username) < 5)
	{
		printf("length must greater than 5\n");
		return 0;
	}

	if(strlen(username) < 10)
		sprintf(username,"%s%s",username,username);

	// 将用户名重新排序 用户名只有前10位有效
	char orderusername[10];
	orderusername[0] = username[3];
	orderusername[1] = username[7];
	orderusername[2] = username[8];
	orderusername[3] = username[6];
	orderusername[4] = username[9];
	orderusername[5] = username[5];
	orderusername[6] = username[2];
	orderusername[7] = username[1];
	orderusername[8] = username[0];
	orderusername[9] = username[4];

	char ordersn[25] = {0};
	int temp;
	int j = 0;

	for(int i = 0; i < 10; i++)
	{
		int mid[4] = {0};
		int k = 0;

		temp = orderusername[i] + numbers[i];
		
		while(temp != 0)
		{
			if((temp & 0xf) > 9)
				mid[k++] += (temp & 0xf) + 0x57;
			else
				mid[k++] += (temp & 0xf) + 0x30;

			temp = temp>>4;
		}

		k = 0;
		while(!mid[3-k])
			k++;
		ordersn[j++] = mid[2-k];
		ordersn[j++] = mid[3-k];
	}
	
	// 再将序列号重新排序
	sn[3] = ordersn[0];
	sn[4] = ordersn[1];
	sn[1] = ordersn[2];
	sn[0] = ordersn[3];
	sn[5] = ordersn[4];
	sn[2] = ordersn[5];
	sn[9] = ordersn[6];
	sn[8] = ordersn[7];
	sn[7] = '-';
	sn[16] = ordersn[8];
	sn[10] = ordersn[9];
	sn[13] = ordersn[10];
	sn[6] = ordersn[11];
	sn[12] = ordersn[12];
	sn[14] = ordersn[13];
	sn[15] = '-';
	sn[19] = ordersn[14];
	sn[17] = ordersn[15];
	sn[20] = ordersn[16];
	sn[21] = ordersn[17];
	sn[18] = ordersn[18];
	sn[22] = ordersn[19];
	sn[11] = '1';
	sn[23] = '\0';

	printf("the sn for you is: %s\n",sn);

	return 0;
}


--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2008年04月13日 02:15 pm

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 485
活跃值: (12)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
2
学习并支持一下。。。
2008-4-13 14:25
0
雪    币: 297
活跃值: (21)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
3
原来如此,楼主厉害
2008-4-13 23:55
0
雪    币: 200
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这个是明码的很好破的
rriijjkk
ad95f5b-a9b1a9e-ce73bac

在00401B88   .  50            PUSH EAX   下断单步就行了。可以在CPU的EAX处看到注册码!
2008-4-14 15:33
0
雪    币: 1969
活跃值: (46)
能力值: (RANK:550 )
在线值:
发帖
回帖
粉丝
5
学习并支持一下
2008-4-15 08:35
0
游客
登录 | 注册 方可回帖
返回
//