【文章标题】: "易语言写的一个漂亮的creakme"破解分析
【文章作者】: netwind
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
1、去掉neg.
od加载程序,bp MessageBoxA下断。f9运行,断下,取消断点,堆栈如下分布:
0012F9F4 10062176 /CALL 到 MessageBoxA 来自 krnln.10062170
0012F9F8 00000000 |hOwner = NULL
0012F9FC 0040C0CF |Text = "本关你要去掉我(*^__^*) 嘻嘻",A1,"",AD,"",A1,"",AD,""
0012FA00 0040C0CB |Title = "NAG"
0012FA04 00002000 \Style = MB_OK|MB_TASKMODAL
在代码窗口 右键 转到 10062170 ,也就是跳到call MessageBoxA的地方了
1006216B . 50 push eax ; /Style
1006216C . 52 push edx ; |Title
1006216D . 51 push ecx ; |Text
1006216E . 6A 00 push 0 ; |hOwner = NULL
10062170 . FF15 A0260C10 call dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
10062176 . 5F pop edi
这个就是neg窗口 开始想改下跳转 跳过去,但发现 这段代码 在creakme.exe 释放的一个库文件里
并且api调用都是调用这里的,改库文件麻烦。
我们在 call 下一行 pop edi 下断 f9运行弹出neg ,点确定, 然后单步 f8 几下子,
程序流程就回到 creakme.exe本身的代码区域了,如下:
00472368 68 04000080 push 80000004
0047236D 6A 00 push 0
0047236F 68 CBC04000 push 0040C0CB ; ASCII "NAG"
00472374 68 01030080 push 80000301
00472379 6A 00 push 0
0047237B 68 00000000 push 0
00472380 68 04000080 push 80000004
00472385 6A 00 push 0
00472387 68 CFC04000 push 0040C0CF
0047238C 68 03000000 push 3
00472391 BB 00030000 mov ebx, 300
00472396 E8 82040000 call 0047281D
0047239B 83C4 28 add esp, 28
程序返回到了0047239b 很显然call 0047281d就是用来弹neg的,我们直接nop它 保存,第一关就过了。
2、使按钮和输入框有效
按钮和输入框由CreateWindowExA创建,其是否有效是由该函数的输入参数style的值决定。
那么我们的思路就是在改函数创建button 和 edit 控件时 把输入参数给该掉,就可以了。
cm调用的 代码都是在它释放的库文件里
我们直接修改它的库文件。
在程序开始部分简单跟几下 或者断WriteFileA你会发现 它释放如下文件
C:\Documents and Settings\netwind\Local Settings\Temp\E_4\krnln.fnr
程序调用的api都是在这个文件里的。
我们直接od加载
右键,查找所有模块间调用 找到CreateWindowExA,双击,有如下代码:
100AE0D3 |. FF50 5C call dword ptr [eax+5C]
100AE0D6 |. 85C0 test eax, eax
100AE0D8 0F85 03BF0000 jnz 100B9FE1 //这个跳转 是我修改了的 //这里始终都跳,也可以写jmp,
100AE0DE 90 nop
100AE0DF 90 nop
100AE0E0 90 nop
100AE0E1 90 nop //这里的指令执行不到,没用被我nop了
100AE0E2 90 nop
100AE0E3 90 nop
100AE0E4 90 nop
100AE0E5 90 nop
100AE0E6 90 nop
100AE0E7 90 nop
100AE0E8 |> 57 push edi
100AE0E9 |. 56 push esi
100AE0EA |. E8 F8FEFFFF call 100ADFE7
100AE0EF |. FF75 D0 push dword ptr [ebp-30] ; /lParam
100AE0F2 |. FF75 D4 push dword ptr [ebp-2C] ; |hInst
100AE0F5 |. FF75 D8 push dword ptr [ebp-28] ; |hMenu
100AE0F8 |. FF75 DC push dword ptr [ebp-24] ; |hParent
100AE0FB |. FF75 E0 push dword ptr [ebp-20] ; |Height
100AE0FE |. FF75 E4 push dword ptr [ebp-1C] ; |Width
100AE101 |. FF75 E8 push dword ptr [ebp-18] ; |Y
100AE104 |. FF75 EC push dword ptr [ebp-14] ; |X
100AE107 |. FF75 F0 push dword ptr [ebp-10] ; |Style
100AE10A |. FF75 F4 push dword ptr [ebp-C] ; |WindowName
100AE10D |. FF75 F8 push dword ptr [ebp-8] ; |Class
100AE110 |. FF75 FC push dword ptr [ebp-4] ; |ExtStyle
100AE113 |. FF15 58250C10 call dword ptr [<&USER32.CreateWindow>; \CreateWindowExA
push dword ptr [ebp-10] ; |Style 就是这个参数决定着button ,edit 是否激活的。
我们就把上面来个跳转找 到一 空白区 加几条指令 判断Style 的值,换成我们想要的值然后再跳回来。
这里是我加的 修改style参数的代码,空白空间不连续,我多来了几个跳转。
100B9FE1 817D F0 002F0>cmp dword ptr [ebp-10], 4C012F00 //这个就是原button 的style.
100B9FE8 90 nop
100B9FE9 74 17 je short 100BA002 如果是这个4c012f00就去修改了它
100B9FEB EB 55 jmp short 100BA042 如果不是就继续往下判断是不是edit的stye.
100B9FED 90 nop
100B9FEE 90 nop
100B9FEF 90 nop
。。。。。。。。。。
100BA002 C745 F0 80008>mov dword ptr [ebp-10], 50800080 //把button的style改为 50800080使有效
100BA009 ^ E9 DA40FFFF jmp 100AE0E8 //改完后返回去
。。。。。。。。。
100BA042 817D F0 80080>cmp dword ptr [ebp-10], 44010880 //到这判断是不是edit 的参数44010880
100BA049 74 3A je short 100BA085 //是就跳去修改
100BA04B ^ E9 9840FFFF jmp 100AE0E8 //不是就返回
。。。。。。。。。。。。。。。。。。。。。
100BA085 C745 F0 80000>mov dword ptr [ebp-10], 44000080 //修改edit 的style参数为4400080
100BA08C 74 4A jmp short 100BA0D8 //改完后返回 这里空间不够,多跳了一下
。。。。。。。。。。。。。
100BA0D8 ^ E9 0B40FFFF jmp 100AE0E8
好了,修改完毕 保存 。
这时 重新打开 cm 运行发现,还是没效果,其实cm每运行时 都重新释放那个库,所以,下面我们让它只生产一次。
重加载cm bp WriteFile用上面类似方法断下,有如下代码:
004013C7 . 8D85 68FDFFFF lea eax, dword ptr [ebp-298]
004013CD . 50 push eax
004013CE . E8 FD1E0000 call 004032D0
004013D3 . /E9 81000000 jmp 00401459 //这里又被我改了
004013D8 |90 nop
004013D9 |90 nop
004013DA |90 nop
004013DB |90 nop
004013DC . |53 push ebx ; /hTemplateFile
004013DD . |68 80000000 push 80 ; |Attributes = NORMAL
004013E2 . |6A 02 push 2 ; |Mode = CREATE_ALWAYS
004013E4 . |53 push ebx ; |pSecurity
004013E5 . |53 push ebx ; |ShareMode
004013E6 . |68 00000040 push 40000000 ; |Access = GENERIC_WRITE
004013EB . |50 push eax ; |FileName
004013EC . |FF15 20704000 call dword ptr [<&KERNEL32.CreateFile>; \CreateFileA
004013F2 . |83F8 FF cmp eax, -1
004013F5 . |8945 08 mov dword ptr [ebp+8], eax
004013F8 . |74 17 je short 00401411
004013FA . |8D4D D8 lea ecx, dword ptr [ebp-28]
004013FD . |53 push ebx ; /pOverlapped
004013FE . |51 push ecx ; |pBytesWritten
004013FF . |57 push edi ; |nBytesToWrite
00401400 . |56 push esi ; |Buffer
00401401 . |50 push eax ; |hFile
00401402 . |FF15 0C704000 call dword ptr [<&KERNEL32.WriteFile>>; \WriteFile
00401408 . |FF75 08 push dword ptr [ebp+8] ; /hObject
0040140B . |FF15 08704000 call dword ptr [<&KERNEL32.CloseHandl>; \CloseHandle
00401411 > |03F7 add esi, edi
00401413 . |3B75 F4 cmp esi, dword ptr [ebp-C]
00401416 .^|0F82 51FFFFFF jb 0040136D
0040141C . |385D A4 cmp byte ptr [ebp-5C], bl
0040141F . |75 0C jnz short 0040142D
00401421 > |C745 FC 20814>mov dword ptr [ebp-4], 00408120 ; ASCII "Not found the kernel library!"
00401428 . |E9 92000000 jmp 004014BF
0040142D > |8D85 6CFEFFFF lea eax, dword ptr [ebp-194]
00401433 . |50 push eax
00401434 . |8D85 68FDFFFF lea eax, dword ptr [ebp-298]
0040143A . |50 push eax
0040143B . |E8 801E0000 call 004032C0
00401440 . |8D45 A4 lea eax, dword ptr [ebp-5C]
00401443 . |50 push eax
00401444 . |8D85 68FDFFFF lea eax, dword ptr [ebp-298]
0040144A . |50 push eax
0040144B . |E8 801E0000 call 004032D0
00401450 . |83C4 10 add esp, 10
00401453 . |8D85 68FDFFFF lea eax, dword ptr [ebp-298]
00401459 > \50 push eax ; /FileName
0040145A . FF15 04704000 call dword ptr [<&KERNEL32.LoadLibrar>; \LoadLibraryA
你会发现每次他都生成一个库 再loadlibraryA,我们发现在004013CE . E8 FD1E0000 call 004032D0
执行完后eax,保存的就是要生成库的路径,那么我们直接从这jmp 到loadlibrary的地方,就避免了他每次重生产那库了。
修改完保存,再把上面修改好的库放到对应的目录
再运行cm,就一切ok了。
3、解密
od加载我们修改好的,bp MessageBoxA 断下后 堆栈如下:
0012F830 10062176 /CALL 到 MessageBoxA 来自 krnln.10062170
0012F834 00000000 |hOwner = NULL
0012F838 0040C18A |Text = ""D7,"",A2,"",B2,"崧?,B4,"砦?
0012F83C 0040C15A |Title = "警",B8,"?
0012F840 00002000 \Style = MB_OK|MB_TASKMODAL
这里是是警告 ,提示错误,我们在内存窗口 看看40c15a的内容,在内存窗口右键转到40c15a
内容如下:
0040C15A BE AF B8 E6 00 C7 EB CA E4 C8 EB D7 A2 B2 E1 C2 警告.请输入注册
0040C16A EB 00 00 00 00 00 00 54 9F 40 00 00 00 00 00 00 ?.....T烜......
0040C17A 14 40 B9 A7 CF B2 00 D7 A2 B2 E1 B3 C9 B9 A6 00 @恭喜.注册成功.
0040C18A D7 A2 B2 E1 C2 EB B4 ED CE F3 00 38 00 00 00 53 注册码错误.8...S
放了成功时的提示串和不成功时的串,很明显 注册码对时 就会把成功提示串压栈,我们找找看 哪里是否有 push 004c181
指令 这个指令就是把成功提示压栈。
我们在内存窗口搜索 二进制串81 c1 4c 00 找到地址00472778 放的这个串,我们在代码窗口 转到00472778 直接
是敏感代码部分,附近有如下代码:
00472651 E8 BDFEFFFF call 00472513 ; 把机器码读入到eax,符号到edx
00472656 8945 F8 mov dword ptr [ebp-8], eax
00472659 8955 FC mov dword ptr [ebp-4], edx
0047265C 68 01030080 push 80000301
00472661 6A 00 push 0
00472663 68 40E20100 push 1E240
00472668 68 01030080 push 80000301
0047266D 6A 00 push 0
0047266F FF75 F8 push dword ptr [ebp-8]
00472672 68 02000000 push 2
00472677 BB CC000000 mov ebx, 0CC
0047267C E8 9C010000 call 0047281D
00472681 83C4 1C add esp, 1C
00472684 99 cdq
00472685 8945 F0 mov dword ptr [ebp-10], eax ; 机器码转为整数后 前4字节给eax
00472688 8955 F4 mov dword ptr [ebp-C], edx //后4字节给edx
0047268B 816D F0 F3220D0>sub dword ptr [ebp-10], 0D22F3
00472692 835D F4 00 sbb dword ptr [ebp-C], 0
00472696 8145 F0 CF07000>add dword ptr [ebp-10], 7CF
0047269D 8355 F4 00 adc dword ptr [ebp-C], 0
004726A1 DF6D F0 fild qword ptr [ebp-10] ; 写入到寄存器
004726A4 DD5D E0 fstp qword ptr [ebp-20]
004726A7 DD45 E0 fld qword ptr [ebp-20] //把机器码 转浮点数放FPU寄存器
004726AA DC0D 6CC14000 fmul qword ptr [40C16C] // 把浮点数*2005.0000000
004726B0 DD5D D8 fstp qword ptr [ebp-28] ; 放到后面变量里
004726B3 DD45 D8 fld qword ptr [ebp-28] ; 读出来
004726B6 E8 58FEFFFF call 00472513 //这个call 相当与把运算过的机器码 变整数压栈后传给eax
004726BB 8945 F0 mov dword ptr [ebp-10], eax
004726BE 8955 F4 mov dword ptr [ebp-C], edx
004726C1 DF6D F0 fild qword ptr [ebp-10]
004726C4 DD5D E0 fstp qword ptr [ebp-20]
004726C7 DD45 E0 fld qword ptr [ebp-20]
004726CA DC35 74C14000 fdiv qword ptr [40C174] //除以5.000000
004726D0 DD5D D8 fstp qword ptr [ebp-28]
004726D3 DD45 D8 fld qword ptr [ebp-28]
004726D6 E8 38FEFFFF call 00472513
004726DB 8945 F0 mov dword ptr [ebp-10], eax //到这里运算完毕,运算后前4字节在eax里
004726DE 8955 F4 mov dword ptr [ebp-C], edx //后4字节在edx里
004726E1 6A FF push -1 //这以下对注册码处理
004726E3 6A 08 push 8 //实际上就是把注册码变整数压栈了
004726E5 68 07000116 push 16010007
004726EA 68 01000152 push 52010001
004726EF E8 35010000 call 00472829
004726F4 83C4 10 add esp, 10
004726F7 8945 E4 mov dword ptr [ebp-1C], eax
004726FA 68 04000080 push 80000004
004726FF 6A 00 push 0
00472701 8B45 E4 mov eax, dword ptr [ebp-1C]
00472704 85C0 test eax, eax
00472706 75 05 jnz short 0047270D
00472708 B8 59C14000 mov eax, 0040C159
0047270D 50 push eax
0047270E 68 01000000 push 1
00472713 BB 64010000 mov ebx, 164
00472718 E8 00010000 call 0047281D
0047271D 83C4 10 add esp, 10
00472720 8945 DC mov dword ptr [ebp-24], eax
00472723 8955 E0 mov dword ptr [ebp-20], edx
00472726 8B5D E4 mov ebx, dword ptr [ebp-1C]
00472729 85DB test ebx, ebx
0047272B 74 09 je short 00472736
0047272D 53 push ebx
0047272E E8 F0000000 call 00472823
00472733 83C4 04 add esp, 4
00472736 DD45 DC fld qword ptr [ebp-24]
00472739 E8 D5FDFFFF call 00472513
0047273E 8945 E8 mov dword ptr [ebp-18], eax
00472741 8955 EC mov dword ptr [ebp-14], edx
00472744 8B55 EC mov edx, dword ptr [ebp-14]
00472747 8B45 E8 mov eax, dword ptr [ebp-18]
0047274A 3945 F0 cmp dword ptr [ebp-10], eax //这里注册码前4字节跟运算过后的机器码前4字节比较
0047274D 75 03 jnz short 00472752
0047274F 3955 F4 cmp dword ptr [ebp-C], edx //后4字节比较
00472752 0F85 3B000000 jnz 00472793
00472758 68 04000080 push 80000004
0047275D 6A 00 push 0
0047275F 68 7CC14000 push 0040C17C
00472764 68 01030080 push 80000301
00472769 6A 00 push 0
0047276B 68 00000000 push 0
00472770 68 04000080 push 80000004
00472775 6A 00 push 0
00472777 68 81C14000 push 0040C181 //如果相等就执行这里了,成功提示消息压栈
0047277C 68 03000000 push 3
00472781 BB 00030000 mov ebx, 300
00472786 E8 92000000 call 0047281D
0047278B 83C4 28 add esp, 28
0047278E E9 36000000 jmp 004727C9
00472793 68 04000080 push 80000004
00472798 6A 00 push 0
0047279A 68 5AC14000 push 0040C15A
0047279F 68 01030080 push 80000301
004727A4 6A 00 push 0
004727A6 68 00000000 push 0
004727AB 68 04000080 push 80000004
004727B0 6A 00 push 0
004727B2 68 8AC14000 push 0040C18A //不相等就会执行到这,错误警告消息压栈
004727B7 68 03000000 push 3
004727BC BB 00030000 mov ebx, 300
004727C1 E8 57000000 call 0047281D
注册算法简单,就是把机器码运算下 得到一个结果,如果注册码不是这个结果,就错了。
我用c套汇编把代码 简单列出如下:
#include <stdio.h>
#include <windows.h>
inline void key() 这个就是那个call ,call 00472513 就是把浮点数 变整数压栈 返回给eax
{
_asm{
push ebp
mov ebp, esp
sub esp, 0xc
fistp qword ptr [ebp-0xC] //从寄存器读入浮点数到堆栈(这里是机器码)
mov eax, dword ptr [ebp-0xC]
mov edx, dword ptr [ebp-8]
mov esp, ebp
pop ebp
}
}
void main()
{
long a;
double b=-2141937092,c=2005,d=5; //b为机器码
_asm{
fld b
call key //把机器码放到栈上,并传给eax
xor eax,0x1e240
sub eax,0xd22f3
add eax,0x7cf
mov dword ptr [ebp-0x24], eax
mov dword ptr [ebp-0x20], edx
fild qword ptr [ebp-0x24] //写入fpu寄存器
fmul c
fdiv d
fstp qword ptr [ebp-0x34] //读入栈
lea eax,dword ptr [ebp-0x34]
mov a,eax
}
memcpy(&b,(double *)a,8);
printf("%0.0f\n",b);
}
我列的代码就是 把机器码的运算的过程 ,运算完了就得到注册码了。
修改过的文件放附件里了.
--------------------------------------------------------------------------------
【经验总结】
由于对浮点指令完全陌生,google的时间远比调试的时间长,汇编代码编写能力也差强人意,所以耗了n久时间,这个cm作
者累坏我了,还是感谢作者,毕竟有些收获,希望作者最好把代码放出来。
petnt大侠已经放出破文了,自己感觉分析的思路 不一样,就放出来交流。petnt大侠修改cm 远比我修改库文件的方法简单
多了,当时我想过直接在库里找压栈参数的值 修改就完了,但没搜到,于是来了个麻烦的跳来跳去的方法 呵呵。
有不对之处 望大家指导!
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2008年01月08日 16:23:02
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课