【文章标题】: 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
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!