对于IntelliJ IDEA 5.0 验证分析

    技术2022-05-11  111

    Crack IntelliJ IDEA 5.0   这是一个Java IDE,含有非常好的功能,因此来进行分析。作者:vhly[FR]日期:2006/10/04 - 2006/10/05目标:IntelliJ IDEA 5.0工具:DJ Java Decompiler 3.7.7方法:静态分析、爆破(修改类功能重新编译)简述:当程序执行时,首先检测是否已注册,      如果未注册(如下文件未找到)将执行注册对话框,否则无法使用,没有试用设置      ConfigPath + File.separatorChar + "idea50.key";      ConfigPath + File.separatorChar + "idea40.key";      SystemPath + File.separatorChar + "idea.license";      BinPath    + File.separatorChar + "idea.license";    可以找到注册验证方法,但是由于使用了大数运算的 modPow 方法,进行逆向运算时需要大量时间,因此只进行爆破。操作步骤:  请将Patch_TNT目录下的 LicenseDataImpl_TNT_by_vhly[FR].class重命名为 LicenseDataImpl.class  将改名后的类添加到 idea.jar 文件的 com/intellij/licensecommon/license/ 目录下  运行:完成注册:注意序列号应该是这种个是 XXXXX-XXXXX-XXXXX-XXXXX-XXXXX步骤1:分析注册验证方法1) 寻找程序执行流程   分析bin目录中的idea.bat文件,找出如下语句(用于执行软件)         "%JAVA_EXE%" %JVM_ARGS% -cp "%CLASS_PATH%" %IDEA_MAIN_CLASS_NAME% %*   其中JAVA_EXE 指向 java.exe; IDEA_MAIN_CLASS_NAME 指向 com.intellij.idea.Main   因此要分析 com.intellij.idea.Main类的执行过程(IDEA采用了插件的方式,即使是Main类也是用插件管理的方式来运行)   Main的执行流程:       public static void main(String ... args) 执行软件入口点,该方法调用了插件管理器              PluginManager.main(args, (com.intellij.idea.Main.class).getName(), "start");                      此处执行插件管理器的调用 指定类的指定方法 即:调用 com.intellij.idea.Main类的方法 start() 参数为 args                      由此可知 实际上执行了 Main.start(args);       protected static void start(String as[]) 可以将这个方法看作类的入口之一              if(!checkStartupPossible())   // 检查版本以及只可以有一个实例  支持 1.4以上              System.exit(-1);              ......  // 日志设置              mainImpl(as);  // 执行入口点 真正的main的实现       protected static void mainImpl(String as[])              .... // 界面设置 装载本地库              if("Compuware".equals(ApplicationInfoImpl.getShadowInstance().getPackageCode())) // 返回空    LicenseManager.setInstance(new CompuwareLicense());        if("Fabrique".equals(ApplicationInfoImpl.getShadowInstance().getPackageCode()))  // 返回空    LicenseManager.setInstance(new FabriqueLicense());        由于上面的语句不会执行因此 LicenseManager.getInstance() 实际上会返回 IdeaLicense对象                      LicenseManager.getInstance().startUp(new com.intellij.ide.license.LicenseManager.StartupAction(as) {  // 指定调用 方法 a(String args[])      // 调用验证的方法在此处调用      public void proceed()      {                      Main.a(args);      }      public void cancel()      {        System.exit(-1);      }    });  // 调用startUp方法实际上执行了 proceed()方法 程序流程为 调用 Main类的 a(String args[])方法  private static void a(String as[]) //执行程序    IdeaApplication app = new IdeaApplication(as); // 此处为软件的执行过程 已经没有License验证le    SwingUtilities.invokeLater(new Runnable(app) {                public void run()      {        app.run();      }    });   LicenseManager实例 对应 LicenseManager.getInstance();  同时该类是个不完全抽象类  private static LicenseManager a;  public abstract boolean supportsRegistration(); // 是否可以注册  public abstract void register(); // 调用注册  public abstract void startUp(StartupAction startupaction); // 执行相应的命令  public abstract String licensedToMessage();   // 显示 License To:  public abstract String licensedRestrictionsMessage();  public abstract LicenseFactory createLicenseFactory(); // 设置 LicenseFactory 用于或去注册数据 LicenseData  public abstract boolean shouldCheckForUpdates(); // 检测升级  public LicenseManager()  {  }  public static LicenseManager getInstance()  {    if(a == null)              a = new IdeaLicense(); // 默认为 IdeaLicense (包路径com/intellij/ide/license/impl)          return a;  }  public boolean isEap()  // what meen the "isEap"? HAHAHAHAAAA  {    return false;  }    通过startUp方法调用 Main.a(args);  a(String args[])方法的步骤    IdeaApplication app = new IdeaApplication(as); // 设定IdeaApplication    同时由于 app又是一个 Runnable     new Thread(app).start(); // 调用 IdeaApplication类的 run方法    IdeaApplication类的执行流程  protected IdeaApplication(String as[])  // 首先为构造方法 由 Main.a调用  {    c = true; //     a.assertTrue(d == null); // a 使用了 log4J 的 Logger    d = this;    b = as;    boolean flag = "true".equals(System.getProperty("idea.is.internal"));    ApplicationManagerEx.createApplication("componentSets/IdeaComponents", flag, false, "idea");        e = a(); // 返回 属性 com.intellij.appStarter 指定的类    e.premain(as); // 内部类 IdeaApplication$IdeaStarter      premain(String args[]) // 方法预处理        final Splash splash = new Splash(ApplicationInfoImpl.getShadowInstance().getLogoUrl());                  SwingUtilities.invokeLater(new Runnable() {splash.show();});        装载开始画面        IdeaApplication.b(); // 调用 IdeaApplication方法 b()          // 设置Alloy外观 注意以下为 Alloy 的注册信息 哈哈          AlloyLookAndFeel.setProperty("alloy.licenseCode", "4#JetBrains#1ou2uex#6920nk");          AlloyLookAndFeel.setProperty("alloy.isToolbarEffectsEnabled", "false");          ...............  }  调用 IdeaApplication类的 run()方法    public void run()    {    ApplicationEx applicationex = ApplicationManagerEx.getApplicationEx();              // 装载附加类库    e.main(b); 调用 IdeaStarter 的 main方法 main(String ... args)    e = null;    }  调用 IdeaStarter类的main方法  public void main(String args[])    ... // 应用程序扩展    ((WindowManagerImpl)WindowManager.getInstance()).showFrame();  // 显示界面  // 此时的程序如果可以执行到这里那么程序就可以使用了但是 LicenseManager 和 IdeaLicense不会让程序执行到上述代码。  2) 分析 LicenseManager、IdeaLicense重点分析 IdeaLicense构造方法public IdeaLicense()    {        Log.FACTORY = new IdeaLoggerFactory();        d = LicenseFile.create();  // LicenseFile d; 创建授权文件类 便于 存储、读取        MessagePolicy.setMessages(new LicenseMessagesImpl()); // LicenseMessageImpl 包含了全部的授权信息提示 包括出错、过期等信息        g = createLicenseClient();  // ClientImplementation g; // 可以从这个类分析,但是现在没用        NetworkLicenseSource networklicensesource = new NetworkLicenseSource(g);    // 网络认证 尚不考虑        a(networklicensesource);        a();  // 此处重要    }    private void a()    {        e = new LicenseAuthorizorImpl();  // LicenseAuthorizor 用于授权验证        GetFromUser getfromuser = new GetFromUser(e, createLicenseFactory()); // GetFromUser 包含了通过用户获得的 Name & Key        createLicensePolicyBuilder().createRegister(e, getfromuser, this, d, g);  // 重要 通过分析这个方法,可以找到验证    // createLicensePolicyBuilder() 返回 PolicyBuild 类  调用 PolicyBuild的如下方法流程:    public void createRegister(LicenseAuthorizor licenseauthorizor, GetFromUser getfromuser, LicenseTarget licensetarget, LicenseTarget licensetarget1, LicenseClient licenseclient)    {      addValidation(licenseauthorizor, getfromuser, licensetarget, licensetarget1, licenseclient);    }  // 创建注册属性  // addValidation 添加 CheckValid命令类    protected void addValidation(LicenseAuthorizor licenseauthorizor, GetFromUser getfromuser, LicenseTarget licensetarget, LicenseTarget licensetarget1, LicenseClient licenseclient)    {    licenseauthorizor.addPolicy(new NotNull(getfromuser));    // 注册信息不能为空 LicenseData    addPoliciesForValidity(licenseauthorizor, getfromuser);     // 检测注册信息是否正确    licenseauthorizor.addPolicy(new CheckNeedUpgrade(getfromuser)); // 检测升级    licenseauthorizor.addPolicy(new CheckEvaluationExpired(getfromuser));  // 检测是否过期    licenseauthorizor.addPolicy(new UserAcceptsLicense(new Cancel()));  // 当注册显示正确之后 显示授权内容 用户必须  accept    licenseauthorizor.addPolicy(new SaveToTarget(licensetarget));  // 保存授权    licenseauthorizor.addPolicy(new SaveFromUser(licensetarget1)); // 保存授权    licenseauthorizor.addPolicy(new BroadcastUsing(licenseclient));  // 网络认证广播    licenseauthorizor.addPolicy(new CheckWillNeedUpgrade(licensetarget1));  // 检查是否需要升级    licenseauthorizor.addPolicy(new CheckWillExpire());   // 检查是否要过期    licenseauthorizor.addPolicy(new Proceed()); // 执行正常的程序    }  // 其中 addPolicy方法就是添加执行的命令    protected void addPoliciesForValidity(LicenseAuthorizor licenseauthorizor, GetFromUser getfromuser)    {      licenseauthorizor.addPolicy(new CheckCancelled(getfromuser));  // 检查是否被取消 即尚未注册      licenseauthorizor.addPolicy(new CheckValid(getfromuser));   // 检查是否正确    }        public class CheckCancelled extends MessagePolicy // 可以暂时不考虑    public class CheckValid extends MessagePolicy    {      public LicenseData check(AuthorizationAction auth, LicenseData licensedata)        throws TerminateCheckException      {        if(licensedata.isValid())  // 处理 isValid即可          return licensedata;   // 着重分析 LicenseData        if(licensedata.isFromUser())          getMessages().showLicenseInvalidMessage(); // 显示 出错信息        else          getMessages().showLicenseCorruptMessage(); // 显示 推出        return myFail.check(auth, licensedata);      }    }    }    public void register()    {        MessagePolicy.getMessages().setShowLicenseEntryAsDialog(true);  // 显示用户注册对话框        MessagePolicy.getMessages().setRegistering(true);        e.checkPolicy(new AuthorizationAction() {});    }3) 分析 LicenseDataLicenseData被定义为一个接口public interface LicenseData  // 由 LicenseDataImpl 实现 也就是要修改的地方{    public abstract boolean willNeedUpgrade();    public abstract boolean isAccepted();   // 是否接受    public abstract void setAccepted(boolean flag);    public abstract boolean needsUpgrade(Date date);    public abstract String getKey(); // 获得序列号    public abstract boolean isEvaluationExpired(Date date); // 是否过期    public abstract boolean isValid();         // 重点分析 爆破点    public abstract boolean isFromUser();    public abstract boolean willExpire();    public abstract long getTimeStamp();    public abstract Date getExpirationDate(); // 获得过期时间 如果为 null 则永不过期    public abstract String getUserName();        // 获得注册用户名    public abstract boolean isNonCommercial();       // 注册类型    public abstract boolean shouldDetectDuplicates();    public abstract Date getUpgradeDeadline();    public abstract boolean isPersonal();           // 注册类型    public abstract boolean isYearAcademic();  // 注册类型    public abstract boolean isOpenSource();       // 注册类型    public abstract int getMajorVersion();  // 版本号    public abstract int getProductId();         // 产品id}类 LicenseDataImplpublic class LicenseDataImpl extends AbstractLicenseData{    public static final int CURRENT_MAJOR_VERSION = 5;    public static final Date DEAD_LINE_DATE = makeDate(2005, 8, 1);    public static final Date FREE_UPGRADE_DATE = makeDate(2005, 4, 1);    private static final long f = 0xa4cb80L;    public static final String IDEA_VERSION = "IntelliJ IDEA 5";    private LicenseInfo g;    protected static Date makeDate(int i, int j, int k)    {        Calendar calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("Europe/Prague"));        calendar.clear();        calendar.set(i, j, k);        return calendar.getTime();    }    public LicenseDataImpl(String s, String s1)    {        super(s, s1);        g = null;    }    public LicenseDataImpl(String s, String s1, long l)    {        super(s, s1, l);        g = null;    }    public String toString()    {        return getUserName() + ":" + getKey();    }    public boolean willNeedUpgrade()    {        if(getMajorVersion() >= getCurrentMajorVersion())            return false;        if(g.generationDate == null)            return true;        else            return a(g.generationDate, FREE_UPGRADE_DATE);    }    protected int getCurrentMajorVersion()    {        return 5;    }    private boolean a(Date date, Date date1)    {        long l = date.getTime();        long l1 = date1.getTime();        return l < l1 - 0xa4cb80L;    }    public boolean needsUpgrade(Date date)    {        if(date == null)            return false;        if(date.before(getUpgradeDeadline()))            return false;        else            return willNeedUpgrade();    }    public boolean isEvaluationExpired(Date date)    {        if(date == null)            return false;        if(!willExpire())            return false;        else            return date.after(g.expirationDate);    }    public boolean isValid()    {      a();        return true; // 注意 ******************* 修改之前为 return g != null;   }    private void a()    {        if(b())            return;        g = new LicenseInfo();   // 修改之前为 g = LicenseDecoder.decodeLicenseKey(s,s1); 用于验证 用户名序列号        g.userName = "vhly[FR]"; // 由于 LicenseInfo 实际为数据类(可以直接修改变量)那么就用这种方法 可以修改为 g.userName = getUserName();        g.customerId = 830213;   // My BirthDay is Good Luck!        g.licenseType = 0;  // isCommercial() => true        g.majorVersion = 2;        g.minorVersion = 5;        g.generationDate = new Date(2006,10,4);  // 生成时间        g.expirationDate = null;          // 完成爆破 修改 LicenseDataImpl 类之后添加到 idea.jar文件中即可          // 但是第一次的注册还是必须的    }    private boolean b()    {        return g != null;    }    public boolean willExpire()    {        return getExpirationDate() != null;    }    public Date getExpirationDate()    {        a();        return g.expirationDate;    }    public boolean isNonCommercial()    {        a();        return g.licenseType == 1;    }    public boolean isCommercial()    {        a();        return g.licenseType == 0;    }    public boolean isSite()    {        a();        return g.licenseType == 2;    }    public boolean isOpenSource()    {        a();        return g.licenseType == 3;    }    public boolean isYearAcademic()    {        a();        return g.licenseType == 5;    }    public boolean shouldDetectDuplicates()    {        return !isSite() && !willExpire();    }    public Date getUpgradeDeadline()    {        return DEAD_LINE_DATE;    }    public boolean isPersonal()    {        a();        return g.licenseType == 4;    }    public Date getGenerationDate()    {        a();        return g.generationDate;    }    public int getMajorVersion()    {        a();        return g.majorVersion;    }    public int getProductId()    {        return g.productId;    }    public static LicenseDataImpl create(String s, String s1)    {        return new LicenseDataImpl(s, s1);    }    public static LicenseDataImpl createFromUser(String s, String s1)    {        LicenseDataImpl licensedataimpl = new LicenseDataImpl(s, s1);        licensedataimpl.setFromUser(true);        licensedataimpl.setAccepted(false);        return licensedataimpl;    }    public static LicenseDataImpl create(String s, String s1, long l)    {        return new LicenseDataImpl(s, s1, l);    }}4) 分析注册认证LicenseDecoder 类的验证方法需要使用的常量    private static final BigInteger a = new BigInteger("86f71688cdd2612ca117d1f54bdae029", 16);    private static final BigInteger b = new BigInteger("10001", 16);    private static final BigInteger c = new BigInteger("846d7cf2385dddd654629dd2ba94ca87", 16);    private static final BigInteger d = new BigInteger("10001", 16);    private static final BigInteger e = new BigInteger("1030230a52c274c376605b2c1", 16);    private static final BigInteger f = new BigInteger("10001", 16);    public static LicenseInfo decodeLicenseKey(String name, String key)  throws InvalidLicenseKeyException    {        if(key.length() < 25)            return a(name, key);        LicenseInfo licenseinfo = new LicenseInfo();        licenseinfo.userName = name;        int i = 0;        for(int j = 0; j < key.length(); j++)        {            char c1 = key.charAt(j);            if(c1 == '-')                i++;        }  // 寻找 分隔符 - 的个数        if(i != 5 && i != 4)          // 必须是4个或者5个            throw new InvalidLicenseKeyException();        if(i == 5)   // 当为5个的情况        {            int k = s1.indexOf('-');            try            {                licenseinfo.customerId = Integer.parseInt(s1.substring(0, k));   // 开始的为用户ID 格式 XXXX-                s1 = s1.substring(k + 1); // 真正的序列号为后面的4个 - 即格式为 XXXXX-XXXXX-XXXXX-XXXXX-XXXXX            }            catch(NumberFormatException numberformatexception)            {                throw new InvalidLicenseKeyException();            }        } else //包含4个-的用户Id为-1        {            licenseinfo.customerId = -1;        }                BigInteger biginteger = LicenseUtil.decodeGroups(s1);  将序列号计算成 BigInteger           /*调用          public static BigInteger decodeGroups(String s) throws InvalidLicenseKeyException                          对于 s1的生成 可以使用 LicenseUtil.encodeGroups(BigInteger big) 使用任意 big        */                BigInteger biginteger1 = biginteger.modPow(b, a);     //   big **(b) mod a  序列号的大数 big 的 b次方 模 a        byte abyte0[] = biginteger1.toByteArray();                    //  长度必须为12            // 取结果的字节数组        if(abyte0.length != 12)                             // 在此处目前无法找出长度为 12 的数值 只能到 16 或者 17            if(abyte0.length == 13)            {                if(abyte0[0] == 0)                {                    byte abyte1[] = new byte[12];                    System.arraycopy(abyte0, 1, abyte1, 0, 12);                    abyte0 = abyte1;                } else                {                    throw new InvalidLicenseKeyException();                }            }            else if(abyte0.length < 12)            {                byte abyte2[] = new byte[12];                System.arraycopy(abyte0, 0, abyte2, 12 - abyte0.length, abyte0.length);                abyte0 = abyte2;            }            else            {                throw new InvalidLicenseKeyException();            }                         // 由于 使用了 modPow方法 对于逆向求解我还未找到相应的解法!!!!!!!!!!        need Help!!!!!!!!!!!!!!!!1                if(name != null)        {            short word0 = LicenseUtil.calculateCheckSum(name, licenseinfo.customerId, abyte0);  // 计算校验和                    //对于生成的数组 最后两位不会参与 CRC32计算                    // 也就时说 计算完 word0之后再设置             if(abyte0[10] != (byte)(word0 & 0xff))                     // [10] = word0 & 0xff                throw new InvalidLicenseKeyException();            if(abyte0[11] != (byte)(word0 >> 8 & 0xff))                // [11] = word0 >> 8 & 0xff                throw new InvalidLicenseKeyException();        }        licenseinfo.licenseType = abyte0[0] >> 4;                  // 0 - 5        licenseinfo.productId = abyte0[0] & 0xf;                   // 5        licenseinfo.minorVersion = abyte0[1] >> 4;                 // 2        licenseinfo.majorVersion = abyte0[1] & 0xf;                // 5        long l = ((long)abyte0[2] & 255L) + (((long)abyte0[3] & 255L) << 8) + (((long)abyte0[4] & 255L) << 16) + (((long)abyte0[5] & 255L) << 24) << 16;        licenseinfo.generationDate = new Date(l);   // 4个字节 的long 作为创建时的时间数        int i1 = (abyte0[6] & 0xff) + ((abyte0[7] & 0xff) << 8);  // 0 为0 则证明 永不过期        if(i1 != 0)            licenseinfo.expirationDate = new Date(l + (long)i1 * 24L * 60L * 60L * 1000L);        return licenseinfo;   // 返回生成的 LicenseInfo 对象 如果执行成功 证明 验证正确    }完整的注册流程String name,key;if(key has 5 '-')  getCustomerId(the first XXXXX)  key = substring(4 -)end iflong sum = 0long temp = 0x39aa400Lfor x each - splite key  // BigInteger LicenseUtil.decodeGroups(String key)  // 每一个 XXXXX 从最后一格 XXXXX开始向前循环  int k = decodeGroup(XXXXX)  sum *= temp  sum += kend forsum**b mod a => sum.modPow(b,a) -> resultresult -> toByteArray -> bufbuf .length must 12short crcsum (name, id, buf) buf的最后2位不参与运算 用于保存计算出的 crc校验和 0    1   2 3 4 5 6 7 8 9 10  11T I MI MA D D D D E E X X  S   S生成的字节数组格式T  4bit  授权类型   0 - 5I  4bit  产品ID     5MI 4bit  min Version 5MA 4bit  maj Version 2D D D D  4Bytes    创建时间E E      2Bytes    过期时间 0 永不过期X X      2Bytes unknown  未知S S      2Bytes CRC32    校验和function decodeGroup(XXXXX) : return intint k =0  for each X in XXXXX // 逆序运算  X = X to number  // if  '0' -> 0; '9' -> 9; 'A' -> 'A' -65 +10  k *= 36  k += X  end for取 一个数的 三十六进制数值end function6) 猜测的注册机方法public String genKey(String name, int cid){  byte buf[] = new byte[12];  buf[0] = 5;  buf[1] = 0x52;  long l = System.currentTimeMills();  l >>= 16;  buf[2] = l & 0xff;  buf[3] = l >> 8 & 0xff;  buf[4] = l >> 16 & 0xff;  buf[5] = l >> 24 & 0xff;  buf[6] = 0;  buf[7] = 0;  buf[8] = 1;  buf[9] = 1;    short word = LicenseUtil.calculateCheckSum(name, id, buf);  buf[10] = word & 0xff;  buf[11] = word >> 8 & 0xff;  BigInteger bt = new BigInteger(buf);  // 计算出了 结果  x**b mod a = bt  (bt = x.modPow(b,a));  x = ?  // 尚未找出正确的序列号  BigInteger bigKey = ?  String key = LicenseUtil.encodeGroups(bigKey);  if(cid == -1)    return key;  else    key = Integer.toString(cid).toUpperCase()+key;  return key;}10月10日,经过这几天的分析。我终于发现实际上序列号,是使用RSA的方法计算的主要是用户序列号 S (BigInteger)授权信息   I (BigInteger) toByteArray()-〉授权数据其中 LicenseDecoder中的 BigInteger a 为RSA公钥的指数部分     LicenseDecoder中的 BigInteger b 为RSA公钥的指数部分由于 IntelliJ 公司有 RSA 的私钥,所以只有 该公司才可以生成符合格式的序列号。对于这种方式,我们需要将 a,b替换为我们的公钥参数,并且使用我们的私钥参数(e, b)执行序列号生成byte [] buf = .... // 根据格式生成授权数据BigInteger I = new BigInteger(buf);BigInteger S = I.modPow(b, e); // 生成我们的 S序列号数值String serial = LicenseUtil.encodeGroups(S);完成  

    最新回复(0)