一个java版的CreackMe分析
在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系统写的:)
可以看得出来,作者在陷阱和实际注册代码中的序列号算法都很简单,我想他真正的目票标是让我们练习怎么走出陷阱,而不是钻牛
角尖,还好陷阱代码并不复杂,不然我想大多数人会在这里花更多的时间,没有耐心的则放弃,难怪经常会看到“破解需要耐心和运气”
这样的字眼。