Java Q&A
by Dave Angel and Andy Wilson

Listing One
public void build ( )
    {
        byte[] encryptedClasses = readClassFiles( );
        writeClassFile( m_destination, m_newName, encryptedClasses );
    }
    protected byte[] readClassFiles( )
    {
        CodePackerClassLoader cl = new CodePackerClassLoader();
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream( bout );
        for ( int i = 0; i < m_pFiles.length; i++ )
        {
            try
            {
                Method c = null;
                byte[] classBytes = cl.readClassFile ( m_pFiles[i], true );
                c = cl.getEncrypt( stripExtension ( m_pFiles[i] ) );
                if ( m_crypto != null )
                {
                    System.out.println ( "Encrypting something " + 
                                              classBytes.length );
                    classBytes = (byte[])m_crypto.invoke( null, 
                                              new Object[] { classBytes } );
                    System.out.println ( "Encrypted something " + 
                                               classBytes.length );
                }
                if ( c != null )
                {
                    System.out.println ( "Switching cryptography method" );
                    m_crypto = c; // switch to new crypto class
                    out.writeByte( 1 ); // crypto class
                } else if ( i == ( m_pFiles.length - 1 ) ) 
                {
                    // is this the last file in the list
                    // main class is always last class in list
                    out.writeByte( 2 ); 
                } else
                {
                    out.writeByte( 0 ); // do nothing special with the class
                }
                out.writeInt( classBytes.length);
                out.write( classBytes );
            } catch ( Exception e )
            {
                System.out.println ( e.getMessage() + " occured in build " );
            }
        }
        return bout.toByteArray();
    }
    protected String stripExtension( String className )
    {
        if ( className.endsWith( ".class" ) == true )
        {
            return className.substring(0, className.length() - 
                                                       ".class".length() );
        }
        return className;
    }
    public void writeClassFile ( String name, String newName, byte[] bytes )
    {
        try
        {
            RandomAccessFile inFile = new RandomAccessFile( name,"r" );
            RandomAccessFile outFile = new RandomAccessFile( newName, "rw" );
            // write magic number, major and minor version info
            int magic = inFile.readInt();
            int minor = inFile.readUnsignedShort();
            int major = inFile.readUnsignedShort();
            
            outFile.writeInt( magic );          
            outFile.writeShort( minor );    
            outFile.writeShort( major ); 
            
            // write the attribute table
            int constCount = inFile.readUnsignedShort();
            outFile.writeShort( constCount + 2 );
            
            int codePackerAttributeIndex = constCount;
            long[] offsetTable = new long[ codePackerAttributeIndex ] ;
            short j = 0;
            for ( j=0; j < (constCount-1); j++ )
            {
                int type = inFile.readUnsignedByte();
                outFile.writeByte( type );
                    
                switch ( type )
                {        
                case 1:// utf-8
                    int len = inFile.readUnsignedShort();
                    byte[] utf8bytes = new byte[len];
                    inFile.read ( utf8bytes );
                    //System.out.println ( (j+1) + " UTF8 " + 
                                                   new String(utf8bytes) );
                    outFile.writeShort( len );
                    outFile.write( utf8bytes );
                    break;
                case 3: // int
                case 4: // float; 
                    outFile.writeInt(inFile.readInt());
                    break;
                case 5: // long
                case 6: // double
                    outFile.writeLong(inFile.readLong());
                    j++;
                    break;
                case 7: // class
                case 8: // string
                    if ( type == 7 )
                    {
                        int tmp = inFile.readShort();
                        //System.out.println ( (j+1) + 
                                          " Class utf8 at index " + tmp );
                        offsetTable[ j+1 ] = outFile.getFilePointer();
                        outFile.writeShort ( tmp );
                    } else
                    {
                        outFile.writeShort(inFile.readShort());
                    }
                    break;
                case 9: // field
                case 10: // method
                case 11: // interface
                case 12: // name and type
                    outFile.writeInt(inFile.readInt());
                    break;
                default:
                    System.out.print ( "Unknown type" );
                    break;
                }
            }
            // write a new UTF8 object for the new class name
            int classNameIndex = j+1;
            outFile.writeByte( (byte) 1 );
            String tmpName = stripExtension ( newName );
            outFile.writeShort( tmpName.getBytes().length );
            outFile.write( tmpName.getBytes() );
            
            // write the type, length and new attribute info
            int customAttributeIndex = j+2;
            outFile.writeByte( (byte) 1 );
            outFile.writeShort( "CodePackerCustomAttribute".
                                                      getBytes().length );
            outFile.write( "CodePackerCustomAttribute".getBytes() );
            
            /* Attribute table is now written */
            // write access flags
            outFile.writeShort( inFile.readUnsignedShort() ); // access Flags
            
            // replace this index with classConstIndex to represent new name
            int thisIndex = inFile.readUnsignedShort(); // this index
            outFile.writeShort( thisIndex );        
            long currentPosition = outFile.getFilePointer();
            
            outFile.seek( offsetTable[ thisIndex ] );
            outFile.writeShort ( classNameIndex );
            outFile.seek ( currentPosition );
            
            // write the super class 
            outFile.writeShort( inFile.readUnsignedShort() );
                
            // write the interface table
            int interface_count = inFile.readUnsignedShort();
            byte[] interfaceBuffer = new byte[ interface_count * 2 ];
            inFile.read( interfaceBuffer );
            
            outFile.writeShort( interface_count );
            outFile.write ( interfaceBuffer );
                
            // write the field table
            writeFieldMethodTable( inFile, outFile );
            
            // write the method table 
            writeFieldMethodTable( inFile, outFile );
            
            // write attribute table
            long attributesOffset = outFile.getFilePointer();
            System.out.println ( "Attribute Offset at " + attributesOffset );
            int attributes_count = inFile.readUnsignedShort();
            outFile.writeShort ( attributes_count ); 
            
            // write exisiting attributes
            for ( int k = 0; k < attributes_count; k++ )
            {
                writeAttributes( inFile, outFile );
            }
            // write class data attributes
            int attributesAdded = writeAttributeRecords( outFile, 
                                           customAttributeIndex, bytes );
            // write attribute to indicate start of attribute table
            outFile.writeShort ( customAttributeIndex );
            outFile.writeInt ( 9 );
            outFile.writeByte ( (byte) 1 );
            outFile.writeLong ( attributesOffset );
            attributesAdded++;
            
            // set the attribute count with the new attributes           
            outFile.seek ( attributesOffset );
            outFile.writeShort( attributes_count + attributesAdded );
            System.out.println ( "attributes: " + ( attributes_count + 
                                                   attributesAdded ) ); 
        } catch ( Exception e )
        {
            e.printStackTrace();
        }      
    }


Listing Two
import java.util.*;
public class Encrypt extends Container
{
    public static int seed;   //note, it's really three bytes
    public static int magic;
    public static byte scrabble(byte value)
    {
        int temp = seed | (value<<24);
        temp *= magic;
        seed = temp & 0xffffff;
        temp >>>= 24;
        return (byte)temp;
    }
    public static byte[] encrypt(byte[] bytes)
    {
        System.out.println ( "Encrypt.encrypt() " + bytes.length );
        //Given an array of bytes, produce another array of bytes,
        // somewhat longer, that are an encoding of that array
        int size = bytes.length;
        byte[] buffer = new byte[size+3];
        seed = (int)(16000000.*java.lang.Math.random());
        magic = 653216881;
        for (int i=0; i<size; ++i)
        {
            buffer[i] = scrabble(bytes[i]);
        }
        buffer[size] = (byte)(seed>>16);
        buffer[size+1] = (byte)(seed>>8);
        buffer[size+2] = (byte)seed;
        return buffer;
    }
    public static byte[] decrypt(byte[] bytes)
    {
        //Given an encoded array of bytes, produce another array of bytes,
        // somewhat shorter, that are the original set
        magic = 1278932113;
        int size = bytes.length-3;
        byte[] buffer = new byte[size];

        seed = bytes[size]&255;
        seed = (seed<<8) | (bytes[size+1]&255);
        seed = (seed<<8) | (bytes[size+2]&255);

        for (int i=size-1; i>=0; --i)
        {
            buffer[i] = scrabble(bytes[i]);
        }
        return buffer;
    }
};




6


