public class CodeBlock {
	/**the index of the method that a code block belongs to*/
	public int methodId;
	/**the offset for the start of a code block in DEX file*/
	public long startAddress;
	/**the offset for the end of a code block in DEX file*/
	public long endAddress;
	/**the index of a code block*/
	public int codeBlockId;
	/**The instruction list for a code block*/
	public ArrayList <Instruction> instructionList= new ArrayList <Instruction>();
	/**
	 * print out the instruction list.
	 * */
	public void printInsList(){
		System.out.println("Code block:	");
		for(Instruction ins: instructionList){
			ins.printFields();
		}
	}
} public class CodeComponent extends DataComponent{
	/**list of code blocks in DEX file*/
	public ArrayList <CodeBlock> codeBlockList= new ArrayList <CodeBlock>();
	
	
} public class Instruction {
	/**the offset of an instruction in DEX file*/
	public long address;
	/**the index number of an instruction in the instruction list of a code block */
	public int instructionId;
	/**the hex format of the opcode*/
	public int op;
	/**the string format of a opcode*/
	public String opcode;
	/**the length of an instruction in DEX file*/
	public int length;
	/**
	 * The Code block Id this instruction belongs to
	 **/
	public int codeBlockId;
	private boolean hasRegister=false;
	/**
	 * True if the instruction has an operand
	 * */
	public boolean hasOperand=false;
	/**
	 * which named as same as in google's DEX format web site,
	 * which is also the explanation for the opcode
	 * */
	public String opcodeSuffix=null;
	/**
	 * the meaning of the operand of an instruction
	 * */
	public String operandSuffix=null;
	
	protected int registerNum;
	protected int regStartByte;
	protected int regLength;
	protected int oprStartByte;
	protected int oprLength;
	
	/**the content of the operand*/
	private long operand;
	private int[] registerList;// 
	private CodeComponentBuilder ccb;
	public Instruction(){
		
	}
	/**A construction method that calls the method in father class 'MethodComponent', 
	 * which let an instance of Instruction class finding the DEX file in memory
	 * @param ccb DEX file loaded in memory
	 * */
	public Instruction(CodeComponentBuilder ccb){
		this.ccb=ccb;
	}
	/**print fields of an instruction*/
	public void printFields(){
		if(opcodeSuffix!=null){
			System.out.print("	"+this.address+"	"+this.opcodeSuffix+"	");
		}else{
			System.out.print("	"+this.address+"	"+this.opcode+"	");
		}
		if (this.hasRegister==true){
			for(int i=0;i<this.registerList.length;i++){
				if (i!=0){
					System.out.print(",");
				}
				System.out.print("v"+this.registerList[i]);
				
				
			}
		}
		if (this.hasOperand==true){
			System.out.print("	"+this.operand+"	#"+this.operandSuffix);
			instantiateOperand();
		}
		System.out.println("");
	}
	/**
	 * @param staticValueType is the type of the static value for your search, 
	 * which currently support "STRING".
	 * 
	 * @param registerNumber the number (start with 0) of the register you want to search, which may out of the bounds
	 * */
	public String staticBackTrace(String staticValueType,int registerNumber){
		int regNmu=-1;				
		boolean found=false;
		String staticValue="";
		if(this.registerList.length>registerNumber){
			regNmu=this.registerList[registerNumber];
		}else{
			try{
				throw new FileNotFoundException("WARNING:	the registerNum indicated for back tracing is out of Bounds. "
					+ "\n	ERROR occured by instruction address: "+this.address);
			}
			catch (Exception e){
				e.printStackTrace();
			}
		}
	

				for(int i=this.instructionId-1;i>=0;i--){
					
					Instruction ins=ccb.codeComponent.codeBlockList.get(this.codeBlockId).instructionList.get(i);
					
					
					//if ... (control flow opcode)then jump to .. .
					if (this.hasRegister()&&regNmu!=-1&&ins.registerList!=null){
						for(int reg:ins.registerList){
							
							if (regNmu==reg){
								found=true;
							}
							
						}
						if(found==true){
							
								if(staticValueType.equals("STRING")){//searching string type static 
									
									if (ins.opcode.startsWith("const-string")){
										
										staticValue=this.ccb.mb.ssb.ss.stringElementList.get((int)ins.operand).stringContent;	
										return staticValue;
										
									}
									
								}																							
								break;
						}
					}						
				}
		

		return staticValue;

	}
	/**get the list filled with registers that is order by destination register first*/
	protected void parseOperand(){
		
			ccb.readBinary(this.address+oprStartByte, oprLength, "ADDRESS");
			operand=(long)ccb.tempValue;
		
	}

	private void vAvB(){
		this.hasRegister=true;
		this.registerList=new int[2];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int) (ccb.tempValue-ccb.tempValue/16*16);
		registerList[1]=(int)ccb.tempValue/16;
	}
	private void vAA(){
		this.hasRegister=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
	}
	private void vAAvBBvCC(){
		this.hasRegister=true;
		this.registerList=new int[3];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 1, "ADDRESS");
		registerList[1]=(int)ccb.tempValue;
		ccb.readBinary(this.address+3, 1, "ADDRESS");
		registerList[2]=(int)ccb.tempValue;
	}
	private void vAAvBBCC(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[2];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 1, "ADDRESS");
		registerList[1]=(int)ccb.tempValue;
		ccb.readBinary(this.address+3, 1, "ADDRESS");
		this.operand=ccb.tempValue;
	}
	private void vAAvBBBB(){
		this.hasRegister=true;
		this.registerList=new int[2];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		registerList[1]=(int)ccb.tempValue;
	}
	private void vAAAAvBBBB(){
		this.hasRegister=true;
		this.registerList=new int[2];
		ccb.readBinary(this.address+1, 2, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+3, 2, "ADDRESS");
		registerList[1]=(int)ccb.tempValue;
	}
	private void vAB(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		this.operand=(int)ccb.tempValue/16;
		registerList[0]=(int) (ccb.tempValue-ccb.tempValue/16*16);
	}
	private void vAABBBB(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		this.operand=(int)ccb.tempValue;
	}
	private void vAA8B(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		int low=(int)ccb.tempValue;
		ccb.readBinary(this.address+4, 2, "ADDRESS");
		int high=(int) ccb.tempValue;
		
		this.operand=(high<<16)+low;		
	}
	private void vAABBBB0000(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		this.operand=ccb.tempValue<<16;
	}
	private void vAA16B(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		int low8=(int) ccb.tempValue;
		ccb.readBinary(this.address+4, 2, "ADDRESS");
		long low16=ccb.tempValue<<16+low8;
		ccb.readBinary(this.address+6, 2, "ADDRESS");
		int high8=(int) ccb.tempValue;
		ccb.readBinary(this.address+8, 2, "ADDRESS");
		long high16=ccb.tempValue<<16+high8;
		this.operand=high16<<32+low16;
	}
	private void vAABBBB12(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[1];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int)ccb.tempValue;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		this.operand=ccb.tempValue<<48;
	}
	private void vAvBCCCC(){
		this.hasRegister=true;
		this.hasOperand=true;
		this.registerList=new int[2];
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		registerList[0]=(int) (ccb.tempValue-ccb.tempValue/16*16);
		registerList[1]=(int)ccb.tempValue/16;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		this.operand=(int)ccb.tempValue;
	}
	private void AA(){
		this.hasOperand=true;
		ccb.readBinary(this.address+1, 1, "ADDRESS");
		this.operand=ccb.tempValue;
	}
	private void AAAA(){
		this.hasOperand=true;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		this.operand=ccb.tempValue;
	}
	private void AAAAAAAA(){
		this.hasOperand=true;
		ccb.readBinary(this.address+2, 2, "ADDRESS");
		int low=(int)ccb.tempValue;
		ccb.readBinary(this.address+4, 2, "ADDRESS");
		int high=(int) ccb.tempValue;
		this.operand=high<<16+low;	
	}
	private void vCvDvEvFvGBBBB(){

		
		this.regStartByte=1;
		this.regLength=1;
		ccb.readBinary(this.address+this.regStartByte, this.regLength, "ADDRESS");
		if(ccb.tempValue!=0){
			this.registerNum =(int) (ccb.tempValue/16);
			int fifthReg=(int)(ccb.tempValue-ccb.tempValue/16*16);
			this.hasRegister=true;
			this.regStartByte=4;
			this.registerList =new int[this.registerNum];
			ccb.readBinary(this.address+this.regStartByte,regLength,"ADDRESS");
			long low=(int)ccb.tempValue;
			ccb.readBinary(this.address+this.regStartByte+1,regLength,"ADDRESS");
			long high= ccb.tempValue;
			switch (this.registerNum){
			case 5:
					this.registerList[4] = fifthReg;
			case 4: 
					this.registerList[3] = (int) (high/16);
			case 3: 
					this.registerList[2] = (int) (high-high/16*16);
			case 2: 
					this.registerList[1] = (int) (low/16);
			case 1: 
					this.registerList[0] = (int) (low-low/16*16);
					
			default:break;
			}
													
	
		}else {
			this.registerNum=0;
			
		}

		this.hasOperand=true;
		this.oprStartByte=2;
		this.oprLength=2;
		this.parseOperand();
	}
	private void vCCCCvNNNNBBBB(){
		this.regStartByte=1;
		this.regLength=1;
		ccb.readBinary(this.address+this.regStartByte, this.regLength, "ADDRESS");
		if(ccb.tempValue!=0){
			this.hasRegister=true;
			this.registerNum =(int) (ccb.tempValue);
			this.registerList =new int[this.registerNum];
			ccb.readBinary(this.address+4, 2, "ADDRESS");
			int startNum=(int) (ccb.tempValue);
			for(int i=0;i<registerNum;i++){
				this.registerList[i]=startNum;
				startNum++;
			}
		}else ccb.tempValue=0;
		
		this.hasOperand=true;
		this.oprStartByte=2;
		this.oprLength=2;
		this.parseOperand();
	}
	//
	public int identifyToken(int op){
		switch(op){
			case 0x1:
				this.length= 2;
				this.opcode="move";
				vAvB();				
				return this.length;
			case 0x2:
				this.length= 4;
				this.opcode="move/from16";
				vAAvBBBB();
				return this.length;
			case 0x3:
				this.length= 5;
				this.opcode="move/16";
				vAAAAvBBBB();
				return this.length;
			case 0x4:
				this.length= 2;
				this.opcode="move-wide";
				vAvB();	
				
				return this.length;
			case 0x5:
				this.length= 4;
				this.opcode="move-wide/from16";
				vAAvBBBB();	
				return this.length;
			case 0x6:
				this.length= 5;
				this.opcode="move-wide/16";				
				vAAAAvBBBB();	
				return this.length;
			case 0x7:
				this.length= 2;
				this.opcode="move-object";
				vAvB();
				return this.length;
			case 0x8:
				this.length= 4;
				this.opcode="move-object/from16";
				vAAvBBBB();	
				return this.length;
			case 0x9:
				this.length= 5;
				this.opcode="move-object/16";
				vAAAAvBBBB();	
				return this.length;
			case 0xa:
				this.length= 2;
				this.opcode="move-result";
				vAA();
				return this.length;
			case 0xb:
				this.length= 2;
				this.opcode="move-result-wide";
				vAA();
				return this.length;
			case 0xc:
				this.length= 2;
				this.opcode="move-result-object";
				vAA();
				return this.length;
			case 0xd:
				this.length= 2;
				this.opcode="move-exception";
				vAA();
				return this.length;
			case 0xe:
				this.length= 2;
				this.opcode="return-void";
				return this.length;
			case 0xf:
				this.length= 2;
				this.opcode="return";
				vAA();
				return this.length;
			case 0x10:
				this.length= 2;
				this.opcode="return-wide";
				vAA();
				return this.length;
			case 0x11:
				this.length= 2;
				this.opcode="return-object";
				vAA();
				return this.length;
			case 0x12:
				this.length= 2;
				this.opcode="const/4";
				vAB();
				this.operandSuffix="int(4 bits)";
				return this.length;
			case 0x13:
				this.length= 4;
				this.opcode="const/16";
				vAABBBB();
				this.operandSuffix="int(16 bits)";
				return this.length;
			case 0x14:
				this.length= 6;
				this.opcode="const";
				vAA8B();
				this.operandSuffix="constant(32-bit)";
				return this.length;
			case 0x15:
				this.length= 4;
				this.opcode="const/high16";
				vAABBBB0000();
				this.operandSuffix="int(32 bits)";
				return this.length;
			case 0x16:
				this.length= 4;
				this.opcode="const-wide/16";
				vAABBBB();
				this.operandSuffix="int(16 bits)";
				return this.length;
			case 0x17:
				this.length= 6;
				this.opcode="const-wide/32";
				vAA8B();
				this.operandSuffix="int(32 bits)";
				return this.length;
			case 0x18:
				this.length= 10;
				this.opcode="const-wide";
				vAA16B();
				this.operandSuffix="int(64 bits)";
				return this.length;
			case 0x19:
				this.length= 4;
				this.opcode="const-wide/high16";				
				vAABBBB12();
				this.operandSuffix="int(64 bits)";
				return this.length;
			case 0x1a:
				this.length= 4;
				this.opcode="const-string";
				vAABBBB();
				this.operandSuffix="string id";
				return this.length;
			case 0x1b:
				this.length= 6;
				this.opcode="const-string/jumbo";
				vAA8B();
				this.operandSuffix="string id";
				return this.length;
			case 0x1c:
				this.length= 4;
				this.opcode="const-class";
				vAABBBB();
				this.operandSuffix="type id";
				return this.length;
			case 0x1d:
				this.length= 2;
				this.opcode="monitor-enter";
				vAA();
				return this.length;
			case 0x1e:
				this.length= 2;
				this.opcode="monitor-exit";
				vAA();
				return this.length;
			case 0x1f:
				this.length= 4;
				this.opcode="check-cast";
				vAABBBB();
				this.operandSuffix="type id";
				return this.length;
			case 0x20:
				this.length= 4;
				this.opcode="instance-of";
				vAvBCCCC();
				this.operandSuffix="type id";
				return this.length;
			case 0x21:
				this.length= 2;
				this.opcode="array-length";
				vAvB();
				return this.length;
			case 0x22:
				this.length= 4;
				this.opcode="new-instance";
				vAABBBB();
				this.operandSuffix="type id";
				return this.length;
			case 0x23:
				this.length= 4;
				this.opcode="new-array";
				vAvBCCCC();
				this.operandSuffix="type id";
				return this.length;
			case 0x24:
				this.length= 6;
				this.opcode="filled-new-array";
				vCvDvEvFvGBBBB();
				this.operandSuffix="type id";
				return this.length;
			case 0x25:
				this.length= 6;
				this.opcode="filled-new-array/range";
				vCCCCvNNNNBBBB();
				this.operandSuffix="type id";
				return this.length;
			case 0x26:
				this.length= 6;
				this.opcode="fill-array-data";				
				vAA8B();
				this.operandSuffix="array-data-payload offset";
				 if (this.operand>=32768){
					 this.operand-=65536;
				 }
				 this.operand=this.address+(this.operand*2);
				return this.length;
			case 0x27:
				this.length= 2;
				this.opcode="throw";
				vAA();
				return this.length;
			case 0x28:
				this.length= 2;
				this.opcode="goto";
				AA();
				this.operandSuffix="goto offset";
				 if (this.operand>=8){
					 this.operand-=16;
				 }
				 this.operand=this.address+(this.operand*2);
				return this.length;
			case 0x29:
				this.length= 4;
				this.opcode="goto/16";
				AAAA();
				this.operandSuffix="goto offset";
				 if (this.operand>=128){
					 this.operand-=256;
				 }
				 this.operand=this.address+(this.operand*2);
				return this.length;
			case 0x2a:
				this.length= 6;
				this.opcode="goto/32";
				AAAAAAAA();
				this.operandSuffix="goto offset";
				 if (this.operand>=32768){
					 this.operand-=65536;
				 }
				 this.operand=this.address+(this.operand*2);
				return this.length;
			case 0x2b:
				this.length= 6;
				this.opcode="packed-switch";
				vAA8B();
				this.operandSuffix="packed-switch-payload offset";
				if (this.operand>=32768){
					 this.operand-=65536;
				 }
				this.operand=this.address+(this.operand*2);
				return this.length;
			case 0x2c:
				this.length= 6;
				this.opcode="sparse-switch";
				vAA8B();
				this.operandSuffix="sparse-switch-payload offset";
				if (this.operand>=32768){
					 this.operand-=65536;
				 }
				this.operand=this.address+(this.operand*2);
				return this.length;
			default:
				if					((0x3e<=op && op<=0x43) | op==0x73 | (0x79<=op && op<=0x7a) | op>=0xe3){
					this.length= 2;
					this.opcode="UNUSED";
					return this.length;
				}else				if(0x2d<=op && op<=0x31){
					this.length= 4;
					this.opcode="cmpkind";
					switch (op){
						case 0x2d:this.opcodeSuffix="cmpl-float";break;
						case 0x2e:this.opcodeSuffix="cmpg-float";break;
						case 0x2f:this.opcodeSuffix="cmpl-double";break;
						case 0x30:this.opcodeSuffix="cmpg-double";break;
						case 0x31:this.opcodeSuffix="cmp-long";break;
					}
					vAAvBBvCC();
					return this.length;
				}else 				if(0x32<=op && op<=0x37){
					this.length= 4;
					this.opcode="if-test";
					switch (op){
						case 0x32: this.opcodeSuffix="if-eq";break;
						case 0x33: this.opcodeSuffix="if-ne";break;
						case 0x34: this.opcodeSuffix="if-lt";break;
						case 0x35: this.opcodeSuffix="if-ge";break;
						case 0x36: this.opcodeSuffix="if-gt";break;
						case 0x37: this.opcodeSuffix="if-le";break;
					}
					vAvBCCCC();
					this.operandSuffix="goto offset";
					 if (this.operand>=128){
						 this.operand-=256;						 
					 }
					 this.operand=this.address+(this.operand*2);
					return this.length;
				}else				if(0x38<=op && op<=0x3d){
					this.length= 4;
					this.opcode="if-testz";
					switch (op){
					case 0x38: this.opcodeSuffix="if-eqz";break;
					case 0x39: this.opcodeSuffix="if-nez";break;
					case 0x3a: this.opcodeSuffix="if-ltz";break;
					case 0x3b: this.opcodeSuffix="if-gez";break;
					case 0x3c: this.opcodeSuffix="if-gtz";break;
					case 0x3d: this.opcodeSuffix="if-lez";break;
				}
					vAABBBB();
					this.operandSuffix="goto offset";
					 if (this.operand>=128){
						 this.operand-=256;						 
					 }
					 this.operand=this.address+(this.operand*2);
					return this.length;
				}else				if(0x44<=op && op<=0x51){
					this.length= 4;
					this.opcode="arrayop";
					switch(op){
						case 0x44: this.opcodeSuffix="aget";break;
						case 0x45: this.opcodeSuffix="aget-wide";break;
						case 0x46: this.opcodeSuffix="aget-object";break;
						case 0x47: this.opcodeSuffix="aget-boolean";break;
						case 0x48: this.opcodeSuffix="aget-byte";break;
						case 0x49: this.opcodeSuffix="aget-char";break;
						case 0x4a: this.opcodeSuffix="aget-short";break;
						case 0x4b: this.opcodeSuffix="aput";break;
						case 0x4c: this.opcodeSuffix="aput-wide";break;
						case 0x4d: this.opcodeSuffix="aput-object";break;
						case 0x4e: this.opcodeSuffix="aput-boolean";break;
						case 0x4f: this.opcodeSuffix="aput-byte";break;
						case 0x50: this.opcodeSuffix="aput-char";break;
						case 0x51: this.opcodeSuffix="aput-short";break;
					}
					vAAvBBvCC();
					return this.length;
				}else				if(0x52<=op && op<=0x5f){
					this.length= 4;
					this.opcode="iinstanceop";
					switch(op){
					case 0x52: this.opcodeSuffix="iget";break;
					case 0x53: this.opcodeSuffix="iget-wide";break;
					case 0x54: this.opcodeSuffix="iget-object";break;
					case 0x55: this.opcodeSuffix="iget-boolean";break;
					case 0x56: this.opcodeSuffix="iget-byte";break;
					case 0x57: this.opcodeSuffix="iget-char";break;
					case 0x58: this.opcodeSuffix="iget-short";break;
					case 0x59: this.opcodeSuffix="iput";break;
					case 0x5a: this.opcodeSuffix="iput-wide";break;
					case 0x5b: this.opcodeSuffix="iput-object";break;
					case 0x5c: this.opcodeSuffix="iput-boolean";break;
					case 0x5d: this.opcodeSuffix="iput-byte";break;
					case 0x5e: this.opcodeSuffix="iput-char";break;
					case 0x5f: this.opcodeSuffix="iput-short";break;
					}
					vAvBCCCC();
					this.operandSuffix="field id";
					return this.length;
				}else				if(0x60<=op && op<=0x6d){
					this.length= 4;
					this.opcode="sstaticop";
					switch(op){
						case 0x60: this.opcodeSuffix="sget";break;
						case 0x61: this.opcodeSuffix="sget-wide";break;
						case 0x62: this.opcodeSuffix="sget-object";break;
						case 0x63: this.opcodeSuffix="sget-boolean";break;
						case 0x64: this.opcodeSuffix="sget-byte";break;
						case 0x65: this.opcodeSuffix="sget-char";break;
						case 0x66: this.opcodeSuffix="sget-short";break;
						case 0x67: this.opcodeSuffix="sput";break;
						case 0x68: this.opcodeSuffix="sput-wide";break;
						case 0x69: this.opcodeSuffix="sput-object";break;
						case 0x6a: this.opcodeSuffix="sput-boolean";break;
						case 0x6b: this.opcodeSuffix="sput-byte";break;
						case 0x6c: this.opcodeSuffix="sput-char";break;
						case 0x6d: this.opcodeSuffix="sput-short";break;
					}
					vAABBBB();
					this.operandSuffix= "field id";
					return this.length;
				}else				if(0x6e<=op && op<=0x72){//
					this.length= 6;
					this.opcode="invoke-kind";
					switch(op){
						case 0x6e: this.opcodeSuffix="invoke-virtual";break;
						case 0x6f: this.opcodeSuffix="invoke-super";break;
						case 0x70: this.opcodeSuffix="invoke-direct";break;
						case 0x71: this.opcodeSuffix="invoke-static";break;
						case 0x72: this.opcodeSuffix="invoke-interface";break;
					}
					vCvDvEvFvGBBBB();
									this.operandSuffix="method id";
					return this.length;
				}else				if(0x74<=op && op<=0x78){//
					this.length= 6;
					this.opcode="invoke-kind/range";
					switch(op){
						case 0x74: this.opcodeSuffix="invoke-virtual/range";break;
						case 0x75: this.opcodeSuffix="invoke-super/range";break;
						case 0x76: this.opcodeSuffix="invoke-direct/range";break;
						case 0x77: this.opcodeSuffix="invoke-static/range";break;
						case 0x78: this.opcodeSuffix="invoke-interface/range";break;
					
					}
					vCCCCvNNNNBBBB();
					this.operandSuffix="method id";
					return this.length;
				}else				if(0x7b<=op && op<=0x8f){
					this.length= 2;
					this.opcode="unop";
					switch(op){
						case 0x7b: this.opcodeSuffix="neg-int";break;
						case 0x7c: this.opcodeSuffix="not-int";break;
						case 0x7d: this.opcodeSuffix="neg-long";break;
						case 0x7e: this.opcodeSuffix="not-long";break;
						case 0x7f: this.opcodeSuffix="neg-float";break;
						case 0x80: this.opcodeSuffix="neg-double";break;
						case 0x81: this.opcodeSuffix="int-to-long";break;
						case 0x82: this.opcodeSuffix="int-to-float";break;
						case 0x83: this.opcodeSuffix="int-to-double";break;
						case 0x84: this.opcodeSuffix="long-to-int";break;
						case 0x85: this.opcodeSuffix="long-to-float";break;
						case 0x86: this.opcodeSuffix="long-to-double";break;
						case 0x87: this.opcodeSuffix="float-to-int";break;
						case 0x88: this.opcodeSuffix="float-to-long";break;
						case 0x89: this.opcodeSuffix="float-to-double";break;
						case 0x8a: this.opcodeSuffix="double-to-int";break;
						case 0x8b: this.opcodeSuffix="double-to-long";break;
						case 0x8c: this.opcodeSuffix="double-to-float";break;
						case 0x8d: this.opcodeSuffix="int-to-byte";break;
						case 0x8e: this.opcodeSuffix="int-to-char";break;
						case 0x8f: this.opcodeSuffix="int-to-short";break;
					}
					
					vAvB();
					return this.length;
				}else				if(0x90<=op && op<=0xaf){
					this.length= 4;
					this.opcode="binop";
					switch(op){
						case 0x90: this.opcodeSuffix="add-int";break;
						case 0x91: this.opcodeSuffix="sub-int";break;
						case 0x92: this.opcodeSuffix=" mul-int";break;
						case 0x93: this.opcodeSuffix="div-int";break;
						case 0x94: this.opcodeSuffix="rem-int";break;
						case 0x95: this.opcodeSuffix="and-int";break;
						case 0x96: this.opcodeSuffix="or-int";break;
						case 0x97: this.opcodeSuffix="xor-int";break;
						case 0x98: this.opcodeSuffix="shl-int";break;
						case 0x99: this.opcodeSuffix="shr-int";break;
						case 0x9a: this.opcodeSuffix="ushr-int";break;
						case 0x9b: this.opcodeSuffix="add-long";break;
						case 0x9c: this.opcodeSuffix="sub-long";break;
						case 0x9d: this.opcodeSuffix="mul-long";break;
						case 0x9e: this.opcodeSuffix="div-long";break;
						case 0x9f: this.opcodeSuffix="rem-long";break;
						case 0xa0: this.opcodeSuffix="and-long";break;
						case 0xa1: this.opcodeSuffix="or-long";break;
						case 0xa2: this.opcodeSuffix="xor-long";break;
						case 0xa3: this.opcodeSuffix="shl-long";break;
						case 0xa4: this.opcodeSuffix="shr-long";break;
						case 0xa5: this.opcodeSuffix="ushr-long";break;
						case 0xa6: this.opcodeSuffix="add-float";break;
						case 0xa7: this.opcodeSuffix="sub-float";break;
						case 0xa8: this.opcodeSuffix="mul-float";break;
						case 0xa9: this.opcodeSuffix="div-float";break;
						case 0xaa: this.opcodeSuffix="rem-float";break;
						case 0xab: this.opcodeSuffix="add-double";break;
						case 0xac: this.opcodeSuffix="sub-double";break;
						case 0xad: this.opcodeSuffix="mul-double";break;
						case 0xae: this.opcodeSuffix="div-double";break;
						case 0xaf: this.opcodeSuffix="rem-double";break;
					}
					vAAvBBvCC();
					return this.length;
				}else				if(0xb0<=op && op<=0xcf){
					this.length= 2;
					this.opcode="binop/2addr";
					switch(op){
						case 0xb0: this.opcodeSuffix="add-int/2addr";break;
						case 0xb1: this.opcodeSuffix="sub-int/2addr";break;
						case 0xb2: this.opcodeSuffix="mul-int/2addr";break;
						case 0xb3: this.opcodeSuffix="div-int/2addr";break;
						case 0xb4: this.opcodeSuffix="rem-int/2addr";break;
						case 0xb5: this.opcodeSuffix="and-int/2addr";break;
						case 0xb6: this.opcodeSuffix="or-int/2addr";break;
						case 0xb7: this.opcodeSuffix="xor-int/2addr";break;
						case 0xb8: this.opcodeSuffix="shl-int/2addr";break;
						case 0xb9: this.opcodeSuffix="shr-int/2addr";break;
						case 0xba: this.opcodeSuffix="ushr-int/2addr";break;
						case 0xbb: this.opcodeSuffix="add-long/2addr";break;
						case 0xbc: this.opcodeSuffix="sub-long/2addr";break;
						case 0xbd: this.opcodeSuffix="mul-long/2addr";break;
						case 0xbe: this.opcodeSuffix="div-long/2addr";break;
						case 0xbf: this.opcodeSuffix="rem-long/2addr";break;
						case 0xc0: this.opcodeSuffix="and-long/2addr";break;
						case 0xc1: this.opcodeSuffix="or-long/2addr";break;
						case 0xc2: this.opcodeSuffix="xor-long/2addr";break;
						case 0xc3: this.opcodeSuffix="shl-long/2addr";break;
						case 0xc4: this.opcodeSuffix="shr-long/2addr";break;
						case 0xc5: this.opcodeSuffix="ushr-long/2addr";break;
						case 0xc6: this.opcodeSuffix="add-float/2addr";break;
						case 0xc7: this.opcodeSuffix="sub-float/2addr";break;
						case 0xc8: this.opcodeSuffix="mul-float/2addr";break;
						case 0xc9: this.opcodeSuffix="div-float/2addr";break;
						case 0xca: this.opcodeSuffix="rem-float/2addr";break;
						case 0xcb: this.opcodeSuffix="add-double/2addr";break;
						case 0xcc: this.opcodeSuffix="sub-double/2addr";break;
						case 0xcd: this.opcodeSuffix="mul-double/2addr";break;
						case 0xce: this.opcodeSuffix="div-double/2addr";break;
						case 0xcf: this.opcodeSuffix="rem-double/2addr";break;
					}
					vAvB();
					return this.length;
				}else 				if(0xd0<=op && op<=0xd7){
					this.length= 4;
					this.opcode="binop/lit16";
					switch(op){
						case 0xd0: this.opcodeSuffix="add-int/lit16";break;
						case 0xd1: this.opcodeSuffix="rsub-int";break;
						case 0xd2: this.opcodeSuffix="mul-int/lit16";break;
						case 0xd3: this.opcodeSuffix="div-int/lit16";break;
						case 0xd4: this.opcodeSuffix="rem-int/lit16";break;
						case 0xd5: this.opcodeSuffix="and-int/lit16";break;
						case 0xd6: this.opcodeSuffix="or-int/lit16";break;
						case 0xd7: this.opcodeSuffix="xor-int/lit16";break;
						
					}
					vAvBCCCC();
					this.operandSuffix="constant(16 bits)";
					return this.length;
				}else 				if(op>=0xd8 && op<=0xe2){
					this.length= 4;
					this.opcode="binop/lit8";
					switch(op){
					
						case 0xd8: this.opcodeSuffix="add-int/lit8";break;
						case 0xd9: this.opcodeSuffix="rsub-int/lit8";break;
						case 0xda: this.opcodeSuffix="mul-int/lit8";break;
						case 0xdb: this.opcodeSuffix="div-int/lit8";break;
						case 0xdc: this.opcodeSuffix="rem-int/lit8";break;
						case 0xdd: this.opcodeSuffix="and-int/lit8";break;
						case 0xde: this.opcodeSuffix="or-int/lit8";break;
						case 0xdf: this.opcodeSuffix="xor-int/lit8";break;
						case 0xe0: this.opcodeSuffix="shl-int/lit8";break;
						case 0xe1: this.opcodeSuffix="shr-int/lit8";break;
						case 0xe2: this.opcodeSuffix="ushr-int/lit8";break;
					}
					vAAvBBCC();
					this.operandSuffix="constant(8 bits)";
					return this.length;
				}
		}
		return -1;
	}
	/**
	 * Return true if this instruction has a register list
	 * */
	public boolean hasRegister(){
		if(this.registerList!=null){
			return true;
		}else
			return false;
	}
	/**
	 * Obtain the register list of a instruction.
	 * */
	public int[] getRegList(){
			return this.registerList;
	}
	/**
	 * Obtain the operand of a instruction
	 * */
	public long getOperand(){
		return this.operand;
	}
	public void instantiateOperand() {
		switch(this.operandSuffix){
		case "method id":
			this.ccb.mb.ms.methodElementList.get((int) this.operand).printFields();break;
		case "string id":
			System.out.print("	"+this.ccb.mb.ssb.ss.stringElementList.get((int) this.operand).stringContent);break;
		case "field id" :
			break;
		default:
			break;
		}
	}
	
} public class MethodComponent extends DataComponent{
	public ArrayList <MethodElement> methodElementList= new ArrayList <MethodElement>();
	
	public void searchingQuery(String keyword){
		for(int i=0;i<methodElementList.size();i++){
			
			if(methodElementList.get(i).methodName==keyword){
				

				System.out.println(i+" "+methodElementList.get(i));
				
			}
			
		}		
	}
} public class MethodElement {
	/**offset of a method definition in DEX file*/
	public long address;	
	/**Index of a method in Method Component generated by RAPID*/
	public int methodId;
	/**The string of the class type of a method */
	public String className;
	/**The string of the name of a method
	 * */
	public String methodName;
	/**The string of the returnValue type of a method
	 * No return value is represented by string 'void'.
	 * */
	public String returnValueType;
	/**The string array for the type of each parameter
	 * The value of this parameter can be NULL, if the 'returnValueType' field is 0;
	 * Avoiding 'NonePointerException' checking 'returnValueType' before using.
	 * */
	public String[] parameterType;
	/**True, if the method have a code block defined in DEX file, default is false.
	 **/
	public boolean hasCodeBlock=false;
	/**The instance of the codeBlock, the value of which can be NULL.
	 * Avoiding 'NonePointerException' checking flag 'hasCodeBlock' before using. 
	 * */
	public CodeBlock codeBlock;

	public MethodElement(){
		
	}
	/**
	 * Usually used for constructing the method for use's searching purpose.
	 * */
	public MethodElement(String className,String methodName,String returnValue,String[] parameterType){
		this.className=className;
		this.methodName=methodName;
		this.returnValueType=returnValue;
		this.parameterType=parameterType;
		
	}	
	/**
	 * Returning true if the parameterType[] is not NULL.
	 * */
	public boolean hasParameter(){
		if (parameterType==null)
			return false;
		else{
			return true;
		}
	}
	/**
	 * print fields of address, className, methodName ,returnValue and parameterType[]
	 * */
	public void printFields(){
		if(this.returnValueType==null){
			this.returnValueType="";
		}
		if(this.returnValueType==null){
			this.className="";
		}
		if(this.returnValueType==null){
			this.methodName="";
		}
		System.out.print("	"+this.address+"	"+this.returnValueType+" "+this.className+"."+this.methodName+" ");
		System.out.print("(");
		if(this.hasParameter()){
			
			for(int j=0;j<this.parameterType.length;j++){
				System.out.print(this.parameterType[j]);
				if(j!=(this.parameterType.length-1)){
					System.out.print(", ");
				}
			}
		
		}
		System.out.println(")");

	}
	
} public class StringComponent extends DataComponent{
	public ArrayList<StringElement> stringElementList = new ArrayList<StringElement>();

	public void searchingQuery(String keyword){
		for(int i=0;i<stringElementList.size();i++){
			
			if(stringElementList.get(i).stringContent==keyword){
				

			System.out.println(i+" "+stringElementList.get(i));
				
			}
			
		}		
	}

} public class StringElement {
/**offset of a string in DEX file*/
public long address;
/**length of a string in DEX file*/
public long stringLength;
/**index of a string in string component generated by RAPID*/
public int stringId;
/**ASCII form of a string's content*/
public String stringContent;

} public class DexLoader {
	private FileChannel fin = null;
	private FileInputStream input = null;	
	/**the instance of the buffer in memory where stores the DEX file*/
	public MappedByteBuffer mbb;
	
	private String unzippedFileDir;
	
	public DexLoader(String unzippedFileDir){
		this.unzippedFileDir=unzippedFileDir;
		getCodeBuffer();
		
	}
	
	private void getCodeBuffer()  {
		
		File file = new File(this.unzippedFileDir);		
	try{
			input = new FileInputStream(file);
			fin= input.getChannel();		
			mbb = fin.map(FileChannel.MapMode.READ_ONLY,0,file.length());
		}catch(Exception e){
			System.out.println("ERROR:	Sorry, Directory: "+unzippedFileDir+" does not exist.");
		}
				

	}
	
	
	/**cleaning the buffer*/
	public void cleanBuffer(){
		
		AccessController.doPrivileged(new PrivilegedAction<Object>() {   
		  public Object run() {   
		    try {   
		       Method getCleanerMethod = mbb.getClass().getMethod("cleaner", new Class[0]);   
		       getCleanerMethod.setAccessible(true);   
		       sun.misc.Cleaner cleaner = (sun.misc.Cleaner)   
		       getCleanerMethod.invoke(mbb, new Object[0]);   
		       cleaner.clean();
		       fin.close();
		       input.close();   
		     } catch (Exception e) {  
		    	 System.out.println("ERROR:	Sorry cleanning buffer has occured exceptions.");
		    	 e.printStackTrace();   
		     }   
		    return null;   
		   }   
		});		
	}	
} public class MethodComponentBuilder extends ComponentBuilder{
	/**creating a StringComponentBuilder for using strings stored in String Component*/
	public StringComponentBuilder ssb;
	/**creating an Method Component*/
	public MethodComponent ms= new MethodComponent();
	/**A construction method that calls the method in father class 'MethodComponent', 
	 * which let Method Component finding the DEX file in memory
	 * @param mbb DEX file loaded in memory
	 * */
	public MethodComponentBuilder(MappedByteBuffer mbb) {
		super(mbb);
		// TODO Auto-generated constructor stub
		buildComponent();
	}
	private void buildComponent() {
		getSectionOffsets();
		ssb = new StringComponentBuilder(mbb);
		
		readBinary(0x58,4,"ADDRESS");//Method_ids
			long size=tempValue;
		readBinary(0x5c,4,"ADDRESS");//Method_ids
			long stratAddress=tempValue;

		for(int i=0;i<size;i++){
			MethodElement me = new MethodElement();
			me.methodId=i;
			me.address=stratAddress;
			getProtoType(me);
			getClassName(me);
			getMethodName(me);
			ms.methodElementList.add(me);			
			stratAddress=stratAddress+8;
		}
	}
	private void getMethodName(MethodElement me){
		readBinary(me.address+4,4,"ADDRESS");
		long methodNameStringId=tempValue;	
		me.methodName=ssb.ss.stringElementList.get((int)methodNameStringId).stringContent;//Method_ids
		
	}
	private void getClassName(MethodElement me){
		
		readBinary(me.address,2,"ADDRESS");//classNameId
			long classNameId=tempValue;		
		readBinary(0x40,4,"ADDRESS");
			long size =tempValue;						
		readBinary(0x44,4,"ADDRESS");
			long stratAddress =tempValue;			
		readBinary(stratAddress+(classNameId*4),4,"ADDRESS");//Type_ids
			long classStringId=tempValue;
			
				String cName= ssb.ss.stringElementList.get((int)classStringId).stringContent;
						
				me.className=typeTranslate(cName);
		
	}
	private void getProtoType(MethodElement me){
		readBinary(me.address+2,2,"ADDRESS");
		long protoId=tempValue;
		readBinary(0x4c,4,"ADDRESS");
		long protoIdStart=tempValue;
		readBinary(protoIdStart+(protoId*12)+4,4,"ADDRESS");	
		long returnTypeStringId=tempValue;
		
		readBinary(0x44,4,"ADDRESS");
		long typeIdStrat=tempValue;
		readBinary(typeIdStrat+4*returnTypeStringId,4,"ADDRESS");
		returnTypeStringId=tempValue;
		me.returnValueType=typeTranslate(ssb.ss.stringElementList.get((int)returnTypeStringId).stringContent);//return value obtained
		
		readBinary(protoIdStart+(protoId*12)+4+4,4,"ADDRESS");
		long parametersOffset=tempValue;
		if(parametersOffset!=0){
				readBinary(parametersOffset,4,"ADDRESS");
				int parameterNumber=(int) tempValue;			
				me.parameterType=new String[parameterNumber];			
				parametersOffset=parametersOffset+4;			
				for(int i=0;i<parameterNumber;i++){
					readBinary(parametersOffset+(2*i),2,"ADDRESS");				
					long parameterMethodId=tempValue;
					
					readBinary(typeIdStrat+4*parameterMethodId,4,"ADDRESS");
					parameterMethodId=tempValue;
					me.parameterType[i]=typeTranslate(ssb.ss.stringElementList.get((int)parameterMethodId).stringContent);
				}
			
		}

	}
	private String typeTranslate(String rawType){
		int arrayLevel=0;
		String readableType = rawType;
		while(readableType.startsWith("[")){
			readableType=readableType.substring(1,readableType.length());
			arrayLevel++;
		}
		if(readableType.startsWith("V")){
			readableType="void";
		}
		if(readableType.startsWith("Z")){
			readableType="boolean";
		}
		if(readableType.startsWith("B")){
			readableType="byte";
		}
		if(readableType.startsWith("S")){
			readableType="short";
		}
		if(readableType.startsWith("C")){
			readableType="char";
		}
		if(readableType.startsWith("I")){
			readableType="int";
		}
		if(readableType.startsWith("J")){
			readableType="long ";
		}
		if(readableType.startsWith("F")){
			readableType="float";//(64bit)
		}
		if(readableType.startsWith("D")){
			readableType="double";//(64bit)
		}

		if(readableType.startsWith("L")){
			readableType=readableType.substring(1,readableType.length()).replaceAll("/", ".").replaceAll(";", "");
		}		
		for(int i=0;i<arrayLevel;i++){
			readableType=readableType+"[]";
		}
		
		return readableType;
	}
} public class CodeComponentBuilder extends ComponentBuilder{
	/**creating a Code Component*/
	public CodeComponent codeComponent= new CodeComponent();
	/**creating a Method Component*/
	public MethodComponentBuilder mb = new MethodComponentBuilder(mbb);	
	
	private ArrayList<MethodElement> methodList=mb.ms.methodElementList;
	/**A construction method that calls the method in father class, which let Code Component find the DEX file in memory*/
	public CodeComponentBuilder(MappedByteBuffer mbb)  {
		super(mbb);
		// TODO Auto-generated constructor stub
		
		buildComponent();
	}
	private void buildComponent() {
		
		getSectionOffsets();
		loadCodeBlockToMethod();
		
	}
	/**
	 * parsing the code block into instructions
	 * */
	public void loadInstructionToCodeBlock(){
		parseCodeToStatement();
	}

	private void parseCodeToStatement(){
		for(int i=0;i<codeComponent.codeBlockList.size();i++){//cs.codeStream.size()
			
			long startAddress=codeComponent.codeBlockList.get(i).startAddress+16;
			long endAddress=codeComponent.codeBlockList.get(i).endAddress;
			int serialNum=0;
			 while(startAddress<endAddress){
				 	
				 	//System.out.println("startAddress:"+startAddress);
				 	readBinary(startAddress,1,"ADDRESS");
				 	int op=(int)tempValue;
				 	Instruction ins= new Instruction(this);
				 	ins.codeBlockId=i;
				 	ins.instructionId=serialNum;
				 	if(op!=0){
				 			ins.address=startAddress;
				 			ins.identifyToken(op);
				 			
				 			
				 	}else if(op==0){

						 	readBinary(startAddress,2,"ADDRESS");
						 	op=(int)tempValue;	
						 	
						 	
						 	if(op!=0){
						 		
						 		readBinary(startAddress+2,2,"ADDRESS");
						 		int size=(int)tempValue;
						 		switch(op){
								case 0x100:								 											
									ins.op=op;
									ins.opcode="packed-switch-payload";
									ins.length = (size*2+4)*2;	
									ins.address=startAddress;								
									break;
								case 0x200:	
									ins.op=op;
									ins.opcode="sparse-switch-payload";
									ins.length = (size*4+2)*2;
									ins.address=startAddress;
									break;
								case 0x300:	
									readBinary(startAddress+2+2,4,"ADDRESS");
									int size2=(int)tempValue;
									ins.op=op;
									ins.opcode="array-data-payload";
									ins.length=((size*size2+1)/2+4)*2;
									ins.address=startAddress;
									break;
								default:
							 		ins.length=1;
							 		ins.op=op;
							 		ins.opcode="nod";
							 		ins.address=startAddress;
									break;
								}

						 	}else if (op==0x0){
						 		ins.length=1;
						 		ins.op=op;
						 		ins.opcode="nod";
						 		ins.address=startAddress;
						 	}
						 	
	 		
				 	}
				 	
				 	codeComponent.codeBlockList.get(i).instructionList.add(ins);
				 				 	
				 	startAddress=startAddress+ins.length;
				 	serialNum++;
			 }

		}
		
	}
	private void loadCodeBlockToMethod() {		
		long startAddress=this.ClassDataStart;	
		long sectionSize=this.ClassDataSize;	
		long[] methodCount = new long[4];

		for(int j=0;j<sectionSize;j++){//sectionSize
				for(int i=0;i<4;i++){//class header 
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
						//tempUlebValue
						//tempUlebByteNum
					
					methodCount[i]=tempUlebValue;
					startAddress=startAddress+tempUlebByteNum;
				}
				
				for(int i=0;i<methodCount[0];i++){
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field1
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field2
				}//Static field count
				for(int i=0;i<methodCount[1];i++){
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field1
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field2
				}//Instance field count
				int methodId=0;
				for(int i=0;i<methodCount[2];i++){
					CodeBlock ce = new CodeBlock();
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					methodId=methodId+(int) tempUlebValue;					
					ce.methodId=methodId;
					//field1 Method id
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field2 Access flags
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field3 Method offset
					ce.startAddress=tempUlebValue;
					if(tempUlebValue!=0){
						readBinary(tempUlebValue+2+2+2+2+4,4,"ADDRESS");//# Number of registers
																		//# Size of input args
																		//# Size of input args
																		//# Number of try_items
																		//# Debug info
						long bytecodeSize=tempValue;
						ce.endAddress=ce.startAddress+2+2+2+2+4+4+(bytecodeSize*2);
						MethodElement method=methodList.get(methodId);
						method.hasCodeBlock=true;
						method.codeBlock=ce;
					}else{
						ce.endAddress=0;
					}
					codeComponent.codeBlockList.add(ce);
					
					//System.out.println(msb.ms.methodStream.get(methodId).codeStartAddress);
				}//Direct method count
				methodId=0;
				
				for(int i=0;i<methodCount[3];i++){
					CodeBlock ce = new CodeBlock();
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field1 Method id
					methodId=methodId+(int) tempUlebValue;
					ce.methodId=methodId;
					
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					//field2 Access flags
					startAddress=startAddress+tempUlebByteNum;
					readBinary(startAddress,1,"ADDRESS");
					getUleb(startAddress,tempValue);
					startAddress=startAddress+tempUlebByteNum;
					//field3 Method offset
					ce.startAddress=tempUlebValue;
					if(tempUlebValue!=0){
						readBinary(tempUlebValue+2+2+2+2+4,4,"ADDRESS");//# Number of registers
																		//# Size of input args
																		//# Size of input args
																		//# Number of try_items
																		//# Debug info
						long bytecodeSize=tempValue;
						ce.endAddress=ce.startAddress+2+2+2+2+4+4+(bytecodeSize*2);
						
						MethodElement method=methodList.get(methodId);
						method.hasCodeBlock=true;
						method.codeBlock=ce;
					}else{
						ce.endAddress=0;
					}
					codeComponent.codeBlockList.add(ce);
				}//Virtual method count
				
		}
				

	}

} public class StringComponentBuilder extends ComponentBuilder{
	/**creating an String Component*/
	public StringComponent ss= new StringComponent();
	/**A construction method that calls the method in father class 'MethodComponent', 
	 * which let String Component finding the DEX file in memory
	 * @param mbb DEX file loaded in memory
	 * */
	public StringComponentBuilder(MappedByteBuffer mbb){
		super(mbb);
		// TODO Auto-generated constructor stub
		buildComponent();
		
	}
	private void parseStringName(StringElement se){
		readBinary(se.address,se.stringLength,"ASCII");
		se.stringContent=tempString;
		//return this.stringName;
	}
	private void buildComponent() {
		
		
		readBinary(0x38,4,"ADDRESS");
		long size=tempValue;
		readBinary(0x3c,4,"ADDRESS");
		long startAddress=tempValue;//0x70
		int count=0;	
			while(count<size){
				StringElement se=new StringElement();
				
				readBinary(startAddress,4,"ADDRESS");				
				long stringAddress=tempValue;	
				readBinary(stringAddress,1,"VALUE");
				getUleb(stringAddress,tempValue);				
				long ulebLength=tempUlebByteNum;
				
				long stringLength=tempUlebValue;
				se.stringId=count;
				se.address=stringAddress+ulebLength;
				se.stringLength=stringLength;
				
				parseStringName(se);
				ss.stringElementList.add(se);
				startAddress=startAddress+4;
				
				count++;

			}
					
	}

} public class ZipDecompressor {
	private String apkDir;	
	private String unzippedDir;
	
	public ZipDecompressor(){
		
	}
	/**setting the full path for either storing APK files and 
	 * the directory of storing the unzipped DEX file if its necessary for the users
	 * @param apkDir full path storing APK files
	 * @param unzippedDir full path with a directory storing the unzipped DEX file
	 * */	
	public void setApkDir(String apkDir, String unzippedDir){
		this.apkDir=apkDir;
		this.unzippedDir=unzippedDir;
	}	
	/**unzipping a file*/	
	public String unzipFile(String unzippingFileName) throws Exception{

		
		
		//System.out.println(apkDir); 
		FileInputStream fileInput=new FileInputStream(new File(apkDir));
		ZipInputStream zin=new ZipInputStream(fileInput);
        
       
	            	BufferedInputStream bin=new BufferedInputStream(zin);              
	            	if(unzippedDir!=null){
	            		File uzDir=new File (unzippedDir);
	            		if(!uzDir.exists()){
	            			uzDir.mkdir();  
	            		}
	            	}
	            	
		            	
	            	String parent=unzippedDir; 
		            ZipEntry entry;  
		                while((entry = zin.getNextEntry())!= null){  		                	
		                	 //System.out.println(entry.getName()); 
		                    if(entry.getName().equals(unzippingFileName)&& (!entry.isDirectory())){  
		                    
		                    	parent = parent+entry.getName();
			                    FileOutputStream out=new FileOutputStream(parent);  
			                    BufferedOutputStream Bout=new BufferedOutputStream(out);  
			                    int b;  
				                while((b=bin.read())!=-1){  
				                        Bout.write(b);  
				                }  
				                Bout.close();  
				                out.close();  
				                break;
		                    }   
		                }		               
		                bin.close();  
		                zin.close();  
		                fileInput.close();

		               //fc.close();		               
		                return parent;
					
		            	
		
	}
	/**read the content of a file*/
	 public void readZipFile(String file) throws Exception {  
         ZipFile zf = new ZipFile(this.apkDir);  
         InputStream in = new BufferedInputStream(new FileInputStream(apkDir));  
         ZipInputStream zin = new ZipInputStream(in);  
         ZipEntry ze;  
         while ((ze = zin.getNextEntry()) != null) {  
             if (ze.isDirectory()) {
             } else {  
                 System.err.println("file - " + ze.getName() + " : "  
                         + ze.getSize() + " bytes");  
                 long size = ze.getSize();  
                 if(ze.getName().equals(file)){ 
	                 
	                     BufferedReader br = new BufferedReader(  
	                             new InputStreamReader(zf.getInputStream(ze)));  
	                     String line;  
	                     while ((line = br.readLine()) != null) {  
	                         System.out.println(line);  
	                     }  
	                     br.close();  
	                  
                 }
                 System.out.println();  
             }  
         }  
         zin.closeEntry();  
     }  
	
}
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import dataComponent.CodeBlock;
import dataComponent.CodeComponent;
import dataComponent.Instruction;
import dataComponent.MethodComponent;
import dataComponent.MethodElement;
import dataComponent.StringComponent;
import dataComponent.StringElement;
import systemComponent.DexLoader;
import systemComponent.MethodComponentBuilder;
import systemComponent.CodeComponentBuilder;
import systemComponent.StringComponentBuilder;
import systemComponent.ZipDecompressor;
	}

	private void setApiLevel(int level){
		if(level>this.apiLevel){
			this.apiLevel=level;
		}
	}
	private void setApkList(){
		
        File dir = new File(this.apkDir);
        
        FilenameFilter searchSuffix = new FilenameFilter(){
            @Override
            public boolean accept(File dir, String name) {
                if(name.endsWith(".apk")||name.endsWith(".dex")){
                	return true;  
                }else{
                	return false;
                }
            	      
            }
        };
        
        File []files = dir.listFiles();
        if (files.length==0){
        	try {
				throw new FileNotFoundException("WARNING:	No 'APK' file found in directory: "+this.apkDir+".");
			} catch ( FileNotFoundException e) {
				// TODO Auto-generated catch block				
				e.printStackTrace();
			}
        }
        for(File f : files){
                if(searchSuffix.accept(dir, f.getName())){
                    apkList.add(f);
                }
        }

	}

	/**
	 * A 'call back' method where RAPID can receive the APIs.
	 * User has to call this method. The parameter should be an implementation for interface: 'QueryBlock',
	 * Also, function 'queries()'of the interface have to be overridden as an API container where user calls the RAPID APIs
	 * @param queryBlock the implementation of interface 'QueryBlock',
	 * */	
	public void setQuery(QueryBlock queryBlock) {
		System.out.println("#	RAPID version "+VERSION+": a forensic examnation tool for DEX file ");
		if(this.apkDir==System.getProperty("user.dir")){
			setApkList();
		}	
		String unzippedFile;
		if(this.targetFile!=null){
				if(this.targetFile.endsWith(".apk")){
					File targetFile=new File(this.targetFile);
					unzippedFile=unzipApk(targetFile);
					System.out.println("#	DEX file is successfully decompressed from APK file: "+unzippedFile+".");
				}else{
					unzippedFile=this.targetFile;
				}
				loadIntoMemory(unzippedFile);
				instructionLevelQuary();
				System.out.println("#	Data Components has been constructed.");
				System.out.println("#	Performing queries.....");
				System.out.println("");
				queryBlock.queries();
				System.out.println("");
				dl.cleanBuffer();
				
		} else{
				for (int i=0;i<apkList.size();i++){
					
		
					File currentApk = apkList.get(i);
					if(currentApk.getName().endsWith(".apk")){
						unzippedFile=unzipApk(currentApk);
						System.out.println("#	DEX file is successfully decompressed from APK file: "+currentApk+".");
					}else{
						System.out.println("#	DEX file "+currentApk.getName()+" is ready");
						unzippedFile=currentApk.getAbsolutePath();
					}
		
					loadIntoMemory(unzippedFile);
					
					if (0==i){
							instructionLevelQuary();
					}else{
						switch (apiLevel){
							case 0: stringLevelQuary();
									break; 
							case 1: methodLevelQuary();
									break; 
							case 2: codeLevelQuary();
									break; 
							case 3: instructionLevelQuary();
									break;
						}
					}
				
					System.out.println("#	Data Components has been constructed.");
					System.out.println("#	Performing queries.....");
					System.out.println("");
				
					queryBlock.queries();
					
					dl.cleanBuffer();
				}
		}
	}

	private void stringLevelQuary() {
				StringComponentBuilder scb = new StringComponentBuilder(dl.mbb);
				this.stringComponent = scb.ss;			
	}
	private void methodLevelQuary() {
				MethodComponentBuilder mcb=new MethodComponentBuilder(dl.mbb);
				this.stringComponent = mcb.ssb.ss;
				this.methodComponent = mcb.ms;
	}
	private void codeLevelQuary() {
				CodeComponentBuilder ccb= new CodeComponentBuilder(dl.mbb);