一直想进论坛,以前没法申请,好不容易找机会申请了,竟然没有邀请码,写文章我没有实力,所以只能以爆破来作为见面礼了,老鸟们不要笑话,老大给次学习的机会吧!
一:用16进制编辑器实现爆破
运行程序,用户名输入test,输入任意一串假码,“Go”一下,如图所示!
用OD载入程序,使用插件搜索ASCII码和UNICODE,没有任何可用信息,用LordPE打开程序,看一下PE信息,IMAGE_BASE为0040 0000,导入表被加密了,如图所示
,用C32ASM打开程序,看到程序的PE结构被手动修改了,如图所示!
我们仔细看一下发现在MessageBox字符串下有一些有意思的字符串——“成功 恭喜 失败 加油”,有图1可知“失败 加油”就是错误提示!也就是说MessageBox函数调用的参数是做为变量保存到的内存中的,当输入的用户名和密码正解的时候程序就会将“成功 恭喜”这两个变量压入栈空间作为MessageBox的参数,从而显示“成功 恭喜”的窗口,如果输入的用户名和密码不正确就将“失败 加油”压入栈空间做为MessageBox的参数,所以我们只要将offset:80处的“失败 加油”修改为“成功 恭喜”,就实现了爆破!
二:修改变量偏移实现爆破
在上面我们知道程序是通过MessageBox函数来显示成功或者是失败的,用OD载入程序,bp MessageBoxA下断点,回车后,F9执行程序,输入假码,Alt+F9返回程序领空!返回后的代码如下:
004006EE E8 BDFEFFFF CALL 004005B0;关键CALL
004006F3 05 08000400 ADD EAX,40008
004006F8 59 POP ECX
004006F9 C1E0 04 SHL EAX,4:将“失败加油”的内存地址保存到EAX中
004006FC 59 POP ECX
004006FD 8D48 08 LEA ECX,DWORD PTR DS:[EAX+8]
00400700 6A 00 PUSH 0
00400702 51 PUSH ECX
00400703 50 PUSH EAX ;失败加油入栈,作为MessageBox的参数!
00400704 FF75 08 PUSH DWORD PTR SS:[EBP+8]
00400707 FF15 00024000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; USER32.MessageBoxA
显然,我们只要将add eax,40008,那句修改为add eax,40009,就可以将“恭喜 成功”字符入栈做为MessageBox的参数了!
不过这样的修改并不是完美的,当用户输入正解的用户名或者密码时,程序会载入004000a0处的内容,显然程序会出错的,但是我们可以通过修改汇编指令实现完美爆破,我们可以加上一个条件开关,cmp eax,1,如果zf=1让程序执行add eax,40009,如果zf=0,让程序执行add eax,4008,想法是好的,但是我们完成的任务是在有限的空间内写一个条件判定,不管怎么写我们的代码总会超过要修改的代码空间,所以上面的思路直接就被枪毙了,必须换一种思路才可以,先不管eax的值是多少,直接用and指令让eax为0,然后直接将“恭喜 成功”字符的地址保存到eax中,换言之让程序的关键跳失去意义,如图所示!
虽然上面修改栈没有保持栈平衡(少了两句POP指令),但是不影响程序执行,这里做下说明,如果我们用vc6.0作开发环境,执行如图5中的代码,会出现如图所示的错误,如图所示
中我们人为的多增加了两个栈空间,当函数执行完毕后,系统会调用_chkesp()来检测是否栈平衡,显然会出现5中的错误,如果我们加上add esp,8,就不会出现错误提示了,如图
那么是不是程序必须保证栈平衡才可以正常运行呢?答案肯定是否!一方面这个程序使用的是Multiple DLL的运行时库,并没有_chkesp()来检测栈超越的问题,另一方面,这个程序本身在弹完框后就退出了,根本用不到再去使用栈的数据,所以不会出现问题(菜鸟的想法)!
三:关键跳实现爆破
一中只所以要修改代码是因为我们没有找到关键跳,那么程序的关键跳在哪呢?上面的代码中ADD EAX,40008是一句关键句,有前面的内容可知,如果eax=0,程序就会加载失败和加油字符的地址,如果eax=1,程序就会加载成功和恭喜字符的地址,程序是通过EAX的值来做跳转开关的,所以关键跳应该可以在mov eax,1或者变形语句附近找到!在关键跳上F2下断,重新加载程序!F7进入关键call,拖动鼠标找到ret、retn、retf、leave等类似关键指令,在Windows中函数通过call与retn指令配合实现执行,所以有时候我们可以将retn指令来做为判断一个函数结束的标志。找啊,找啊找到如下代码!
0040067E |. 3ACB |CMP CL,BL
00400680 /75 0F |JNZ SHORT 00400691
00400682 |FEC3 |INC BL
00400684 |80FB 3C |CMP BL,3C
00400687 ^|72 DD \JB SHORT 00400666
00400689 |6A 01 PUSH 1
0040068B |58 POP EAX;mov eax,1
0040068C |5F POP EDI
0040068D |5E POP ESI
0040068E |5B POP EBX
0040068F |C9 LEAVE
00400690 |C3 RET
00400691 \33C0 XOR EAX,EAX
00400693 ^ EB F7 JMP SHORT 0040068C;跳到pop edi处
从上面的代码可以看出,如果cl与bl不相等就会执行xor eax,eax,然后跳到pop edi处,从而不执行mov eax,1的指令,那么它是不是就是关键跳呢?我们从cmp cl,bl向上看代码,如下:
00400664 32DB xor bl,bl ; bl=0
00400666 0FB6CB movzx ecx,bl ; ecx=bl
00400669 8BC1 mov eax,ecx ; eax=bl
0040066B 6A 0A push 0A
0040066D 99 cdq
0040066E 5E pop esi
0040066F 8A4C0D BC mov cl,byte ptr ss:[ebp+ecx-44]
00400673 F7FE idiv esi
00400675 B2 06 mov dl,6
00400677 F6EA imul dl
00400679 2AC8 sub cl,al
0040067B 80C1 60 add cl,60
0040067E 3ACB cmp cl,bl
上面的代码第一次执行的时候,cl与bl都是0,显然JNZ SHORT 00400691这条指令是不会跳转的,也就说JNZ SHORT 00400691处不是关键跳,真正的关键跳应该是下面的JB SHORT 00400666,也就是小于的时候跳,所以只要修改成jnb让程序不实现跳转就可以实现mov eax,1的目的!在这条语句上右键选择“汇编”,输入jnb SHORT 00400666,如图所示!
然后“保存文件”就可以实现爆破了!
四:傀儡算法实现爆破
摸不到头绪的时候,给程序加上一个傀儡算法也是一个不错的主意,破解就是一种游戏,既然是游戏,玩的开心就是最重要的,我们写入几行汇编指令,让程序的用户名和密码都为1,然后通过cmp 1,1决定跳转,这是一个恒等式,程序自然就被破解了代码如下:
mov dword ptr ss:[ebp+8],1
mov ss:[ebp+c],1
mov ebx,ss:[ebp+8]
cmp ebx,ss:[ebp+c]
jnz 00400693
jmp 00400689
修改后如图8所示!
五:将"ShellCode"制作成万能注册码实现爆破
这里虽然说是ShellCode, 其实只是应用了ShellCode的一种寻址思想而已,程序本身并没有问题,在编写ShellCode的时候精确的定位ShellCode的执行位置是非常重要的,通常的方法——jmp esp,想一下方法2中,如果我们将and eax,0作为注册码输入到程序中,然后修改程序关键CALL的返回地址,让它jmp esp到栈区去执行我们的代码是不是就相当于有了一个“万能注册码”!因为程序本身没有类似缓栈溢出的问题,所以我们无法通过输入超多的字符修改返回地址,同时因为函数返回后并不是栈空间马上恢复到输入的注册码位置处,所以我们的工作变成了修改返回地址处的指令!
1:完成jmp esp操作
分析栈空间的排列不难看出程序返回后,必须回复三个栈空间才可以到达我们输入注册码的地方,如图所示!
显然我们在jmp esp的时候,必须先手动恢复栈帧!构建如下代码(程序空间正好够用):
add esp,c;回收多出的三个栈空间
jmp esp;进入栈区执行我们输入的注册码也就是shellcode
add eax,400090;将成功与恭喜进栈用于函数显示
当关键call执行完成后,返回地址为add esp,c处,这样回收3个栈空间后,再让程序jmp esp,去执行我们输入的注册码——ShellCode,然后将400090处的参数入栈,如图所示!
将修改保存后,进行第二步!
2:ShellCode的内容
ShellCode的内容很简单,就是让eax为0,但是这里三种方法,and eax,0 or eax,1 xor eax,eax,这三种运算都可以使eax为0,但是所占字节是不一样的,我们选择xor eax,eax,ShellCode的注册码内容只有两句!
xor eax,eax;eax清零
jmp 4006FB8;跳转返回地址处的指令
执行清0后,再跳转到add eax,400090处,看一下图10中的地址就明白了!
上面两句对应的机器码16进制表示为:33C0 E9F40C2D00为了保证4字节对齐,我们nop掉剩余一部分代码,修改为33C09090 E9F40C2D00!
3:用C32ASM查看注册码的内容
新建一个1.txt,用C32ASM打开,输入上面的机器码,然后粘贴出来为“3缾轸
-”,这一串乱码字符就是我们修改程序后的万能注册码了!我们运行修改后的程序测试一下!如图所示!
如果想继续玩下去,其实还可以将user制作成一个特定的用户,将注册码制作成一个指定的密码!
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!