2
2
3
3
import org .apache .commons .lang3 .tuple .Pair ;
4
4
5
- import java .io .ByteArrayOutputStream ;
6
- import java .io .DataInputStream ;
7
- import java .io .File ;
8
- import java .io .InputStream ;
5
+ import javax .crypto .Cipher ;
6
+ import javax .crypto .CipherOutputStream ;
7
+ import javax .crypto .SecretKey ;
8
+ import javax .crypto .SecretKeyFactory ;
9
+ import javax .crypto .spec .SecretKeySpec ;
10
+ import java .io .*;
9
11
import java .net .URL ;
10
12
import java .net .URLClassLoader ;
11
- import java .util .HashMap ;
12
- import java .util .Map ;
13
+ import java .security .NoSuchAlgorithmException ;
14
+ import java .security .spec .InvalidKeySpecException ;
15
+ import java .util .*;
13
16
import java .util .stream .Collectors ;
14
17
import java .util .stream .Stream ;
15
18
16
19
17
20
public class EncryptClassLoader extends URLClassLoader {
18
21
private ClassLoader superloader ;
19
- private byte [] key = null ;
20
22
private Map <String , Class > loadedClassPool = new HashMap <>();
21
23
private Map <String , Pair <String , String >> encryptKeyMap = new HashMap <>();
22
24
private String machineCode ;
23
25
private Long expireTs ;
26
+ private static final String DEFAULTALGORITHM = "AES" ;
27
+ private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding" ;
28
+ public static final byte [] m_datapadding = {0x7F };
29
+ public static final byte [] m_ending = {0x00 };
30
+ private static final String [] CONFUSEDSTRS = {"i" , "I" , "l" , "O" , "0" , "1" };
24
31
32
+ public static final byte [] mzHeader = {0x4D , 0x5A , 0x50 , 0x00 , 0x02 , 0x00 , 0x00 , 0x00 , 0x04 , 0x00 , 0x0f , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
25
33
public EncryptClassLoader (URL [] urls , ClassLoader parent ) {
26
34
super (urls , parent );
27
35
this .superloader = parent ;
@@ -36,13 +44,13 @@ public EncryptClassLoader(ClassLoader loader) {
36
44
37
45
private void init () {
38
46
try (DataInputStream dInput = new DataInputStream (superloader .getResourceAsStream ("META-INF/config.bin" ))) {
39
- dInput .read (CipherUtil . mzHeader );
47
+ dInput .read (mzHeader );
40
48
byte [] paddingbyte = new byte [1 ];
41
49
dInput .read (paddingbyte );
42
50
checkPadding (paddingbyte );
43
51
byte [] machineCodeByte = new byte [16 ];
44
52
dInput .read (machineCodeByte );
45
- machineCode = CipherUtil . bytesToHexString (machineCodeByte );
53
+ machineCode = bytesToHexString (machineCodeByte );
46
54
expireTs = dInput .readLong ();
47
55
checkExpire ();
48
56
int classNameBytesLen = 0 ;
@@ -52,13 +60,13 @@ private void init() {
52
60
classNameBytesLen = dInput .readInt ();
53
61
byte [] classNameEncryptBytes = new byte [classNameBytesLen ];
54
62
dInput .read (classNameEncryptBytes );
55
- byte [] decryptClassNameBytes = CipherUtil . decryptByte (classNameEncryptBytes , machineCode .getBytes ());
63
+ byte [] decryptClassNameBytes = decryptByte (classNameEncryptBytes , machineCode .getBytes ());
56
64
String className = new String (decryptClassNameBytes );
57
- String confusedName = CipherUtil . decodeConfusedNameByCode (String .valueOf (dInput .readLong ()));
65
+ String confusedName = decodeConfusedNameByCode (String .valueOf (dInput .readLong ()));
58
66
int keyLength = dInput .readInt ();
59
67
byte [] keyEncryptByte = new byte [keyLength ];
60
68
dInput .read (keyEncryptByte );
61
- byte [] keyDecryptByte = CipherUtil . decryptByte (keyEncryptByte , machineCode .getBytes ());
69
+ byte [] keyDecryptByte = decryptByte (keyEncryptByte , machineCode .getBytes ());
62
70
String key = new String (keyDecryptByte );
63
71
encryptKeyMap .put (className , Pair .of (confusedName , key ));
64
72
}
@@ -68,7 +76,7 @@ private void init() {
68
76
}
69
77
70
78
private void checkPadding (byte [] paddingbyte ) {
71
- if (paddingbyte != CipherUtil . m_datapadding ) {
79
+ if (paddingbyte != m_datapadding ) {
72
80
System .err .println ("jar package corrupted" );
73
81
System .exit (1 );
74
82
}
@@ -126,9 +134,9 @@ protected synchronized Class<?> loadClass(String name, boolean resolve)
126
134
InputStream in = loadEncryptDataStream (encryptKeyMap .get (classname ).getKey ());
127
135
if (in != null ) {
128
136
ByteArrayOutputStream out = new ByteArrayOutputStream ();
129
- CipherUtil . decryptByte (machineCode .getBytes (), in , out );
137
+ decryptByte (machineCode .getBytes (), in , out );
130
138
byte [] bytes = out .toByteArray ();
131
- byte [] decrypt =CipherUtil . decryptByte (bytes ,encryptKeyMap .get (classname ).getValue ().getBytes ());
139
+ byte [] decrypt =decryptByte (bytes ,encryptKeyMap .get (classname ).getValue ().getBytes ());
132
140
if (bytes != null ) {
133
141
clazz = defineClass (name , decrypt , 0 , bytes .length );
134
142
}
@@ -149,82 +157,75 @@ protected synchronized Class<?> loadClass(String name, boolean resolve)
149
157
return null ;
150
158
}
151
159
152
- private Class loadByEncryptClass (String name ) {
153
- return null ;
154
160
161
+ private InputStream loadEncryptDataStream (String confusedName ) {
162
+ return superloader .getResourceAsStream ("META-INF/ext/" + confusedName );
155
163
}
156
164
157
- public byte [] encrptClass (String name ) {
158
- try {
159
- return CipherUtil .encryptByte (loadClassData (name ), key );
160
- } catch (Exception ex ) {
161
- ex .printStackTrace ();
165
+
166
+ private static void doCopy (InputStream is , OutputStream os ) throws IOException {
167
+ byte [] bytes = new byte [64 ];
168
+ int numBytes ;
169
+ while ((numBytes = is .read (bytes )) != -1 ) {
170
+ os .write (bytes , 0 , numBytes );
162
171
}
163
- return null ;
172
+ os .flush ();
173
+ os .close ();
174
+ is .close ();
164
175
}
165
-
166
- public byte [] decrptClass (String name ) {
176
+ private static byte [] decryptByte (byte [] bytes , byte [] key ) {
167
177
try {
168
- return CipherUtil .decryptByte (loadClassData (name ), key );
178
+ Cipher cipher = Cipher .getInstance (DEFAULT_CIPHER_ALGORITHM );
179
+ cipher .init (Cipher .DECRYPT_MODE , toKey (key ));
180
+ return cipher .doFinal (bytes );
169
181
} catch (Exception ex ) {
170
182
ex .printStackTrace ();
171
183
}
172
184
return null ;
173
185
}
174
-
175
- private byte [] loadClassData (String name ) {
176
- try {
177
- String tmpname = name ;
178
- if (tmpname .contains ("." )) {
179
- tmpname = tmpname .replaceAll ("\\ ." , "/" );
180
- }
181
- if (!tmpname .endsWith (".class" )) {
182
- tmpname += ".class" ;
183
- }
184
- InputStream in = superloader .getResourceAsStream (tmpname );
185
- byte [] bytes = new byte [in .available ()];
186
- in .read (bytes );
187
- return bytes ;
188
- } catch (Exception ex ) {
189
- System .out .println (name );
190
- ex .printStackTrace ();
191
- return null ;
186
+ private static String bytesToHexString (byte [] bytes ) {
187
+ StringBuilder builder = new StringBuilder ();
188
+ for (byte b : bytes ) {
189
+ builder .append (String .format ("%02X" , b ));
192
190
}
191
+ return builder .toString ();
193
192
}
194
193
195
- private InputStream loadEncryptDataStream (String confusedName ) {
196
- return superloader .getResourceAsStream ("META-INF/ext/" + confusedName );
197
- }
198
-
199
- private InputStream loadClassDataStream (String name ) {
200
- InputStream in = null ;
194
+ private static void decryptByte (byte [] key , InputStream is , OutputStream os ) {
201
195
try {
202
- String tmpname = name ;
203
- if (tmpname .contains ("." )) {
204
- tmpname = tmpname .replaceAll ("\\ ." , "/" );
205
- }
206
- if (!tmpname .endsWith (".class" )) {
207
- tmpname += ".class" ;
208
- }
209
- in = superloader .getResourceAsStream (tmpname );
196
+ Cipher cipher = Cipher .getInstance (DEFAULTALGORITHM );
197
+ cipher .init (Cipher .DECRYPT_MODE , toKey (key ));
198
+ //CipherInputStream cis=new CipherInputStream(is, cipher);
199
+ CipherOutputStream out = new CipherOutputStream (os , cipher );
200
+ doCopy (is , out );
210
201
} catch (Exception ex ) {
211
- System .out .println (name );
212
202
ex .printStackTrace ();
213
-
214
203
}
215
- return in ;
216
204
}
217
-
218
- public static void main (String [] args ) {
219
-
220
- }
221
-
222
- public byte [] getKey () {
223
- return key ;
224
- }
225
-
226
- public void setKey (byte [] key ) {
227
- this .key = key ;
205
+ private static SecretKey toKey (byte [] keybyte ) throws NoSuchAlgorithmException , InvalidKeySpecException {
206
+ SecretKeySpec key = new SecretKeySpec (keybyte , DEFAULTALGORITHM );
207
+ SecretKeyFactory skf = SecretKeyFactory .getInstance (DEFAULTALGORITHM );
208
+ return skf .generateSecret (key );
209
+ }
210
+ private static List <String > getConfusedName (int length , Random random ) {
211
+ StringBuilder builder = new StringBuilder ();
212
+ StringBuilder builder1 = new StringBuilder ();
213
+ for (int i = 0 ; i < length ; i ++) {
214
+ int pos = random .nextInt (CONFUSEDSTRS .length );
215
+ builder1 .append (pos );
216
+ builder .append (CONFUSEDSTRS [pos ]);
217
+ }
218
+ List <String > retList = new ArrayList <>();
219
+ retList .add (builder .toString ());
220
+ retList .add (builder1 .toString ());
221
+ return retList ;
222
+ }
223
+ private static String decodeConfusedNameByCode (String code ){
224
+ StringBuilder builder = new StringBuilder ();
225
+ for (char input :code .toCharArray ()){
226
+ builder .append (CONFUSEDSTRS [Integer .parseInt (String .valueOf (input ))]);
227
+ }
228
+ return builder .toString ();
228
229
}
229
230
230
231
0 commit comments