首页
社区
课程
招聘
[原创]海颐应用开发平台v4.0算法分析手记
2008-2-29 23:23 8635

[原创]海颐应用开发平台v4.0算法分析手记

2008-2-29 23:23
8635
【破解目标】:海颐应用开发平台  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!
    好了,总结完毕,睡觉~~~

[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞0
打赏
分享
最新回复 (14)
雪    币: 485
活跃值: (12)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
petnt 12 2008-2-29 23:27
2
0
分析的不错,不过分析国产不容易骗到精华。。。
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 26 2008-3-1 00:25
3
0
哈,美丽破船起航了。
雪    币: 65
活跃值: (811)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
美丽破船 5 2008-3-1 04:26
4
0
能骗到精华当然好!
骗不到也没有关系哈,反正是为了学习,又不是为了精华·~~
雪    币: 65
活跃值: (811)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
美丽破船 5 2008-3-9 10:54
5
0
第一次分析java的程序就被沉到海底!
下次俺也整个OD来分析~
反正我要骗精华的决心是坚定不移的~
雪    币: 164
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
河边渔者 1 2008-3-10 10:38
6
0
楼主陷于执着了
雪    币: 257
活跃值: (105)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
舵手 3 2008-3-12 09:43
7
0
只看你贴出的代码,用javassist修改getUserInfo这个方法的返回值就可以暴破。
这个程序的注册算法过于简单,可以找些国外的软件练练手,人家的算法真正能让我们学到东西。
分析国内的软件,最好不要贴子名字,这样既不会给自己惹麻烦,也有机会骗到精华
别外,我叫舵手不是驼手:)
雪    币: 65
活跃值: (811)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
美丽破船 5 2008-3-14 10:15
8
0
下次注意~!~
雪    币: 8188
活跃值: (4243)
能力值: ( LV15,RANK:2459 )
在线值:
发帖
回帖
粉丝
ccfer 16 2008-3-14 10:22
9
0
我觉得不是因为你写的不好,可能是现在不流行这个
我发现最近似乎流行驱动方面的内容,即便写的比较垃圾也容易混到精
雪    币: 6073
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
forgot 26 2008-3-14 10:48
10
0
就像几年前脱dbpe也可以混到精
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
海风日影 2008-3-14 10:54
11
0
能忽悠到大多数人的就可以混到精华
雪    币: 32410
活跃值: (18730)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2008-3-14 10:59
12
0
现在精华帖的一个必要条件:
国产软件涉及到版权注册保护分析,不设精华(隐去软件名也不设)。

公告: 看雪论坛对于所涉及到目标软件的管理2007.10.15
雪    币: 328
活跃值: (39)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
vhly 5 2008-3-25 10:16
13
0
就是呀,今天搜了一下Java关键字,发现这片,发现舵手了,发现好多人,不过不是驼手呀,这样可不好
雪    币: 2118
活跃值: (5855)
能力值: ( LV13,RANK:409 )
在线值:
发帖
回帖
粉丝
xwtwho 1 2008-3-26 15:25
14
0
这个好像还没有涉及到用汇编来看JAVA代码吧,并且现在有的class用jad都搞不完全,

大家有没有看过破解JAVA程序的资料(汇编级的),介绍一下啊
雪    币: 272
活跃值: (307)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
wangshq397 8 2008-3-26 17:03
15
0
应该起名字叫老外的某java的算法分析。
游客
登录 | 注册 方可回帖
返回