好友
阅读权限 35
听众
最后登录 1970-1-1
本帖最后由 solly 于 2020-4-17 23:43 编辑
有一段时间没有发贴了,前段时间研究了一个基因测序软件有关的注册破解 ,当然我并不知道如何使用这个软件,并且没有仪器,软件也跑不起来,其代码是java编译的,
但是通过 Excelior Jet 7.6 预编译为一个 300 多兆的EXE执行文件了,是不能反编译成java源码的,不过其编译后,会在数据节中保留所有class和jar包的信息,但所有的jar包 中
的class文件都清成0字节长度了,只保留了 *.properties, *.xml 等资源文件,没有其它可用的信息了。本软件采用的是 FlexLM.jar + EccpressoAll.jar 进行授权管理的,但这两个
jar包中的class也已经被清成0字节了,经研究是采用的 11.9 版本的flexlm.jar。
但是,由于java的jar库可存在多级打包的原因,以及其版本管理的问题(版本冲突,会出现一些jar包改名,导致没有编译), 有几个jar包中的class没有被 Excelior Jet 清零,
其中就包括了本贴所含的算法组件,并且这个算法组件包自带单独的注册校验部分,应该是一个比较完整的算法库。
这个组件是 GAComponents.jar,GA 表示 Genetic Algorithm 的意思,给有兴趣的各位玩玩,不懂的或没兴趣的,就不要看了。该jar包附在后面作附件 。
该组件目录结果如下:
上图中已反编译的几个类,就是这个组件的注册类,用来注册组件,如果注册成功,则生成一个UDP监听线程(端口5000,地址230.0.0.1),用于给算法授权。
其中 Registrar.class 主要代码如下: [Java] 纯文本查看 复制代码
public boolean checkLicense() {
boolean licenseValidity = false;
if (this.isLicenseExpired()) {
this.displayMessage("This license has been expired. Please contact PE Biosystems Sales Representative.");
return false;
}
final String licenseString = this.readLicense();
if (licenseString != null) {
licenseValidity = this.checkLicense(licenseString);
}
if (!licenseValidity) {
final Frame aFrame = new Frame();
aFrame.setIconImage(this.iconImage);
final RegistrarView registrarView = new RegistrarView(aFrame, this);
registrarView.show();
if (this.license == null) {
return false;
}
if (!this.checkLicense(this.license)) {
this.displayMessage("The application could not start because the registration code is not valid.");
return false;
}
this.writeLicense();
licenseValidity = true;
}
if (this.validLicense == null) {
return false;
}
this.appNumber = this.validLicense.getAppNumber();
this.serialNumber = this.validLicense.getSerialNumber();
this.numUsers = this.validLicense.getNumUsers();
if (this.validLicense.isExceedsLegalLicenseCount(this.numUsers, this.license)) {
this.displayMessage("The maximum concurrent user number of this license has been reached.");
return false;
}
this.validLicense.startListening(this.license);
return true;
}
这个类调用另一个类进行注册码校验,这个类是 License.class,如下图所示:
其主要注册算法代码如下:
[Java] 纯文本查看 复制代码
public boolean areLicenseDigitsValid() {
final byte[] digit = new byte[11];
this.appNumber = 0;
this.serialNumber = 0;
this.numUsers = 0;
final long scrambleCode = this.licenseDigits[9] * 16 + this.licenseDigits[10];
this.unscrambleDigits(this.licenseDigits, digit, scrambleCode);
final byte crc = this.getCRCDigits(digit, 8L);
final byte testCrc = digit[8];
boolean valid = crc == testCrc;
if (valid) {
final int testSerialNumber = digit[0] * 4096 + digit[1] * 256 + digit[2] * 16 + digit[3];
final int testAppNumber = digit[4] * 256 + digit[5] * 16 + digit[6] * 1;
final int testNumUsers = digit[7];
final int testScrambleCode = this.scrambleCode(testSerialNumber + testAppNumber + testNumUsers);
valid = (scrambleCode == testScrambleCode);
if (valid) {
this.licenseHasBeenValidated = true;
this.appNumber = (short)testAppNumber;
this.serialNumber = (short)testSerialNumber;
this.numUsers = (short)testNumUsers;
}
}
return valid;
}
private byte getNthRandomByte(final long num) {
final long kTwoToThe31MinusOne = 2147483647L;
final long kTwoToThe31MinusTwo = kTwoToThe31MinusOne - 1L;
final long kByteScaling = (kTwoToThe31MinusOne - 1L) / 256L;
final long kTwoToThe32Plus1 = kTwoToThe31MinusOne * 2L + 2L;
final long kSevenToThe5 = 16807L;
long r = 1L;
for (int k = 0; k < num + 12L; ++k) {
r *= kSevenToThe5;
r = (int)r;
if (r < 0L) {
r += kTwoToThe32Plus1;
}
r %= kTwoToThe31MinusOne;
}
final byte b = (byte)(r / kByteScaling);
return b;
}
private void unscrambleDigits(final byte[] scramDigits, final byte[] clearDigits, final long scrambleCode) {
for (int k = 0; k < 9; ++k) {
final long scramblebyte = 9L * scrambleCode + k;
final byte nthrandombyte = this.getNthRandomByte(scramblebyte);
clearDigits[k] = (byte)(scramDigits[k] - nthrandombyte & 0xF);
}
}
private byte getCRCDigits(final byte[] digit, final long numToCRC) {
long crc = 0L;
for (int k = 0; k < (int)numToCRC; ++k) {
crc <<= 1;
if (crc > 15L) {
crc |= 0x1L;
}
crc += digit[k];
crc &= 0xFL;
}
return (byte)crc;
}
private short scrambleCode(final long number) {
short key = 0;
final int scrambleCase = (int)number % 451;
switch (scrambleCase) {
case 0: {
key = 90;
break;
}
case 1: {
key = 181;
break;
}
//这里省略了400多个分支,可自己去反编译看源码
case 449: {
key = 101;
break;
}
case 450: {
key = 231;
break;
}
}
return key;
}
上面代码中的方法 areLicenseDigitsValid() 是注册码校验算法,注册码由11个字节组成,其中前8个字节是注册信息(含 3 字节的appNumber,4 字节的 serialNumber,1 字
节的userNumbers),第 9 字节是 CRC 32 较验,不过用了 4 bit,第10,11字节是一个加密key的索引值,用该key对前面的9字节进行加密。
注册检查时,会要求输入用户名,机构名和注册码,注册成功则会保存注册信息,下次再调用该检查函数时,就不会要求输入注册码了。
注册算法并不复杂,看其源码很容易理解,注册机代码如下,两个 java 类文件:
主java类文件:Main.java 如下:
[Java] 纯文本查看 复制代码
import com.apldbio.gacomponents.library.registration.License;
import com.apldbio.gacomponents.library.registration.Registrar;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
//
reg();
}
public static void reg() {
short appNumber = 0x555; /// 12 bits (1~0xFFF)
short serialNumber = 0x6666; /// 16 bits (1~0x7FFF)
short numUsers = 0x0F; /// 4 bits (1~0x0F)
LicenseKey key = new LicenseKey();
String sn = key.getSN(serialNumber, appNumber, numUsers);
System.out.println("SN: " + sn);
//// check
// License aLicense = new License(sn);
//
// if (aLicense.getCodeValidity()) {
// System.out.println("check1: ok");
// } else {
// System.out.println("check1: fail");
// }
//
// if (aLicense.areLicenseDigitsValid()) {
// System.out.println("check2: ok");
// } else {
// System.out.println("check2: fail");
// }
//
// if (aLicense.getAppNumber() == appNumber) {
// System.out.println("check3: ok");
// } else {
// System.out.println("check3: fail");
// }
//
// System.out.println("App No.: " + aLicense.getAppNumber());
// System.out.println("Serial: " + aLicense.getSerialNumber());
// System.out.println("Users: " + aLicense.getNumUsers());
Registrar reg1 = new Registrar("d:/reg.txt", "991231", appNumber, null);
boolean b = reg1.checkLicense();
if (b) {
System.out.println("check: ok");
} else {
System.out.println("check: fail");
}
}
}
其生成注册码在控制台显示,然后调用组件的注册函数,弹出一个对话框,要求输入注册码信息,注册码就是在控制台中显示的注册码 。成功后就会生成注册文件 d:/reg.txt,
不过该文件不是文本格式的文件,生成该文件后,下次运行注册机就不会要求输入注册信息了,直接在Eclipse的控件台中显示校验成功。
上面代码中的 Registrar reg1 = new Registrar("d:/reg.txt", "991231", appNumber, null); 就是组件的校验类,调用其 checkLicense() 方法进行校验,第1个参数是注册文件名,
第2个参数是过期时间,“991231”表示 2099 年 12 月 31 日,第三个参数是appNumber,必须与注册码中的一致,第4个参数是一个图标,可设置为 null。
另一个是注册码计算的 java 类,文件是: LicenseKey.java,如下所示:
[Java] 纯文本查看 复制代码
public class LicenseKey
{
private byte[] licenseDigits;
private short appNumber;
private short serialNumber;
private short numUsers;
final static short[] scrambleKeys = { /// 451 elements
90, 181, 255, 226, 162, 202, 55, 51, 228, 37, 43, 14, 183, 198, 199, 118, 137, 122, 38, 7, 8, 244, 50, 193, 218,
241, 25, 235, 147, 185, 26, 227, 169, 127, 22, 107, 58, 142, 215, 130, 20, 141, 110, 66, 180, 182, 174, 150, 46,
45, 210, 5, 170, 135, 149, 65, 54, 173, 146, 247, 74, 158, 19, 245, 9, 203, 248, 240, 222, 160, 129, 88, 108, 82,
80, 49, 113, 84, 1, 176, 12, 3, 64, 6, 191, 47, 163, 184, 100, 251, 189, 73, 212, 39, 211, 239, 166, 188, 232,
121, 17, 159, 42, 128, 161, 18, 109, 249, 79, 53, 116, 81, 167, 0, 77, 134, 139, 187, 56, 152, 168, 164, 83, 194,
209, 172, 126, 151, 192, 145, 60, 196, 178, 94, 197, 186, 27, 224, 34, 230, 234, 213, 15, 96, 48, 44, 123, 231,
52, 220, 179, 219, 148, 76, 41, 11, 29, 91, 36, 62, 144, 156, 28, 153, 92, 206, 106, 236, 204, 61, 89, 217, 223,
238, 115, 40, 207, 86, 63, 154, 132, 68, 177, 102, 254, 131, 32, 175, 136, 229, 67, 75, 30, 114, 70, 237, 157, 93,
120, 95, 165, 171, 71, 201, 221, 205, 125, 216, 97, 112, 119, 98, 99, 101, 140, 155, 24, 31, 250, 10, 4, 195, 72,
138, 111, 59, 16, 246, 13, 200, 143, 23, 252, 124, 21, 105, 35, 57, 242, 117, 243, 103, 69, 85, 214, 133, 104, 33,
233, 225, 253, 78, 2, 190, 208, 87, 204, 6, 241, 110, 140, 111, 200, 73, 56, 247, 188, 30, 237, 7, 240, 252, 217,
87, 104, 131, 196, 130, 39, 14, 21, 41, 190, 107, 5, 211, 122, 230, 248, 78, 213, 177, 67, 102, 60, 195, 138, 79,
84, 181, 141, 219, 245, 33, 222, 218, 36, 212, 244, 183, 186, 57, 234, 71, 156, 246, 154, 193, 129, 108, 225, 40,
115, 151, 146, 253, 11, 109, 159, 135, 149, 116, 18, 194, 65, 152, 51, 2, 8, 228, 163, 62, 168, 150, 81, 192, 27,
185, 13, 124, 16, 250, 61, 113, 83, 82, 167, 214, 98, 42, 31, 136, 93, 236, 95, 173, 139, 197, 32, 189, 207, 145,
227, 117, 132, 224, 19, 176, 74, 191, 68, 216, 127, 208, 96, 0, 35, 172, 15, 29, 126, 48, 106, 174, 49, 220, 23,
148, 166, 97, 64, 105, 158, 25, 4, 3, 38, 72, 24, 91, 9, 239, 10, 20, 55, 26, 88, 143, 77, 133, 75, 223, 155, 90,
210, 164, 52, 242, 58, 238, 160, 125, 86, 85, 215, 182, 226, 251, 123, 187, 99, 112, 92, 147, 89, 203, 184, 76,
44, 101, 231
};
public LicenseKey() {
this.licenseDigits = new byte[11];
this.appNumber = 1;
this.serialNumber = 1;
this.numUsers = 1;
}
public String getSN(short serialNumber, short appNumber, short numUsers) {
byte[] digit = new byte[11];
for (int k = 0; k < 11; ++k) {
// digit[k] = 0;
this.licenseDigits[k] = 0;
}
// this.serialNumber = 0x6666;
// this.appNumber = 0x555;
// this.numUsers = 15;
this.serialNumber = serialNumber;
this.appNumber = appNumber;
this.numUsers = numUsers;
long scrambleCode = this.scrambleCode(this.serialNumber + this.appNumber + this.numUsers);
// System.out.println("Scramble Code: " + scrambleCode);
digit[0] = (byte) ((this.serialNumber / 4096));
digit[1] = (byte) ((this.serialNumber % 4096) / 256);
digit[2] = (byte) ((this.serialNumber % 256) / 16);
digit[3] = (byte) ((this.serialNumber % 16));
digit[4] = (byte) ((this.appNumber / 256));
digit[5] = (byte) ((this.appNumber % 256) / 16);
digit[6] = (byte) ((this.appNumber % 16));
digit[7] = (byte) this.numUsers;
digit[8] = this.getCRCDigits(digit, 8L);
digit[9] = (byte) (scrambleCode / 16);
digit[10] = (byte) (scrambleCode % 16);
this.getScrambleDigits(this.licenseDigits, digit, scrambleCode);
licenseDigits[9] = digit[9];
licenseDigits[10] = digit[10];
String sn = convertLicenseDigitsToString();
return sn;
}
private String convertLicenseDigitsToString( ) {
String ss = "RBCDSWGHTJKLMNVP";
StringBuffer sn = new StringBuffer();
for(int i=0; i<11; i++) {
sn.append(ss.charAt(this.licenseDigits[i]));
}
return sn.toString();
}
private void getScrambleDigits(final byte[] scramDigits, final byte[] clearDigits, final long scrambleCode) {
for (int k = 0; k < 9; ++k) {
final long scramblebyte = 9L * scrambleCode + k;
final byte nthrandombyte = this.getNthRandomByte(scramblebyte);
scramDigits[k] = (byte)(clearDigits[k] + nthrandombyte & 0xF);
}
}
private byte getNthRandomByte(final long num) {
final long kTwoToThe31MinusOne = 2147483647L;
//final long kTwoToThe31MinusTwo = kTwoToThe31MinusOne - 1L;
final long kByteScaling = (kTwoToThe31MinusOne - 1L) / 256L;
final long kTwoToThe32Plus1 = kTwoToThe31MinusOne * 2L + 2L;
final long kSevenToThe5 = 16807L;
long r = 1L;
for (int k = 0; k < num + 12L; ++k) {
r *= kSevenToThe5;
r = (int)r;
if (r < 0L) {
r += kTwoToThe32Plus1;
}
r %= kTwoToThe31MinusOne;
}
final byte b = (byte)(r / kByteScaling);
return b;
}
private byte getCRCDigits(final byte[] digit, final long numToCRC) {
long crc = 0L;
for (int k = 0; k < (int)numToCRC; ++k) {
crc <<= 1;
if (crc > 15L) {
crc |= 0x1L;
}
crc += digit[k];
crc &= 0x0FL; // 4 bits
}
return (byte)crc;
}
private short scrambleCode(final long number) {
int scrambleIndex = (int)(number % 451);
return scrambleKeys[scrambleIndex];
}
}
直接在 Eclipse 中运行注册机,如下图所示,在 eclipse 中的控制台输出中显示了注册码。
注册码输入界面如下图所示:
分析完毕,算法组件见附件:
GAComponent.rar
(666.36 KB, 下载次数: 45)
免费评分
查看全部评分
本帖被以下淘专辑推荐:
· 学习及教程 | 主题: 1126, 订阅: 1118
· 实用工具 | 主题: 164, 订阅: 94
· 实用软件 | 主题: 62, 订阅: 25