PageRenderTime 110ms CodeModel.GetById 45ms app.highlight 53ms RepoModel.GetById 3ms app.codeStats 0ms

/projects/sandmark-3.4/src/sandmark/obfuscate/scalarmerger/ScalarMerger.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 459 lines | 225 code | 81 blank | 153 comment | 39 complexity | 21cbb49fd190ca5a5b94d177225cc209 MD5 | raw file
  1package sandmark.obfuscate.scalarmerger;
  2
  3
  4
  5/**
  6 * Provides a method obfuscator that combines two int variables
  7 * into a single long, making access to either more confusing.
  8 *
  9 * @author Gregg Townsend
 10 *    (<a href="mailto:gmt@cs.arizona.edu">gmt@cs.arizona.edu</a>)
 11 * @version 1.0, August 29, 2003
 12 */
 13
 14public class ScalarMerger extends sandmark.obfuscate.MethodObfuscator {
 15
 16
 17
 18   private static boolean DEBUG = false;
 19
 20
 21
 22   /**
 23    * Applies this obfuscation to a single method.
 24    */
 25   public void apply(sandmark.program.Method meth) throws Exception {
 26
 27      if (meth.isInterface() || meth.isAbstract() || meth.isNative()) {
 28         return;                        // nothing to do
 29      }
 30
 31      // Choose two local int variables (ix1 and ix2) for merging.
 32
 33      int[] vscores = tally(meth);      // tally integer accesses
 34      skipArgs(meth, vscores);          // invalidate entries of incoming args
 35      int ix1 = best(vscores);          // get best integer candidate
 36      int ix2 = best(vscores);          // get next-best candidate
 37      if (ix1 < 0 || ix2 < 0) {         // if couldn't find two candidates
 38         return;
 39      }
 40
 41      // Allocate a new long variable and initilize it to 1,
 42      // effectively initializing the two ints to 0 and 1.
 43      // The actual value doesn't matter because the translation
 44      // of the old code will reinitialize each half separately.
 45
 46      int lx = vscores.length;          // index of new long variable
 47
 48      org.apache.bcel.generic.InstructionList il = meth.getInstructionList();
 49      org.apache.bcel.generic.InstructionHandle ih =
 50         il.insert(new org.apache.bcel.generic.LCONST(1));
 51      il.append(ih, new org.apache.bcel.generic.LSTORE(lx));
 52
 53      // trace actions, if enabled
 54
 55      if (DEBUG) {
 56         System.err.println("ScalarMerger: (" + ix1 + "," + ix2 + ")->" + lx +
 57            " in " + meth.getClassName() + "." + meth.getName());
 58      }
 59
 60      // Replace instructions that operate on the ints
 61
 62      fixup(il, ix1, ix2, lx);
 63
 64      // Clean up and exit.
 65
 66      meth.setInstructionList(il);
 67      meth.setMaxLocals();
 68      meth.setMaxStack();
 69   }
 70
 71
 72
 73   /**
 74    * Checks all local variable instructions in an instruction list and
 75    * tallies variable usage.
 76    * Returns an array of scores corresponding to local variables.
 77    * A positive score counts the number of integer accesses.
 78    * A negative score indicates a variable that is used for a
 79    * non-integer value.
 80    */
 81   private int[] tally(sandmark.program.Method meth) {
 82
 83      int n = meth.getMaxLocals();
 84      int[] vscores = new int[n];
 85
 86      org.apache.bcel.generic.Instruction[] ilist =
 87         meth.getInstructionList().getInstructions();
 88
 89      for (int i = 0; i < ilist.length; i++) {
 90         org.apache.bcel.generic.Instruction ins = ilist[i];
 91         if (ins instanceof org.apache.bcel.generic.LocalVariableInstruction) {
 92            int v = ((org.apache.bcel.generic.LocalVariableInstruction)(ins))
 93               .getIndex();
 94            if (ins instanceof org.apache.bcel.generic.ILOAD
 95            ||  ins instanceof org.apache.bcel.generic.IINC
 96            ||  ins instanceof org.apache.bcel.generic.ISTORE) {
 97               if (vscores[v] >= 0) {
 98                  vscores[v]++;
 99               }
100            } else {
101               vscores[v] = -1;
102            }
103         }
104      }
105      return vscores;
106   }
107
108
109
110   /**
111    * Invalidates tally entries that correspond to method arguments.
112    */
113   private void skipArgs(sandmark.program.Method meth, int[] vscores) {
114      org.apache.bcel.generic.Type[] types = meth.getArgumentTypes();
115      int n = meth.isStatic() ? 0 : 1;
116      for (int i = 0; i < types.length; i++) {
117         n += types[i].getSize();
118      }
119      for (int i = 0; i < n; i++) {
120         vscores[i] = -1;
121      }
122   }
123
124
125
126   /**
127    * Scans an array of tallies and returns the index of the most active
128    * integer local variable after resetting that variable's score to zero.
129    * Ties are broken in favor of lower numbered variables.
130    * Returns -1 if there is no local variable with a positive score.
131    */
132   private int best(int[] vscores) {
133      int leader = -1;
134      int peak = 0;
135      for (int i = 0; i < vscores.length; i++) {
136         if (vscores[i] > peak) {
137            leader = i;
138            peak = vscores[i];
139         }
140      }
141      if (leader >= 0) {
142         vscores[leader] = 0;
143      }
144      return leader;
145   }
146
147
148
149   /**
150    *  Scans the instruction list and replaces instructions
151    *  that operate on the chosen integer locals.
152    */
153   private void fixup(
154         org.apache.bcel.generic.InstructionList il, int ix1, int ix2, int lx) {
155
156      org.apache.bcel.generic.InstructionHandle[] hlist =
157         il.getInstructionHandles();
158      for (int i = 0; i < hlist.length; i++) {
159         org.apache.bcel.generic.InstructionHandle ih = hlist[i];
160         org.apache.bcel.generic.Instruction ins = ih.getInstruction();
161         if (ins instanceof org.apache.bcel.generic.LocalVariableInstruction) {
162            int ivx = 
163               ((org.apache.bcel.generic.LocalVariableInstruction)ins)
164               .getIndex();
165            boolean lefthalf = (ivx == ix1);
166            boolean righthalf = (ivx == ix2);
167            if (lefthalf || righthalf) {
168               if (ins instanceof org.apache.bcel.generic.ILOAD) {
169                  fixLoad(il, ih, lx, lefthalf);
170               } else if (ins instanceof org.apache.bcel.generic.ISTORE) {
171                  fixStore(il, ih, lx, lefthalf);
172               } else if (ins instanceof org.apache.bcel.generic.IINC) {
173                  fixIncr(il, ih, lx, lefthalf);
174               } else {
175                  throw new java.lang.Error("non-int access to local " + ivx);
176               }
177            }
178         }
179      }
180   }
181
182
183
184   /**
185    *  Replaces an ILOAD with a load from one half of lx.
186    *  The generated sequence is as follows:
187    *  <PRE>
188    *
189    *   left half   right half  comments
190    *   ---------   ----------  ---------------------------------------------
191    *   LLOAD lx    LLOAD lx    load combined value
192    *               BIPUSH 32   push shift count
193    *               LSHR        position to low 32 bits
194    *   L2I         L2I         convert low 32 bits to int
195    *   
196    *  </PRE>
197    */
198   private void fixLoad(
199         org.apache.bcel.generic.InstructionList il,
200         org.apache.bcel.generic.InstructionHandle ih,
201         int lx, boolean lefthalf) {
202
203      ih.setInstruction(new org.apache.bcel.generic.LLOAD(lx));
204      if (lefthalf) {
205         ih = il.append(ih, new org.apache.bcel.generic.BIPUSH((byte) 32));
206         ih = il.append(ih, new org.apache.bcel.generic.LSHR());
207      }
208      ih = il.append(ih, new org.apache.bcel.generic.L2I());
209   }
210
211
212
213   /**
214    *  Replace an ISTORE with a store into one half of lx.
215    *  The generated sequence is as follows:
216    *  <PRE>
217    *
218    *   left half   right half  comments
219    *   ---------   ----------  ---------------------------------------------
220    *   I2L         I2L         convert the new int to a long
221    *   BIPUSH 32               push shift count
222    *   LSHL                    shift into position
223    *   LLOAD lx    LLOAD lx    load the old combined value
224    *   DUP2_X2     DUP2_X2     hide a second copy below the new value
225    *   LXOR        LXOR        compute bitwise difference to new value
226    *   LCONST_1    LCONST_1    load long constant 1
227    *   LNEG        LNEG        convert to all-ones mask
228    *   BIPUSH 32   BIPUSH 32   push shift count
229    *   LSHL   !=!= LUSHR       shift to make half ones, half zeroes
230    *   LAND        LAND        select the change bits for the correct half
231    *   LXOR        LXOR        change the bits in the combined value
232    *   LSTORE lx   LSTORE lx   store new combined value
233    *   
234    *  </PRE>
235    */
236   private void fixStore(
237         org.apache.bcel.generic.InstructionList il,
238         org.apache.bcel.generic.InstructionHandle ih,
239         int lx, boolean lefthalf) {
240
241      ih.setInstruction(new org.apache.bcel.generic.I2L());
242      if (lefthalf) {
243         ih = il.append(ih, new org.apache.bcel.generic.BIPUSH((byte)32));
244         ih = il.append(ih, new org.apache.bcel.generic.LSHL());
245      }
246      ih = il.append(ih, new org.apache.bcel.generic.LLOAD(lx));
247      ih = il.append(ih, new org.apache.bcel.generic.DUP2_X2());
248      ih = il.append(ih, new org.apache.bcel.generic.LXOR());
249      ih = il.append(ih, new org.apache.bcel.generic.LCONST(1));
250      ih = il.append(ih, new org.apache.bcel.generic.LNEG());
251      ih = il.append(ih, new org.apache.bcel.generic.BIPUSH((byte)32));
252      if (lefthalf) {
253         ih = il.append(ih, new org.apache.bcel.generic.LSHL());
254      } else {
255         ih = il.append(ih, new org.apache.bcel.generic.LUSHR());
256      }
257      ih = il.append(ih, new org.apache.bcel.generic.LAND());
258      ih = il.append(ih, new org.apache.bcel.generic.LXOR());
259      ih = il.append(ih, new org.apache.bcel.generic.LSTORE(lx));
260   }
261   /*
262    *   This fixStore code was also tested, but it is longer and less confusing:
263    *   I2L         I2L         convert store value to long
264    *   BIPUSH 32   BIPUSH 32   push shift count
265    *   LSHL        LSHL        shift int to top of long
266    *               BIPUSH 32   push shift count again
267    *               LUSHR       shift back down (clears I2D sign bit extension)
268    *   LCONST_1    LCONST_1    load long constant 1
269    *   LNEG        LNEG        convert to all-ones mask
270    *   BIPUSH 32   BIPUSH 32   push shift count
271    *   LUSHR  !=!= LSHL        shift to make half ones, half zeroes
272    *   LLOAD lx    LLOAD lx    load old combined value
273    *   LAND        LAND        preserve old value of other variable
274    *   LADD        LADD        combine with new value for this variable
275    *   LSTORE lx   LSTORE lx   store combined long value
276    */
277
278
279
280   /**
281    *  Replaces an IINC with an increment of half of lx.
282    *  The generated sequence is as follows:
283    *  <PRE>
284    *
285    *   left half   right half  comments
286    *   ---------   ----------  ---------------------------------------------
287    *   LLOAD lx    LLOAD lx    load combined value
288    *               DUP2        duplicate for later use
289    *               DUP2        duplicate for later use
290    *   BIPUSH n    BIPUSH n    push increment value
291    *   I2L         I2L         convert to long
292    *   BIPUSH 32               push shift count
293    *   LSHL                    position increment value
294    *   LADD        LADD        add increment to long value
295    *               LXOR        compute bitwise difference vs. old value
296    *               LCONST_1    load long constant 1
297    *               LNEG        convert to all-ones mask
298    *               BIPUSH 32   push shift count
299    *               LUSHR       shift to fill top half with zeroes
300    *               LAND        isolate changes to lower half
301    *               LXOR        apply changes to original value
302    *   LSTORE lx   LSTORE lx   store combined long value
303    *   
304    *  </PRE>
305    */
306   private void fixIncr(
307         org.apache.bcel.generic.InstructionList il,
308         org.apache.bcel.generic.InstructionHandle ih,
309         int lx, boolean lefthalf) {
310
311      int k =
312         ((org.apache.bcel.generic.IINC)(ih.getInstruction())).getIncrement();
313      ih.setInstruction(new org.apache.bcel.generic.LLOAD(lx));
314      if (!lefthalf) {
315         ih = il.append(ih, new org.apache.bcel.generic.DUP2());
316         ih = il.append(ih, new org.apache.bcel.generic.DUP2());
317      }
318      ih = il.append(ih, new org.apache.bcel.generic.BIPUSH((byte)k));
319      ih = il.append(ih, new org.apache.bcel.generic.I2L());
320      if (lefthalf) {
321         ih = il.append(ih, new org.apache.bcel.generic.BIPUSH((byte)32));
322         ih = il.append(ih, new org.apache.bcel.generic.LSHL());
323      }
324      ih = il.append(ih, new org.apache.bcel.generic.LADD());
325      if (!lefthalf) {
326         ih = il.append(ih, new org.apache.bcel.generic.LXOR());
327         ih = il.append(ih, new org.apache.bcel.generic.LCONST(1));
328         ih = il.append(ih, new org.apache.bcel.generic.LNEG());
329         ih = il.append(ih, new org.apache.bcel.generic.BIPUSH((byte)32));
330         ih = il.append(ih, new org.apache.bcel.generic.LUSHR());
331         ih = il.append(ih, new org.apache.bcel.generic.LAND());
332         ih = il.append(ih, new org.apache.bcel.generic.LXOR());
333      }
334      ih = il.append(ih, new org.apache.bcel.generic.LSTORE(lx));
335   }
336
337
338
339   /**
340    * Returns "Scalar Merger", the short name of this algorithm.
341    */
342   public java.lang.String getShortName() {
343      return "Merge Local Integers";
344   }
345
346
347
348   /**
349    * Returns "Scalar Merger", the long name of this algorithm.
350    */
351   public java.lang.String getLongName() {
352      return "Scalar Merger";
353   }
354
355
356
357   /**
358    * Returns an HTML description of this obfuscator's function.
359    */
360   public java.lang.String getAlgHTML() {
361      return
362         "<HTML><BODY>" +
363         "Scalar Merger combines two int variables into a single long" +
364         "variable, making access to either more confusing.\n" +
365         "<TABLE>" +
366         "<TR><TD>" +
367         "Author: <a href =\"mailto:gmt@cs.arizona.edu\">Gregg Townsend</a> " +
368         "</TR></TD>" +
369         "</TABLE>" +
370         "</BODY></HTML>";
371
372   }
373
374
375
376   /**
377    * Returns the URL within the source tree
378    * of an HTML file describing this obfuscator.
379    */
380   public java.lang.String getAlgURL() {
381      return "sandmark/obfuscate/scalarmerger/doc/help.html";
382   }
383
384
385
386   /**
387    * Returns the name of the author of this obfuscator.
388    */
389   public java.lang.String getAuthor() {
390      return "Gregg Townsend";
391   }
392
393
394
395   /**
396    * Returns the e-mail address of the author of this obfuscator.
397    */
398   public java.lang.String getAuthorEmail() {
399      return "gmt@cs.arizona.edu";
400   }
401
402
403
404   /**
405    * Returns a brief description of this obfuscator.
406    */
407   public java.lang.String getDescription() {
408      return
409         "Scalar Merger combines two int variables into a single long " +
410         "variable, making access to either more confusing.";
411   }
412
413   
414
415   /**
416    * Returns a list of modification properties characterizing
417    * this obfuscator.
418    */
419   public sandmark.config.ModificationProperty[] getMutations() {
420      return new sandmark.config.ModificationProperty[] {
421         sandmark.config.ModificationProperty.I_ADD_LOCAL_VARIABLES,
422         sandmark.config.ModificationProperty.I_CHANGE_LOCAL_VARIABLES,
423         sandmark.config.ModificationProperty.I_CHANGE_METHOD_BODIES,
424         sandmark.config.ModificationProperty.I_ADD_METHOD_CODE,
425         sandmark.config.ModificationProperty.I_MODIFY_METHOD_CODE,
426         sandmark.config.ModificationProperty.I_REMOVE_METHOD_CODE,
427         sandmark.config.ModificationProperty.PERFORMANCE_DEGRADE_MED,
428      };
429   }
430
431
432
433   /**
434    * Applies the obfuscation to every method in the jar file
435    * given as the command argument.
436    *
437    * <P> Usage:  java sandmark.obfuscate.scalarmerger.ScalarMerger file.jar
438    *
439    * <P> Writes:  CHANGED.jar
440    */
441   public static void main(String[] args) throws java.lang.Exception {
442      sandmark.program.Application app =
443         new sandmark.program.Application(args[0]);
444      sandmark.obfuscate.scalarmerger.ScalarMerger obfuscator =
445         new sandmark.obfuscate.scalarmerger.ScalarMerger();
446      java.util.Iterator itr = app.classes();
447      while (itr.hasNext()) {
448         sandmark.program.Class cls = (sandmark.program.Class) itr.next();
449         sandmark.program.Method[] methods = cls.getMethods();
450         for (int i = 0;  i < methods.length; i++) {
451            obfuscator.apply(methods[i]);
452         }
453      }
454      app.save("CHANGED.jar");
455   }
456
457
458
459} // class ScalarMerger