【破解目标】:海颐应用开发平台 V 4.0
【所需工具】:Jad及其相关的JAVA环境
【破解目的】:从来没有用过JAD,用来练练手
【下载地址】:公司内部使用,不对外公布,否则我也不贴出来了……
【软件介绍】:
这个程序是我们地方的一个软件公司(海颐软件,不知道大家听过没有!)基于Struts和Spring等技术开发并整理的一个傻瓜式的J2EE应用程序开发平台,它是以Eclipse插件的形式发布出来的,其功能非常的强大,它可以让我们仅通过简单的配置,利用它提供的各种强大的组件(如AJAX树,导航菜单,工具栏,动态表格等等),自动的生成一个J2EE程序(可以自动生成JSP页面和WEB配置页面哦~),更可贵的是它几乎兼容了所有现在流行的数据库和应用服务器。
【破解分析】:
我在看雪潜水这么长时间,一篇精华都没有,怎么说也说不过去,可是我水平有限,如果自己再不努力争取,精华可真的就与我无缘了,最近学校的事又比较多,整天瞎忙活。就发一个前几天破解我们本地一家软件公司产品时的破解手记。
论坛上关于JAVA的程序分析不是很多哈,或许还让我给骗个精华呢,嘎嘎!
废话不多说了,分析JAVA的程序我还是第一次,不知道该从哪里开始。在网上找了好久也没有什么具体的结果,只知道JAVA程序用JAD可以反编译
用WinRAR将JAR解压缩,开始大概的浏览一下这个程序的结构,在com.highsoft.tools_1.0.0/toolset/jars/highsoft/mr-25-free包中找到了很多0oO组合的文件夹,还有很多类似的CLASS文件,在看雪精华中有过介绍,是“舵手”老大写的《浅谈JAVA程序破解》,说是这种混淆方式在JAVA程序的加密保护中很常见,我用JAD反编译了几个,也没有看出个所以然来,郁闷。
自己尝试着在Eclipse中建了个插件工程,我发现:似乎每个Eclipse插件程序都有这个配置文件,还有一个激活器叫Activator的类。就从这里入手了!
在designtool包中找到了,Activator.class文件而且还发现了一个可疑的文件:license.adp,从字面上讲就是“证书,许可”可是用HEX编辑器打开全是乱码,估计应该是加密了……
先不管它,用JAD将Activator.class反编译过来分析一下,找到了两个方法,代码如下:
public boolean checkLicense()
{
String as[] = new String[8];
int i = getUserInfo(as);
if(!c)
{
String s = null;
if(i == -5)
s = "\u65E0\u6548\u7684\u6D77\u9890\u5E94\u7528\u5F00\u53D1\u5E73\u53F0license,\u8BF7\u8054\u7CFB0535-6582295\uFF01";//无效的海颐应用开发平台license,请联系0535-6582295!
else
if(i == -1)
s = "\u6D77\u9890\u5E94\u7528\u5F00\u53D1\u5E73\u53F0" + as[4];//海颐应用开发平台
else
if(i >= 0)
{
s = "\u6D77\u9890\u5E94\u7528\u5F00\u53D1\u5E73\u53F0" + as[4];//海颐应用开发平台
c = true;
}
}
return i != -5 && i != -1;
}
public int getUserInfo(String as[])
{
g g1 = null;
d d1 = null;
b b = null;
if(d1 == null)
return -5;
g1 = d1.getLicenseInfo();
as[0] = g1.getUserID();
as[1] = d1.getCompanyName();
as[2] = g1.getProductName();
if(!g1.isDevelopEdition())
{
as[3] = "\u6B63\u5F0F\u7248";//正式版
as[4] = "\u65E0\u671F\u9650\u9650\u5236";//无期限限制
return -2;
}
if(g1.isEvalEdition() && g1.isTempEdition())
{
as[3] = "\u6B63\u5F0F\u7248";//正式版
as[4] = "\u65E0\u671F\u9650\u9650\u5236";//无期限限制
return -3;
}
if(g1.getEvalExpDate() == null)
as[3] = "\u8BD5\u7528\u7248";//试用版
int i = b.verifyLicense(g1);
if(i < 0)
{
as[4] = "license\u5DF2\u8FC7\u671F";//license已过期
return -1;
}
switch(i)
{
case 0: // '\0'
as[4] = "\u8BD5\u7528\u671F\u9650\u5373\u5C06\u5230\u671F";//试用期限即将到期
break;
case 1: // '\001'
as[4] = "1\u5929\u540E\u5230\u671F";//1天后到期
break;
default:
as[4] = i + "\u5929\u540E\u5230\u671F";//i天后到期
break;
}
return i;
}。
别的不说,单从方法名称上讲,我就应该兴奋的跳楼了,哈哈,至于“\u65E0\u6548\u7684\u6D77\u9890\u5E94\u7528\u5F00\u53D1\u5E73\u53F0license,\u8BF7\u8054\u7CFB0535-6582295\uFF01”之类的字符串,难不倒我,哈哈,这些是宽字节的ASCII,我们直接用IE就可以识别它们,连查表都省了,写一个HTML页面,代码如下:
<html>
<head><script>
document.write("\u65E0\u6548\u7684\u6D77\u9890\u5E94\u7528\u5F00\u53D1\u5E73\u53F0license,\u8BF7\u8054\u7CFB0535-6582295\uFF01");
</script>
</head>
</html>
依此类推,给源程序加上注释,(我已经加好了,嘿嘿~)
当然,这样子直接改代码?好像不能编译哈!按照驼手老大说的,难道让我直接修改它的字节码?不太现实哈!
既然爆破行不通,那我们就回过头来,仔细的想一下,我们还有一个文件license.adp没有用上,我们搜索文件夹中包含license字符的文件(我用的是DreamWeaver 8来查找,比较方便!)
经过分析筛选我定位到了iv.java的Write方法和c.java的Read方法,我分别给出它们的代码:
iv.java中的关键代码如下:
public void write(d d1)
throws IOException
{
String s = null;
s = "." + File.separator + "license";
File file = new File(s);
if((new File(s + ".bak")).exists())
(new File(s + ".bak")).delete();
if(file.exists())
file.renameTo(new File(s + ".bak"));
try
{
a = new BufferedOutputStream(new FileOutputStream(s));
byte abyte0[] = a(d1.getUserID());
a(abyte0);
abyte0 = a(d1.getLicenseKey());
a(abyte0);
abyte0 = a(d1.getCompanyName());
a(abyte0);
abyte0 = a(d1.getInstallRoot());
a(abyte0);
try
{
System.out.println(String.valueOf(a(d1)));
abyte0 = a(String.valueOf(a(d1)));
}
catch(Throwable throwable)
{
System.out.println(throwable.toString());
abyte0 = a(String.valueOf("false"));
}
a(abyte0);
if(d1.getMacAddress() != null)
a(a(d1.getMacAddress()));
if((new File(s + ".bak")).exists())
(new File(s + ".bak")).delete();
}
catch(Exception exception)
{
exception.printStackTrace();
if(a != null)
{
a.close();
a = null;
}
File file1 = new File(s);
if(file1.exists())
file1.delete();
if((new File(s + ".bak")).exists())
(new File(s + ".bak")).renameTo(new File(s));
}
finally
{
if(a != null)
a.close();
}
return;
}
byte[] a(String s)
{
try {
return s.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
return s.getBytes();
}
public boolean a(d d1)
throws Exception
{
if(!d1.getFlag())
return false;
g g1 = (new e()).getLicenseInfo(d1.getUserID(), d1.getLicenseKey());
if(g1.isTempEdition() || g1.isEvalEdition())
{
System.out.println("Evaluation");
return false;
}
String s = null;
if(System.getProperty("os.name").startsWith("Windows"))
{
String s1 = System.getProperty("java.library.path");
if(s1 == null || s1.length() == 0)
{
System.out.println("old JVM");
return false;
}
for(StringTokenizer stringtokenizer = new StringTokenizer(s1, ";", false); stringtokenizer.hasMoreElements();)
{
String s2 = stringtokenizer.nextToken();
int i = s2.toLowerCase().indexOf("system32");
if(i != -1)
{
s2 = s2.substring(0, i + 8);
s = s2 + "\\highsoft.dll";
break;
}
}
} else
{
s = "/usr/lib/.highsoft.so";
}
if(s == null)
{
System.out.println("System can not be identified.");
return false;
}
byte abyte0[] = a(System.getProperty("user.name"));
byte abyte1[] = a(d1.getLicenseKey());
byte abyte2[] = new byte[abyte1.length + abyte0.length];
System.arraycopy(abyte0, 0, abyte2, 0, abyte0.length);
System.arraycopy(abyte1, 0, abyte2, abyte0.length, abyte1.length);
byte abyte3[] = (new iw()).encode(abyte2);
BufferedOutputStream bufferedoutputstream = new BufferedOutputStream(new FileOutputStream(s, true));
bufferedoutputstream.write(abyte3.length);
for(int j = 0; j < abyte3.length; j++)
{
abyte3[j] ^= 61482;
bufferedoutputstream.write(abyte3[j]);
}
bufferedoutputstream.close();
return true;
}
private BufferedOutputStream a;
}
C.java中的关键代码如下:
public d read()
throws IOException
{
PushbackInputStream pushbackinputstream;
Exception exception1;
pushbackinputstream = null;
d d2;
try
{
String s = "license.adp";
pushbackinputstream = new PushbackInputStream(a(s));
String s1 = a(a(pushbackinputstream));
String s2 = a(a(pushbackinputstream));
String s3 = a(a(pushbackinputstream));
String s4 = a(a(pushbackinputstream));
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
boolean flag = Boolean.valueOf(a(a(pushbackinputstream))).booleanValue();
String s5 = null;
if(pushbackinputstream.available() > 0)
s5 = a(a(pushbackinputstream));
d d1 = new d();
d1.setUserID(s1);
d1.setLicenseKey(s2);
d1.setCompanyName(s3);
d1.setInstallRoot(s4);
d1.setFlag(flag);
d1.setMacAddress(s5);
d2 = d1;
}
catch(IOException ioexception)
{
throw ioexception;
}
catch(Exception exception)
{
throw new IOException(exception.toString());
}
finally
{
}
return d2;
}
String a(byte abyte0[])
{
try {
return new String(abyte0, "utf-8");
} catch (UnsupportedEncodingException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
return new String(abyte0);
}
private byte[] a(PushbackInputStream pushbackinputstream)
throws IOException
{
int i = pushbackinputstream.read();
byte abyte0[] = new byte[i];
for(int j = 0; j < i; j++)
abyte0[j] = (byte)(pushbackinputstream.read() ^ 0xf02a);
return abyte0;
}
static Class a;
}
哈哈,不用我多说了哦~
很明显c.java的Read方法就是分析并读取license.adp文件的哦,我们用Eclipse新建一个工程,将C.java文件复制到工程目录中,然后将license.adp文件放到工程的根目录下,然后新建一个类,编写代码如下:
package src;
import java.io.*;
import java.util.*;
public class Cstr {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
c c1 = new c();
try {
c1.read();
} catch (IOException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
}
}
运行结果如下:
######################################
试用版
GTBBAAAAAAAAQSS9AAAANJP8
试用版
######################################
哈哈,分析出来了,问题也很明显了哦……
至于iv.java的Write方法,这个需要我们有点耐性,通过它的代码我们可以知道,它需要我们准备好KEY,用户ID,还有公司名称,用户ID,和公司名称这个好说,但是这个KEY怎么弄呢?
经过分析代码,我发现,在iw.java中有一个createKey方法,代码如下:
public String createKey(g g1)
{
for(int i = 0; i < b.length; i++)
b[i] = 0;
b[0] = (byte)g1.getProductID();
if(g1.canOLAP())
b[0] |= 0x80;
if(g1.canRunWithServer())
b[0] |= 0x40;
long l = 0L;
if(g1.isTempEdition() || g1.isEvalEdition())
{
l = g1.getEvalExpDate().getTime();
b[0] |= 0x20;
if(g1.isTempEdition())
b[0] |= 0x10;
} else if(!g1.isEvalEdition() && !g1.isTempEdition())
{
l = g1.getMaintainExpDate().getTime();
b[0] |= 0x10;
}
b[1] |= 0x2;
if(g1.isDevelopEdition())
b[1] |= 0x10;
b[2] = (byte)g1.getConcurrentUser();
b[3] = (byte)g1.getConcurrentEngin();
b[4] = (byte)g1.getCPUNumber();
if(g1.isUpgrade())
b[4] |= 0x80;
l -= d;
l /= 0x5265c00L;
b[5] = (byte)(int)(l & 255L);
b[6] = (byte)(int)(l >> 8 & 255L);
b[7] |= g1.getConcurrentUser() >> 8 & 0x3;
b[7] |= (g1.getConcurrentEngin() >> 8 & 0x3) << 2;
int j = 0;
for(int k = 0; k < b.length - 2; k++)
j += b[k] & 0xff;
b[13] = (byte)j;
int i1 = (int)(Math.random() * 10D);
int j1 = (int)(Math.random() * 10D);
b[14] |= (byte)(i1 & 0xf);
b[14] <<= 4;
b[14] |= (byte)(j1 & 0xf);
if(!g1.isEvalEdition() && !g1.isTempEdition())
a(b);
for(int k1 = 0; k1 < j1; k1++)
{
byte byte0 = b[k1];
b[k1] = b[Math.abs(i1 - k1)];
b[Math.abs(i1 - k1)] = byte0;
}
a(g1);
byte abyte0[] = encode(b);
return new String(abyte0);
}
前后对应的看一下代码,思路就很清晰了,把相关的JAVA文件都考到工程中,作相应的修改,然后编写如下代码:
package src;
import java.io.*;
import java.util.*;
public class Cstr {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
g g = new g();
Date date = new Date();
g.setAllowedBuilderNumber(1);
g.setCPUNumber(1);
g.setConcurrentUser(1);
g.setConcurrentEngin(1);
g.setProductID(1);
g.setProductName("聚星亭");
g.setDevelopEdition(false);
g.setIsUpgrade(true);
g.setUserID("美丽の破船");
g.setEvalExpDate(date);
g.setMaintainExpDate(date);
System.out.println(g.createKey());
}
}
运行结果如下:
###########################################
ZIQAAAAAAQAAAAMBQKUC6IH7
##########################################
哈哈,这个就应该是KEY了哦……
那我们在看这个iv.java的Write方法,就很明了了,它的功能大概是在功能的根目录下生成一个名叫license的文件没有扩展名,好像还要在我们的系统目录的System32目录下生成一个名叫highsoft.dll的文件,我们没有办法手工去创建它,我们让它运行,一切不就OK了,好,我们在写如下的代码:
package src;
import java.io.*;
import java.util.*;
public class Cstr {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
d d1 = new d();
g g1 = null;
iv iv = new iv();
d1.setCompanyName("聚星亭");
d1.setInstallRoot("D:\\编程开发工具\\J2EE工具\\eclipse3.2.1\\plugins");
d1.setMacAddress("00-17-31-EC-64-16");
d1.setUserID("美丽の破船");
d1.setFlag(true);
g1 = d1.getLicenseInfo();
Date date = new Date();
g1.setAllowedBuilderNumber(12);
g1.setCPUNumber(1);
g1.setConcurrentUser(1);
g1.setConcurrentEngin(1);
g1.setProductID(1);
g1.setProductName("聚星亭");
g1.setDevelopEdition(false);
g1.setIsUpgrade(true);
g1.setUserID("美丽の破船");
g1.setMaintainExpDate(date);
d1.setLicenseKey(g1.createKey());
try {
iv.write(d1);
} catch (IOException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
}
}
好了,我们再看一下工程的根目录,的确多了一个名叫license的文件!!!
哈哈,可以读取,也就是它们的格式是一样的哦!我们试者用现在的这个license.adp去替换程序原本的那个文件试试,由于我自己用JAR打包的jar文件在Eclipse中不干活,我们就不破坏它的结构,用WinRAR打开com.highsoft.tools_1.0.0目录下的designtool.jar文件,然后将现在的这个license.adp文件拖到WinRAR窗口中完成替换,再同样打开com.highsoft.tools_1.0.jar文件,将designtool.jar替换掉包中原先的那个文件,最后把现在的这个com.highsoft.tools_1.0.jar放到Eclipse的plugins目录下,重新启动Eclipse程序,查看效果,显示正版了!
【最后小结】
好了,破解就告一段落,下面总结一下,至于JAVA程序的破解,我等菜菜没有什么资格去评论。所以总结就是,要破解JAVA程序,要学好JAVA是必须的,最好再就是研究一下javassist!
好了,总结完毕,睡觉~~~
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)