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) 分析 LicenseData
LicenseData被定义为一个接口
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
}
类 LicenseDataImpl
public 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 if
long sum = 0
long temp = 0x39aa400L
for x each - splite key // BigInteger LicenseUtil.decodeGroups(String key)
// 每一个 XXXXX 从最后一格 XXXXX开始向前循环
int k = decodeGroup(XXXXX)
sum *= temp
sum += k
end for
sum**b mod a => sum.modPow(b,a) -> result
result -> toByteArray -> buf
buf .length must 12
short crcsum (name, id, buf) buf的最后2位不参与运算 用于保存计算出的 crc校验和
0 1 2 3 4 5 6 7 8 9 10 11
T I MI MA D D D D E E X X S S
生成的字节数组格式
T 4bit 授权类型 0 - 5
I 4bit 产品ID 5
MI 4bit min Version 5
MA 4bit maj Version 2
D D D D 4Bytes 创建时间
E E 2Bytes 过期时间 0 永不过期
X X 2Bytes unknown 未知
S S 2Bytes CRC32 校验和
function decodeGroup(XXXXX) : return int
int 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 function
6) 猜测的注册机方法
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);
完成
这是一个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) 分析 LicenseData
LicenseData被定义为一个接口
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
}
类 LicenseDataImpl
public 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 if
long sum = 0
long temp = 0x39aa400L
for x each - splite key // BigInteger LicenseUtil.decodeGroups(String key)
// 每一个 XXXXX 从最后一格 XXXXX开始向前循环
int k = decodeGroup(XXXXX)
sum *= temp
sum += k
end for
sum**b mod a => sum.modPow(b,a) -> result
result -> toByteArray -> buf
buf .length must 12
short crcsum (name, id, buf) buf的最后2位不参与运算 用于保存计算出的 crc校验和
0 1 2 3 4 5 6 7 8 9 10 11
T I MI MA D D D D E E X X S S
生成的字节数组格式
T 4bit 授权类型 0 - 5
I 4bit 产品ID 5
MI 4bit min Version 5
MA 4bit maj Version 2
D D D D 4Bytes 创建时间
E E 2Bytes 过期时间 0 永不过期
X X 2Bytes unknown 未知
S S 2Bytes CRC32 校验和
function decodeGroup(XXXXX) : return int
int 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 function
6) 猜测的注册机方法
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);
完成