@@ -25,7 +25,7 @@ public static void scan(Logger logger, ZipFile file) {
2525 .filter (entry -> entry .getName ().endsWith (".class" ))
2626 .anyMatch (entry -> {
2727 try {
28- return scanClass (readAllBytes (file .getInputStream (entry )));
28+ return scanClass (getByteArray (file .getInputStream (entry )));
2929 } catch (IOException e ) {
3030 throw new RuntimeException (e );
3131 }
@@ -45,66 +45,149 @@ public static void scan(Logger logger, ZipFile file) {
4545 logger .info ("Fractureiser not detected in {}" , file .getName ());
4646 }
4747
48+ private static byte [] getByteArray (InputStream inputStream ) throws IOException {
49+ ByteArrayOutputStream buffer = new ByteArrayOutputStream ();
50+
51+ int nRead ;
52+ byte [] data = new byte [16384 ];
53+
54+ while ((nRead = inputStream .read (data , 0 , data .length )) != -1 ) {
55+ buffer .write (data , 0 , nRead );
56+ }
57+
58+ return buffer .toByteArray ();
59+ }
60+
4861 private static final AbstractInsnNode [] SIG1 = new AbstractInsnNode [] {
4962 new TypeInsnNode (NEW , "java/lang/String" ),
5063 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
5164 new TypeInsnNode (NEW , "java/lang/String" ),
5265 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
5366 new MethodInsnNode (INVOKESTATIC , "java/lang/Class" , "forName" , "(Ljava/lang/String;)Ljava/lang/Class;" ),
54- new MethodInsnNode (INVOKEVIRTUAL , "java/lang/Class" , "getConstructor" , "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;" ),
67+ new MethodInsnNode (INVOKEVIRTUAL , "java/lang/Class" , "getConstructor" ,
68+ "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;" ),
5569 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
5670 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
5771 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
58- new MethodInsnNode (INVOKESPECIAL , "java/net/URL" , "<init>" , "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)V" ),
59- new MethodInsnNode (INVOKEVIRTUAL , "java/lang/reflect/Constructor" , "newInstance" , "([Ljava/lang/Object;)Ljava/lang/Object;" ),
60- new MethodInsnNode (INVOKESTATIC , "java/lang/Class" , "forName" , "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;" ),
72+ new MethodInsnNode (INVOKESPECIAL , "java/net/URL" , "<init>" ,
73+ "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)V" ),
74+ new MethodInsnNode (INVOKEVIRTUAL , "java/lang/reflect/Constructor" , "newInstance" ,
75+ "([Ljava/lang/Object;)Ljava/lang/Object;" ),
76+ new MethodInsnNode (INVOKESTATIC , "java/lang/Class" , "forName" ,
77+ "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;" ),
6178 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
62- new MethodInsnNode (INVOKEVIRTUAL , "java/lang/Class" , "getMethod" , "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;" ),
63- new MethodInsnNode (INVOKEVIRTUAL , "java/lang/reflect/Method" , "invoke" , "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" ),
79+ new MethodInsnNode (INVOKEVIRTUAL , "java/lang/Class" , "getMethod" ,
80+ "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;" ),
81+ new MethodInsnNode (INVOKEVIRTUAL , "java/lang/reflect/Method" , "invoke" ,
82+ "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" ),
6483 };
6584
6685 private static final AbstractInsnNode [] SIG2 = new AbstractInsnNode [] {
6786 new MethodInsnNode (INVOKESTATIC , "java/lang/Runtime" , "getRuntime" , "()Ljava/lang/Runtime;" ),
6887 new MethodInsnNode (INVOKESTATIC , "java/util/Base64" , "getDecoder" , "()Ljava/util/Base64$Decoder;" ),
69- new MethodInsnNode (INVOKEVIRTUAL , "java/lang/String" , "INVOKEVIRTUAL" , "(Ljava/lang/String;)Ljava/lang/String;" ),//TODO:FIXME: this might not be in all of them
88+ new MethodInsnNode (INVOKEVIRTUAL , "java/lang/String" , "concat" ,
89+ "(Ljava/lang/String;)Ljava/lang/String;" ), // TODO:FIXME: this might not be in all of them
7090 new MethodInsnNode (INVOKEVIRTUAL , "java/util/Base64$Decoder" , "decode" , "(Ljava/lang/String;)[B" ),
7191 new MethodInsnNode (INVOKESPECIAL , "java/lang/String" , "<init>" , "([B)V" ),
7292 new MethodInsnNode (INVOKEVIRTUAL , "java/io/File" , "getPath" , "()Ljava/lang/String;" ),
7393 new MethodInsnNode (INVOKEVIRTUAL , "java/lang/Runtime" , "exec" , "([Ljava/lang/String;)Ljava/lang/Process;" ),
7494 };
7595
96+ // The IP
97+ private static final AbstractInsnNode [] SIG3 = new AbstractInsnNode [] {
98+ new IntInsnNode (BIPUSH , 56 ),
99+ new InsnNode (BASTORE ),
100+ new InsnNode (DUP ),
101+ new InsnNode (ICONST_1 ),
102+ new IntInsnNode (BIPUSH , 53 ),
103+ new InsnNode (BASTORE ),
104+ new InsnNode (DUP ),
105+ new InsnNode (ICONST_2 ),
106+ new IntInsnNode (BIPUSH , 46 ),
107+ new InsnNode (BASTORE ),
108+ new InsnNode (DUP ),
109+ new InsnNode (ICONST_3 ),
110+ new IntInsnNode (BIPUSH , 50 ),
111+ new InsnNode (BASTORE ),
112+ new InsnNode (DUP ),
113+ new InsnNode (ICONST_4 ),
114+ new IntInsnNode (BIPUSH , 49 ),
115+ new InsnNode (BASTORE ),
116+ new InsnNode (DUP ),
117+ new InsnNode (ICONST_5 ),
118+ new IntInsnNode (BIPUSH , 55 ),
119+ new InsnNode (BASTORE ),
120+ new InsnNode (DUP ),
121+ new IntInsnNode (BIPUSH , 6 ),
122+ new IntInsnNode (BIPUSH , 46 ),
123+ new InsnNode (BASTORE ),
124+ new InsnNode (DUP ),
125+ new IntInsnNode (BIPUSH , 7 ),
126+ new IntInsnNode (BIPUSH , 49 ),
127+ new InsnNode (BASTORE ),
128+ new InsnNode (DUP ),
129+ new IntInsnNode (BIPUSH , 8 ),
130+ new IntInsnNode (BIPUSH , 52 ),
131+ new InsnNode (BASTORE ),
132+ new InsnNode (DUP ),
133+ new IntInsnNode (BIPUSH , 9 ),
134+ new IntInsnNode (BIPUSH , 52 ),
135+ new InsnNode (BASTORE ),
136+ new InsnNode (DUP ),
137+ new IntInsnNode (BIPUSH , 10 ),
138+ new IntInsnNode (BIPUSH , 46 ),
139+ new InsnNode (BASTORE ),
140+ new InsnNode (DUP ),
141+ new IntInsnNode (BIPUSH , 11 ),
142+ new IntInsnNode (BIPUSH , 49 ),
143+ new InsnNode (BASTORE ),
144+ new InsnNode (DUP ),
145+ new IntInsnNode (BIPUSH , 12 ),
146+ new IntInsnNode (BIPUSH , 51 ),
147+ new InsnNode (BASTORE ),
148+ new InsnNode (DUP ),
149+ new IntInsnNode (BIPUSH , 13 ),
150+ new IntInsnNode (BIPUSH , 48 )
151+ };
152+
76153 private static boolean same (AbstractInsnNode a , AbstractInsnNode b ) {
77154 if (a instanceof TypeInsnNode ) {
78- return ((TypeInsnNode )a ).desc .equals (((TypeInsnNode )b ).desc );
155+ TypeInsnNode aa = (TypeInsnNode ) a ;
156+ return aa .desc .equals (((TypeInsnNode ) b ).desc );
79157 }
80158 if (a instanceof MethodInsnNode ) {
81- return ((MethodInsnNode )a ).owner .equals (((MethodInsnNode )b ).owner ) && ((MethodInsnNode )a ).desc .equals (((MethodInsnNode )b ).desc );
159+ MethodInsnNode aa = (MethodInsnNode ) a ;
160+ return aa .owner .equals (((MethodInsnNode ) b ).owner )
161+ && aa .name .equals (((MethodInsnNode ) b ).name )
162+ && aa .desc .equals (((MethodInsnNode ) b ).desc );
82163 }
83164 if (a instanceof InsnNode ) {
84165 return true ;
85166 }
86167 throw new IllegalArgumentException ("TYPE NOT ADDED" );
87168 }
88169
89- private static boolean scanClass (byte [] clazz ) {
170+ public static boolean scanClass (byte [] clazz ) {
90171 ClassReader reader = new ClassReader (clazz );
91172 ClassNode node = new ClassNode ();
92173 try {
93174 reader .accept (node , 0 );
94175 } catch (Exception e ) {
95- return false ;//Yes this is very hacky but should never happen with valid clasees
176+ return false ;// Yes this is very hacky but should never happen with valid clasees
96177 }
97178 for (MethodNode method : node .methods ) {
98179 {
99- //Method 1, this is a hard detect, if it matches this it is 100% chance infected
180+ // Method 1, this is a hard detect, if it matches this it is 100% chance
181+ // infected
100182 boolean match = true ;
101183 int j = 0 ;
102184 for (int i = 0 ; i < method .instructions .size () && j < SIG1 .length ; i ++) {
103- if (method .instructions .get (i ).getOpcode () == -1 ) {
185+ AbstractInsnNode insn = method .instructions .get (i );
186+ if (insn .getOpcode () == -1 ) {
104187 continue ;
105188 }
106- if (method . instructions . get ( i ) .getOpcode () == SIG1 [j ].getOpcode ()) {
107- if (!same (method . instructions . get ( i ) , SIG1 [j ++])) {
189+ if (insn .getOpcode () == SIG1 [j ].getOpcode ()) {
190+ if (!same (insn , SIG1 [j ++])) {
108191 match = false ;
109192 break ;
110193 }
@@ -119,18 +202,19 @@ private static boolean scanClass(byte[] clazz) {
119202 }
120203
121204 {
122- //Method 2, this is a near hard detect, if it matches this it is 95% chance infected
205+ // Method 2, this is a near hard detect, if it matches this it is 95% chance
206+ // infected
123207 boolean match = false ;
124- outer :
125- for (int q = 0 ; q < method .instructions .size (); q ++) {
208+ outer : for (int q = 0 ; q < method .instructions .size (); q ++) {
126209 int j = 0 ;
127210 for (int i = q ; i < method .instructions .size () && j < SIG2 .length ; i ++) {
128- if (method .instructions .get (i ).getOpcode () != SIG2 [j ].getOpcode ()) {
211+ AbstractInsnNode insn = method .instructions .get (i );
212+ if (insn .getOpcode () != SIG2 [j ].getOpcode ()) {
129213 continue ;
130214 }
131215
132- if (method . instructions . get ( i ) .getOpcode () == SIG2 [j ].getOpcode ()) {
133- if (!same (method . instructions . get ( i ) , SIG2 [j ++])) {
216+ if (insn .getOpcode () == SIG2 [j ].getOpcode ()) {
217+ if (!same (insn , SIG2 [j ++])) {
134218 continue outer ;
135219 }
136220 }
@@ -144,34 +228,48 @@ private static boolean scanClass(byte[] clazz) {
144228 return true ;
145229 }
146230 }
147- }
148- return false ;
149- }
150231
151- // Java 8 equivalent of InputStream.readAllBytes()
152- private static byte [] readAllBytes (InputStream inputStream ) throws IOException {
153- final int bufLen = 1024 ;
154- byte [] buf = new byte [bufLen ];
155- int readLen ;
156- IOException exception = null ;
232+ // Method 3, this looks for a byte array with the IP. This is a likely match.
233+ {
234+ boolean match = false ;
235+ // where we're looking in the SIG3 array
236+ int pos = 0 ;
237+ for (int i = 0 ; i < method .instructions .size (); i ++) {
238+ if (pos == SIG3 .length ) {
239+ break ;
240+ }
241+ AbstractInsnNode insn = method .instructions .get (i );
242+ if (insn .getOpcode () == -1 ) {
243+ continue ;
244+ }
245+ if (insn .getOpcode () == SIG3 [pos ].getOpcode ()) {
246+ // the opcode matches
247+
248+ if (SIG3 [pos ].getType () == AbstractInsnNode .INT_INSN ) {
249+ // check if operand matches
250+ IntInsnNode iInsn = (IntInsnNode ) insn ;
251+ IntInsnNode sigInsn = (IntInsnNode ) SIG3 [pos ];
252+ if (iInsn .operand == sigInsn .operand ) {
253+ // operands match
254+ match = true ;
255+ pos ++;
256+ }
257+ } else {
258+ // this is a regular InsnNode; just match
259+ match = true ;
260+ pos ++;
261+ }
262+ } else {
263+ match = false ;
264+ pos = 0 ;
265+ }
266+ }
157267
158- try {
159- ByteArrayOutputStream outputStream = new ByteArrayOutputStream ();
160-
161- while ((readLen = inputStream .read (buf , 0 , bufLen )) != -1 )
162- outputStream .write (buf , 0 , readLen );
163-
164- return outputStream .toByteArray ();
165- } catch (IOException e ) {
166- exception = e ;
167- throw e ;
168- } finally {
169- if (exception == null ) inputStream .close ();
170- else try {
171- inputStream .close ();
172- } catch (IOException e ) {
173- exception .addSuppressed (e );
268+ if (match ) {
269+ return true ;
270+ }
174271 }
175272 }
273+ return false ;
176274 }
177275}
0 commit comments