首页
社区
课程
招聘
[讨论]java类文件保护策略
发表于: 2011-4-8 12:27 7850

[讨论]java类文件保护策略

2011-4-8 12:27
7850
java类文件保护策略,通常来说java的类文件理论上是无法保护的,一般只能混淆,或者使用classloader,最近研究某开源项目的代码的时候忽然发现了一个非常给力的class文件,用jd-gui打开直接无法认识,然后用2进制编辑器打开可以看见明显的类声明及常量池中的全部常量。
不过反编译工具却无法认出。所以想问问有没有达人见到过或者对此种保护略知一二的,讲讲我们以后也可以用这种方式来加密自己的代码。

个人分析:估计有可能是利用jd-gui的某些小bug导致无法被反编译的目的,如同以前swf的加密方式中多加了一个“;”就导致asv无法识别as脚本之类的目的。

附件中我给出具体class,供达人研究。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (5)
雪    币: 504
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
是這樣碼
package com.atlassian.extras.decoder.v2;

import com.atlassian.extras.common.LicenseException;
import com.atlassian.extras.common.org.springframework.util.DefaultPropertiesPersister;
import com.atlassian.extras.decoder.api.AbstractLicenseDecoder;
import java.io.*;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Properties;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.apache.commons.codec.binary.Base64;

public class Version2LicenseDecoder extends AbstractLicenseDecoder
{

    public Version2LicenseDecoder()
    {
    }

    public boolean canDecode(String licenseString)
    {
        licenseString = removeWhiteSpaces(licenseString);
        int pos = licenseString.lastIndexOf('X');
        if(pos == -1 || pos + 3 >= licenseString.length())
            return false;
        String lengthStr;
        int encodedLicenseLength;
        try
        {
            int version = Integer.parseInt(licenseString.substring(pos + 1, pos + 3));
            if(version != 1 && version != 2)
                return false;
        }
        catch(NumberFormatException e)
        {
            return false;
        }
        lengthStr = licenseString.substring(pos + 3);
        encodedLicenseLength = Integer.valueOf(lengthStr, 31).intValue();
        if(pos != encodedLicenseLength)
            return false;
        return true;
    }

    public Properties doDecode(String licenseString)
    {
        String encodedLicenseTextAndHash = getLicenseContent(removeWhiteSpaces(licenseString));
        byte zippedLicenseBytes[] = checkAndGetLicenseText(encodedLicenseTextAndHash);
        Reader licenseText = unzipText(zippedLicenseBytes);
        return loadLicenseConfiguration(licenseText);
    }

    protected int getLicenseVersion()
    {
        return 2;
    }

    private Reader unzipText(byte licenseText[])
    {
        ByteArrayInputStream in = new ByteArrayInputStream(licenseText);
        in.skip(LICENSE_PREFIX.length);
        InflaterInputStream zipIn = new InflaterInputStream(in, new Inflater());
        try
        {
            return new InputStreamReader(zipIn, "UTF-8");
        }
        catch(UnsupportedEncodingException e)
        {
            throw new LicenseException(e);
        }
    }

    private String getLicenseContent(String licenseString)
    {
        String lengthStr = licenseString.substring(licenseString.lastIndexOf('X') + 3);
        try
        {
            int encodedLicenseLength = Integer.valueOf(lengthStr, 31).intValue();
            return licenseString.substring(0, encodedLicenseLength);
        }
        catch(NumberFormatException e)
        {
            throw new LicenseException((new StringBuilder()).append("Could NOT decode license length <").append(lengthStr).append(">").toString(), e);
        }
    }

    private byte[] checkAndGetLicenseText(String licenseContent)
    {
        byte licenseText[];
        try
        {
            byte decodedBytes[] = Base64.decodeBase64(licenseContent.getBytes());
            ByteArrayInputStream in = new ByteArrayInputStream(decodedBytes);
            DataInputStream dIn = new DataInputStream(in);
            int textLength = dIn.readInt();
            licenseText = new byte[textLength];
            dIn.read(licenseText);
            byte hash[] = new byte[dIn.available()];
            dIn.read(hash);
            try
            {
                Signature signature = Signature.getInstance("SHA1withDSA");
                signature.initVerify(PUBLIC_KEY);
                signature.update(licenseText);
                if(!signature.verify(hash))
                    throw new LicenseException("Failed to verify the license.");
            }
            catch(InvalidKeyException e)
            {
                throw new LicenseException(e);
            }
            catch(SignatureException e)
            {
                throw new LicenseException(e);
            }
            catch(NoSuchAlgorithmException e)
            {
                throw new LicenseException(e);
            }
        }
        catch(IOException e)
        {
            throw new LicenseException(e);
        }
        return licenseText;
    }

    private Properties loadLicenseConfiguration(Reader text)
    {
        try
        {
            Properties props = new Properties();
            (new DefaultPropertiesPersister()).load(props, text);
            return props;
        }
        catch(IOException e)
        {
            throw new LicenseException("Could NOT load properties from reader", e);
        }
    }

    private static String removeWhiteSpaces(String licenseData)
    {
        if(licenseData == null || licenseData.length() == 0)
            return licenseData;
        char chars[] = licenseData.toCharArray();
        StringBuffer buf = new StringBuffer(chars.length);
        for(int i = 0; i < chars.length; i++)
            if(!Character.isWhitespace(chars[i]))
                buf.append(chars[i]);

        return buf.toString();
    }

    public static String packLicense(byte text[], byte hash[])
        throws LicenseException
    {
        try
        {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            DataOutputStream dOut = new DataOutputStream(out);
            dOut.writeInt(text.length);
            dOut.write(text);
            dOut.write(hash);
            byte allData[] = out.toByteArray();
            String result = (new String(Base64.encodeBase64(allData))).trim();
            result = (new StringBuilder()).append(result).append('X').append("0").append(2).append(Integer.toString(result.length(), 31)).toString();
            result = split(result);
            return result;
        }
        catch(IOException e)
        {
            throw new LicenseException(e);
        }
    }

    private static String split(String licenseData)
    {
        if(licenseData == null || licenseData.length() == 0)
            return licenseData;
        char chars[] = licenseData.toCharArray();
        StringBuffer buf = new StringBuffer(chars.length + chars.length / 76);
        for(int i = 0; i < chars.length; i++)
        {
            buf.append(chars[i]);
            if(i > 0 && i % 76 == 0)
                buf.append('\n');
        }

        return buf.toString();
    }

    public static final int VERSION_NUMBER_1 = 1;
    public static final int VERSION_NUMBER_2 = 2;
    public static final int VERSION_LENGTH = 3;
    public static final int ENCODED_LICENSE_LENGTH_BASE = 31;
    public static final byte LICENSE_PREFIX[] = {
        13, 14, 12, 10, 15
    };
    public static final char SEPARATOR = 88;
    private static final PublicKey PUBLIC_KEY;
    private static final int ENCODED_LICENSE_LINE_LENGTH = 76;

    static
    {
        try
        {
            String pubKeyEncoded = "MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIvfweZvmGo5otwawI3no7Udanxal3hX2haw962KL/nHQrnC4FG2PvUFf34OecSK1KtHDPQoSQ+DHrfdf6vKUJphw0Kn3gXm4LS8VK/LrY7on/wh2iUobS2XlhuIqEc5mLAUu9Hd+1qxsQkQ50d0lzKrnDqPsM0WA9htkdJJw2nS";
            KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            PUBLIC_KEY = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64("MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIvfweZvmGo5otwawI3no7Udanxal3hX2haw962KL/nHQrnC4FG2PvUFf34OecSK1KtHDPQoSQ+DHrfdf6vKUJphw0Kn3gXm4LS8VK/LrY7on/wh2iUobS2XlhuIqEc5mLAUu9Hd+1qxsQkQ50d0lzKrnDqPsM0WA9htkdJJw2nS".getBytes())));
        }
        catch(NoSuchAlgorithmException e)
        {
            throw new Error(e);
        }
        catch(InvalidKeySpecException e)
        {
            throw new Error(e);
        }
    }
}
2011-4-8 12:48
0
雪    币: 3149
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
经过测试,可以反编译出如楼上的代码
2011-4-8 13:14
0
雪    币: 204
活跃值: (329)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
如何加密的?哥们用的什么工具啊?这么给力,我用的最新版本的JD-gui都不行。
忽然想到把我封存多年的jad拿来一试,果然和楼主得到的代码一模一样。难道真的是jd-gui的bug?究竟是什么bug呢?
2011-4-8 13:19
0
雪    币: 1478
活跃值: (4007)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
最新的jd-gui 0.3.3可以直接打开阿
2011-4-8 23:59
0
雪    币: 257
活跃值: (105)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
以为有新东西出来了
2011-4-10 21:18
0
游客
登录 | 注册 方可回帖
返回
//