首页
论坛
专栏
课程

一个java版的CreackMe简单分析

2005-7-16 12:42 8038

一个java版的CreackMe简单分析

2005-7-16 12:42
8038
原程序地址:http://bbs.pediy.com/showthread.php?s=&threadid=1735&highlight=java

    在pediy上看到这个java版的CreakMe,我们知道java不可能编译成真正的exe文件,所以用文本编辑器打开exe便可得到java原代码,
(我不知道,会不会有些工具把java做成exe后,在exe中存放的是字节码,如果再加上混淆,分析难度就更大了)首先找出Register按
钮的响应代码,简单分析如下(为了便于阅读,代码做了一点修改):
private void calculateSerial(){                                        //点注册按钮执行的函方法
        boolean fakeSuccess=false;                                //注册成功于否的标志
        String name = textField1.getText();                        //读取输入的用户名
        String serial = textField2.getText();                        //读取序列号
        if(name.length()==0 || serial.length()==0)                //如果name或serial的长度有为零的,显示错误信息
                label3.setText("You Must Enter A Name Serial");   
        else{
                char[] charName = name.toCharArray();
                char[] charSerial = serial.toCharArray();      
                Character[] chName=new Character[charName.length];
                Character[] chSerial = new Character[charSerial.length];
                if(name.length()>6 && serial.length()==9){                        //name 长度大于六,serial 长度等于九
                        for(int i=0;i<charName.length;i++)
                                chName[i]=new Character(charName[i]);                //包装为char对象数组,便于后面以对像处理
                        for(int i=0;i<charSerial.length;i++)
                                chSerial[i]=new Character(charSerial[i]);
                        if(chName[0].compareTo(chSerial[4]) == 0){                //name 第一位和 serial 第五位相等
                                if(chSerial[0].compareTo(chSerial[8]) == 0){        //serial 的第一位和第九位要一致
                                        int tmp = (int) ((Character.getNumericValue(charName[1])+
                                                        Character.getNumericValue(charName[2])+
                                                        Character.getNumericValue(charName[3]))/3);
                                        if( Character.getNumericValue(charSerial[2]) == tmp){
                                        //name 二、三、四位非负整型的平均值等于serial第三位
                                                int tmp1 = (int) ((Character.getNumericValue(charName[charName.length-3])+
                                                                Character.getNumericValue(charName[charName.length-2])+
                                                                Character.getNumericValue(charName[charName.length-1]))/3);  
                                                if( Character.getNumericValue(charSerial[6] )== tmp1){
                                                //name 倒数一、二、三位非负整型的平均值等于serial的第七位
                                                        fakeSuccess=true;
                                                }
                                        }  
                                }
                        }
                }
                if(fakeSuccess)
                        label3.setText("Try Another Approach. Registration FAILED");   
                else
                        label3.setText("Invalid Serial. Try Again");      
        }   
        textField1.setText("");
        textField2.setText("");        
}
    按照上的算法写出注册机后发现还是不能注册成功,而且注册机会算出乱码,显然是有问题了。干脆把fakeSuccess改为true试试,
任意输入注册码后提示 Try Another Approach. Registration FAILED,明白了,Register按钮根本是作者设的陷阱,把目光移到其它
的代码找找,OK,在该类的构造函数中找到如下代码:
String name=args[0];         //用户名
String serial = args[1];     //序列号
StringBuffer correct = new StringBuffer();
for(int i=0;i<name.length();i++){
        char a = name.charAt(i);
        int b = Character.getNumericValue(a);
        int c = b % 10;      //分别取出用户名中的每个字符对应的 Unicode 的非负整型值模 10 便得到序列号的相应位的字符
        Integer d = new Integer(c);
        correct.append(d.toString());   //这里转换为字符串
}
if(serial.equals(correct.toString())){  //如果序列号正确则把注册信息写入INI文件
        registered = true;
                try{
                        FileWriter theFile=new FileWriter("C:/Windows/systen.ini");
                        theFile.write("Well done "+name+"\n\n");
                        theFile.write("  You've cracked daPope's CrackMe #1");
                        theFile.write("using "+serial+" as serialnumber\n\n");         
                        theFile.flush();
                }
                catch(Exception e){};
                textField1.setBackground (new java.awt.Color (204, 204, 204));
                textField2.setBackground (new java.awt.Color (204, 204, 204));   
                textField1.setEditable(false);  //
                textField2.setEditable(false);            
                label3.setText("Cool! daPope's CrackMe #1 Successfully Registered");  //显示注册成功信息
                setTitle("daPope's CrackMe #1 - Registered");
        }
    其实上面的代码就是注册机了。接下来的问题是如何把我们计算出来的序列号提交给程序,序列号输入框和注册按钮根本是个摆设,
当然,用户名输入框也一样,我不能通过这两个输入框和注册按钮提交信息。程序中注册信息是从构造函数的参数中得到,那我们把注
册信息放在命令行中不就行了。
    在构造函数中还调用了checkForWinIce方法检查c盘根目录下的autoexec.bat中是否存在"winice.exe"这个字符串,检测ICE是否存在
的简单方法。isRegistered 是用户检测注册后保存注册信息的文件是否存在,但并没有检查注册信息是否正确,所以在C盘windows目
录下建立一个systen.ini的文件,不管内容有没有,就算是破解成功。当然,你想通过程序注册,要确保你的C盘有windows这个目录,不
然也不会注册成功,好像是专为98系统写的:)
    可以看得出来,作者在陷阱和实际注册代码中的序列号算法都很简单,我想他真正的目票标是让我们练习怎么走出陷阱,而不是钻牛
角尖,还好陷阱代码并不复杂,不然我想大多数人会在这里花更多的时间,没有耐心的则放弃,难怪经常会看到“破解需要耐心和运气”
这样的字眼。

[公告][征集寄语] 看雪20周年年会(12.28上海) | 感恩有你,一路同行

最新回复 (13)
小剑 2005-7-17 13:45
2
0
没学过 JAVA
skyege 2 2005-7-17 15:06
3
0
第一次看到这种文章,收藏,学习哈
pendan2001 4 2005-7-17 16:21
4
0
舵手 3 2005-7-17 17:24
5
0
熬了两个通宵,终于搞定了一个WEB报表系统的服务端,用狗加密的那种,由于是国产软件,不能放出来和大家共享,下面把JVM规范指令助记码和意义贴上来,如果要破解JAVA软件会用得到的,东西摘自《Java 虚拟机规范》,二进制指令查相关资料:
变量到操作数栈:iload,iload_<n>,lload,lload_<n>,fload,fload_<n>,dload,dload_<n>,aload,aload_<n>
操作数栈到变量:istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstor_<n>,astore,astore_<n>
常数到操作数栈:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_<i>,lconst_<l>,fconst_<f>,dconst_<d>
加:iadd,ladd,fadd,dadd
减:isub,lsub,fsub,dsub
乘:imul,lmul,fmul,dmul
除:idiv,ldiv,fdiv,ddiv
余数:irem,lrem,frem,drem
取负:ineg,lneg,fneg,dneg
移位:ishl,lshr,iushr,lshl,lshr,lushr
按位或:ior,lor
按位与:iand,land
按位异或:ixor,lxor
类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换)
          i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换)

创建类实便:new
创建新数组:newarray,anewarray,multianwarray
访问类的域和类实例域:getfield,putfield,getstatic,putstatic
把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload
从操作数栈存存储到数组:bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore
获取数组长度:arraylength
检相类实例或数组属性:instanceof,checkcast
操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap
有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene,
            if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl
            fcmpg,dcmpl,dcmpg
复合条件转移:tableswitch,lookupswitch
无条件转移:goto,goto_w,jsr,jsr_w,ret
调度对象的实便方法:invokevirtual
调用由接口实现的方法:invokeinterface
调用需要特殊处理的实例方法:invokespecial
调用命名类中的静态方法:invokestatic
方法返回:ireturn,lreturn,freturn,dreturn,areturn,return
异常:athrow
finally关键字的实现使用:jsr,jsr_w,ret
舵手 3 2005-7-17 17:29
6
0
没有搞清楚在JVM中对boolean的操作,只知道JVM不支持boolean,在JVM中boolean是用int表示的,具体含意是否的C中的定义一样?望高手能指点一下。如果那位有更详尽的JVM的资料,共享下!
hunter_boy 3 2005-7-18 21:37
7
0
JVM不支持boolean ????
我是吃java饭的呀
不要打击我哈

IBM  <=== 网址

IBM对java很感兴趣的
舵手 3 2005-7-19 09:38
8
0
如果你详细阅读过jvm规范的话就不会说我打击你了,下面这一段摘自《Inside Java Virtual Machine》,这个能说明问题:
All the primitive types of the Java programming language, except boolean, are primitive types of the Java Virtual Machine. When a compiler translates Java source code into bytecodes, it uses ints or bytes to represent booleans. In the Java Virtual Machine, false is represented by integer zero and true by any non-zero integer. Operations involving boolean values use ints. Arrays of boolean are accessed as arrays of byte, though they may be represented on the heap as arrays of byte or as bit fields
hunter_boy 3 2005-7-19 12:35
9
0
谢谢大哥的指点

我做的事都是应用层的,看来真是应该好好学习学习呀
舵手 3 2005-7-19 12:51
10
0
不敢当,我也是最近搞破解才找了点JVM的资料看了一下!
七夜 1 2005-7-19 15:16
11
0
楼主解说一下关于如何解狗的。
舵手 3 2005-7-20 13:11
12
0
服务端是JAVA写的,虽然是混淆了,就像这样:
        OoOoo00OooOOo00Oo0oo0 OOOO00O0o00O0OOoooOoo = OOoOoO0OOO0O0O00OOOOo(OO000oo0Ooo0O0O0OoO0o);
        HashMap Ooo0oOooOO0OOo00ooOoO = Ooo0o0000Oo0OOO0O0o0O(OOOO00O0o00O0OOoooOoo);
        HashMap Oo0oo0Oo000ooo0OoOoOO = OOO00oOO0OOoOO000OooO(OOOO00O0o00O0OOoooOoo);
        String OooOOOO00o0000oOoooO0 = OOO00oOo00O00000oooo0(OOOO00O0o00O0OOoooOoo, "default-locale");
        HashMap OO0o0oooO0Ooo00000oOO = Oo0oo0oo0O0OO0o0oO0oO(OOOO00O0o00O0OOoooOoo);
        return new Oooo0o0oOoO0O0OO000O0(Ooo0oOooOO0OOo00ooOoO, Oo0oo0Oo000ooo0OoOoOO, OooOOOO00o0000oOoooO0, OO0o0oooO0Ooo00000oOO)
但还是比看汇编代码容易,破解过程也就没什么值得介绍了。正在破解开发端,delphi开发的,搞了几天了,头都大了,汇编功低差,很难搞定,不知有没有高手愿意指点我一二?
lidan 2006-1-14 02:00
13
0
对付java程序就是一个方法!不管函数的调用,直接看代码!
舵手 3 2006-1-14 11:57
14
0
最初由 lidan 发布
对付java程序就是一个方法!不管函数的调用,直接看代码!


上面我贴出的代码你能看懂不?
游客
登录 | 注册 方可回帖
返回