Candidate release of source code.

This commit is contained in:
Dan 2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View file

@ -0,0 +1,234 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.opinion;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import ghidra.app.cmd.register.SetRegisterCmd;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MemoryConflictHandler;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.store.LockException;
import ghidra.javaclass.format.*;
import ghidra.javaclass.format.attributes.CodeAttribute;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.javaclass.format.constantpool.ConstantPoolUtf8Info;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.LanguageCompilerSpecPair;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
public class JavaLoader extends AbstractLibrarySupportLoader {
private static final String JAVA_NAME = "Java Class File";
private Register alignmentReg;
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();
boolean validClass = false;
if (checkClass(provider)) {
validClass = true;
}
if (validClass) {
loadSpecs.add(new LoadSpec(this, 0,
new LanguageCompilerSpecPair("JVM:BE:32:default", "default"), true));
}
return loadSpecs;
}
private boolean checkClass(ByteProvider provider) {
BinaryReader reader = new BinaryReader(provider, false);
ClassFileJava classFile;
try {
classFile = new ClassFileJava(reader);
}
catch (IOException e) {
return false;
} catch (RuntimeException re) {
return false;
}
int magic = classFile.getMagic();
if (magic == 0xCAFEBABE) {
return true;
}
return false;
}
@Override
public String getName() {
return JAVA_NAME;
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, MemoryConflictHandler handler, TaskMonitor monitor, MessageLog log)
throws IOException {
try {
doLoad(provider, program, monitor);
}
catch (LockException e) {
e.printStackTrace();
}
catch (MemoryConflictException e) {
e.printStackTrace();
}
catch (AddressOverflowException e) {
e.printStackTrace();
}
catch (CancelledException e) {
e.printStackTrace();
}
catch (DuplicateNameException e) {
e.printStackTrace();
}
}
public void load(ByteProvider provider, Program program, TaskMonitor monitor)
throws IOException {
load(provider, null, null, program, null, monitor, null);
}
private void doLoad(ByteProvider provider, Program program, TaskMonitor monitor)
throws LockException, MemoryConflictException, AddressOverflowException,
CancelledException, DuplicateNameException, IOException {
AddressFactory af = program.getAddressFactory();
AddressSpace space = af.getAddressSpace("constantPool");
Memory memory = program.getMemory();
alignmentReg = program.getRegister("alignmentPad");
BinaryReader reader = new BinaryReader(provider, false);
ClassFileJava classFile = new ClassFileJava(reader);
Address address = space.getAddress(0);
// Create a block of memory with just the right size
memory.createInitializedBlock("_" + provider.getName() + "_", address,
provider.getInputStream(0), provider.length(), monitor, false);
createMethodLookupMemoryBlock( program, monitor );
createMethodMemoryBlocks(program, provider, classFile, monitor);
}
private void createMethodLookupMemoryBlock(Program program, TaskMonitor monitor) {
Address address = toAddr( program, JavaClassUtil.LOOKUP_ADDRESS );
MemoryBlock block = null;
Memory memory = program.getMemory();
try {
block = memory.createInitializedBlock( "method_lookup", address, JavaClassUtil.METHOD_INDEX_SIZE, (byte) 0xff, monitor, false );
}
catch (LockException | DuplicateNameException | MemoryConflictException
| AddressOverflowException | CancelledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
block.setRead( true );
block.setWrite( false );
block.setExecute( false );
}
private void createMethodMemoryBlocks(Program program, ByteProvider provider,
ClassFileJava classFile, TaskMonitor monitor) {
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
MethodInfoJava[] methods = classFile.getMethods();
monitor.setMessage("Processing Methods...");
monitor.setProgress(0);
monitor.setMaximum(methods.length);
long codeOffset = 0x10000;
Address start = toAddr(program, codeOffset);
try {
//program.setImageBase(start, true);
//for (MethodInfoJava method : methods) {
for (int i = 0, max = methods.length; i < max; ++i){
MethodInfoJava method = methods[i];
monitor.incrementProgress(1);
CodeAttribute code = method.getCodeAttribute();
if (code == null) {
continue;
}
int length = code.getCodeLength();
long offset = code.getCodeOffset();
Memory memory = program.getMemory();
short nameIndex = method.getNameIndex();
short descriptorIndex = method.getDescriptorIndex();
ConstantPoolUtf8Info methodNameInfo = (ConstantPoolUtf8Info) constantPool[nameIndex];
ConstantPoolUtf8Info methodDescriptorInfo =
(ConstantPoolUtf8Info) constantPool[descriptorIndex];
String methodName = methodNameInfo.getString() + methodDescriptorInfo.getString();
MemoryBlock memoryBlock =
memory.createInitializedBlock(methodName, start,
provider.getInputStream(offset), length, monitor, false);
Address methodIndexAddress = JavaClassUtil.toLookupAddress( program, i );
program.getMemory( ).setInt( methodIndexAddress, (int) start.getOffset() );
setAlignmentInfo(program,
new AddressSet(memoryBlock.getStart(), memoryBlock.getEnd()));
start = start.add(length + 1);
while (start.getOffset() % 4 != 0) {
start = start.add(1);
}
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
private void setAlignmentInfo(Program program, AddressSet set) {
AddressIterator addressIterator = set.getAddresses(true);
int alignmentValue = 3;
while (addressIterator.hasNext()) {
Address address = addressIterator.next();
SetRegisterCmd cmd =
new SetRegisterCmd(alignmentReg, address, address,
BigInteger.valueOf(alignmentValue));
cmd.applyTo(program);
if (alignmentValue == 0) {
alignmentValue = 3;
}
else {
alignmentValue--;
}
}
}
private Address toAddr(Program program, long offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
}
}

View file

@ -0,0 +1,168 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.javaclass.format.DescriptorDecoder;
import ghidra.javaclass.format.JavaClassConstants;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.data.*;
/**
*
* This is a utility class for generating pcode for the multianewarray operation.
* Note that the newarray operation, which creates arrays of primitive types, does not
* reference the constant pool and does not require pcode injection (but see
* ConstantPoolJava.getRecord()
*
*/
public class ArrayMethods {
static final String ARRAY_REF = "arrayref";
static final String CLASS_NAME = "className";
static final String DIMENSION = "dim";
static final String MULTIANEWARRAY = "multianewarrayOp";
static final String PROCESS_ADDITIONAL_DIMENSIONS = "multianewarrayProcessAdditionalDimensionsOp";
static final int MAX_PCODE_OP_ARGS = 7;
//private constructor to enforce noninstantiability
private ArrayMethods(){
throw new AssertionError();
}
/**
* Emits pcode for the multianewarray op, which is used to create new multi-dimensional arrays
* It is modeled with two black-box pcode ops: multianewarrayOp and multianewarrayProcessAdditionalDimensionsOp.
* The second op is need because pcode operations are limited to 8 input parameters, whereas multianewarray
* takes between 1 and 256 parameters.
*
* The first argument to multianewarrayOp is a reference to the class of the new array. The remaining seven arguments
* are array dimensions. Additional array dimensions are consumed from the stack with calls to
* multianewarrayProcessAdditionalDimensionsOp, which takes a reference returned by multianewarrayOp as its first argument
* and a dimension as its second argument.
* @param constantPoolIndex
* @param constantPool
* @param dimensions
* @return
*/
public static String getPcodeForMultiANewArray(int constantPoolIndex, AbstractConstantPoolInfoJava[] constantPool,
int dimensions) {
StringBuilder pCode = new StringBuilder();
//pop all of the dimensions off the stack
for (int i = dimensions; i >= 1; --i){
String iAsString = Integer.toString(i);
PcodeTextEmitter.emitPopCat1Value(pCode, DIMENSION + iAsString);
}
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, CLASS_NAME, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(constantPoolIndex),ConstantPoolJava.CPOOL_MULTIANEWARRAY);
//emit the call to multianewarrayOp
String[] multianewarrayOpArgs = null;
//if clause: more dimension arguments than will fit in a call to multianewarrayOp
//-1 since the first argument will be a reference to the class name
if (dimensions > (MAX_PCODE_OP_ARGS -1) ){
multianewarrayOpArgs = new String[MAX_PCODE_OP_ARGS];
multianewarrayOpArgs[0] = CLASS_NAME;
for (int i = 1; i < MAX_PCODE_OP_ARGS; ++i){
multianewarrayOpArgs[i] = DIMENSION + Integer.toString(i);
}
}
else {
//+1 for the class reference
multianewarrayOpArgs = new String[dimensions + 1];
multianewarrayOpArgs[0] = CLASS_NAME;
for (int i = 1; i < dimensions + 1; ++i){
multianewarrayOpArgs[i] = DIMENSION + Integer.toString(i);
}
}
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, ARRAY_REF, 4, MULTIANEWARRAY, CLASS_NAME,"dim1","dim2");
//consume any additional arguments
for (int i = MAX_PCODE_OP_ARGS; i <= dimensions; ++i){
String[] args = {ARRAY_REF, DIMENSION + Integer.toString(i)};
PcodeTextEmitter.emitVoidPcodeOpCall(pCode, PROCESS_ADDITIONAL_DIMENSIONS, args);
}
PcodeTextEmitter.emitPushCat1Value(pCode, ARRAY_REF);
return pCode.toString();
}
/**
* The array type codes can be found in the JVM documentation for the
* "newarray" instruction.
* @param code
* @return
*/
public static String getPrimitiveArrayToken(int code){
switch(code){
case JavaClassConstants.T_BOOLEAN:
return "boolean";
case JavaClassConstants.T_CHAR:
return "char";
case JavaClassConstants.T_FLOAT:
return "float";
case JavaClassConstants.T_DOUBLE:
return "double";
case JavaClassConstants.T_BYTE:
return "byte";
case JavaClassConstants.T_SHORT:
return "short";
case JavaClassConstants.T_INT:
return "int";
case JavaClassConstants.T_LONG:
return "long";
default:
throw new IllegalArgumentException("Invalid primitive type code: " + code);
}
}
public static DataType getArrayBaseType(int i, DataTypeManager dtManager) {
String primitiveType = null;
switch(i){
case JavaClassConstants.T_BOOLEAN:
primitiveType = "Z";
break;
case JavaClassConstants.T_CHAR:
primitiveType = "C";
break;
case JavaClassConstants.T_FLOAT:
primitiveType = "F";
break;
case JavaClassConstants.T_DOUBLE:
primitiveType = "D";
break;
case JavaClassConstants.T_BYTE:
primitiveType = "B";
break;
case JavaClassConstants.T_SHORT:
primitiveType = "S";
break;
case JavaClassConstants.T_INT:
primitiveType = "I";
break;
case JavaClassConstants.T_LONG:
primitiveType = "J";
break;
default:
throw new IllegalArgumentException("Invalid primitive type code: " + i);
}
return DescriptorDecoder.getDataTypeOfDescriptor(primitiveType, dtManager);
}
}

View file

@ -0,0 +1,345 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.io.IOException;
import java.util.List;
import ghidra.javaclass.format.*;
import ghidra.javaclass.format.constantpool.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.ConstantPool;
import ghidra.program.model.listing.Program;
public class ConstantPoolJava extends ConstantPool {
public static final String CPOOL_OP = "cpool";
private ClassFileJava classFile;
private AbstractConstantPoolInfoJava[] constantPool;
private DataTypeManager dtManager = null;
//the following constants must agree with the definitions in JVM.slaspec
public static final String CPOOL_ANEWARRAY = "0";
public static final String CPOOL_CHECKCAST = "1";
public static final String CPOOL_GETFIELD = "2";
public static final String CPOOL_GETSTATIC = "3";
public static final String CPOOL_LDC = "4"; //also used for ldc_w
public static final String CPOOL_LDC2_W = "5";
public static final String CPOOL_INSTANCEOF = "6";
public static final String CPOOL_INVOKEDYNAMIC = "7";
public static final String CPOOL_INVOKEINTERFACE = "8";
public static final String CPOOL_INVOKESPECIAL = "9";
public static final String CPOOL_INVOKESTATIC = "10";
public static final String CPOOL_INVOKEVIRTUAL = "11";
public static final String CPOOL_MULTIANEWARRAY = "12";
public static final String CPOOL_NEW = "13";
public static final String CPOOL_NEWARRAY = "14";
public static final String CPOOL_PUTSTATIC = "15";
public static final String CPOOL_PUTFIELD = "16";
public static final String CPOOL_ARRAYLENGTH = "17";
public ConstantPoolJava(Program program) throws IOException {
ClassFileAnalysisState analysisState = ClassFileAnalysisState.getState(program);
classFile = analysisState.getClassFile();
constantPool = classFile.getConstantPool();
dtManager = program.getDataTypeManager();
}
private void fillinMethod(int name_and_type_index, Record res,JavaInvocationType methodType) {
ConstantPoolNameAndTypeInfo methodNameAndType = (ConstantPoolNameAndTypeInfo) constantPool[name_and_type_index];
int name_index = methodNameAndType.getNameIndex();
res.tag = ConstantPool.POINTER_METHOD;
res.token = ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
int descriptor_index = methodNameAndType.getDescriptorIndex();
ConstantPoolUtf8Info descriptorInfo = (ConstantPoolUtf8Info) constantPool[descriptor_index];
String descriptor = descriptorInfo.getString();
FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(res.token);
res.type = new PointerDataType(funcDef);
DataType returnType = DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor, dtManager);
funcDef.setReturnType(returnType);
List<DataType> params = DescriptorDecoder.getDataTypeList(descriptor, dtManager);
ParameterDefinitionImpl[] paramDefs;
//invokestatic and invokedynamic don't have a this pointer on the stack
if (methodType.equals(JavaInvocationType.INVOKE_STATIC) || methodType.equals(JavaInvocationType.INVOKE_DYNAMIC)){
paramDefs = new ParameterDefinitionImpl[params.size()];
for (int i = 0, max = params.size(); i < max; ++i){
ParameterDefinitionImpl currentParam = new ParameterDefinitionImpl("", params.get(i), null);
paramDefs[i]= currentParam;
}
res.hasThisPtr = false;
}
//invokeinterface, invokespecial, and invokevirtual do have a this pointer
else {
paramDefs = new ParameterDefinitionImpl[params.size() + 1];
ParameterDefinitionImpl thisParam = new ParameterDefinitionImpl("objectRef", new Pointer32DataType(DataType.VOID), null);
paramDefs[0] = thisParam;
for (int i = 1, max = params.size(); i <= max; ++i){
ParameterDefinitionImpl currentParam = new ParameterDefinitionImpl("", params.get(i-1), null);
paramDefs[i]= currentParam;
}
res.hasThisPtr = true;
}
funcDef.setArguments(paramDefs);
}
//ref array does not include the first element passed to the cpool operator.
//ref[0] is the constant pool index
//ref[1] is a defined constant which represents the bytecode operation
@Override
public Record getRecord(long[] ref) {
Record res = new Record();
String op = Long.toString(ref[1]);
/*The newarray operation doesn't actually reference the constant pool.
* However, it does use "array type codes" to determine the primitive
* type of the elements of the new array. We use the cpool operator to
* look up the string token corresponding to the primitive type.
*/
if (op.equals(CPOOL_NEWARRAY)){
res.tag = ConstantPool.POINTER_METHOD;
res.token = ArrayMethods.getPrimitiveArrayToken((int)ref[0]);
DataType elementType = ArrayMethods.getArrayBaseType((int) ref[0], dtManager);
res.type = dtManager.getPointer(elementType);
return res;
}
/*arraylength instruction does not reference the constant pool */
if (op.equals(CPOOL_ARRAYLENGTH)){
res.tag = ConstantPool.ARRAY_LENGTH;
res.token = "length";
res.type = new PointerDataType(DWordDataType.dataType);
return res;
}
AbstractConstantPoolInfoJava poolRef = constantPool[(int)ref[0]];
short name_and_type_index;
ConstantPoolNameAndTypeInfo fieldNameAndType;
short descriptor_index;
ConstantPoolUtf8Info descriptorInfo;
String descriptor;
StringBuilder sb = null;
String[] parts = null;
switch(op){
case CPOOL_ANEWARRAY:
case CPOOL_NEW:
res.tag = ConstantPool.CLASS_REFERENCE;
int name_index = ((ConstantPoolClassInfo)poolRef).getNameIndex();
String fullyQualifiedName = ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
parts = fullyQualifiedName.split("/");
res.token = parts[parts.length-1];
sb = new StringBuilder();
for (String part : parts) {
sb.append(CategoryPath.DELIMITER_CHAR);
sb.append(part);
}
DataTypePath dataPath = new DataTypePath(sb.toString(), res.token);
res.type = new PointerDataType(dtManager.getDataType(dataPath));
break;
//TODO
case CPOOL_CHECKCAST:
res.tag = ConstantPool.CHECK_CAST;
res.token = "checkcast";
setTypeNameInfo(poolRef,res);
break;
case CPOOL_INSTANCEOF:
res.tag = ConstantPool.INSTANCE_OF;
res.token = "instanceof";
setTypeNameInfo(poolRef, res);
break;
case CPOOL_GETFIELD:
case CPOOL_PUTFIELD:
name_and_type_index = ((ConstantPoolFieldReferenceInfo)poolRef).getNameAndTypeIndex();
fieldNameAndType = (ConstantPoolNameAndTypeInfo)constantPool[name_and_type_index];
name_index = fieldNameAndType.getNameIndex();
res.tag = ConstantPool.POINTER_FIELD;
res.token = ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
descriptor_index = fieldNameAndType.getDescriptorIndex();
descriptorInfo = (ConstantPoolUtf8Info) constantPool[descriptor_index];
descriptor = descriptorInfo.getString();
DataType type = DescriptorDecoder.getDataTypeOfDescriptor(descriptor, dtManager);
res.type = new PointerDataType(type);
break;
//for references to static fields, we want the class name to be part of the token
case CPOOL_GETSTATIC:
case CPOOL_PUTSTATIC:
name_and_type_index = ((ConstantPoolFieldReferenceInfo)poolRef).getNameAndTypeIndex();
int class_index = ((ConstantPoolFieldReferenceInfo)poolRef).getClassIndex();
fieldNameAndType = (ConstantPoolNameAndTypeInfo)constantPool[name_and_type_index];
name_index = fieldNameAndType.getNameIndex();
ConstantPoolClassInfo classInfo = (ConstantPoolClassInfo)constantPool[class_index];
int classNameIndex = classInfo.getNameIndex();
fullyQualifiedName = ((ConstantPoolUtf8Info)constantPool[classNameIndex]).getString();
res.tag = ConstantPool.POINTER_FIELD;
String className = getClassName(fullyQualifiedName);
res.token = className + "." + ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
descriptor_index = fieldNameAndType.getDescriptorIndex();
descriptorInfo = (ConstantPoolUtf8Info) constantPool[descriptor_index];
descriptor = descriptorInfo.getString();
type = DescriptorDecoder.getDataTypeOfDescriptor(descriptor, dtManager);
res.type = new PointerDataType(type);
//res.type = type;
break;
case CPOOL_INVOKEDYNAMIC:
name_and_type_index = ((ConstantPoolInvokeDynamicInfo)poolRef).getNameAndTypeIndex();
fillinMethod(name_and_type_index,res,JavaInvocationType.INVOKE_DYNAMIC);
break;
case CPOOL_INVOKEINTERFACE:
name_and_type_index = ((ConstantPoolInterfaceMethodReferenceInfo)poolRef).getNameAndTypeIndex();
fillinMethod(name_and_type_index,res,JavaInvocationType.INVOKE_INTERFACE);
break;
case CPOOL_INVOKESPECIAL:
if (poolRef instanceof ConstantPoolMethodReferenceInfo) {
name_and_type_index = ((ConstantPoolMethodReferenceInfo)poolRef).getNameAndTypeIndex();
}
else{
name_and_type_index = ((ConstantPoolInterfaceMethodReferenceInfo)poolRef).getNameAndTypeIndex();
}
fillinMethod(name_and_type_index,res,JavaInvocationType.INVOKE_SPECIAL);
break;
case CPOOL_INVOKESTATIC:
if (poolRef instanceof ConstantPoolMethodReferenceInfo) {
name_and_type_index = ((ConstantPoolMethodReferenceInfo)poolRef).getNameAndTypeIndex();
class_index = ((ConstantPoolMethodReferenceInfo)poolRef).getClassIndex();
}
else{
name_and_type_index = ((ConstantPoolInterfaceMethodReferenceInfo)poolRef).getNameAndTypeIndex();
class_index = ((ConstantPoolInterfaceMethodReferenceInfo)poolRef).getClassIndex();
}
classInfo = (ConstantPoolClassInfo)constantPool[class_index];
classNameIndex = classInfo.getNameIndex();
fullyQualifiedName = ((ConstantPoolUtf8Info)constantPool[classNameIndex]).getString();
className = getClassName(fullyQualifiedName);
fillinMethod(name_and_type_index,res,JavaInvocationType.INVOKE_STATIC);
res.token = className + "." + res.token;
break;
case CPOOL_INVOKEVIRTUAL:
name_and_type_index = ((ConstantPoolMethodReferenceInfo)poolRef).getNameAndTypeIndex();
fillinMethod(name_and_type_index,res,JavaInvocationType.INVOKE_VIRTUAL);
break;
//in this case, the constant pool entry can be a reference to:
//int, float, string literal, or a symbolic reference to a class,
//method type, or method handle
case CPOOL_LDC:
if (poolRef instanceof ConstantPoolIntegerInfo) {
res.tag = ConstantPool.PRIMITIVE;
res.token = "int";
res.value = ((ConstantPoolIntegerInfo)poolRef).getValue();
res.type = IntegerDataType.dataType;
}
else if (poolRef instanceof ConstantPoolFloatInfo) {
res.tag = ConstantPool.PRIMITIVE;
res.token = "float";
res.value = ((ConstantPoolFloatInfo)poolRef).getRawBytes() & 0xffffffffL;
res.type = FloatDataType.dataType;
}
else if (poolRef instanceof ConstantPoolStringInfo) {
int string_index = ((ConstantPoolStringInfo)poolRef).getStringIndex();
res.tag = ConstantPool.STRING_LITERAL;
res.byteData = ((ConstantPoolUtf8Info) constantPool[string_index]).getBytes();
res.type = DescriptorDecoder.getReferenceTypeOfDescriptor("java/lang/String", dtManager, false);
}
else if (poolRef instanceof ConstantPoolClassInfo){
res.tag = ConstantPool.CLASS_REFERENCE;
name_index = ((ConstantPoolClassInfo)poolRef).getNameIndex();
fullyQualifiedName = ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
className = getClassName(fullyQualifiedName);
res.token = className + ".class";
res.type = DescriptorDecoder.getReferenceTypeOfDescriptor(fullyQualifiedName, dtManager, false);
}
//standard java compilers don't seem to emit the following two
else if (poolRef instanceof ConstantPoolMethodTypeInfo){
res.tag = ConstantPool.POINTER_METHOD;
name_index = ((ConstantPoolMethodTypeInfo)poolRef).getDescriptorIndex();
res.token = ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
res.type = dtManager.getPointer(DWordDataType.dataType);
}
//TODO set the token?
else if (poolRef instanceof ConstantPoolMethodHandleInfo){
res.tag = ConstantPool.POINTER_METHOD;
res.type = dtManager.getPointer(DWordDataType.dataType);
}
break;
//must be a constant of type long or double
//according to JVM spec
case CPOOL_LDC2_W:
if (poolRef instanceof ConstantPoolLongInfo) {
res.tag = ConstantPool.PRIMITIVE;
res.token = "long";
res.value = ((ConstantPoolLongInfo)poolRef).getValue();
res.type = LongDataType.dataType;
}
else {
res.tag = ConstantPool.PRIMITIVE;
res.token = "double";
res.value = ((ConstantPoolDoubleInfo)poolRef).getRawBytes();
res.type = DoubleDataType.dataType;
}
break;
case CPOOL_MULTIANEWARRAY:
res.tag = ConstantPool.POINTER_METHOD;
res.type = new PointerDataType(DataType.VOID);
int nameIndex = ((ConstantPoolClassInfo) poolRef).getNameIndex();
ConstantPoolUtf8Info utf8Info = (ConstantPoolUtf8Info) constantPool[nameIndex];
String classNameWithSemicolon = utf8Info.getString();
res.token = DescriptorDecoder.getTypeNameFromDescriptor(classNameWithSemicolon, false, false);
default:
break;
}
return res;
}
private void setTypeNameInfo(AbstractConstantPoolInfoJava poolRef, Record res) {
int name_index = ((ConstantPoolClassInfo)poolRef).getNameIndex();
String fullyQualifiedName = ((ConstantPoolUtf8Info)constantPool[name_index]).getString();
String[] parts = null;
StringBuilder sb = null;
if (fullyQualifiedName.startsWith("[")){
//TODO: how to get instanceof X to display, where X is an array type?
//need to decide how to handle multidimensional arrays
//remove the brackets
//check whether it's a primitive type
parts = fullyQualifiedName.split("/");
sb = new StringBuilder();
for (String part : parts) {
sb.append(CategoryPath.DELIMITER_CHAR);
sb.append(part);
}
}
else {
parts = fullyQualifiedName.split("/");
sb = new StringBuilder();
for (String part : parts) {
sb.append(CategoryPath.DELIMITER_CHAR);
sb.append(part);
}
}
DataTypePath dataPath = new DataTypePath(sb.toString(), parts[parts.length-1]);
res.type = new PointerDataType(dtManager.getDataType(dataPath));
}
private String getClassName(String fullyQualifiedName){
int lastSlash = fullyQualifiedName.lastIndexOf("/");
return fullyQualifiedName.substring(lastSlash+1, fullyQualifiedName.length());
}
public AbstractConstantPoolInfoJava[] getConstantPool(){
return constantPool;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectGetField extends InjectPayloadJava {
public InjectGetField(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = ReferenceMethods.getPcodeForGetField(constantPoolIndex, constantPool);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectGetStatic extends InjectPayloadJava {
public InjectGetStatic(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = ReferenceMethods.getPcodeForGetStatic(constantPoolIndex, constantPool);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectInvokeDynamic extends InjectPayloadJava {
public InjectInvokeDynamic(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = InvokeMethods.getPcodeForInvoke(constantPoolIndex, constantPool, JavaInvocationType.INVOKE_DYNAMIC);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectInvokeInterface extends InjectPayloadJava {
public InjectInvokeInterface(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = InvokeMethods.getPcodeForInvoke(constantPoolIndex, constantPool, JavaInvocationType.INVOKE_INTERFACE);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectInvokeSpecial extends InjectPayloadJava {
public InjectInvokeSpecial(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = InvokeMethods.getPcodeForInvoke(constantPoolIndex, constantPool, JavaInvocationType.INVOKE_SPECIAL);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectInvokeStatic extends InjectPayloadJava {
public InjectInvokeStatic(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = InvokeMethods.getPcodeForInvoke(constantPoolIndex, constantPool, JavaInvocationType.INVOKE_STATIC);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectInvokeVirtual extends InjectPayloadJava {
public InjectInvokeVirtual(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = InvokeMethods.getPcodeForInvoke(constantPoolIndex, constantPool, JavaInvocationType.INVOKE_VIRTUAL);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectLdc extends InjectPayloadJava {
public InjectLdc(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = LdcMethods.getPcodeForLdc(constantPoolIndex, constantPool);
return pcodeText;
}
}

View file

@ -0,0 +1,43 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.io.IOException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectLookupSwitch extends InjectPayloadJava {
public InjectLookupSwitch(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
String pcodeText = null;
try {
pcodeText = SwitchMethods.getPcodeForLookupSwitch(injectContext, program);
} catch (IOException e) {
e.printStackTrace();
pcodeText = "SP = SP;\n";
}
return pcodeText;
}
}

View file

@ -0,0 +1,39 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectMultiANewArray extends InjectPayloadJava {
public InjectMultiANewArray(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
int dimensions = (int) injectContext.inputlist.get(1).getOffset();
String pcodeText = ArrayMethods.getPcodeForMultiANewArray(constantPoolIndex, constantPool, dimensions);
return pcodeText;
}
}

View file

@ -0,0 +1,185 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.io.IOException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.*;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
import ghidra.app.plugin.processors.sleigh.template.OpTpl;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.pcodeCPort.slgh_compile.PcodeParser;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeXMLException;
import ghidra.sleigh.grammar.Location;
import ghidra.util.Msg;
import ghidra.util.xml.XmlUtilities;
import ghidra.xml.XmlPullParser;
import ghidra.xml.XmlPullParserFactory;
/**
* Subclasses of this class are used to generate pcode to inject for modeling
* java bytecode in pcode.
*
*/
public abstract class InjectPayloadJava extends InjectPayloadCallother {
private SleighLanguage language;
private SAXParser saxParser;
/**
* Subclasses use this method to generate pcode text for a particular java
* bytecode op requiring pcode injection.
*
* @param program The program containing the op.
* @param context The context associated with the op.
* @return
*/
abstract String getPcodeText(Program program, String context);
public InjectPayloadJava(String sourceName, SleighLanguage language) {
super(sourceName);
this.language = language;
try {
saxParser = getSAXParser();
}
catch (PcodeXMLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
SleighLanguage getLanguage() {
return language;
}
InjectContext getInjectContext(Program program, String context) {
InjectContext injectContext = new InjectContext();
injectContext.language = language;
try {
injectContext.restoreXml(saxParser, context, program.getAddressFactory());
saxParser.reset();
}
catch (PcodeXMLException e1) {
Msg.info(this, e1.getMessage());
e1.printStackTrace();
}
return injectContext;
}
AbstractConstantPoolInfoJava[] getConstantPool(Program program) {
ConstantPoolJava cPool = null;
try {
cPool = new ConstantPoolJava(program);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return cPool.getConstantPool();
}
//from DecompileCallback.java
private static SAXParser getSAXParser() throws PcodeXMLException {
try {
SAXParserFactory saxParserFactory = XmlUtilities.createSecureSAXParserFactory(false);
saxParserFactory.setFeature("http://xml.org/sax/features/namespaces", false);
saxParserFactory.setFeature("http://xml.org/sax/features/validation", false);
return saxParserFactory.newSAXParser();
}
catch (Exception e) {
Msg.error(PcodeInjectLibraryJava.class, e.getMessage());
throw new PcodeXMLException("Failed to instantiate XML parser", e);
}
}
/**
* This method is used to generate and compile pcode for a given
* callotherfixup.
*
* @param parser Used to parse pcode.
* @param program The program containing the callotherfixup
* @param context The context of the callotherfixup.
* @return An array of OpTpl (for passing to
* PcodeInjectLibrary.adjustUniqueBase)
*/
public OpTpl[] getPcode(PcodeParser parser, Program program, String context) {
String sourceName = getSource();
Location loc = new Location(sourceName, 1);
InjectParameter[] input = getInput();
for (InjectParameter element : input) {
parser.addOperand(loc, element.getName(), element.getIndex());
}
InjectParameter[] output = getOutput();
for (InjectParameter element : output) {
parser.addOperand(loc, element.getName(), element.getIndex());
}
String pcodeText = getPcodeText(program, context);
String constructTplXml =
PcodeParser.stringifyTemplate(parser.compilePcode(pcodeText, sourceName, 1));
if (constructTplXml == null) {
throw new SleighException("pcode compile failed " + sourceName);
}
final SAXParseException[] exception = new SAXParseException[1];
XmlPullParser xmlParser = null;
try {
xmlParser =
XmlPullParserFactory.create(constructTplXml, sourceName, new ErrorHandler() {
@Override
public void warning(SAXParseException e) throws SAXException {
Msg.warn(this, e.getMessage());
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
exception[0] = e;
}
@Override
public void error(SAXParseException e) throws SAXException {
exception[0] = e;
}
}, false);
}
catch (SAXException e) {
e.printStackTrace();
}
ConstructTpl constructTpl = new ConstructTpl();
try {
constructTpl.restoreXml(xmlParser, language.getAddressFactory());
}
catch (UnknownInstructionException e) {
e.printStackTrace();
}
if (exception[0] != null) {
throw new SleighException("pcode compiler returned invalid xml " + sourceName,
exception[0]);
}
OpTpl[] opTemplates = constructTpl.getOpVec();
setTemplate(constructTpl);
return opTemplates;
}
}

View file

@ -0,0 +1,179 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.plugin.processors.sleigh.PcodeEmit;
import ghidra.javaclass.format.*;
import ghidra.javaclass.format.constantpool.ConstantPoolUtf8Info;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.lang.InjectPayload;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.Msg;
public class InjectPayloadJavaParameters implements InjectPayload {
private InjectParameter[] noParams;
private boolean analysisStateRecoverable;
public InjectPayloadJavaParameters() {
noParams = new InjectParameter[0];
analysisStateRecoverable = true;
}
@Override
public String getName() {
return "javaparameters";
}
@Override
public int getType() {
return CALLMECHANISM_TYPE;
}
@Override
public String getSource() {
return "javaparameters";
}
@Override
public int getParamShift() {
return 0;
}
@Override
public InjectParameter[] getInput() {
return noParams;
}
@Override
public InjectParameter[] getOutput() {
return noParams;
}
@Override
public void inject(InjectContext context, PcodeEmit emit) {
//not used
}
@Override
public PcodeOp[] getPcode(Program program, InjectContext con) {
if (!analysisStateRecoverable) {
return new PcodeOp[0];
}
ClassFileAnalysisState analysisState;
try {
analysisState = ClassFileAnalysisState.getState(program);
}
catch (IOException e) {
Msg.error(this, e.getMessage(), e);
analysisStateRecoverable = false;
return new PcodeOp[0];
}
ClassFileJava classFile = analysisState.getClassFile();
MethodInfoJava methodInfo = analysisState.getMethodInfo(con.baseAddr);
if (methodInfo == null){
return new PcodeOp[0];
}
int descriptorIndex = methodInfo.getDescriptorIndex();
ConstantPoolUtf8Info descriptorInfo = (ConstantPoolUtf8Info)(classFile.getConstantPool()[descriptorIndex]);
String descriptor = descriptorInfo.getString();
List<JavaComputationalCategory> paramCategories = new ArrayList<>();
if (!methodInfo.isStatic()){
paramCategories.add(JavaComputationalCategory.CAT_1);//for the this pointer
}
paramCategories.addAll(DescriptorDecoder.getParameterCategories(descriptor));
int numOps = paramCategories.size();
if (paramCategories.size() == 0){
//no this pointer, no parameters: nothing to do
return new PcodeOp[0];
}
AddressSpace paramSpace = program.getAddressFactory().getAddressSpace("parameterSpace");
int paramSpaceID = paramSpace.getBaseSpaceID();
AddressSpace lva = program.getAddressFactory().getAddressSpace("localVariableArray");
int lvaID = lva.getBaseSpaceID();
AddressSpace constant = program.getAddressFactory().getConstantSpace();
PcodeOp[] resOps = new PcodeOp[1 + 3*numOps];
int seqNum = 0;
//create varnodes for incrementing pointer by 4 or 8 bytes
Varnode zero = new Varnode(constant.getAddress(0),4);
Varnode four = new Varnode(constant.getAddress(4),4);
Varnode eight = new Varnode(constant.getAddress(8),4);
Address LVAregAddress = program.getRegister("LVA").getAddress();
Varnode LVA = new Varnode(LVAregAddress,4);
//initialize LVA to contain 0
PcodeOp copy = new PcodeOp(con.baseAddr,seqNum, PcodeOp.COPY);
copy.setInput(zero, 0);
copy.setOutput(LVA);
resOps[seqNum++] = copy;
//create temp storage locations
Address temp4Address = analysisState.getNextUniqueAddress();
Varnode temp4 = new Varnode(temp4Address,4);
Address temp8Address = analysisState.getNextUniqueAddress();
Varnode temp8 = new Varnode(temp8Address,8);
Varnode tempLocation = null;
Varnode increment = null;
for (JavaComputationalCategory cat : paramCategories){
if (cat.equals(JavaComputationalCategory.CAT_1)){
tempLocation = temp4;
increment = four;
}
else {
tempLocation = temp8;
increment = eight;
}
//copy value from parameterSpace to temporary
PcodeOp load = new PcodeOp(con.baseAddr, seqNum, PcodeOp.LOAD);
load.setInput(new Varnode(constant.getAddress(paramSpaceID),4), 0);
load.setInput(LVA, 1);
load.setOutput(tempLocation);
resOps[seqNum++] = load;
//copy temporary to LVA
PcodeOp store = new PcodeOp(con.baseAddr, seqNum, PcodeOp.STORE);
store.setInput(new Varnode(constant.getAddress(lvaID),4), 0);
store.setInput(LVA,1);
store.setInput(tempLocation, 2);
resOps[seqNum++] = store;
//increment LVA reg
PcodeOp add = new PcodeOp(con.baseAddr, seqNum, PcodeOp.INT_ADD);
add.setInput(LVA, 0);
add.setInput(increment, 1);
add.setOutput(LVA);
resOps[seqNum++] = add;
}
return resOps;
}
@Override
public boolean isFallThru() {
return true;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectPutField extends InjectPayloadJava {
public InjectPutField(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = ReferenceMethods.getPcodeForPutField(constantPoolIndex, constantPool);
return pcodeText;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class InjectPutStatic extends InjectPayloadJava {
public InjectPutStatic(String sourceName, SleighLanguage language) {
super(sourceName, language);
}
@Override
public String getPcodeText(Program program, String context) {
InjectContext injectContext = getInjectContext(program, context);
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);
int constantPoolIndex = (int) injectContext.inputlist.get(0).getOffset();
String pcodeText = ReferenceMethods.getPcodeForPutStatic(constantPoolIndex, constantPool);
return pcodeText;
}
}

View file

@ -0,0 +1,151 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.util.List;
import ghidra.javaclass.format.DescriptorDecoder;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
/**
*
* asymmetry in java - methods passed on stack, read from local variable array
*
*/
public class InvokeMethods {
static final String CALL_TARGET = "call_target";
static final String CAT_1_RETURN = "return_value";
static final String CAT_2_RETURN = "cat2_return_value";
static final String PARAMETER = "param";
static final String PARAMETER_PART2 = "parampart";
static final String THIS = "this";
static final String STATIC_OFFSET = "0";
static final String PARAM_SPACE = "parameterSpace";
//private constructor to enforce noninstantiability
private InvokeMethods(){
throw new AssertionError();
}
/**
* Emits the pcode for an invoke instruction.
* @param offset - the index of the constant pool element containing a symbolic reference
* to a method or a call site specifier.
* @param constantPool - the constant pool
* @param type - the JavaInvocationType of the invocation
* @return - the pcode as a string
*/
public static String getPcodeForInvoke(int offset, AbstractConstantPoolInfoJava[] constantPool, JavaInvocationType type) {
StringBuilder pCode = new StringBuilder();
String descriptor = DescriptorDecoder.getDescriptorForInvoke(offset, constantPool, type);
List<JavaComputationalCategory> categories = DescriptorDecoder.getParameterCategories(descriptor);
boolean includeThisPointer = type.equals(JavaInvocationType.INVOKE_VIRTUAL) || type.equals(JavaInvocationType.INVOKE_SPECIAL) || type.equals(JavaInvocationType.INVOKE_INTERFACE);
int stackPurge = DescriptorDecoder.getStackPurge(descriptor);
if (includeThisPointer){
stackPurge += 4;
}
emitPcodeToMoveParams(pCode, categories, includeThisPointer, stackPurge);
emitPcodeToResolveMethodReference(pCode, offset, constantPool, type);
PcodeTextEmitter.emitIndirectCall(pCode, CALL_TARGET);
JavaComputationalCategory retType = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(descriptor);
switch (retType){
case CAT_1:
PcodeTextEmitter.emitPushCat1Value(pCode, CAT_1_RETURN);
break;
case CAT_2:
PcodeTextEmitter.emitPushCat2Value(pCode, CAT_2_RETURN);
break;
default:
break;
}
return pCode.toString();
}
/**
* Emits pcode to move the parameters from the stack to the space parameterSpace
* Note: if there is an implicit this parameter, then this method will assign it to a varnode
* named InvokeMethods.THIS
* @param pCode - the pcode buffer
* @param categories - the list of computational categories on the top of the stack
* @param includeThisPointer - true if the first element on the stack is an implicit this parameter
*/
static void emitPcodeToMoveParams(StringBuilder pCode, List<JavaComputationalCategory> categories, boolean includeThisPointer, int totalSize){
//pop the parameters off of the stack
for (int i = categories.size() - 1; i >= 0; --i){
switch (categories.get(i)){
case CAT_1:
PcodeTextEmitter.emitPopCat1Value(pCode, PARAMETER + Integer.toString(i));
totalSize -= 4;
PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, Integer.toString(totalSize) + ":4", PARAMETER + Integer.toString(i));
break;
case CAT_2:
PcodeTextEmitter.emitPopCat1Value(pCode, PARAMETER + Integer.toString(i));
PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, Integer.toString(totalSize-8) + ":4", PARAMETER + Integer.toString(i));
PcodeTextEmitter.emitPopCat1Value(pCode, PARAMETER_PART2 + Integer.toString(i));
PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, Integer.toString(totalSize-4) + ":4", PARAMETER_PART2 + Integer.toString(i));
totalSize -= 8;
break;
default:
throw new IllegalArgumentException("Invalid category!");
}
}
//pop off the this pointer if there is one
if (includeThisPointer){
PcodeTextEmitter.emitPopCat1Value(pCode, THIS);
totalSize -= 4;
PcodeTextEmitter.emitWriteToMemory(pCode, PARAM_SPACE, 4, Integer.toString(totalSize) + ":4", THIS);
}
}
/**
* Emits pcode to assign the result of a cpool op to the call_target register for an invocation.
* @param pCode - the pcode buffer
* @param offset - index of the method reference in the constant pool
* @param constantPool - the constant pool
* @param type - the type of the invocation
*/
static void emitPcodeToResolveMethodReference(StringBuilder pCode, int offset, AbstractConstantPoolInfoJava[] constantPool, JavaInvocationType type){
switch (type){
case INVOKE_DYNAMIC:
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, ConstantPoolJava.CPOOL_OP, STATIC_OFFSET, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKEDYNAMIC);
break;
case INVOKE_INTERFACE:
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, ConstantPoolJava.CPOOL_OP, THIS, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKEINTERFACE);
break;
case INVOKE_SPECIAL:
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, ConstantPoolJava.CPOOL_OP, THIS, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKESPECIAL);
break;
case INVOKE_STATIC:
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, ConstantPoolJava.CPOOL_OP, STATIC_OFFSET, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKESTATIC);
break;
case INVOKE_VIRTUAL:
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, CALL_TARGET, ConstantPoolJava.CPOOL_OP, THIS, Integer.toString(offset), ConstantPoolJava.CPOOL_INVOKEVIRTUAL);
break;
default:
throw new IllegalArgumentException("Unimplemented JavaMethodType: " + type.toString());
}
}
}

View file

@ -0,0 +1,20 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
public enum JavaComputationalCategory {
CAT_1, CAT_2, VOID;
}

View file

@ -0,0 +1,20 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
public enum JavaInvocationType {
INVOKE_DYNAMIC, INVOKE_INTERFACE, INVOKE_SPECIAL, INVOKE_STATIC, INVOKE_VIRTUAL;
}

View file

@ -0,0 +1,68 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.javaclass.format.constantpool.ConstantPoolTagsJava;
/**
*
* This is a utility class containing a method to generate pcode of the ldc, ldc_w, and ldc2_w bytecode ops.
*
*/
public class LdcMethods {
static final String VALUE = "value";
//private constructor to enforce noninstantiability
private LdcMethods(){
throw new AssertionError();
}
/**
* Generates a String of pcode modeling an ldc, ldc_w, or ldc2_w bytecode ops, which refer to constants
* in the constant pool.
*
* @param constantPoolIndex - the index of item in the constant pool.
* @param constantPool - the constant pool
* @return - String of pcode.
*/
public static String getPcodeForLdc(int constantPoolIndex, AbstractConstantPoolInfoJava[] constantPool){
byte tag = constantPool[constantPoolIndex].getTag();
StringBuilder pCode = new StringBuilder();
switch (tag){
case ConstantPoolTagsJava.CONSTANT_Class:
case ConstantPoolTagsJava.CONSTANT_Float:
case ConstantPoolTagsJava.CONSTANT_Integer:
case ConstantPoolTagsJava.CONSTANT_MethodHandle:
case ConstantPoolTagsJava.CONSTANT_MethodType:
case ConstantPoolTagsJava.CONSTANT_String:
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(constantPoolIndex),ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case ConstantPoolTagsJava.CONSTANT_Double:
case ConstantPoolTagsJava.CONSTANT_Long:
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(constantPoolIndex),ConstantPoolJava.CPOOL_LDC2_W);
PcodeTextEmitter.emitPushCat2Value(pCode,VALUE);
break;
default:
throw new IllegalArgumentException("Invalid load from constant pool: tag " + tag);
}
return pCode.toString();
}
}

View file

@ -0,0 +1,255 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.jdom.JDOMException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.plugin.processors.sleigh.template.OpTpl;
import ghidra.pcodeCPort.slgh_compile.PcodeParser;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
/**
*
* This class handles dynamic pcode injection for modeling JVM bytecode.
*
* There are some notable differences between a java .class file and an executable compiled for
* a physical processor, which present some complications when modeling bytecode with pcode.
* Some of the bigger differences are:
* 1) The java compiler does not know the runtime layout of a class - that's up to the JVM implementer
* to decide. Methods and fields of a class are accessed by name in a .class file - there are no
* vtables or field offsets in bytecode.
* 2) Each method gets its own area in memory called a method stack. It is not assumed to be contiguous
* with other method stacks. There is no concept of the address of a method.
* 3) Parameters are passed to a method on the operand stack in left to right order (first parameter pushed first).
* However, within a method body, parameters to that method are not read from the operand stack. Instead,
* they are read from an area known as the "local variable array".
* 4) Many bytecode operations reference the constant pool. For example, some of the bytes of an invoke
* instruction encode an index in the constant pool. By reading data at that index, you can determine
* the signature, name, and class of the method to be invoked.
* 5) In some instances, you must consult the constant pool to determine how an instruction changes the
* the stack. For instance, a getfield operation can push either 4 or 8 bytes on to the stack,
* depending on the "computational category" of the field (object references, integers, and floats are
* 4 bytes, longs and doubles are 8). You cannot determine the computational category from the bytes
* of the instruction, you must examine the element at the encoded index into the constant pool.
*
* The file JVM.slaspec contains definitions of fake registers which are not part of the JVM specification
* but are necessary to model class files in pcode. These registers include return_value, cat2_return_value,
* switch_target, return_address
*
* defined pcodeop naming schemes:
* ops ending in "CallOther" are used for pcode injection
* ops ending in "Op" are used as black-box ops.
*
* How to implement pcode injection for an operation "testop":
* JVM.pspec:
* ensure that the processor spec properties includes:
* <property key="pcodeInjectLibraryClass" value="ghidra.app.util.pcodeInject.PcodeInjectLibraryJava"/>
* (this property needs to be there for injection to work. It's included as a step here for
* general pcode injection documentation. In the general case, modify the value as appropriate.)
* JVM.slaspec:
* define a new pcodeop testopCallOther
* define a new pcodeop testopOp, if necessary (if you need a black-box operation to model testop)
* in the pcode for testop, emit a call to testopCallOther.
* JVM.cspec:
* define a callotherfixup targetop "testopCallOther" - see the other callotherfixups
* PcodeInectLibrary.java:
* define a constant string "testopCallOther"
* in the constructor for this class, add the newly-defined string to the set implementedOps
* add a case statement restorePcodeXml
* Create a subclass of InjectPayloadJava to generate/compile pcode
* (Use/add to PcodeTextEmitter.java to actually emit pcode text)
* See ConstantPoolJava.java for examples of the use of the CPOOL pcode op.
*
* TODO:
* 1) For the lookupswitch op, the disassembly is correct but the decompilation is not.
* There are several possible ways of modeling the instruction in pcode - either using
* injection (SwitchMethods.java) or pcode (JVM.slaspec). In all cases, there seems to
* be an issue with getting the decompiler to follow pointers to the various switch clauses.
* The file LookupSwitchHex.java in the resource directory contains a class file with
* 4 methods with switch statements. Currently the first method is modeled using a
* pcode loop and the last three are modeled using pcode injection (which is possible because
* there are actually 4 separate lookupswitch instructions, the only difference is the amount
* of padding bytes).
*
* possible improvements:
*
* 2) incorporate exceptions.
* 6) decide how to display the information used in an invokedynamic instruction
* 7) jsr/ret instructions are not modeled using pcode injection. The jsr (jump to subroutine)
* instruction is deprecated and can't appear in class files with version >= 51.0. This is a
* strange instruction which pushes the address of the following instruction onto the operand
* stack and then jumps to a subroutine. The subroutine ends with a ret instruction, which
* retrieves the address from a local variable (not from the stack). The JVM handles moving
* the address from the stack to the local variable. In order to model this, you would need
* to determine which ret instruction is paired with a jsr instruction and inject pcode to move
* the address into the local variable that the ret instruction reads from. You will need to
* follow flow from the beginning of the subroutine the corresponding ret instruction(s?).
*
*/
public class PcodeInjectLibraryJava extends PcodeInjectLibrary {
public static final int CONSTANT_POOL_START_ADDRESS = 0xa;
//names of defined pcode ops that require pcode injection
public static final String GETFIELD = "getFieldCallOther";
public static final String GETSTATIC = "getStaticCallOther";
public static final String INVOKE_DYNAMIC = "invokedynamicCallOther";
public static final String INVOKE_INTERFACE = "invokeinterfaceCallOther";
public static final String INVOKE_SPECIAL = "invokespecialCallOther";
public static final String INVOKE_STATIC = "invokestaticCallOther";
public static final String INVOKE_VIRTUAL = "invokevirtualCallOther";
public static final String LDC = "ldcCallOther";
public static final String LDC2_W = "ldc2_wCallOther";
public static final String LDC_W = "ldc_wCallOther";
public static final String LOOKUP_SWITCH = "lookupswitchCallOther";
public static final String MULTIANEWARRAY = "multianewarrayCallOther";
public static final String PUTFIELD = "putFieldCallOther";
public static final String PUTSTATIC = "putStaticCallOther";
//size of one stack element in the jvm (in bytes)
public static final int REFERENCE_SIZE = 4;
private SleighLanguage language;
private Set<String> implementedOps;
private PcodeParser parser;
private InjectPayloadJavaParameters paramPayload;
public PcodeInjectLibraryJava(SleighLanguage l) {
super(l);
language = l;
implementedOps = new HashSet<>();
implementedOps.add(GETFIELD);
implementedOps.add(GETSTATIC);
implementedOps.add(INVOKE_DYNAMIC);
implementedOps.add(INVOKE_INTERFACE);
implementedOps.add(INVOKE_SPECIAL);
implementedOps.add(INVOKE_STATIC);
implementedOps.add(INVOKE_VIRTUAL);
implementedOps.add(LDC);
implementedOps.add(LDC2_W);
implementedOps.add(LDC_W);
implementedOps.add(LOOKUP_SWITCH);
implementedOps.add(MULTIANEWARRAY);
implementedOps.add(PUTFIELD);
implementedOps.add(PUTSTATIC);
String translateSpec = language.buildTranslatorTag(language.getAddressFactory(),
getUniqueBase(), language.getSymbolTable());
paramPayload = null;
parser = null;
try {
parser = new PcodeParser(translateSpec);
}
catch (JDOMException e1) {
e1.printStackTrace();
}
}
@Override
protected InjectPayloadSleigh allocateInject(String sourceName, String name, int tp) {
InjectPayloadJava payload = null;
if (tp != InjectPayload.CALLOTHERFIXUP_TYPE)
return super.allocateInject(sourceName, name, tp);
switch (name) {
case GETFIELD:
payload = new InjectGetField(sourceName, language);
break;
case GETSTATIC:
payload = new InjectGetStatic(sourceName, language);
break;
case INVOKE_DYNAMIC:
payload = new InjectInvokeDynamic(sourceName, language);
break;
case INVOKE_INTERFACE:
payload = new InjectInvokeInterface(sourceName, language);
break;
case INVOKE_SPECIAL:
payload = new InjectInvokeSpecial(sourceName, language);
break;
case INVOKE_STATIC:
payload = new InjectInvokeStatic(sourceName, language);
break;
case INVOKE_VIRTUAL:
payload = new InjectInvokeVirtual(sourceName, language);
break;
case LDC:
case LDC2_W:
case LDC_W:
payload = new InjectLdc(sourceName, language);
break;
case LOOKUP_SWITCH:
payload = new InjectLookupSwitch(sourceName, language);
break;
case MULTIANEWARRAY:
payload = new InjectMultiANewArray(sourceName, language);
break;
case PUTFIELD:
payload = new InjectPutField(sourceName, language);
break;
case PUTSTATIC:
payload = new InjectPutStatic(sourceName, language);
break;
default:
return super.allocateInject(sourceName, name, InjectPayload.CALLOTHERFIXUP_TYPE);
}
return payload;
}
@Override
/**
* This method is called by DecompileCallback.getPcodeInject.
*/
public InjectPayload getPayload(int type, String name, Program program, String context) {
if (type == InjectPayload.CALLMECHANISM_TYPE) {
if (paramPayload == null) {
paramPayload = new InjectPayloadJavaParameters();
}
return paramPayload;
}
if (!implementedOps.contains(name)) {
return super.getPayload(type, name, program, context);
}
InjectPayloadJava payload =
(InjectPayloadJava) super.getPayload(InjectPayload.CALLOTHERFIXUP_TYPE, name, program,
context);
synchronized (parser) {
OpTpl[] opTemplates = payload.getPcode(parser, program, context);
adjustUniqueBase(opTemplates);
//clear the added symbols so that the parser can be used again without
//duplicate symbol name conflicts.
parser.clearSymbols();
}
return payload;
}
@Override
public ConstantPool getConstantPool(Program program) throws IOException {
return new ConstantPoolJava(program);
}
}

View file

@ -0,0 +1,220 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
/**
*
* This is a utility class containing methods to emit pcode for decompile callbacks occurring during analysis of
* Java .class files
*
* All methods in this class take a StringBuilder object as the first argument. The generated pcode is emitted
* into that object.
*
*/
public class PcodeTextEmitter {
static final String RAM = "ram";
//private constructor to enforce noninstantiability
private PcodeTextEmitter(){
throw new AssertionError();
}
/**
* Emits pcode to push a value of computational category 1 onto the stack.
* @param pCode - StringBuilder to hold pcode.
* @param valueName - name of varnode to push.
*/
public static void emitPushCat1Value(StringBuilder pCode, String valueName){
pCode.append("SP = SP - 4;\n*:4 SP = ");
pCode.append(valueName);
pCode.append(";\n");
}
/**
* Emits pcode to push a value of computational category 2 onto the stack.
* @param pCode - StringBuilder to hold pcode.
* @param valueName - name of varnode to push.
*/
public static void emitPushCat2Value(StringBuilder pCode, String valueName){
pCode.append("SP = SP - 8;\n*:8 SP = ");
pCode.append(valueName);
pCode.append(";\n");
}
/**
* Emits pcode to pop a value of computational category 2 from the stack.
* @param pCode - StringBuilder to hold pcode.
* @param destName - name of destination varnode.
*/
public static void emitPopCat2Value(StringBuilder pCode, String destName){
pCode.append(destName);
pCode.append(":8 = *:8 SP;\nSP = SP + 8;\n");
}
/**
* Emits pcode to pop a value of computational category 1 from the stack.
* @param pCode - StringBuilder to hold pcode.
* @param destName - name of destination varnode.
*/
public static void emitPopCat1Value(StringBuilder pCode, String destName){
pCode.append(destName);
pCode.append(":4 = *:4 SP;\nSP = SP + 4;\n");
}
/**
* Emits pcode to assign four bytes resulting from a call to a black-box pcodeop
* @param pCode StringBuilder to hold the pcode
* @param lhs - varnode name for holding result
* @param pcodeop - name of pcodeop
* @param args - zero or more arguments for the pcodeop
*/
public static void emitAssignVarnodeFromPcodeOpCall(StringBuilder pCode, String varnodeName, int size, String pcodeop, String... args){
pCode.append(varnodeName);
pCode.append(":");
pCode.append(Integer.toString(size));
pCode.append(" = ");
pCode.append(pcodeop);
pCode.append("(");
for (int i = 0, numArgs = args.length; i < numArgs; ++i){
pCode.append(args[i]);
if (i < numArgs - 1){
pCode.append(",");
}
}
pCode.append(");\n");
}
/**
* Emits pcode to call a void black-box pcodeop
* @param pCode StringBuilder to hold the pcode
* @param pcodeop - name of pcodeop
* @param args - zero or more arguments for the pcodeop
*/
public static void emitVoidPcodeOpCall(StringBuilder pCode, String pcodeop, String... args){
pCode.append(pcodeop);
pCode.append("(");
for (int i = 0, numArgs = args.length; i < numArgs; ++i){
pCode.append(args[i]);
if (i < numArgs - 1){
pCode.append(",");
}
}
pCode.append(");\n");
}
/**
* Appends the pcode to assign an integer constant to a register
* @param pCode
* @param constantPool
* @param index
*/
public static void emitAssignConstantToRegister(StringBuilder pCode, String register, int constant){
pCode.append(register);
pCode.append(" = 0x");
pCode.append(Integer.toHexString(constant));
pCode.append(";\n");
}
/**
* Appends the pcode to assign a register to the result of a pcode op call with arguments args
* @param pCode
* @param register
* @param pcodeop
* @param args
*/
public static void emitAssignRegisterFromPcodeOpCall(StringBuilder pCode, String register, String pcodeop, String... args){
pCode.append(register);
pCode.append(" = ");
pCode.append(pcodeop);
pCode.append("(");
for (int i = 0, numArgs = args.length; i < numArgs; ++i){
pCode.append(args[i]);
if (i < numArgs - 1){
pCode.append(",");
}
}
pCode.append(");\n");
}
/**
* Appends the pcode to emit a label definition.
* @param pCode
* @param caseName
*/
public static void emitLabelDefinition(StringBuilder pCode, String caseName){
pCode.append("<");
pCode.append(caseName);
pCode.append(">\n");
}
public static void emitWriteToMemory(StringBuilder pCode, String space, int size, String offset, String value){
pCode.append("*[");
pCode.append(space);
pCode.append("]:");
pCode.append(Integer.toString(size));
pCode.append(" ");
pCode.append(offset);
pCode.append(" = ");
pCode.append(value);
pCode.append(";\n");
}
public static void emitIndirectCall(StringBuilder pCode, String target){
pCode.append("call [");
pCode.append(target);
pCode.append("];\n");
}
public static void emitAddToStackPointer(StringBuilder pCode, int amount){
pCode.append("SP = SP + ");
pCode.append(Integer.toString(amount));
pCode.append(";\n");
}
public static void emitSignExtension(StringBuilder pCode, String dest, int size, String src){
pCode.append(dest);
pCode.append(":");
pCode.append(Integer.toString(size));
pCode.append(" = sext(");
pCode.append(src);
pCode.append(");\n");
}
public static void emitZeroExtension(StringBuilder pCode, String dest, int size, String src){
pCode.append(dest);
pCode.append(":");
pCode.append(Integer.toString(size));
pCode.append(" = zext(");
pCode.append(src);
pCode.append(");\n");
}
public static void emitTruncate(StringBuilder pCode, String dest, int size, String src){
pCode.append(dest);
pCode.append(" = ");
pCode.append(src);
pCode.append(":");
pCode.append(Integer.toString(size));
pCode.append(";\n");
}
}

View file

@ -0,0 +1,315 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import ghidra.javaclass.format.DescriptorDecoder;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.javaclass.format.constantpool.ConstantPoolFieldReferenceInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolNameAndTypeInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolUtf8Info;
/**
*
* This class contains methods to produce pcode for the getstatic, putstatic
* getfield, and putfield instructions.
*
* This class is a non-instantiable utility class.
*
* In .class files, a getstatic, getfield, putstatic, or putfield instruction is
* followed by a two-byte index into the constant pool. At that index is a symbolic
* reference to a field, which ultimately yields the class, the field name, and the
* JVM type of the element to be gotten or put.
*/
public class ReferenceMethods {
static final String VALUE = "value";
static final String TEMP = "temp";
static final String NEW_VALUE = "newValue";
static final String OBJECT_REF = "objectRef";
static final String FIELD_OFFSET = "fieldOffset";
static final String STATIC_OFFSET = "staticOffset";
//private constructor to enforce noninstantiability
private ReferenceMethods(){
throw new AssertionError();
}
/**
* Generate a String of pcode for a getstatic op.
* @param index - the index of the field reference in the constant pool
* @param constantPool - the constant pool of the class file
* @return - the pcode string
*/
public static String getPcodeForGetStatic(int index, AbstractConstantPoolInfoJava[] constantPool) {
StringBuilder pCode = new StringBuilder();
//determine the computational category and push a value of the correct size onto the operand stack
String descriptor = getDescriptorForFieldRef(constantPool, index);
switch (descriptor.charAt(0)){
case DescriptorDecoder.BASE_TYPE_BYTE: //signed byte
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 1, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitSignExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_BOOLEAN: //boolean
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 1, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitZeroExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_CHAR: //char
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 2, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitZeroExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_SHORT: //signed short
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 2, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitSignExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_ARRAY: //array dimension
case DescriptorDecoder.BASE_TYPE_FLOAT: //float
case DescriptorDecoder.BASE_TYPE_INT: //int
case DescriptorDecoder.BASE_TYPE_REFERENCE: //object reference
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_DOUBLE: //double
case DescriptorDecoder.BASE_TYPE_LONG: //long
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitPushCat2Value(pCode,VALUE);
break;
default:
throw new IllegalArgumentException("Invalid descriptor: " + descriptor);
}
return pCode.toString();
}
/**
* Generate a String of pcode for a putstatic op.
* @param index - the index of the field reference in the constant pool
* @param constantPool - the constant pool of the class file
* @return - the pcode string
*/
public static String getPcodeForPutStatic(int index, AbstractConstantPoolInfoJava[] constantPool){
StringBuilder pCode = new StringBuilder();
String descriptor = getDescriptorForFieldRef(constantPool, index);
switch (descriptor.charAt(0)){
case DescriptorDecoder.BASE_TYPE_BYTE: //signed byte
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 1, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 1, STATIC_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_BOOLEAN: //boolean
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 1, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 1, STATIC_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_CHAR: //char
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 2, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 2, STATIC_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_SHORT: //signed short
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 2, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 2, STATIC_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_ARRAY: //array dimension
case DescriptorDecoder.BASE_TYPE_FLOAT: //float
case DescriptorDecoder.BASE_TYPE_INT: //int
case DescriptorDecoder.BASE_TYPE_REFERENCE: //object reference
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 4, STATIC_OFFSET, NEW_VALUE);
break;
case DescriptorDecoder.BASE_TYPE_DOUBLE: //double
case DescriptorDecoder.BASE_TYPE_LONG: //long
PcodeTextEmitter.emitPopCat2Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", Integer.toString(index), ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 8, STATIC_OFFSET, NEW_VALUE);
break;
default:
throw new IllegalArgumentException("Invalid descriptor: " + descriptor);
}
return pCode.toString();
}
/**
* Generate pcode for a getfield op
* @param index - the index of the field reference in the constant pool
* @param constantPool
* @return - the pcode string
*/
public static String getPcodeForGetField(int index, AbstractConstantPoolInfoJava[] constantPool) {
StringBuilder pCode = new StringBuilder();
String descriptor = getDescriptorForFieldRef(constantPool, index);
switch (descriptor.charAt(0)){
case DescriptorDecoder.BASE_TYPE_BYTE: //signed byte
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 1, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitSignExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_BOOLEAN: //boolean
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 1, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitZeroExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_CHAR: //char
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 2, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitZeroExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_SHORT: //signed short
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, TEMP, 2, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitSignExtension(pCode, VALUE, 4, TEMP);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_ARRAY: //array dimension
case DescriptorDecoder.BASE_TYPE_FLOAT: //float
case DescriptorDecoder.BASE_TYPE_INT: //int
case DescriptorDecoder.BASE_TYPE_REFERENCE: //object reference
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, VALUE, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitPushCat1Value(pCode,VALUE);
break;
case DescriptorDecoder.BASE_TYPE_DOUBLE: //double
case DescriptorDecoder.BASE_TYPE_LONG: //long
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, VALUE, 8, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitPushCat2Value(pCode,VALUE);
break;
default:
throw new IllegalArgumentException("Invalid descriptor: " + descriptor);
}
return pCode.toString();
}
/**
* Generate pcode for a putfield op
* @param index - the index of the field reference in the constant pool
* @param constantPool - the constant pool
* @return - the pcode
*/
public static String getPcodeForPutField(int index, AbstractConstantPoolInfoJava[] constantPool) {
StringBuilder pCode = new StringBuilder();
//determine the computational category and push a value of the correct size onto the operand stack
String descriptor = getDescriptorForFieldRef(constantPool, index);
switch (descriptor.charAt(0)){
case DescriptorDecoder.BASE_TYPE_BYTE: //signed byte
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 1, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 1, FIELD_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_BOOLEAN: //boolean
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 1, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 1, FIELD_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_CHAR: //char
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 2, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 2, FIELD_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_SHORT: //signed short
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitTruncate(pCode, TEMP, 2, NEW_VALUE);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 2, FIELD_OFFSET, TEMP);
break;
case DescriptorDecoder.BASE_TYPE_ARRAY: //array dimension
case DescriptorDecoder.BASE_TYPE_FLOAT: //float
case DescriptorDecoder.BASE_TYPE_INT: //int
case DescriptorDecoder.BASE_TYPE_REFERENCE: //object reference
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 4, FIELD_OFFSET, NEW_VALUE);
break;
case DescriptorDecoder.BASE_TYPE_DOUBLE: //double
case DescriptorDecoder.BASE_TYPE_LONG: //long
PcodeTextEmitter.emitPopCat2Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 8, FIELD_OFFSET, NEW_VALUE);
break;
default:
throw new IllegalArgumentException("Invalid descriptor: " + descriptor);
}
return pCode.toString();
/*JavaComputationalCategory category = DescriptorDecoder.getComputationalCategoryOfDescriptor(descriptor);
switch (category){
case CAT_1:
//top of operand stack is NEW_VALUE, the new value for the field
//next on stack is OBJECT_REF, a pointer to the object whose field is to be modified
PcodeTextEmitter.emitPopCat1Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 4, FIELD_OFFSET, NEW_VALUE);
break;
case CAT_2:
PcodeTextEmitter.emitPopCat2Value(pCode, NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(pCode, OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, OBJECT_REF, Integer.toString(index), ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitWriteToMemory(pCode, PcodeTextEmitter.RAM, 8, FIELD_OFFSET, NEW_VALUE);
break;
default:
throw new IllegalArgumentException("Bad computational category for descriptor " + descriptor);
}
return pCode.toString();*/
}
/**
* Returns the descriptor of a field reference in the constant pool
* @param constantPool
* @param index
* @return
*/
static String getDescriptorForFieldRef(AbstractConstantPoolInfoJava[] constantPool, int index){
ConstantPoolFieldReferenceInfo fieldRef = (ConstantPoolFieldReferenceInfo) constantPool[index];
int nameAndTypeIndex = fieldRef.getNameAndTypeIndex();
ConstantPoolNameAndTypeInfo nameAndTypeInfo = (ConstantPoolNameAndTypeInfo) constantPool[nameAndTypeIndex];
ConstantPoolUtf8Info descriptorInfo = (ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
return descriptorInfo.getString();
}
}

View file

@ -0,0 +1,67 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
/**
*
* This is a utility class for generating pcode for the lookupswitch operation.
*
* This class is evolving and may eventually be replaced.
*
*/
import java.io.IOException;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.listing.Program;
public class SwitchMethods {
static final String KEY = "key";
static final String SWITCH_TARGET = "switch_target";
public static String getPcodeForLookupSwitch(InjectContext injectContext, Program program) throws IOException {
StringBuilder pCode = new StringBuilder();
int defaultAddr = (int) injectContext.inputlist.get(0).getOffset();
int numPairs = (int) injectContext.inputlist.get(1).getOffset();
int padding = (int) injectContext.inputlist.get(2).getOffset();
PcodeTextEmitter.emitPopCat1Value(pCode, KEY);
int target = (int) (injectContext.baseAddr.getOffset() + defaultAddr);
PcodeTextEmitter.emitAssignConstantToRegister(pCode, SWITCH_TARGET, target);
ByteProvider provider = new MemoryByteProvider(program.getMemory(),injectContext.baseAddr);
byte[] bytes = provider.readBytes(1 + padding + 8, 8 * numPairs);
for (int i = 0, length = bytes.length ; i < length; i += 8){
int match = ((bytes[i] << 24) & 0xff000000) | ((bytes[i+1] << 16) & 0xff0000) | ((bytes[i+2] <<8) & 0xff00) | (bytes[i+3] & 0xff);
int offset = ((bytes[i+4] << 24) & 0xff000000) | ((bytes[i+5] << 16) & 0xff0000) | ((bytes[i+6] <<8) & 0xff00) | (bytes[i+7] & 0xff);
target = (int) (injectContext.baseAddr.getOffset() + offset);
pCode.append("if (key != " + match +") goto <test"+i+">;\n");
pCode.append(SWITCH_TARGET);
pCode.append(" = inst_start + " +offset+ ";\n");
//uncomment this to have the decompiler display multiple switch(address) statements
//pCode.append("goto [switch_target];\n");
PcodeTextEmitter.emitLabelDefinition(pCode, "test"+i);
}
pCode.append("SP=SP;\n");
provider.close();
return pCode.toString();
}
}

View file

@ -0,0 +1,224 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.analyzers;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.cmd.data.CreateStringCmd;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.services.Analyzer;
import ghidra.app.util.importer.MessageLog;
import ghidra.docking.settings.*;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StringDataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.NotEmptyException;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractJavaAnalyzer implements Analyzer {
@Override
final public boolean added(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog log) throws CancelledException {
try {
return analyze(program, set, monitor, log);
}
catch (Exception e) {
log.appendException(e);
e.printStackTrace();
}
return false;
}
@Override
final public void analysisEnded(Program program) {
}
@Override
final public void registerOptions(Options options, Program program) {
}
@Override
final public void optionsChanged(Options propertyList, Program program) {
}
@Override
final public boolean removed(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog log) throws CancelledException {
return false;
}
final public void restoreDefaultOptions(Options propertyList, Program program) {
}
@Override
final public boolean supportsOneTimeAnalysis() {
return false;
}
public abstract boolean analyze(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog log) throws Exception;
protected void changeDataSettings(Program program, TaskMonitor monitor) {
monitor.setMessage("Changing data settings...");
Address address = program.getMinAddress();
while (!monitor.isCancelled()) {
Data data = getDataAt(program, address);
if (data == null) {
data = getDataAfter(program, address);
}
if (data == null) {
break;
}
int numComponents = data.getNumComponents();
for (int i = 0; i < numComponents; ++i) {
if (monitor.isCancelled()) {
break;
}
Data component = data.getComponent(i);
byte[] bytes = new byte[component.getLength()];
try {
program.getMemory().getBytes(component.getAddress(), bytes);
}
catch (MemoryAccessException e) {
}
boolean isAscii = true;
for (byte b : bytes) {
if (b < ' ' || b > '~') {
isAscii = false;
}
}
if (isAscii && bytes.length > 1) {
changeFormatToString(component);
}
/*
* if (component.getFieldName().equals("magic") ||
* component.getFieldName().equals("identifier") ||
* component.getFieldName().equals("compression") ||
* component.getFieldName().equals("format") ||
* component.getFieldName().equals("type")) {
* changeFormatToString(component); }
*/
}
address = address.add(data.getLength());
}
}
protected void removeEmptyFragments(Program program) throws NotEmptyException {
ProgramModule rootModule = program.getListing().getRootModule("Program Tree");
Group[] children = rootModule.getChildren();
for (Group child : children) {
if (child instanceof ProgramFragment) {
ProgramFragment fragment = (ProgramFragment) child;
if (fragment.isEmpty()) {
rootModule.removeChild(fragment.getName());
}
}
}
}
protected void changeFormatToString(Data data) {
SettingsImpl settings = new SettingsImpl(data);
settings.setDefaultSettings(settings);
SettingsDefinition[] settingsDefinitions = data.getDataType().getSettingsDefinitions();
for (SettingsDefinition settingsDefinition : settingsDefinitions) {
if (settingsDefinition instanceof FormatSettingsDefinition) {
FormatSettingsDefinition format = (FormatSettingsDefinition) settingsDefinition;
format.setChoice(data, FormatSettingsDefinition.CHAR);
}
}
}
protected ProgramFragment createFragment(Program program, String fragmentName, Address start,
Address end) throws Exception {
ProgramModule module = program.getListing().getDefaultRootModule();
ProgramFragment fragment = getFragment(module, fragmentName);
if (fragment == null) {
fragment = module.createFragment(fragmentName);
}
fragment.move(start, end.subtract(1));
return fragment;
}
protected ProgramFragment getFragment(ProgramModule module, String fragmentName) {
Group[] groups = module.getChildren();
for (Group group : groups) {
if (group.getName().equals(fragmentName)) {
return (ProgramFragment) group;
}
}
return null;
}
protected Data getDataAt(Program program, Address address) {
return program.getListing().getDefinedDataAt(address);
}
protected Data getDataAfter(Program program, Data data) {
return getDataAfter(program, data.getMaxAddress());
}
protected Data getDataAfter(Program program, Address address) {
return program.getListing().getDefinedDataAfter(address);
}
protected Address toAddr(Program program, long offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
}
protected Address toCpAddr(Program program, long offset) {
return program.getAddressFactory().getAddressSpace("constantPool").getAddress(offset);
}
protected Data createData(Program program, Address address, DataType datatype)
throws Exception {
if (datatype instanceof StringDataType) {
CreateStringCmd cmd = new CreateStringCmd(address);
if (!cmd.applyTo(program)) {
throw new RuntimeException(cmd.getStatusMsg());
}
}
else {
CreateDataCmd cmd = new CreateDataCmd(address, datatype);
if (!cmd.applyTo(program)) {
throw new RuntimeException(cmd.getStatusMsg());
}
}
return program.getListing().getDefinedDataAt(address);
}
protected boolean setPlateComment(Program program, Address address, String comment) {
SetCommentCmd cmd = new SetCommentCmd(address, CodeUnit.PLATE_COMMENT, comment);
return cmd.applyTo(program);
}
protected Function createFunction(Program program, Address entryPoint) {
CreateFunctionCmd cmd = new CreateFunctionCmd(entryPoint);
cmd.applyTo(program);
return program.getListing().getFunctionAt(entryPoint);
}
protected Address find(Program program, Address start, byte[] values, TaskMonitor monitor) {
return program.getMemory().findBytes(start, values, null, true, monitor);
}
}

View file

@ -0,0 +1,837 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.analyzers;
import java.util.*;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.plugin.core.analysis.AnalysisWorker;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.javaclass.format.*;
import ghidra.javaclass.format.attributes.*;
import ghidra.javaclass.format.constantpool.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.BasicCompilerSpec;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class JavaAnalyzer extends AbstractJavaAnalyzer implements AnalysisWorker {
private MessageLog log;
@Override
public String getName() {
return "Java Class Analyzer";
}
@Override
public AnalyzerType getAnalysisType() {
return AnalyzerType.BYTE_ANALYZER;
}
@Override
public boolean getDefaultEnablement(Program program) {
try {
return JavaClassUtil.isClassFile(program);
}
catch (Exception e) {
//ignore
}
return false;
}
@Override
public String getDescription() {
return "Analyzes Java .class files.";
}
@Override
public AnalysisPriority getPriority() {
return AnalysisPriority.FORMAT_ANALYSIS;
}
@Override
public boolean canAnalyze(Program program) {
try {
return JavaClassUtil.isClassFile(program);
}
catch (Exception e) {
// ignore
}
return false;
}
@Override
public boolean isPrototype() {
return false;
}
@Override
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog messageLog) throws Exception {
this.log = messageLog;
AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
//changed third param to true so that decompiler switch analysis runs during auto-analysis
return manager.scheduleWorker(this, null, true, monitor);
}
@Override
public boolean analysisWorkerCallback(Program program, Object workerContext,
TaskMonitor monitor) throws Exception {
Address address =
program.getAddressFactory().getAddressSpace("constantPool").getMinAddress();
ByteProvider provider = new MemoryByteProvider(program.getMemory(), address);
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
ClassFileJava classFile = new ClassFileJava(reader);
DataType classFileDataType = classFile.toDataType();
Data cpClassFileData = createData(program, address, classFileDataType);
if (cpClassFileData == null) {
log.appendMsg("Unable to create header data.");
}
Data constantPoolData = cpClassFileData.getComponent(4);
markupConstantPoolAndReferences(program, classFile, constantPoolData, monitor);
createProgramDataTypes(program, classFile, monitor, log);
markupFields(program, classFile, monitor);
markupMethods(program, classFile, monitor);
disassembleMethods(program, classFile, monitor);
labelOperands(program, classFile);
recordJavaVersionInfo(program, classFile);
BasicCompilerSpec.enableJavaLanguageDecompilation(program);
return true;
}
private void disassembleMethods(Program program, ClassFileJava classFile, TaskMonitor monitor)
throws MemoryAccessException, DuplicateNameException, InvalidInputException {
MethodInfoJava[] methods = classFile.getMethods();
for (int i = 0, max = methods.length; i < max; ++i) {
Address index = JavaClassUtil.toLookupAddress(program, i);
int offset = program.getMemory().getInt(index);
Address blockStart =
program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
DisassembleCommand cmd = new DisassembleCommand(blockStart, null, true);
cmd.applyTo(program, monitor);
Function function = createFunction(program, blockStart);
if (function == null) {
continue;
}
setFunctionInfo(function, methods[i], classFile, program.getDataTypeManager());
}
}
/**
* Create datatypes for all classes mentioned in the constant pool
* @param program
* @param classFile
* @param monitor
* @param messageLog
*/
private void createProgramDataTypes(Program program, ClassFileJava classFile,
TaskMonitor monitor, MessageLog messageLog) {
monitor.setMessage("JVM: processing class definitions");
//iterate through the constant pool and add all referenced classes and interfaces
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
DataTypeManager dtm = program.getDataTypeManager();
for (int i = 0, length = constantPool.length; i < length; ++i) {
if (constantPool[i] instanceof ConstantPoolClassInfo) {
addTypeForClassInfoElement(constantPool, i, dtm);
continue;
}
if (constantPool[i] instanceof ConstantPoolNameAndTypeInfo) {
addTypesForNameAndTypeInfo(constantPool, i, dtm);
continue;
}
}
return;
}
private void addTypesForNameAndTypeInfo(AbstractConstantPoolInfoJava[] constantPool, int i,
DataTypeManager dtm) {
ConstantPoolNameAndTypeInfo nameAndType = (ConstantPoolNameAndTypeInfo) constantPool[i];
ConstantPoolUtf8Info utf8 =
(ConstantPoolUtf8Info) constantPool[nameAndType.getDescriptorIndex()];
String descriptor = utf8.getString();
//method descriptor
if (descriptor.contains("(")) {
List<String> classNames = DescriptorDecoder.getTypeNameList(descriptor, true, false);
for (String className : classNames) {
if (isPrimitiveType(className)) {
continue;
}
DescriptorDecoder.resolveClassForString(className, dtm, DWordDataType.dataType);
}
}
//field descriptor
else {
String className = DescriptorDecoder.getTypeNameFromDescriptor(descriptor, true, false);
if (isPrimitiveType(className)) {
return;
}
DescriptorDecoder.resolveClassForString(className, dtm, DWordDataType.dataType);
}
}
private boolean isPrimitiveType(String type) {
switch (type) {
case "boolean":
case "byte":
case "short":
case "char":
case "int":
case "float":
case "long":
case "double":
case "void":
return true;
default:
return false;
}
}
private void addTypeForClassInfoElement(AbstractConstantPoolInfoJava[] constantPool, int index,
DataTypeManager dtm) {
ConstantPoolClassInfo classInfo = (ConstantPoolClassInfo) constantPool[index];
int nameIndex = classInfo.getNameIndex();
ConstantPoolUtf8Info utf8Info = (ConstantPoolUtf8Info) constantPool[nameIndex];
DescriptorDecoder.resolveClassForString(utf8Info.getString(), dtm, DWordDataType.dataType);
}
private void recordJavaVersionInfo(Program program, ClassFileJava classFile) {
Options programInfo = program.getOptions(Program.PROGRAM_INFO);
programInfo.setInt("Major Version", classFile.getMajorVersion());
programInfo.setInt("Minor Version", classFile.getMinorVersion());
String javaVersion;
switch (classFile.getMajorVersion()) {
case 45:
javaVersion = "1.1";
break;
case 46:
javaVersion = "1.2";
break;
case 47:
javaVersion = "1.3";
break;
case 48:
javaVersion = "1.4";
break;
case 49:
javaVersion = "1.5";
break;
case 50:
javaVersion = "1.6";
break;
case 51:
javaVersion = "1.7";
break;
case 52:
javaVersion = "1.8";
break;
case 53:
javaVersion = "1.9";
break;
default:
javaVersion = "Unknown";
break;
}
programInfo.setString("Java Version", javaVersion);
}
private void markupConstantPoolAndReferences(Program program, ClassFileJava classFile,
Data constantPoolData, TaskMonitor monitor) {
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
Map<Integer, Integer> indexMap = getIndexMap(constantPool);
for (int i = 1; i < constantPool.length; i++) {
if (monitor.isCancelled()) {
return;
}
AbstractConstantPoolInfoJava constantPoolInfo = constantPool[i];
BootstrapMethods[] bootstrapMethods =
getBootStrapMethodAttribute(classFile, constantPool, indexMap);
createConstantPoolReference(constantPoolData, constantPoolInfo, bootstrapMethods,
indexMap, i);
}
}
private BootstrapMethods[] getBootStrapMethodAttribute(ClassFileJava classFile,
AbstractConstantPoolInfoJava[] constantPool, Map<Integer, Integer> indexMap) {
AbstractAttributeInfo[] attributes = classFile.getAttributes();
for (AbstractAttributeInfo attribute : attributes) {
short nameIndex = attribute.getAttributeNameIndex();
AbstractConstantPoolInfoJava poolEntry = classFile.getConstantPool()[nameIndex];
if (poolEntry instanceof ConstantPoolUtf8Info) {
String name = ((ConstantPoolUtf8Info) poolEntry).getString();
if (name.equals("BootstrapMethods")) {
return ((BootstrapMethodsAttribute) attribute).getBootstrapMethods();
}
}
}
return null;
}
private void createConstantPoolReference(Data constantPoolData,
AbstractConstantPoolInfoJava constantPoolInfo, BootstrapMethods[] bootstrapMethods,
Map<Integer, Integer> indexMap, int i) {
if (constantPoolInfo instanceof ConstantPoolClassInfo ||
constantPoolInfo instanceof ConstantPoolStringInfo ||
constantPoolInfo instanceof ConstantPoolMethodTypeInfo) {
Data data = constantPoolData.getComponent(indexMap.get(i));
Data indexData = data.getComponent(1);
Object indexValue = indexData.getValue();
if (indexValue instanceof Scalar) {
int index = (int) (((Scalar) indexValue).getValue() & 0xFFFF);
Data referredData = constantPoolData.getComponent(indexMap.get(index));
indexData.addValueReference(referredData.getAddress(), RefType.DATA);
}
}
else if (constantPoolInfo instanceof ConstantPoolFieldReferenceInfo ||
constantPoolInfo instanceof ConstantPoolMethodReferenceInfo ||
constantPoolInfo instanceof ConstantPoolInterfaceMethodReferenceInfo ||
constantPoolInfo instanceof ConstantPoolNameAndTypeInfo) {
Data data = constantPoolData.getComponent(indexMap.get(i));
Data indexData = data.getComponent(1);
Object indexValue = indexData.getValue();
if (indexValue instanceof Scalar) {
int index = (int) (((Scalar) indexValue).getValue() & 0xFFFF);
Data referredData = constantPoolData.getComponent(indexMap.get(index));
indexData.addValueReference(referredData.getAddress(), RefType.DATA);
}
indexData = data.getComponent(2);
indexValue = indexData.getValue();
if (indexValue instanceof Scalar) {
int index = (int) (((Scalar) indexValue).getValue() & 0xFFFF);
Data referredData = constantPoolData.getComponent(indexMap.get(index));
indexData.addValueReference(referredData.getAddress(), RefType.DATA);
}
}
else if (constantPoolInfo instanceof ConstantPoolInvokeDynamicInfo) {
Data data = constantPoolData.getComponent(indexMap.get(i));
Data indexData = data.getComponent(1);
Object indexValue = indexData.getValue();
if (indexValue instanceof Scalar) {
int index = (int) (((Scalar) indexValue).getValue() & 0xFFFF);
BootstrapMethods bootstrapMethod = bootstrapMethods[index];
index = bootstrapMethod.getBootstrapMethodsReference() & 0xFFFF;
Data referredData = constantPoolData.getComponent(indexMap.get(index));
indexData.addValueReference(referredData.getAddress(), RefType.DATA);
}
indexData = data.getComponent(2);
indexValue = indexData.getValue();
if (indexValue instanceof Scalar) {
int index = (int) (((Scalar) indexValue).getValue() & 0xFFFF);
Data referredData = constantPoolData.getComponent(indexMap.get(index));
indexData.addValueReference(referredData.getAddress(), RefType.DATA);
}
}
else if (constantPoolInfo instanceof ConstantPoolMethodHandleInfo) {
Data data = constantPoolData.getComponent(indexMap.get(i));
Data indexData = data.getComponent(2);
Object indexValue = indexData.getValue();
if (indexValue instanceof Scalar) {
int index = (int) (((Scalar) indexValue).getValue() & 0xFFFF);
Data referredData = constantPoolData.getComponent(indexMap.get(index));
indexData.addValueReference(referredData.getAddress(), RefType.DATA);
}
}
}
private Map<Integer, Integer> getIndexMap(AbstractConstantPoolInfoJava[] constantPool) {
Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
// offset starts at one since JVM constant pool indexing starts with 1...
int offset = 1;
for (int i = 1; i < constantPool.length; i++) {
indexMap.put(i, i - offset);
if (constantPool[i] instanceof ConstantPoolLongInfo ||
constantPool[i] instanceof ConstantPoolDoubleInfo) {
offset++;
}
}
return indexMap;
}
private void labelOperands(Program program, ClassFileJava classFile) {
InstructionIterator instructionIt =
program.getListing().getInstructions(toAddr(program, 0x10000), true);
while (instructionIt.hasNext()) {
Instruction instruction = instructionIt.next();
Scalar opValue = instruction.getScalar(0);
if (opValue == null) {
continue;
}
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
int index = (int) (opValue.getValue() & 0xFFFFFFFF);
String opMarkup = getOperandMarkup(program, constantPool, index);
instruction.setComment(CodeUnit.EOL_COMMENT, opMarkup);
}
}
private void markupFields(Program program, ClassFileJava classFile, TaskMonitor monitor) {
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
FieldInfoJava[] fields = classFile.getFields();
for (FieldInfoJava fieldInfo : fields) {
if (monitor.isCancelled()) {
break;
}
ConstantPoolUtf8Info fieldName =
(ConstantPoolUtf8Info) constantPool[fieldInfo.getNameIndex()];
ConstantPoolUtf8Info fieldDescriptor =
(ConstantPoolUtf8Info) constantPool[fieldInfo.getDescriptorIndex()];
Address fieldAddress = toCpAddr(program, fieldInfo.getOffset());
String comment = "FieldName = " + fieldName.getString() + "\n" + "FieldDescriptor = " +
fieldDescriptor.getString() + "\n";
setEolComment(program, fieldAddress, comment);
}
}
private void markupMethods(Program program, ClassFileJava classFile, TaskMonitor monitor) {
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
MethodInfoJava[] methods = classFile.getMethods();
for (MethodInfoJava methodInfo : methods) {
if (monitor.isCancelled()) {
break;
}
ConstantPoolUtf8Info methodName =
(ConstantPoolUtf8Info) constantPool[methodInfo.getNameIndex()];
ConstantPoolUtf8Info methodDescriptor =
(ConstantPoolUtf8Info) constantPool[methodInfo.getDescriptorIndex()];
Address methodAddress = toCpAddr(program, methodInfo.getOffset());
StringBuffer buffer = new StringBuffer();
buffer.append("MethodName = " + methodName.getString() + "\n");
buffer.append("MethodDescriptor = " + methodDescriptor.getString() + "\n");
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
if (codeAttribute != null) { //code attribute is null when method is abstract
LocalVariableTableAttribute localVariableTableAttribute =
codeAttribute.getLocalVariableTableAttribute();
if (localVariableTableAttribute != null) {
LocalVariableJava[] localVariables =
localVariableTableAttribute.getLocalVariables();
long localVariableTableOffset = localVariableTableAttribute.getOffset();
int offsetInTable = 8;
for (LocalVariableJava localVariable : localVariables) {
ConstantPoolUtf8Info descriptor =
(ConstantPoolUtf8Info) constantPool[localVariable.getDescriptorIndex()];
ConstantPoolUtf8Info name =
(ConstantPoolUtf8Info) constantPool[localVariable.getNameIndex()];
String comment = "local: name = " + name + " type = " + descriptor;
buffer.append("\t" + comment + "\n");
Address localVariableAddress =
toCpAddr(program, localVariableTableOffset + offsetInTable);
setEolComment(program, localVariableAddress, comment);
offsetInTable += 10;
}
}
}
setEolComment(program, methodAddress, buffer.toString());
}
}
private String getOperandMarkup(Program program, AbstractConstantPoolInfoJava[] constantPool,
int index) {
if (index >= constantPool.length || index < 0) {
// TODO: < 0 can happen with if<cond> branches backwards. Goto's should be handled.
return "";
}
AbstractConstantPoolInfoJava constantPoolInfo = constantPool[index];
String opMarkup = "";
//
// if (monitor.isCancelled()) {
// break;
// }
if (constantPoolInfo != null) {
switch (constantPoolInfo.getTag()) {
case ConstantPoolTagsJava.CONSTANT_Class: {
ConstantPoolClassInfo info = (ConstantPoolClassInfo) constantPoolInfo;
ConstantPoolUtf8Info utf8 =
(ConstantPoolUtf8Info) constantPool[info.getNameIndex()];
opMarkup = utf8.getString().replaceAll("/", ".");
break;
}
case ConstantPoolTagsJava.CONSTANT_Double: {
ConstantPoolDoubleInfo info = (ConstantPoolDoubleInfo) constantPoolInfo;
double value = info.getValue();
opMarkup = Double.toString(value);
break;
}
case ConstantPoolTagsJava.CONSTANT_Fieldref: {
ConstantPoolFieldReferenceInfo info =
(ConstantPoolFieldReferenceInfo) constantPoolInfo;
ConstantPoolClassInfo classInfo =
(ConstantPoolClassInfo) constantPool[info.getClassIndex()];
ConstantPoolUtf8Info className =
(ConstantPoolUtf8Info) constantPool[classInfo.getNameIndex()];
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[info.getNameAndTypeIndex()];
ConstantPoolUtf8Info fieldName =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
ConstantPoolUtf8Info fieldDescriptor =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
opMarkup = className.getString().replaceAll("/", ".") + "." +
fieldName.getString() + " : " + DescriptorDecoder.getTypeNameFromDescriptor(
fieldDescriptor.getString(), true, true);
break;
}
case ConstantPoolTagsJava.CONSTANT_Float: {
ConstantPoolFloatInfo info = (ConstantPoolFloatInfo) constantPoolInfo;
float value = info.getValue();
opMarkup = Float.toString(value);
break;
}
case ConstantPoolTagsJava.CONSTANT_Integer: {
ConstantPoolIntegerInfo info = (ConstantPoolIntegerInfo) constantPoolInfo;
int value = info.getValue();
opMarkup = Integer.toString(value);
break;
}
case ConstantPoolTagsJava.CONSTANT_InterfaceMethodref: {
ConstantPoolInterfaceMethodReferenceInfo info =
(ConstantPoolInterfaceMethodReferenceInfo) constantPoolInfo;
ConstantPoolClassInfo classInfo =
(ConstantPoolClassInfo) constantPool[info.getClassIndex()];
ConstantPoolUtf8Info className =
(ConstantPoolUtf8Info) constantPool[classInfo.getNameIndex()];
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[info.getNameAndTypeIndex()];
ConstantPoolUtf8Info interfaceName =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
ConstantPoolUtf8Info interfaceDescriptor =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
String descriptor = interfaceDescriptor.getString();
String params = getParameters(descriptor);
String returnType =
DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor,
program.getDataTypeManager()).getName();
opMarkup = className.getString().replaceAll("/", ".") + "." +
interfaceName.getString() + params + " : " + returnType;
break;
}
case ConstantPoolTagsJava.CONSTANT_InvokeDynamic: {
ConstantPoolInvokeDynamicInfo info =
(ConstantPoolInvokeDynamicInfo) constantPoolInfo;
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[info.getNameAndTypeIndex()];
ConstantPoolUtf8Info name =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
opMarkup = name.getString();
break;
}
case ConstantPoolTagsJava.CONSTANT_Long: {
ConstantPoolLongInfo info = (ConstantPoolLongInfo) constantPoolInfo;
long value = info.getValue();
opMarkup = Long.toString(value);
break;
}
case ConstantPoolTagsJava.CONSTANT_MethodHandle: {
ConstantPoolMethodHandleInfo info =
(ConstantPoolMethodHandleInfo) constantPoolInfo;
if (info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_getField ||
info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_getStatic ||
info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_putField ||
info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_putStatic) {
ConstantPoolFieldReferenceInfo field =
(ConstantPoolFieldReferenceInfo) constantPool[info.getReferenceIndex()];
ConstantPoolClassInfo classInfo =
(ConstantPoolClassInfo) constantPool[field.getClassIndex()];
ConstantPoolUtf8Info className =
(ConstantPoolUtf8Info) constantPool[classInfo.getNameIndex()];
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[field.getNameAndTypeIndex()];
ConstantPoolUtf8Info fieldName =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
ConstantPoolUtf8Info fieldInfo =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
String descriptor = fieldInfo.getString();
String params = getParameters(descriptor);
String returnType =
DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor,
program.getDataTypeManager()).getName();
opMarkup = className.getString().replaceAll("/", ".") + "." +
fieldName.getString() + params + " : " + returnType;
}
else if (info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_invokeVirtual ||
info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_invokeStatic ||
info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_invokeSpecial ||
info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_newInvokeSpecial) {
ConstantPoolMethodReferenceInfo method =
(ConstantPoolMethodReferenceInfo) constantPool[info.getReferenceIndex()];
ConstantPoolClassInfo classInfo =
(ConstantPoolClassInfo) constantPool[method.getClassIndex()];
ConstantPoolUtf8Info className =
(ConstantPoolUtf8Info) constantPool[classInfo.getNameIndex()];
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[method.getNameAndTypeIndex()];
ConstantPoolUtf8Info methodName =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
ConstantPoolUtf8Info methodInfo =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
String descriptor = methodInfo.getString();
String params = getParameters(descriptor);
String returnType =
DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor,
program.getDataTypeManager()).getName();
opMarkup = className.getString().replaceAll("/", ".") + "." +
methodName.getString() + params + " : " + returnType;
}
else if (info.getReferenceKind() == MethodHandleBytecodeBehaviors.REF_invokeInterface) {
ConstantPoolInterfaceMethodReferenceInfo interfaceMethod =
(ConstantPoolInterfaceMethodReferenceInfo) constantPool[info.getReferenceIndex()];
ConstantPoolClassInfo classInfo =
(ConstantPoolClassInfo) constantPool[interfaceMethod.getClassIndex()];
ConstantPoolUtf8Info className =
(ConstantPoolUtf8Info) constantPool[classInfo.getNameIndex()];
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[interfaceMethod.getNameAndTypeIndex()];
ConstantPoolUtf8Info interfaceMethodName =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
ConstantPoolUtf8Info interfaceMethodInfo =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
String descriptor = interfaceMethodInfo.getString();
String params = getParameters(descriptor);
String returnType =
DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor,
program.getDataTypeManager()).getName();
opMarkup = className.getString().replaceAll("/", ".") + "." +
interfaceMethodName.getString() + params + " : " + returnType;
}
break;
}
case ConstantPoolTagsJava.CONSTANT_Methodref: {
ConstantPoolMethodReferenceInfo info =
(ConstantPoolMethodReferenceInfo) constantPoolInfo;
ConstantPoolClassInfo classInfo =
(ConstantPoolClassInfo) constantPool[info.getClassIndex()];
ConstantPoolUtf8Info className =
(ConstantPoolUtf8Info) constantPool[classInfo.getNameIndex()];
ConstantPoolNameAndTypeInfo nameAndTypeInfo =
(ConstantPoolNameAndTypeInfo) constantPool[info.getNameAndTypeIndex()];
ConstantPoolUtf8Info methodName =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getNameIndex()];
ConstantPoolUtf8Info methodDescriptor =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
ConstantPoolUtf8Info methodInfo =
(ConstantPoolUtf8Info) constantPool[nameAndTypeInfo.getDescriptorIndex()];
String descriptor = methodInfo.getString();
String params = getParameters(descriptor);
String returnType =
DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor,
program.getDataTypeManager()).getName();
if (methodName.getString().equals("<init>")) {
opMarkup = className.getString() + params;
}
else {
opMarkup = className.getString().replaceAll("/", ".") + "." +
methodName.getString() + params + " : " + returnType;
}
break;
}
case ConstantPoolTagsJava.CONSTANT_MethodType: {
ConstantPoolMethodTypeInfo info = (ConstantPoolMethodTypeInfo) constantPoolInfo;
ConstantPoolUtf8Info methodType =
(ConstantPoolUtf8Info) constantPool[info.getDescriptorIndex()];
opMarkup = methodType.getString();
break;
}
case ConstantPoolTagsJava.CONSTANT_NameAndType: {
ConstantPoolNameAndTypeInfo info =
(ConstantPoolNameAndTypeInfo) constantPoolInfo;
ConstantPoolUtf8Info fieldName =
(ConstantPoolUtf8Info) constantPool[info.getNameIndex()];
ConstantPoolUtf8Info fieldDescriptor =
(ConstantPoolUtf8Info) constantPool[info.getDescriptorIndex()];
opMarkup = fieldName.getString();
break;
}
case ConstantPoolTagsJava.CONSTANT_String: {
ConstantPoolStringInfo info = (ConstantPoolStringInfo) constantPoolInfo;
ConstantPoolUtf8Info utf8 =
(ConstantPoolUtf8Info) constantPool[info.getStringIndex()];
opMarkup = utf8.toString();
break;
}
case ConstantPoolTagsJava.CONSTANT_Utf8: {
ConstantPoolUtf8Info utf8 = (ConstantPoolUtf8Info) constantPoolInfo;
opMarkup = utf8.getString();
break;
}
}
}
return opMarkup;
}
private String getParameters(String descriptor) {
List<String> paramTypeNames = DescriptorDecoder.getTypeNameList(descriptor, true, true);
StringBuilder sb = new StringBuilder();
sb.append("(");
//don't append the last element of the list, which is the return type
for (int i = 0, max = paramTypeNames.size() - 1; i < max; ++i) {
sb.append(paramTypeNames.get(i));
if (i < max - 1) {
sb.append(", ");
}
}
sb.append(")");
return sb.toString();
}
@Override
public String getWorkerName() {
return getName();
}
private boolean setEolComment(Program program, Address address, String comment) {
if (address.getAddressSpace() != program.getAddressFactory().getDefaultAddressSpace()) {
return false;
}
SetCommentCmd cmd = new SetCommentCmd(address, CodeUnit.EOL_COMMENT, comment);
return cmd.applyTo(program);
}
/**
* Sets the name, return type, and parameter types of a method using the information in the constant pool.
* Also overrides the signatures on all method invocations made within the method body.
* @param function - the function (method)
* @param methodDescriptor - the name of the memory block containing the function's code. It is assumed that blockName
* is the concatenation of the method name and the descriptor
* @param constantPool
* @throws DuplicateNameException
* @throws InvalidInputException
*/
private void setFunctionInfo(Function function, MethodInfoJava methodInfo,
ClassFileJava classFile, DataTypeManager dtManager)
throws DuplicateNameException, InvalidInputException {
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
String functionName =
((ConstantPoolUtf8Info) constantPool[methodInfo.getNameIndex()]).getString();
String descriptor =
((ConstantPoolUtf8Info) constantPool[methodInfo.getDescriptorIndex()]).getString();
//note: the name of a function in java is not necessarily unique, but the name together
//with the descriptor is. Hence we append the type names of the parameters and return to
//the function name to avoid an exception being thrown for duplicate names.
List<String> typeNames = DescriptorDecoder.getTypeNameList(descriptor, true, true);
StringBuilder sb = new StringBuilder();
sb.append(functionName);
for (String name : typeNames) {
sb.append("_");
sb.append(name);
}
function.setName(sb.toString(), SourceType.ANALYSIS);
function.setCallingConvention(Function.DEFAULT_CALLING_CONVENTION_STRING);
DataType returnType =
DescriptorDecoder.getReturnTypeOfMethodDescriptor(descriptor, dtManager);
function.setReturnType(returnType, SourceType.ANALYSIS);
List<Variable> params = new ArrayList<>();
//in order to set the parameters for the method, we need to know whether
//there is an implicit 'this' parameter.
if (!methodInfo.isStatic()) {
int thisIndex = classFile.getThisClass();
ConstantPoolClassInfo thisClass = (ConstantPoolClassInfo) constantPool[thisIndex];
int nameIndex = thisClass.getNameIndex();
ConstantPoolUtf8Info thisNameInfo = (ConstantPoolUtf8Info) constantPool[nameIndex];
DataType thisType = DescriptorDecoder.resolveClassForString(thisNameInfo.getString(),
dtManager, DWordDataType.dataType);
ParameterImpl thisParam = new ParameterImpl("this", thisType, function.getProgram());
params.add(thisParam);
}
List<DataType> explicitParams = DescriptorDecoder.getDataTypeList(descriptor, dtManager);
for (int i = 0, max = explicitParams.size(); i < max; ++i) {
ParameterImpl currentParam = new ParameterImpl("param" + Integer.toString(i + 1),
explicitParams.get(i), function.getProgram());
params.add(currentParam);
}
function.replaceParameters(params, FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true,
SourceType.ANALYSIS);
}
}

View file

@ -0,0 +1,277 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.analyzers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.javaclass.format.JavaClassUtil;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
/**
* This analyzer reads through JVM .class files looking for lookupswitch and tableswitch instructions.
* For each such instruction, the analyzer determines all the possible jump targets, disassembles them,
* and then newly-disassembled code to the appropriate function body.
*/
public class JvmSwitchAnalyzer extends AbstractJavaAnalyzer {
private static final String ANALYZER_NAME = "JVM Switch Analyzer";
private static final String ANALYZER_DESCRIPTION =
"Disassembles jump targets of tableswitch " + " and lookupswitch instructions";
private static final String LOOKUPSWITCH_MNEMONIC = "lookupswitch";
private static final String TABLESWITCH_MNEMONIC = "tableswitch";
private static final String DEFAULT_CASE_LABEL = "default";
@Override
public String getName() {
return ANALYZER_NAME;
}
@Override
public AnalyzerType getAnalysisType() {
return AnalyzerType.INSTRUCTION_ANALYZER;
}
@Override
public boolean getDefaultEnablement(Program program) {
return true;
}
@Override
public String getDescription() {
return ANALYZER_DESCRIPTION;
}
@Override
public AnalysisPriority getPriority() {
return AnalysisPriority.DISASSEMBLY;
}
@Override
public boolean canAnalyze(Program program) {
try {
return JavaClassUtil.isClassFile(program);
}
catch (Exception e) {
// ignore
}
return false;
}
@Override
public boolean isPrototype() {
return false;
}
@Override
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
monitor.setMaximum(set.getNumAddresses());
monitor.setProgress(0);
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
BinaryReader reader = new BinaryReader(provider, false);
Listing listing = program.getListing();
InstructionIterator instructionIterator = listing.getInstructions(set, true);
//find the switch instructions and process them
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
monitor.checkCanceled();
monitor.incrementProgress(instruction.getLength());
String mnenomic = instruction.getMnemonicString();
if (!mnenomic.equals(TABLESWITCH_MNEMONIC) && !mnenomic.equals(LOOKUPSWITCH_MNEMONIC)) {
continue; //only care about switch instructions
}
if (instruction.getMnemonicReferences().length > 0) {
continue; //analyzer has already handled this instructions
}
monitor.setMessage("JvmSwitchAnalyzer: " + instruction.getMinAddress());
if (instruction.getMnemonicString().equals(TABLESWITCH_MNEMONIC)) {
processTableSwitch(program, reader, instruction, monitor);
}
else {
processLookupSwitch(program, reader, instruction, monitor);
}
}
return true;
}
private void processTableSwitch(Program program, BinaryReader bReader, Instruction instruction,
TaskMonitor monitor) {
Register alignmentPad = program.getRegister("alignmentPad");
int alignment = instruction.getValue(alignmentPad, false).intValue();
if (instruction.getOperandReferences(0).length == 0) {
Msg.info(this,
"Skipping tableswitch instruction at " + instruction.getAddress().toString() +
" - missing operand reference for default case.");
return; //user manually deleted reference
}
// WARNING: this is very dependent on the sub-constructor for the switch stmt.
//if the op-object order changes for the operand, this will fail
Object[] opObjects = instruction.getOpObjects(0);
Address defaultAddress = instruction.getOperandReferences(0)[0].getToAddress();
long low = ((Scalar) opObjects[1]).getUnsignedValue();
long high = ((Scalar) opObjects[2]).getUnsignedValue();
List<Address> addressesToDisassemble = new ArrayList<>();
//handle the default case
addressesToDisassemble.add(defaultAddress);
addLabelAndReference(program, instruction, defaultAddress, DEFAULT_CASE_LABEL);
long base = instruction.getMemory().getMinAddress().getOffset();
long index = instruction.getMinAddress().getOffset();
index -= base;
index += (1 + alignment + 4 + 4 + 4); //tableswitch opcode + alignment + size of default + size of low + size of high
bReader.setPointerIndex(index);
for (int i = 0; i <= high - low; i++) {
try {
int offset = bReader.readNextInt();
Address toDis = instruction.getMinAddress().add(offset);
addressesToDisassemble.add(toDis);
String label = "case_" + (low + i) + "_(0x" + Long.toHexString(low + i) + ")";
addLabelAndReference(program, instruction, toDis, label);
}
catch (IOException e) {
Msg.error(this, e.getMessage());
}
}
disassembleCases(program, addressesToDisassemble);
fixupFunction(program, instruction, addressesToDisassemble, monitor);
}
private void processLookupSwitch(Program program, BinaryReader bReader, Instruction instruction,
TaskMonitor monitor) {
Register alignmentPad = program.getRegister("alignmentPad");
int alignment = instruction.getValue(alignmentPad, false).intValue();
// WARNING: this is very dependent on the sub-constructor for the switch stmt.
// if the op-object order changes for the operand, this will fail
Object[] opObjects = instruction.getOpObjects(0);
long defaultOffset = ((Scalar) opObjects[0]).getUnsignedValue();
long numberOfCases = ((Scalar) opObjects[1]).getUnsignedValue();
List<Address> addressesToDisassemble = new ArrayList<>();
//handle the default case
Address defaultAddress = instruction.getMinAddress().add(defaultOffset);
addressesToDisassemble.add(defaultAddress);
addLabelAndReference(program, instruction, defaultAddress, DEFAULT_CASE_LABEL);
long base = instruction.getMemory().getMinAddress().getOffset();
long index = instruction.getMinAddress().getOffset();
index -= base;
index += (1 + alignment + 4 + 4); //lookupswitch opcode + alignment + size of default + size of num pairs
bReader.setPointerIndex(index);
for (int i = 0; i < numberOfCases; i++) {
try {
int match = bReader.readNextInt();
int offset = bReader.readNextInt();
Address toDis = instruction.getMinAddress().add(offset);
addressesToDisassemble.add(toDis);
String label = "case_" + match + "_(0x" + Integer.toHexString(match) + ")";
addLabelAndReference(program, instruction, toDis, label);
}
catch (IOException e) {
Msg.error(this, e.getMessage());
}
}
disassembleCases(program, addressesToDisassemble);
fixupFunction(program, instruction, addressesToDisassemble, monitor);
}
private void disassembleCases(Program program, List<Address> addressesToDisassemble) {
for (Address addr : addressesToDisassemble) {
DisassembleCommand dCommand = new DisassembleCommand(addr, null, true);
dCommand.applyTo(program);
}
}
private void addLabelAndReference(Program program, Instruction switchInstruction,
Address target, String label) {
program.getReferenceManager().addMemoryReference(switchInstruction.getMinAddress(), target,
RefType.COMPUTED_JUMP, SourceType.ANALYSIS, CodeUnit.MNEMONIC);
//put switch table cases into namespace for the switch
//create namespace if necessary
Namespace space = null;
String switchName =
switchInstruction.getMnemonicString() + "_" + switchInstruction.getAddress().toString();
try {
space = program.getSymbolTable().createNameSpace(null, switchName, SourceType.ANALYSIS);
}
catch (DuplicateNameException e) {
space = program.getSymbolTable().getNamespace(switchName, null);
}
catch (InvalidInputException e) {
// just go with default space
}
try {
program.getSymbolTable().createLabel(target, label, space, SourceType.ANALYSIS);
}
catch (InvalidInputException e1) {
Msg.error(this, e1.getMessage());
}
}
private void fixupFunction(Program program, Instruction instruction, List<Address> additions,
TaskMonitor monitor) {
Function func =
program.getFunctionManager().getFunctionContaining(instruction.getAddress());
AddressSet newBody = new AddressSet(func.getBody());
for (Address addr : additions) {
newBody.add(addr);
}
try {
func.setBody(newBody);
}
catch (OverlappingFunctionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
CreateFunctionCmd.fixupFunctionBody(program, func, monitor);
}
catch (CancelledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,69 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.analyzers;
import ghidra.util.Msg;
import java.lang.reflect.Field;
public final class MethodHandleBytecodeBehaviors {
/** getfield C.f:T */
public final static int REF_getField = 1;
/** getstatic C.f:T */
public final static int REF_getStatic = 2;
/** putfield C.f:T */
public final static int REF_putField = 3;
/** putstatic C.f:T */
public final static int REF_putStatic = 4;
/** invokevirtual C.m:(A*)T */
public final static int REF_invokeVirtual = 5;
/** invokestatic C.m:(A*)T */
public final static int REF_invokeStatic = 6;
/** invokespecial C.m:(A*)T */
public final static int REF_invokeSpecial = 7;
/** new C; dup; invokespecial C.<init>:(A*)void */
public final static int REF_newInvokeSpecial = 8;
/** invokeinterface C.m:(A*)T */
public final static int REF_invokeInterface = 9;
public final static String getName( int kind ) {
Field [] fields = MethodHandleBytecodeBehaviors.class.getDeclaredFields( );
for (Field field : fields) {
if (field.getName( ).startsWith( "REF_" )) {
try {
Integer value = (Integer) field.get( null );
if (value == kind) {
return field.getName( );
}
}
catch (Exception e) {
Msg.error( MethodHandleBytecodeBehaviors.class, "Unexpected Exception: " + e.getMessage( ), e );
}
}
}
return "Unrecognized kind: 0x" + Integer.toHexString( kind );
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.flags;
public final class ClassFileFlags {
/** Declared public; may be accessed from outside its package. */
public final static short ACC_PUBLIC = 0x0001;
/** Declared final; no subclasses allowed. */
public final static short ACC_FINAL = 0x0010;
/** Treat superclass methods specially when invoked by the invokespecial instruction. */
public final static short ACC_SUPER = 0x0020;
/** Is an interface, not a class. */
public final static short ACC_INTERFACE = 0x0200;
/** Declared abstract; must not be instantiated. */
public final static short ACC_ABSTRACT = 0x0400;
/** Declared synthetic; not present in the source code. */
public final static short ACC_SYNTHETIC = 0x1000;
/** Declared as an annotation type. */
public final static short ACC_ANNOTATION = 0x2000;
/** Declared as an enum type. */
public final static short ACC_ENUM = 0x4000;
}

View file

@ -0,0 +1,40 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.flags;
final class FieldInfoAccessFlags {
/** Declared public; may be accessed from outside its package. */
public final short ACC_PUBLIC = 0x0001;
/** Declared private; usable only within the defining class. */
public final short ACC_PRIVATE = 0x0002;
/** Declared protected; may be accessed within subclasses. */
public final short ACC_PROTECTED = 0x0004;
/** Declared static. */
public final short ACC_STATIC = 0x0008;
/** Declared final; never directly assigned to after object construction (JLS ?17.5). */
public final short ACC_FINAL = 0x0010;
/** Declared volatile; cannot be cached. */
public final short ACC_VOLATILE = 0x0040;
/** Declared transient; not written or read by a persistent object manager. */
public final short ACC_TRANSIENT = 0x0080;
/** Declared synthetic; not present in the source code. */
public final short ACC_SYNTHETIC = 0x1000;
/** Declared as an element of an enum. */
public final short ACC_ENUM = 0x4000;
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.flags;
public final class MethodsInfoAccessFlags {
/** Declared public; may be accessed from outside its package. */
public final static short ACC_PUBLIC = 0x0001;
/** Declared private; accessible only within the defining class. */
public final static short ACC_PRIVATE = 0x0002;
/** Declared protected; may be accessed within subclasses. */
public final static short ACC_PROTECTED = 0x0004;
/** Declared static. */
public final static short ACC_STATIC = 0x0008;
/** Declared final; must not be overridden (5.4.5). */
public final static short ACC_FINAL = 0x0010;
/** Declared synchronized; invocation is wrapped by a monitor use. */
public final static short ACC_SYNCHRONIZED = 0x0020;
/** A bridge method, generated by the compiler. */
public final static short ACC_BRIDGE = 0x0040;
/** Declared with variable number of arguments. */
public final static short ACC_VARARGS = 0x0080;
/** Declared native; implemented in a language other than Java. */
public final static short ACC_NATIVE = 0x0100;
/** Declared abstract; no implementation is provided. */
public final static short ACC_ABSTRACT = 0x0400;
/** Declared strictfp; floating-point mode is FP-strict. */
public final static short ACC_STRICT = 0x0800;
/** Declared synthetic; not present in the source code. */
public final static short ACC_SYNTHETIC = 0x1000;
}

View file

@ -0,0 +1,165 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
/**
* A utility class defining access flags and access utility methods.
* Access flags are as defined by the Java Virtual Machine Specification
* Second Edition, tables 4.1, 4.4, 4.5 and 4.7.
*/
public abstract class AccessFlagsJava {
/** Declared public, may be accessed from outside its package. */
public static final int PUBLIC = 0x0001;
/** Declared private, usable only within the defining class. */
public static final int PRIVATE = 0x0002;
/** Declared protected, may be accessed within subclasses. */
public static final int PROTECTED = 0x0004;
/** Declared static. */
public static final int STATIC = 0x0008;
/**
* Declared final. For classes this means no subclassing allowed.
* For fields it means no further assignment allowed after initialization.
* For methods it means that the method cannot be overridden.
*/
public static final int FINAL = 0x0010;
/** Declared synchronized; invocation is wrapped in a monitor lock. */
public static final int SYNCHRONIZED = 0x0020;
/**
* Treat superclass methods specially when invoked by the
* <i>invokespecial</i> instruction. This access only applies to
* classes, and shares the same value as SYNCHRONIZED.
*/
public static final int SUPER = 0x0020;
/** Declared volatile; cannot be cached. */
public static final int VOLATILE = 0x0040;
/** A bridge method, generated by the compiler. */
public static final int BRIDGE = 0x0040;
/**
* Declared transient; not written or read by a persistent object
* manager
*/
public static final int TRANSIENT = 0x0080;
/** Declared with a variable number of arguments. */
public static final int VARARGS = 0x0080;
/** Declared native; implemented in a language other than Java. */
public static final int NATIVE = 0x0100;
/** Is an interface, not a class. */
public static final int INTERFACE = 0x0200;
/** Declared abstract; must not be instantiated. */
public static final int ABSTRACT = 0x0400;
/** Declared strictfp; floating point mode is FP-strict. */
public static final int STRICT = 0x0800;
/** Declared synthetic, not present in the source file. */
public static final int SYNTHETIC = 0x1000;
/** Declared as an annotation type. */
public static final int ANNOTATION = 0x2000;
/**
* For classes, declared as an enum type. For fields, declared as
* an element of an enum.
*/
public static final int ENUM = 0x4000;
/**
* Return a text representation for a given set of access flags.
* Here are some examples:
* <DL>
* <DD><CODE>"public static final"</CODE>,</DD>
* <DD><CODE>"package private"</CODE>, or</DD>
* <DD><CODE>"protected transient"</CODE>.</DD>
* </DL>
* Note: only access flags that map to Java modifier keywords are returned.
* @param access the mask of flags denoting access permission.
* @return a text representation of the access flags.
*/
public static String toString( int access, boolean isClass ) {
StringBuffer stringBuffer = new StringBuffer();
if ( ( access & PUBLIC ) == PUBLIC ) {
stringBuffer.append( "public " );
}
if ( ( access & PRIVATE ) == PRIVATE ) {
stringBuffer.append( "private " );
}
if ( ( access & PROTECTED ) == PROTECTED ) {
stringBuffer.append( "protected " );
}
if ( ( access & STATIC ) == STATIC ) {
stringBuffer.append( "static " );
}
if ( ( access & FINAL ) == FINAL ) {
stringBuffer.append( "final " );
}
if ( !isClass && ( access & SYNCHRONIZED ) == SYNCHRONIZED ) {
stringBuffer.append( "synchronized " );
}
if ( ( access & VOLATILE ) == VOLATILE ) {
stringBuffer.append( "volatile ");
}
if ( ( access & TRANSIENT ) == TRANSIENT ) {
stringBuffer.append( "transient " );
}
if ( ( access & NATIVE) == NATIVE ) {
stringBuffer.append( "native " );
}
if ( ( access & ABSTRACT ) == ABSTRACT && ( access & INTERFACE ) == 0) {//interfaces are always abstract, so drop the abstract keyword
stringBuffer.append( "abstract " );
}
// trim trailing space
return stringBuffer.toString();
}
public static boolean isStatic( int access ) {
return ( ( access & STATIC ) == STATIC );
}
public static final boolean isPublic( int access ) {
return ( ( access & PUBLIC ) == PUBLIC );
}
public static final boolean isProtected( int access ) {
return ( ( access & PROTECTED ) == PROTECTED );
}
public static final boolean isPackagePrivate( int access ) {
return ( ( access & ( PUBLIC | PRIVATE | PROTECTED ) ) == 0 );
}
public static final boolean isPrivate( int access ) {
return ( ( access & PRIVATE ) == PRIVATE );
}
public static final boolean isInterface( int access ) {
return ( ( access & INTERFACE ) == INTERFACE );
}
}

View file

@ -0,0 +1,126 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
import java.io.IOException;
import java.util.HashMap;
import ghidra.app.plugin.core.analysis.AnalysisState;
import ghidra.app.plugin.core.analysis.AnalysisStateInfo;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.Msg;
/**
* Class for holding the {@link ClassFileJava} and {@link MethodInfoJava} in memory
* for a particular .class file Program. These describe the objects in the constant pool and
* signatures of individual methods. They are parsed directly from the .class
* file (and so can't really change) and are shared via this {@link AnalysisState} with
* any plug-in that needs to do p-code analysis.
*/
public class ClassFileAnalysisState implements AnalysisState {
private Program program;
private ClassFileJava classFile; // Constant-pool and method descriptions
private UniqueAddressFactory uniqueFactory; // Produces temporary registers during p-code generation
private HashMap<Address, MethodInfoJava> methodMap; // Map from address to method description
public ClassFileAnalysisState(Program program) throws IOException {
this.program = program;
AddressFactory factory = program.getAddressFactory();
AddressSpace space = factory.getAddressSpace("constantPool");
if (space == null) {
throw new IllegalStateException("Not a valid class file");
}
Memory memory = program.getMemory();
MemoryByteProvider provider = new MemoryByteProvider(memory, space);
BinaryReader reader = new BinaryReader(provider, false);
classFile = new ClassFileJava(reader);
uniqueFactory =
new UniqueAddressFactory(program.getAddressFactory(), program.getLanguage());
}
/**
* @return the class file information {@link ClassFileJava} held by this {@link AnalysisState}
*/
public ClassFileJava getClassFile() {
return classFile;
}
/**
* Recover the description of the method at a specific address.
* @param addr is the given Address
* @return the MethodInfoJava describing the method, or null if no method is found at the address
*/
public MethodInfoJava getMethodInfo(Address addr) {
if (methodMap == null) {
try {
buildMethodMap();
}
catch (MemoryAccessException e) {
Msg.error(this, e.getMessage(), e);
// methodMap will be non-null but empty
}
}
return methodMap.get(addr);
}
/**
* Generate (the address of) a new temporary register, for use when resolving injections
* during p-code generation for a Java instruction
* @return the address of the next available temporary register
*/
public Address getNextUniqueAddress() {
return uniqueFactory.getNextUniqueAddress();
}
/**
* Walk through the {@link MethodInfoJava} objects in {@link ClassFileJava} and
* create a map from Address to the corresponding object
* @throws MemoryAccessException
*/
private void buildMethodMap() throws MemoryAccessException {
methodMap = new HashMap<>();
MethodInfoJava[] methods = classFile.getMethods();
Memory memory = program.getMemory();
AddressSpace defaultAddressSpace = program.getAddressFactory().getDefaultAddressSpace();
for (int i = 0, max = methods.length; i < max; ++i) {
Address methodIndexAddress = JavaClassUtil.toLookupAddress(program, i);
int offset = memory.getInt(methodIndexAddress);
Address methodStart = defaultAddressSpace.getAddress(offset);
methodMap.put(methodStart, methods[i]);
}
}
/**
* Return persistent <code>ClassFileAnalysisState</code> which corresponds to the specified program instance.
* @param program
* @return <code>ClassFileAnalysisState</code> for specified program instance
*/
public static ClassFileAnalysisState getState(Program program) throws IOException {
ClassFileAnalysisState analysisState =
AnalysisStateInfo.getAnalysisState(program, ClassFileAnalysisState.class);
if (analysisState == null) {
analysisState = new ClassFileAnalysisState(program);
AnalysisStateInfo.putAnalysisState(program, analysisState);
}
return analysisState;
}
}

View file

@ -0,0 +1,409 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.javaclass.format.attributes.AbstractAttributeInfo;
import ghidra.javaclass.format.attributes.AttributeFactory;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.javaclass.format.constantpool.ConstantPoolDoubleInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolFactory;
import ghidra.javaclass.format.constantpool.ConstantPoolLongInfo;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* A class file consists of a single ClassFile structure:
* <pre>
* ClassFile {
* u4 magic;
* u2 minor_version;
* u2 major_version;
* u2 constant_pool_count;
* cp_info constant_pool[constant_pool_count-1];
* u2 access_flags;
* u2 this_class;
* u2 super_class;
* u2 interfaces_count;
* u2 interfaces[interfaces_count];
* u2 fields_count;
* field_info fields[fields_count];
* u2 methods_count;
* method_info methods[methods_count];
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
*/
public class ClassFileJava implements StructConverter {
private int magic;
private short minorVersion;
private short majorVersion;
private short constantPoolCount;
private AbstractConstantPoolInfoJava[] constantPool;
private short accessFlags;
private short thisClass;
private short superClass;
private short interfacesCount;
private short[] interfaces;
private short fieldsCount;
private FieldInfoJava[] fields;
private short methodsCount;
private MethodInfoJava[] methods;
private short attributesCount;
private AbstractAttributeInfo[] attributes;
public ClassFileJava(BinaryReader reader) throws IOException {
magic = reader.readNextInt();
if (magic != JavaClassConstants.MAGIC) {
throw new IOException("Invalid Java Class File.");
}
minorVersion = reader.readNextShort();
majorVersion = reader.readNextShort();
constantPoolCount = reader.readNextShort();
constantPool = new AbstractConstantPoolInfoJava[constantPoolCount];
//NOTE: start at index 1 per JVM specification!!!
for (int i = 1; i < constantPoolCount; i++) {
constantPool[i] = ConstantPoolFactory.get(reader);
//From section 4.4.5 of JVM specification:
//All 8-byte constants take up two entries in the constant_pool table of the class
//file. If a CONSTANT_Long_info or CONSTANT_Double_info structure is the item
//in the constant_pool table at index n, then the next usable item in the pool is
///located at index n+2. The constant_pool index n+1 must be valid but is considered
//unusable.
if (constantPool[i] instanceof ConstantPoolLongInfo ||
constantPool[i] instanceof ConstantPoolDoubleInfo) {
++i;
}
}
accessFlags = reader.readNextShort();
thisClass = reader.readNextShort();
superClass = reader.readNextShort();
interfacesCount = reader.readNextShort();
interfaces = reader.readNextShortArray(interfacesCount);
fieldsCount = reader.readNextShort();
fields = new FieldInfoJava[fieldsCount];
for (int i = 0; i < fieldsCount; i++) {
fields[i] = new FieldInfoJava(reader, this);
}
methodsCount = reader.readNextShort();
methods = new MethodInfoJava[methodsCount];
for (int i = 0; i < methodsCount; i++) {
methods[i] = new MethodInfoJava(reader, this);
}
attributesCount = reader.readNextShort();
attributes = new AbstractAttributeInfo[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = AttributeFactory.get(reader, getConstantPool());
}
}
/**
* The magic item supplies the magic number identifying the class file format;
* it has the value 0xCAFEBABE.
* @return the magic number identifying the class file format
*/
public int getMagic() {
return magic;
}
/**
* Returns the minor version number of this class.
* <p>
* If a class file has major version number M and minor
* version number m, we denote the version of its
* class file format as M.m. Thus, class file format versions may be ordered
* lexicographically, for example, 1.5 < 2.0 < 2.1.
* <p>
* A Java virtual machine implementation can support a class file format of
* version v if and only if v lies in some contiguous range Mi.0 ? v ? Mj.m.
* <p>
* @return the minor version number of this class.
*/
public short getMinorVersion() {
return minorVersion;
}
/**
* Returns the major version number of this class.
* <p>
* If a class file has major version number M and minor
* version number m, we denote the version of its
* class file format as M.m. Thus, class file format versions may be ordered
* lexicographically, for example, 1.5 < 2.0 < 2.1.
* <p>
* A Java virtual machine implementation can support a class file format of
* version v if and only if v lies in some contiguous range Mi.0 ? v ? Mj.m.
* <p>
* @return the major version number of this class.
*/
public short getMajorVersion() {
return majorVersion;
}
/**
* The value of the constant_pool_count item is equal to the number of entries
* in the constant_pool table plus one. A constant_pool index is considered
* valid if it is greater than zero and less than constant_pool_count, with the
* exception for constants of type long and double noted in ?4.4.5.
* @return the number of entries in the constant_pool table plus one
*/
public short getConstantPoolCount() {
return constantPoolCount;
}
/**
* The constant_pool is a table of structures (?4.4) representing various string
* constants, class and interface names, field names, and other constants that are
* referred to within the ClassFile structure and its substructures. The format of
* each constant_pool table entry is indicated by its first "tag" byte.
* <p>
* The constant_pool table is indexed from 1 to constant_pool_count-1.
* @return the constant pool table
*/
public AbstractConstantPoolInfoJava[] getConstantPool() {
return constantPool;
}
/**
* The value of the access_flags item is a mask of flags used to denote access
* permissions to and properties of this class or interface. The interpretation of
* each flag, when set, is as shown in Table 4.1.
* @return a mask of flags used to denote access permissions to and properties of this class or interface
*/
public short getAccessFlags() {
return accessFlags;
}
/**
* The value of the this_class item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Class_info (?4.4.1) structure representing the class or interface
* defined by this class file.
* @return a valid index into the constant_pool table to a CONSTANT_Class_info
*/
public short getThisClass() {
return thisClass;
}
/**
* For a class, the value of the super_class item either must be zero or
* must be a valid index into the constant_pool table. If the value of the
* super_class item is nonzero, the constant_pool entry at that index must be
* a CONSTANT_Class_info (?4.4.1) structure representing the direct superclass
* of the class defined by this class file. Neither the direct superclass nor any of
* its superclasses may have the ACC_FINAL flag set in the access_flags item of
* its ClassFile structure.
* <p>
* If the value of the super_class item is zero, then this class file must represent
* the class Object, the only class or interface without a direct superclass.
* <p>
* For an interface, the value of the super_class item must always be a valid
* index into the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Class_info structure representing the class Object.
* @return a valid index into the constant_pool table to a CONSTANT_Class_info
*/
public short getSuperClass() {
return superClass;
}
/**
* The value of the interfaces_count item gives the number of direct
* superinterfaces of this class or interface type.
* @return the number of direct superinterfaces of this class
*/
public short getInterfacesCount() {
return interfacesCount;
}
/**
* Each value in the interfaces array must be a valid index into
* the constant_pool table. The constant_pool entry at each value
* of interfaces[i], where 0 ? i < interfaces_count, must be a
* CONSTANT_Class_info (?4.4.1) structure representing an interface that is a
* direct superinterface of this class or interface type, in the left-to-right order
* given in the source for the type.
* @return an array of interfaces
*/
public short[] getInterfaces() {
return interfaces;
}
/**
* The value of the fields_count item gives the number of field_info
* structures in the fields table. The field_info (?4.5) structures represent all
* fields, both class variables and instance variables, declared by this class or
* interface type.
* @return the number of field_info structures in the fields table
*/
public short getFieldsCount() {
return fieldsCount;
}
/**
* Each value in the fields table must be a field_info (?4.5) structure giving
* a complete description of a field in this class or interface. The fields table
* includes only those fields that are declared by this class or interface. It does
* not include items representing fields that are inherited from superclasses or
* superinterfaces.
* @return an array of fields
*/
public FieldInfoJava[] getFields() {
return fields;
}
/**
* The value of the methods_count item gives the number of method_info
* structures in the methods table.
* @return the number of method_info structures in the methods table
*/
public short getMethodsCount() {
return methodsCount;
}
/**
* Each value in the methods table must be a method_info (?4.6) structure giving
* a complete description of a method in this class or interface. If neither of the
* ACC_NATIVE and ACC_ABSTRACT flags are set in the access_flags item of a
* method_info structure, the Java virtual machine instructions implementing the
* method are also supplied.
* <p>
* The method_info structures represent all methods declared by this class
* or interface type, including instance methods, class methods, instance
* initialization methods (?2.9), and any class or interface initialization method
* (?2.9). The methods table does not include items representing methods that are
* inherited from superclasses or superinterfaces.
* @return an array of methods
*/
public MethodInfoJava[] getMethods() {
return methods;
}
/**
* The value of the attributes_count item gives the number of attributes
* in the attributes table of this class.
* @return the number of attributes in the attributes table
*/
public short getAttributesCount() {
return attributesCount;
}
/**
* Each value of the attributes table must be an attribute_info structure.
* <p>
* The attributes defined by this specification as appearing in
* the attributes table of a ClassFile structure are the
* InnerClasses (?4.7.6), EnclosingMethod (?4.7.7), Synthetic (?4.7.8),
* Signature (?4.7.9), SourceFile (?4.7.10), SourceDebugExtension
* (?4.7.11), Deprecated (?4.7.15), RuntimeVisibleAnnotations (?4.7.16),
* RuntimeInvisibleAnnotations (?4.7.17), and BootstrapMethods (?4.7.21) attributes.
* <p>
* If a Java virtual machine implementation recognizes class files whose
* version number is 49.0 or above, it must recognize and correctly
* read Signature (?4.7.9), RuntimeVisibleAnnotations (?4.7.16), and
* RuntimeInvisibleAnnotations (?4.7.17) attributes found in the attributes
* table of a ClassFile structure of a class file whose version number is 49.0
* or above.
* <p>
* If a Java virtual machine implementation recognizes class files whose
* version number is 51.0 or above, it must recognize and correctly read
* BootstrapMethods (?4.7.21) attributes found in the attributes table of a
* ClassFile structure of a class file whose version number is 51.0 or above.
* A Java virtual machine implementation is required to silently ignore any or
* all attributes in the attributes table of a ClassFile structure that it does
* not recognize. Attributes not defined in this specification are not allowed to
* affect the semantics of the class file, but only to provide additional descriptive
* information (?4.7.1).
* @return an array of attributes
*/
public AbstractAttributeInfo[] getAttributes() {
return attributes;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "ClassFile";
Structure structure = new StructureDataType(name, 0);
structure.add(DWORD, "magic", null);
structure.add(WORD, "minor_version", null);
structure.add(WORD, "major_version", null);
structure.add(WORD, "constant_pool_count", null);
Structure constantPoolStruct = new StructureDataType("constant_pool", 0);
for (int i = 0; i < constantPool.length; ++i) {
if (constantPool[i] != null) {
constantPoolStruct.add(constantPool[i].toDataType(),
"constant_pool_0x" + Integer.toHexString(i), null);
}
}
structure.add(constantPoolStruct, "constant_pool", null);
structure.add(WORD, "access_flags", null);
structure.add(WORD, "this_class", null);
structure.add(WORD, "super_class", null);
structure.add(WORD, "interfaces_count", null);
if (interfacesCount > 0) {
DataType array = new ArrayDataType(WORD, interfacesCount, WORD.getLength());
structure.add(array, "interfaces", null);
}
structure.add(WORD, "field_count", null);
if (fieldsCount > 0) {
Structure fieldStruct = new StructureDataType("fields", 0);
for (int i = 0; i < fields.length; ++i) {
fieldStruct.add(fields[i].toDataType(), "field_" + i, null);
}
structure.add(fieldStruct, "fields", null);
}
structure.add(WORD, "method_count", null);
if (methodsCount > 0) {
Structure methodsStruct = new StructureDataType("methods", 0);
for (int i = 0; i < methods.length; ++i) {
methodsStruct.add(methods[i].toDataType(), "methods_" + i, null);
}
structure.add(methodsStruct, "methods", null);
}
structure.add(WORD, "attributes_count", null);
if (attributesCount > 0){
Structure attributesStruct = new StructureDataType("attributes", 0);
for (int i = 0; i < attributes.length; ++i) {
attributesStruct.add(attributes[i].toDataType(), "attributes_" + i, null);
}
structure.add(attributesStruct, "attributes", null);
}
return structure;
}
}

View file

@ -0,0 +1,501 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.pcodeInject.*;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.javaclass.format.constantpool.ConstantPoolInterfaceMethodReferenceInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolInvokeDynamicInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolMethodReferenceInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolNameAndTypeInfo;
import ghidra.javaclass.format.constantpool.ConstantPoolUtf8Info;
import ghidra.program.model.data.*;
/**
*
* This is a utility class containing methods to parse information of out of method
* and field descriptors.
*
*/
public class DescriptorDecoder {
public final static byte BASE_TYPE_BYTE = 'B';
public final static byte BASE_TYPE_CHAR = 'C';
public final static byte BASE_TYPE_SHORT = 'S';
public final static byte BASE_TYPE_INT = 'I';
public final static byte BASE_TYPE_LONG = 'J';
public final static byte BASE_TYPE_FLOAT = 'F';
public final static byte BASE_TYPE_DOUBLE = 'D';
public final static byte BASE_TYPE_BOOLEAN = 'Z';
public final static byte BASE_TYPE_STRING = 's';
public final static byte BASE_TYPE_VOID = 'V';
public final static byte BASE_TYPE_CLASS = 'c';
public final static byte BASE_TYPE_ARRAY = '[';
public final static byte BASE_TYPE_REFERENCE = 'L';
public final static byte BASE_TYPE_ENUM = 'e';
public final static byte BASE_TYPE_ANNOTATION = '@';
//private constructor to enforce noninstantiability
private DescriptorDecoder(){
throw new AssertionError();
}
/**
* Calculates the stack purge associated with a given method descriptor. Each parameter of computational category
* one contributes 4 to the stack purge, and each parameter of computational category 2 contributes 8.
* @param methodDescriptor
* @return
*/
public static int getStackPurge(String methodDescriptor){
int stackPurge = 0;
List<JavaComputationalCategory> categories = getParameterCategories(methodDescriptor);
for (JavaComputationalCategory cat : categories){
switch (cat){
case CAT_1:
stackPurge += PcodeInjectLibraryJava.REFERENCE_SIZE;
break;
case CAT_2:
stackPurge += PcodeInjectLibraryJava.REFERENCE_SIZE * 2;
break;
default:
throw new IllegalArgumentException("Bad category!");
}
}
return stackPurge;
}
/**
* Returns the computational category of the return type of a method descriptor.
* @param methodDescriptor
* @return
*/
public static JavaComputationalCategory getReturnCategoryOfMethodDescriptor(String methodDescriptor){
int closeParenIndex = methodDescriptor.indexOf(")");
if (closeParenIndex == -1){
throw new IllegalArgumentException("Invalid method descriptor: " + methodDescriptor);
}
String returnDescriptor = methodDescriptor.substring(closeParenIndex + 1, methodDescriptor.length());
return DescriptorDecoder.getComputationalCategoryOfDescriptor(returnDescriptor);
}
public static DataType getReturnTypeOfMethodDescriptor(String methodDescriptor, DataTypeManager dtManager){
int closeParenIndex = methodDescriptor.indexOf(")");
if (closeParenIndex == -1){
throw new IllegalArgumentException("Invalid method descriptor: " + methodDescriptor);
}
String returnDescriptor = methodDescriptor.substring(closeParenIndex + 1, methodDescriptor.length());
if (returnDescriptor.startsWith("[")){
return getPointerType(returnDescriptor, dtManager);
}
return DescriptorDecoder.getDataTypeOfDescriptor(returnDescriptor, dtManager);
}
/**
* Returns the computational category of a given parameter or field descriptor
* @param descriptor
* @return
*/
public static JavaComputationalCategory getComputationalCategoryOfDescriptor(String descriptor){
//all references to objects start with "L"
//all references to arrays start with "["
//all other descriptors are just one letter.
switch (descriptor.charAt(0)){
case BASE_TYPE_BYTE: //signed byte
case BASE_TYPE_CHAR: //char
case BASE_TYPE_FLOAT: //float
case BASE_TYPE_INT: //int
case BASE_TYPE_REFERENCE: //object reference
case BASE_TYPE_SHORT: //signed short
case BASE_TYPE_BOOLEAN: //boolean
case BASE_TYPE_ARRAY: //array dimension
return JavaComputationalCategory.CAT_1;
case BASE_TYPE_DOUBLE: //double
case BASE_TYPE_LONG: //long
return JavaComputationalCategory.CAT_2;
case BASE_TYPE_VOID: //void (only for return types)
return JavaComputationalCategory.VOID;
default:
throw new IllegalArgumentException("Invalid computational category: " + descriptor);
}
}
/**
* Returns an ordered list of the type names corresponding to the parameters and return of a method.
* @param methodDescriptor
* @return
*/
public static List<String> getTypeNameList(String methodDescriptor, boolean fullyQualifiedName, boolean replaceSlash) {
ArrayList<String> typeNames = new ArrayList<>();
int closeParenIndex = methodDescriptor.indexOf(")");
String argString = methodDescriptor.substring(1, closeParenIndex);
String currentParamTypeName;
int currentPosition = 0;
int len = argString.length();
while (currentPosition < len){
String currentParam = argString.substring(currentPosition, currentPosition + 1);
if (currentParam.equals("[")){
int initialBracket = currentPosition;
while (argString.charAt(currentPosition) == '['){
currentPosition++;
}
//advance past the base type of the array
if (argString.charAt(currentPosition) == 'L'){
int semiColonIndex = argString.indexOf(";", currentPosition);
currentPosition = semiColonIndex + 1;
}
else{
currentPosition++;
}
currentParamTypeName = getTypeNameFromDescriptor(argString.substring(initialBracket,currentPosition), fullyQualifiedName, replaceSlash);
typeNames.add(currentParamTypeName);
continue;
}
//advance to next type in argString
//if it's a reference, it starts with L and ends with a ;
//otherwise you only need to advance one character
switch(currentParam){
case "L":
int semiColonIndex = argString.indexOf(";", currentPosition);
currentParamTypeName = getTypeNameFromDescriptor(argString.substring(currentPosition,semiColonIndex+1), fullyQualifiedName, replaceSlash);
currentPosition = semiColonIndex + 1; //advance past ;
break;
default:
currentParamTypeName = getTypeNameFromDescriptor(currentParam, fullyQualifiedName, replaceSlash);
currentPosition++;
}
typeNames.add(currentParamTypeName);
}
//now add the the name of the return type
String returnType = methodDescriptor.substring(closeParenIndex+1, methodDescriptor.length());
typeNames.add(getTypeNameFromDescriptor(returnType, fullyQualifiedName, replaceSlash));
return typeNames;
}
/**
* Returns the type name for a parameter descriptor
* @param descriptor
* @return
*/
public static String getTypeNameFromDescriptor(String descriptor, boolean fullyQualifiedName, boolean replaceSlash){
if (descriptor.startsWith("L")){
//leave off the initial L and the final ;
String name = descriptor.substring(1, descriptor.length()-1);
if (fullyQualifiedName){
if (replaceSlash){
return name.replace("/", ".");
}
return name;
}
int lastSlash = name.lastIndexOf("/");
//lastSlash+1 so the slash is not included in the name
return name.substring(lastSlash+1, name.length());
}
if (descriptor.startsWith("[")){
int dimension = descriptor.lastIndexOf("[") + 1;
String baseType = getTypeNameFromDescriptor(descriptor.replace("[", ""), fullyQualifiedName, replaceSlash);
StringBuilder sb = new StringBuilder(baseType);
for (int i = 0; i < dimension; ++i){
sb.append("[]");
}
return sb.toString();
}
switch (descriptor.charAt(0)){
case BASE_TYPE_BYTE: //signed byte
return "byte";
case BASE_TYPE_CHAR: //char
return "char";
case BASE_TYPE_FLOAT: //float
return "float";
case BASE_TYPE_INT: //int
return "int";
case BASE_TYPE_SHORT: //signed short
return "short";
case BASE_TYPE_BOOLEAN: //boolean
return "boolean";
case BASE_TYPE_DOUBLE: //double
return "double";
case BASE_TYPE_LONG: //long
return "long";
case BASE_TYPE_VOID: //void (only for return types)
return "void";
default:
throw new IllegalArgumentException("invalid descriptor: " + descriptor);
}
}
public static DataType getReferenceTypeOfDescriptor(String descriptor, DataTypeManager dtManager, boolean includesLandSemi){
if (includesLandSemi){
descriptor = descriptor.substring(1, descriptor.length()-1);
}
String[] parts = descriptor.split("/");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++){
sb.append(CategoryPath.DELIMITER_CHAR);
sb.append(parts[i]);
}
DataTypePath dataPath = new DataTypePath(sb.toString(), parts[parts.length-1]);
DataType referencedType = dtManager.getDataType(dataPath);
return new PointerDataType(referencedType);
}
/**
* Returns the datatype that the JVM uses to store a given parameter or field descriptor.
* @param descriptor
* @return
*/
public static DataType getDataTypeOfDescriptor(String descriptor, DataTypeManager dtManager){
//all references to objects start with "L"
//all references to arrays start with "["
//all other descriptors are just one letter.
if (descriptor.startsWith("[")){
return getPointerType(descriptor, dtManager);
}
switch (descriptor.charAt(0)){
case BASE_TYPE_BYTE:
return SignedByteDataType.dataType;
case BASE_TYPE_CHAR:
return CharDataType.dataType;
case BASE_TYPE_INT:
return IntegerDataType.dataType;
case BASE_TYPE_SHORT:
return ShortDataType.dataType;
case BASE_TYPE_BOOLEAN:
return BooleanDataType.dataType;
case BASE_TYPE_FLOAT:
return FloatDataType.dataType;
case BASE_TYPE_REFERENCE: //object reference
return getReferenceTypeOfDescriptor(descriptor, dtManager, true);
case BASE_TYPE_DOUBLE:
return DoubleDataType.dataType;
case BASE_TYPE_LONG:
return LongDataType.dataType;
case BASE_TYPE_VOID: //void (only for return types)
return DataType.VOID;
default:
throw new IllegalArgumentException("Invalid computational category: " + descriptor);
}
}
public static DataType getPointerType(String descriptor, DataTypeManager dtManager){
int lastBracket = descriptor.lastIndexOf("[");
String baseTypeOfArray = descriptor.substring(lastBracket+1, lastBracket+2);
DataType baseType = null;
switch (baseTypeOfArray.charAt(0)){
case BASE_TYPE_BYTE:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_BYTE, dtManager);
break;
case BASE_TYPE_BOOLEAN:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_BOOLEAN, dtManager);
break;
case BASE_TYPE_CHAR:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_CHAR, dtManager);
break;
case BASE_TYPE_DOUBLE:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_DOUBLE, dtManager);
break;
case BASE_TYPE_FLOAT:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_FLOAT, dtManager);
break;
case BASE_TYPE_INT:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_INT, dtManager);
break;
case BASE_TYPE_LONG:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_LONG, dtManager);
break;
case BASE_TYPE_SHORT:
baseType = ArrayMethods.getArrayBaseType(JavaClassConstants.T_SHORT, dtManager);
break;
case BASE_TYPE_REFERENCE:
return dtManager.getPointer(DWordDataType.dataType);
default:
throw new IllegalArgumentException("Invalid array base type category: " + baseTypeOfArray);
}
return dtManager.getPointer(baseType);
}
/**
* Returns a list of JavaComputationalCategory objects corresponding to the
* parameters of a method (read in left-to-right order).
* @param methodDescriptor
* @return
*/
public static List<JavaComputationalCategory> getParameterCategories(String methodDescriptor){
ArrayList<JavaComputationalCategory> categories = new ArrayList<>();
int closeParenIndex = methodDescriptor.indexOf(")");
String argString = methodDescriptor.substring(1, closeParenIndex);
int currentPosition = 0;
int len = argString.length();
while (currentPosition < len){
String currentParam = argString.substring(currentPosition, currentPosition + 1);
JavaComputationalCategory category = DescriptorDecoder.getComputationalCategoryOfDescriptor(currentParam);
switch (category){
case CAT_1:
categories.add(JavaComputationalCategory.CAT_1);
break;
case CAT_2:
categories.add(JavaComputationalCategory.CAT_2);
break;
default:
throw new IllegalArgumentException("Bad category for param:" + category.name());
}
//advance to next type in argString
//if it's a reference, it starts with L and ends with a ;
//if it's an array, it has one "[" for each dimension, then the type (which might be a reference)
//otherwise you only need to advance one character
switch(currentParam){
case "L":
int semiColonIndex = argString.indexOf(";", currentPosition);
currentPosition = semiColonIndex + 1; //advance past ;
break;
case "[":
//advance past all the ['s
while (argString.charAt(currentPosition) == '['){
currentPosition++;
}
//advance past the base type of the array
if (argString.charAt(currentPosition) == 'L'){
semiColonIndex = argString.indexOf(";", currentPosition);
currentPosition = semiColonIndex + 1;
}
else{
currentPosition++;
}
break;
default:
currentPosition++; //getComputationalCategoryOfDescriptor has already validated currentParam
}
}
return categories;
}
/**
* Returns an ordered list of the JVM data types corresponding to the parameters of a method.
* @param methodDescriptor
* @return
*/
public static List<DataType> getDataTypeList(String methodDescriptor, DataTypeManager dtManager) {
ArrayList<DataType> paramDataTypes = new ArrayList<>();
int closeParenIndex = methodDescriptor.indexOf(")");
String argString = methodDescriptor.substring(1, closeParenIndex);
DataType currentParamType;
int currentPosition = 0;
int len = argString.length();
String currentParam = null;
while (currentPosition < len){
int arrayDimensions = 0;
//if it's an array, decode the number of dimensions
while (argString.charAt(currentPosition) == '['){
arrayDimensions++;
currentPosition++;
}
switch (argString.charAt(currentPosition)){
case BASE_TYPE_BYTE:
case BASE_TYPE_CHAR:
case BASE_TYPE_SHORT:
case BASE_TYPE_INT:
case BASE_TYPE_LONG:
case BASE_TYPE_FLOAT:
case BASE_TYPE_DOUBLE:
case BASE_TYPE_BOOLEAN:
currentParam = argString.substring(currentPosition, currentPosition+1);
currentPosition++;
break;
case BASE_TYPE_REFERENCE:
int semiColonIndex = argString.indexOf(";", currentPosition);
currentParam = argString.substring(currentPosition, semiColonIndex+1);
currentPosition = semiColonIndex + 1;
break;
}
currentParamType = getDataTypeOfDescriptor(currentParam, dtManager);
if (arrayDimensions > 0){
paramDataTypes.add(dtManager.getPointer(currentParamType));
}
else {
paramDataTypes.add(currentParamType);
}
}
return paramDataTypes;
}
/**
* Given an invocation type and an element in the constant pool, follows references in the the constant
* pool and returns the appropriate method descriptor.
* @param offset
* @param constantPool
* @param type
* @return
*/
public static String getDescriptorForInvoke(int offset, AbstractConstantPoolInfoJava[] constantPool, JavaInvocationType type){
String descriptor = null;
int name_and_type_index = 0;
switch (type){
case INVOKE_DYNAMIC:
ConstantPoolInvokeDynamicInfo dynamicInfo = (ConstantPoolInvokeDynamicInfo) constantPool[offset];
name_and_type_index = dynamicInfo.getNameAndTypeIndex();
break;
case INVOKE_INTERFACE:
ConstantPoolInterfaceMethodReferenceInfo interfaceInfo = (ConstantPoolInterfaceMethodReferenceInfo) constantPool[offset];
name_and_type_index = interfaceInfo.getNameAndTypeIndex();
break;
case INVOKE_SPECIAL:
case INVOKE_STATIC:
case INVOKE_VIRTUAL:
ConstantPoolMethodReferenceInfo methodReferenceInfo = (ConstantPoolMethodReferenceInfo) constantPool[offset];
name_and_type_index = methodReferenceInfo.getNameAndTypeIndex();
break;
default:
throw new IllegalArgumentException("unimplemented method type: " + type.name());
}
ConstantPoolNameAndTypeInfo methodNameAndType = (ConstantPoolNameAndTypeInfo) constantPool[name_and_type_index];
int descriptor_index = methodNameAndType.getDescriptorIndex();
ConstantPoolUtf8Info descriptorInfo = (ConstantPoolUtf8Info) constantPool[descriptor_index];
descriptor = descriptorInfo.getString();
return descriptor;
}
public final static String decodeType(ConstantPoolUtf8Info utf,
boolean useFullyQualifiedClassName) {
return DescriptorDecoder.getTypeNameFromDescriptor(utf.getString(), useFullyQualifiedClassName, true);
}
//no L, no ;
public static DataType resolveClassForString(String fullyQualifiedName, DataTypeManager dtm, DataType baseType){
fullyQualifiedName = CategoryPath.DELIMITER_CHAR + fullyQualifiedName;
CategoryPath catPath = new CategoryPath(fullyQualifiedName);
String[] parts = catPath.getPathElements();
DataType dataType = new TypedefDataType(catPath,parts[parts.length-1],baseType);
dtm.resolve(dataType, DataTypeConflictHandler.KEEP_HANDLER);
return dataType;
}
}

View file

@ -0,0 +1,171 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.javaclass.format.attributes.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Each field is described by a field_info structure. No two fields in one class file
* may have the same name and descriptor
* <p>
* The structure has the following format:
* <pre>
* field_info {
* u2 access_flags;
* u2 name_index;
* u2 descriptor_index;
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
*/
public class FieldInfoJava implements StructConverter {
private long _offset;
private short accessFlags;
private short nameIndex;
private short descriptorIndex;
private short attributesCount;
private AbstractAttributeInfo [] attributes;
public FieldInfoJava( BinaryReader reader, ClassFileJava classFile ) throws IOException {
_offset = reader.getPointerIndex();
accessFlags = reader.readNextShort();
nameIndex = reader.readNextShort();
descriptorIndex = reader.readNextShort();
attributesCount = reader.readNextShort();
attributes = new AbstractAttributeInfo[ attributesCount ];
for ( int i = 0 ; i < attributesCount ; i++ ) {
attributes[ i ] = AttributeFactory.get( reader, classFile.getConstantPool() );
}
}
public long getOffset() {
return _offset;
}
/**
* The value of the access_flags item is a mask of flags used to denote access
* permission to and properties of this field. The interpretation of each flag, when
* set, is as shown in Table 4.19.
* @return a mask of flags used to denote access permission to and properties of this field
*/
public short getAccessFlags() {
return accessFlags;
}
/**
* The value of the name_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be
* a CONSTANT_Utf8_info structure which must represent a valid
* unqualified name denoting a field.
* @return a valid index into the constant_pool table
*/
public short getNameIndex() {
return nameIndex;
}
/**
* The value of the descriptor_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure that must represent a valid field
* descriptor.
* @return a valid index into the constant_pool table
*/
public short getDescriptorIndex() {
return descriptorIndex;
}
/**
* The value of the attributes_count item indicates the number of additional
* attributes of this field.
* @return the number of additional attributes
*/
public short getAttributesCount() {
return attributesCount;
}
/**
* Each value of the attributes table must be an attribute structure. A
* field can have any number of attributes associated with it.
* <p>
* The attributes defined by this specification as appearing
* in the attributes table of a field_info structure are
* ConstantValue,
* Synthetic,
* Signature,
* Deprecated,
* RuntimeVisibleAnnotations and
* RuntimeInvisibleAnnotations.
* <p>
* A Java virtual machine implementation must recognize and correctly read
* ConstantValue attributes found in the attributes table of a
* field_info structure. If a Java virtual machine implementation recognizes
* class files whose version number is 49.0 or above, it must recognize and
* correctly read Signature, RuntimeVisibleAnnotations
* and RuntimeInvisibleAnnotations attributes found in the
* attributes table of a field_info structure of a class file whose version
* number is 49.0 or above.
* <p>
* A Java virtual machine implementation is required to silently ignore any or all
* attributes that it does not recognize in the attributes table of a field_info
* structure. Attributes not defined in this specification are not allowed to affect
* the semantics of the class file, but only to provide additional descriptive
* information.
* @return
*/
public AbstractAttributeInfo [] getAttributes() {
return attributes;
}
public ConstantValueAttribute getConstantValueAttribute() {
for ( AbstractAttributeInfo attributeInfo : attributes ) {
if ( attributeInfo instanceof ConstantValueAttribute ) {
return (ConstantValueAttribute) attributeInfo;
}
}
return null;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "field_info" + "|" + attributesCount + "|";
Structure structure = new StructureDataType( name, 0 );
structure.add( WORD, "access_flags", null );
structure.add( WORD, "name_index", null );
structure.add( WORD, "descriptor_index", null );
structure.add( WORD, "attributes_count", null );
for ( int i = 0 ; i < attributes.length ; ++i ) {
structure.add( attributes[ i ].toDataType(), "attributes_" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
public final class JavaClassConstants {
public final static int MAGIC = 0xcafebabe;
public final static byte[] MAGIC_BYTES = { (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe };
// Table 6.1. Array type codes
public static final byte T_BOOLEAN = 4;
public static final byte T_CHAR = 5;
public static final byte T_FLOAT = 6;
public static final byte T_DOUBLE = 7;
public static final byte T_BYTE = 8;
public static final byte T_SHORT = 9;
public static final byte T_INT = 10;
public static final byte T_LONG = 11;
public static final String OPERAND_PLACEHOLDER = "&&&";
}

View file

@ -0,0 +1,56 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import java.util.Arrays;
public class JavaClassUtil {
public final static long LOOKUP_ADDRESS = 0xE0000000L;
//65536 is the maximum size of the methods_count item in a class file
public static final long METHOD_INDEX_SIZE = 65536 * 4;
public final static boolean isClassFile(Program program) {
Options options = program.getOptions(Program.PROGRAM_INFO);
String firmwarePath = options.getString("Firmware Path", "");
if (program.getExecutablePath().toLowerCase().endsWith(".class") ||
firmwarePath.toLowerCase().endsWith(".class")) {
byte[] bytes = new byte[4];
try {
Address address = program.getAddressFactory().getAddressSpace("constantPool").getMinAddress();
program.getMemory().getBytes(address, bytes);
}
catch (Exception e) {
Msg.info(JavaClassUtil.class, e.getLocalizedMessage());
}
return Arrays.equals(bytes, JavaClassConstants.MAGIC_BYTES);
}
return false;
}
public static Address toLookupAddress( Program program, int methodIndex ) {
AddressFactory addressFactory = program.getAddressFactory( );
AddressSpace defaultAddressSpace = addressFactory.getDefaultAddressSpace( );
return defaultAddressSpace.getAddress( JavaClassUtil.LOOKUP_ADDRESS + ( methodIndex * 4 ) );
}
}

View file

@ -0,0 +1,318 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.javaclass.format.attributes.*;
import ghidra.javaclass.format.constantpool.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* Each method, including each instance initialization method (2.9) and the class or
* interface initialization method (2.9), is described by a method_info structure. No
* two methods in one class file may have the same name and descriptor (4.3.3).
* <p>
* The structure has the following format:
* <pre>
* method_info {
* u2 access_flags;
* u2 name_index;
* u2 descriptor_index;
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
*
*/
public class MethodInfoJava implements StructConverter {
private long _offset;
private short accessFlags;
private short nameIndex;
private short descriptorIndex;
private short attributesCount;
private AbstractAttributeInfo[] attributes;
public MethodInfoJava(BinaryReader reader, ClassFileJava classFile) throws IOException {
_offset = reader.getPointerIndex();
accessFlags = reader.readNextShort();
nameIndex = reader.readNextShort();
descriptorIndex = reader.readNextShort();
attributesCount = reader.readNextShort();
attributes = new AbstractAttributeInfo[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = AttributeFactory.get(reader, classFile.getConstantPool());
}
}
/**
* Returns the file offset where this method exists in the class file.
* @return the file offset where this method exists in the class file
*/
public long getOffset() {
return _offset;
}
/**
* The value of the access_flags item is a mask of flags used to denote access
* permission to and properties of this method. The interpretation of each flag,
* when set, is as shown in Table 4.20.
* @return a mask of flags used to denote access permission to and properties of this method
*/
public short getAccessFlags() {
return accessFlags;
}
/**
*
* @return boolean encoding whether the method is static
*/
public boolean isStatic(){
return AccessFlagsJava.isStatic(accessFlags);
}
/**
* The value of the name_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing either one of the special
* method names <init> or <clinit>, or a valid unqualified name
* denoting a method.
* @return a valid index into the constant_pool table
*/
public short getNameIndex() {
return nameIndex;
}
/**
* The value of the descriptor_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing a valid method descriptor.
* @return a valid index into the constant_pool table
*/
public short getDescriptorIndex() {
return descriptorIndex;
}
/**
* The value of the attributes_count item indicates the number of additional
* attributes of this method.
* @return the number of additional attributes of this method
*/
public short getAttributesCount() {
return attributesCount;
}
/**
* Each value of the attributes table must be an attribute structure. A
* method can have any number of optional attributes associated with it.
* <p>
* The attributes defined by this specification as appearing in the attributes
* table of a method_info structure are the
* Code,
* Exceptions,
* Synthetic,
* Signature,
* Deprecated,
* RuntimeVisibleAnnotations,
* RuntimeInvisibleAnnotations,
* RuntimeVisibleParameterAnnotations,
* RuntimeInvisibleParameterAnnotations,
* AnnotationDefault attributes.
* <p>
* A Java virtual machine implementation must recognize and correctly read
* Code and Exceptions attributes found in the
* attributes table of a method_info structure.
* <p>
* If a Java virtual machine
* implementation recognizes class files whose version number is 49.0
* or above, it must recognize and correctly read Signature,
* RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations,
* RuntimeInvisibleParameterAnnotations, and
* AnnotationDefault attributes found in the attributes table of a
* method_info structure of a class file whose version number is 49.0 or above.
* <p>
* A Java virtual machine implementation is required to silently ignore any or
* all attributes in the attributes table of a method_info structure that it does
* not recognize. Attributes not defined in this specification are not allowed to
* affect the semantics of the class file, but only to provide additional descriptive
* information.
* @return
*/
public AbstractAttributeInfo[] getAttributes() {
return attributes;
}
/**
* Return a text representation of this methods signature.
* Here are some examples:
* <DL>
* <DD><CODE>"public int foo(int bar)"</CODE></DD>
* <DD><CODE>"public static final float foo(double bar)"</CODE></DD>
* </DL>
* Note: only access flags that map to Java modifier keywords are returned.
* @param access the mask of flags denoting access permission.
* @return a text representation of the access flags.
*/
public String getMethodSignature(ClassFileJava classFile) {
AbstractConstantPoolInfoJava[] constantPool = classFile.getConstantPool();
ConstantPoolUtf8Info methodName = (ConstantPoolUtf8Info) constantPool[nameIndex];
ConstantPoolUtf8Info methodDescriptor =
(ConstantPoolUtf8Info) constantPool[descriptorIndex];
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(AccessFlagsJava.toString(accessFlags, false).trim());
if (!methodName.getString().equals("<clinit>")) {
stringBuffer.append(' ');
if (methodName.getString().equals("<init>")) {//replace constructors with name of this class
ConstantPoolClassInfo thisClass =
(ConstantPoolClassInfo) constantPool[classFile.getThisClass()];
ConstantPoolUtf8Info thisClassName =
(ConstantPoolUtf8Info) constantPool[thisClass.getNameIndex()];
String className = thisClassName.getString();
String dottedClassName = className.replace('/', '.');
int pos = dottedClassName.lastIndexOf('.');
if (pos > -1) {
stringBuffer.append(dottedClassName.substring(pos + 1));
}
else {
stringBuffer.append(className);
// throw new RuntimeException( );//TODO
}
}
else {
stringBuffer.append(getReturnType(methodDescriptor));
stringBuffer.append(' ');
stringBuffer.append(methodName.getString());
}
stringBuffer.append('(');
CodeAttribute codeAttribute = getCodeAttribute();
if (codeAttribute != null) {
LocalVariableTableAttribute localVariableTable =
codeAttribute.getLocalVariableTableAttribute();
if (localVariableTable != null) {
LocalVariableJava[] localVariables = localVariableTable.getLocalVariables();
int startIndex = getParametersStartIndex();
for (int i = startIndex; i < localVariables.length; ++i) {
if (localVariables[i].getStartPC() == 0x0) {
if (i > startIndex) {
stringBuffer.append(", ");
}
ConstantPoolUtf8Info parameterName =
(ConstantPoolUtf8Info) constantPool[localVariables[i].getNameIndex()];
ConstantPoolUtf8Info parameterDescriptor =
(ConstantPoolUtf8Info) constantPool[localVariables[i].getDescriptorIndex()];
stringBuffer.append(DescriptorDecoder.decodeType(parameterDescriptor,
false));
stringBuffer.append(" ");
stringBuffer.append(parameterName);
}
}
}
}
stringBuffer.append(')');
ExceptionsAttribute exceptionsAttribute = getExceptionsAttribute();
if (exceptionsAttribute != null) {
int i = 0;
for (short s : exceptionsAttribute.getExceptionIndexTable()) {
ConstantPoolClassInfo exceptionClass = (ConstantPoolClassInfo) constantPool[s];
ConstantPoolUtf8Info exceptionClassName =
(ConstantPoolUtf8Info) constantPool[exceptionClass.getNameIndex()];
String className = exceptionClassName.getString();
String dottedClassName = className.replace('/', '.');
if (i == 0) {
stringBuffer.append(" throws ");
}
else {
stringBuffer.append(", ");
}
stringBuffer.append(dottedClassName);
i++;
}
}
}
stringBuffer.append(' ');
return stringBuffer.toString();
}
/**
* If the method is static, then parameters start at index 0,
* otherwise skip index 0 because it contains the 'this' parameter.
*/
private int getParametersStartIndex() {
return AccessFlagsJava.isStatic(accessFlags) ? 0 : 1;
}
/**
* Clips the encoded return type from the method descriptor then
* decodes it into a readable data type.
*/
private String getReturnType(ConstantPoolUtf8Info methodDescriptor) {
String methodDescriptorString = methodDescriptor.getString();
int closeParenthesisPos = methodDescriptorString.indexOf(')') + 1;
String encodedReturnType = methodDescriptorString.substring(closeParenthesisPos);
return DescriptorDecoder.getTypeNameFromDescriptor(encodedReturnType, false, true);
}
public CodeAttribute getCodeAttribute() {
for (AbstractAttributeInfo attributeInfo : attributes) {
if (attributeInfo instanceof CodeAttribute) {
return (CodeAttribute) attributeInfo;
}
}
return null;
}
public ExceptionsAttribute getExceptionsAttribute() {
for (AbstractAttributeInfo attributeInfo : attributes) {
if (attributeInfo instanceof ExceptionsAttribute) {
return (ExceptionsAttribute) attributeInfo;
}
}
return null;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "method_info" + "|" + nameIndex + "|" + descriptorIndex + "|" + attributesCount + "|";
Structure structure = new StructureDataType(name, 0);
structure.add(WORD, "access_flags", null);
structure.add(WORD, "name_index", null);
structure.add(WORD, "descriptor_index", null);
structure.add(WORD, "attributes_count", null);
for (int i = 0; i < attributes.length; ++i) {
structure.add(attributes[i].toDataType(), "attributes_" + i, null);
}
return structure;
}
}

View file

@ -0,0 +1,136 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.StructureDataType;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Attributes are used in the ClassFile, field_info, method_info and
* Code_attribute structures of the class file format.
* <p>
* All attributes have the following general format:
* <pre>
* attribute_info {
* u2 attribute_name_index;
* u4 attribute_length;
* u1 info[attribute_length];
* }
* <pre>
* <p>
* For all attributes, the attribute_name_index must be a valid unsigned
* 16-bit index into the constant pool of the class. The constant_pool entry
* at attribute_name_index must be a CONSTANT_Utf8_info structure
* representing the name of the attribute.
* <p>
* The value of the attribute_length item
* indicates the length of the subsequent information in bytes. The length does
* not include the initial six bytes that contain the attribute_name_index and
* attribute_length items.
* <p>
* Certain attributes are predefined as part of the class file specification. They are
* listed in Table 4.21, accompanied by the version of the Java Platform, Standard
* Edition ("Java SE") and the version of the class file format in which each first
* appeared. Within the context of their use in this specification, that is, in the
* attributes tables of the class file structures in which they appear, the names of
* these predefined attributes are reserved. Of the predefined attributes:
* <p>
* The ConstantValue, Code and Exceptions attributes must be recognized and
* correctly read by a class file reader for correct interpretation of the class file
* by a Java virtual machine implementation.
* <p>
* The InnerClasses, EnclosingMethod and Synthetic attributes must be
* recognized and correctly read by a class file reader in order to properly
* implement the Java platform class libraries.
* <p>
* The RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations,
* RuntimeInvisibleParameterAnnotations and AnnotationDefault attributes
* must be recognized and correctly read by a class file reader in order to properly
* implement the Java platform class libraries, if the class file's version
* number is 49.0 or above and the Java virtual machine implementation recognizes
* class files whose version number is 49.0 or above.
* <p>
* The Signature attribute must be recognized and correctly read by a class file
* reader if the class file's version number is 49.0 or above and the Java virtual
* machine implementation recognizes class files whose version number is 49.0
* or above.
* <p>
* The StackMapTable attribute must be recognized and correctly read by a class
* file reader if the class file's version number is 50.0 or above and the Java virtual
* machine implementation recognizes class files whose version number is 50.0
* or above.
* <p>
* The BootstrapMethods attribute must be recognized and correctly read by a
* class file reader if the class file's version number is 51.0 or above and the Java
* virtual machine implementation recognizes class files whose version number
* is 51.0 or above.
* <p>
* Use of the remaining predefined attributes is optional; a class file reader may use
* the information they contain, or otherwise must silently ignore those attributes.
*/
public abstract class AbstractAttributeInfo implements StructConverter {
private long _offset;
private short attributeNameIndex;
private int attributeLength;
protected AbstractAttributeInfo( BinaryReader reader ) throws IOException {
_offset = reader.getPointerIndex();
attributeNameIndex = reader.readNextShort();
attributeLength = reader.readNextInt();
}
public long getOffset() {
return _offset;
}
/**
* The value of the attribute_name_index item must be a valid index
* into the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Utf8_info structure representing the name of this attribute.
* @see AttributesConstants
* @return the attribute_name_index
*/
public short getAttributeNameIndex() {
return attributeNameIndex;
}
/**
* The value of the attribute_length item indicates the length of the attribute,
* excluding the initial six bytes.
* The value of the attribute_length item is thus dependent on the specific
* attribute.
* @return the length of the attribute, excluding the initial six bytes
*/
public int getAttributeLength() {
return attributeLength;
}
protected StructureDataType getBaseStructure( String name ) {
StructureDataType structure = new StructureDataType( name + "|" + attributeLength + "|", 0 );
structure.add( WORD, "attribute_name_index", null );
structure.add( DWORD, "attribute_length" , null );
return structure;
}
}

View file

@ -0,0 +1,64 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The AnnotationDefault attribute is a variable-length attribute in the attributes
* table of certain method_info structures, namely those representing elements
* of annotation types. The AnnotationDefault attribute records the default value
* for the element represented by the method_info structure.
* <b>
* Each method_info structure representing an element of an annotation type may
* contain at most one AnnotationDefault attribute. The Java virtual machine must
* make this default value available so it can be applied by appropriate reflective APIs.
* <b>
* The AnnotationDefault attribute has the following format:
* <pre>
* AnnotationDefault_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* element_value default_value;
* }
* </pre>
*/
public class AnnotationDefaultAttribute extends AbstractAttributeInfo {
private AnnotationElementValue defaultValue;
public AnnotationDefaultAttribute( BinaryReader reader ) throws IOException {
super( reader );
defaultValue = new AnnotationElementValue( reader );
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "AnnotationDefault_attribute" );
structure.add( defaultValue.toDataType(), "default_value", null );
return structure;
}
}

View file

@ -0,0 +1,240 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.javaclass.format.DescriptorDecoder;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The element_value structure is a discriminated union representing the
* value of an element-value pair. It is used to represent element values
* in all attributes that describe annotations:
* RuntimeVisibleAnnotations,
* RuntimeInvisibleAnnotations,
* RuntimeVisibleParameterAnnotations, and
* RuntimeInvisibleParameterAnnotations.
* <p>
* The element_value structure has the following format:
* <pre>
* element_value {
* u1 tag;
* union {
* u2 const_value_index;
* {
* u2 type_name_index;
* u2 const_name_index;
* } enum_const_value;
* u2 class_info_index;
* annotation annotation_value;
* {
* u2 num_values;
* element_value values[num_values];
* } array_value;
* } value;
* }
* </pre>
*/
public class AnnotationElementValue implements StructConverter {
private byte tag;
private short constantValueIndex;
private short typeNameIndex;
private short constantNameIndex;
private short classInfoIndex;
private AnnotationJava annotation;
private short numberOfValues;
private AnnotationElementValue [] values;
public AnnotationElementValue( BinaryReader reader ) throws IOException {
tag = reader.readNextByte();
if ( tag == DescriptorDecoder.BASE_TYPE_BYTE ||
tag == DescriptorDecoder.BASE_TYPE_CHAR ||
tag == DescriptorDecoder.BASE_TYPE_INT ||
tag == DescriptorDecoder.BASE_TYPE_SHORT ||
tag == DescriptorDecoder.BASE_TYPE_LONG ||
tag == DescriptorDecoder.BASE_TYPE_FLOAT ||
tag == DescriptorDecoder.BASE_TYPE_DOUBLE ||
tag == DescriptorDecoder.BASE_TYPE_BOOLEAN ||
tag == DescriptorDecoder.BASE_TYPE_STRING ) {
constantValueIndex = reader.readNextShort();
}
else if ( tag == DescriptorDecoder.BASE_TYPE_ENUM ) {
typeNameIndex = reader.readNextShort();
constantNameIndex = reader.readNextShort();
}
else if ( tag == DescriptorDecoder.BASE_TYPE_CLASS ) {
classInfoIndex = reader.readNextShort();
}
else if ( tag == DescriptorDecoder.BASE_TYPE_ANNOTATION ) {
annotation = new AnnotationJava( reader );
}
else if ( tag == DescriptorDecoder.BASE_TYPE_ARRAY ) {
numberOfValues = reader.readNextShort();
values = new AnnotationElementValue[ numberOfValues ];
for ( int i = 0 ; i < numberOfValues ; ++i ) {
values[ i ] = new AnnotationElementValue( reader );
}
}
}
/**
* The tag item indicates the type of this annotation element-value pair.
* <p>
* The letters 'B', 'C', 'D', 'F', 'I', 'J', 'S', and 'Z' indicate a primitive type.
* <p>
* These letters are interpreted as BaseType characters ( Table 4.2 ).
* <p>
* The other legal values for tag are listed with their interpretations in Table 4.24.
* @see DataTypeDecoder
* @return the type of this annotation element-value pair
*/
public byte getTag() {
return tag;
}
/**
* The const_value_index item is used if the tag item is one of
* 'B',
* 'C',
* 'D',
* 'F',
* 'I',
* 'J',
* 'S',
* 'Z',
* 's'.
* The value of the const_value_index item must be
* a valid index into the constant_pool table.
* <p>
* The constant_pool entry at that index must be of the correct entry type
* for the field type designated by the tag item, as specified in Table 4.24.
* @return a valid index into the constant_pool table
*/
public short getConstantValueIndex() {
return constantValueIndex;
}
/**
* The value of the type_name_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Utf8_info (?4.4.7) structure representing a valid
* field descriptor (?4.3.2) that denotes the internal form of the binary
* name (?4.2.1) of the type of the enum constant represented by this
* element_value structure.
* @return a valid index into the constant_pool table
*/
public short getTypeNameIndex() {
if ( tag != DescriptorDecoder.BASE_TYPE_ENUM ) {
throw new IllegalArgumentException();
}
return typeNameIndex;
}
/**
* The value of the const_name_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Utf8_info (?4.4.7) structure representing the
* simple name of the enum constant represented by this element_value
* structure.
* @return a valid index into the constant_pool table
*/
public short getConstantNameIndex() {
if ( tag != DescriptorDecoder.BASE_TYPE_ENUM ) {
throw new IllegalArgumentException();
}
return constantNameIndex;
}
/**
* The class_info_index item is used if the tag item is 'c'.
* The class_info_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info (?4.4.7) structure representing the return descriptor
* (?4.3.3) of the type that is reified by the class represented by this
* element_value structure.
* For example, 'V' for Void.class, 'Ljava/lang/Object;' for Object, etc.
* @return a valid index into the constant_pool table
*/
public short getClassInfoIndex() {
return classInfoIndex;
}
/**
* The annotation_value item is used if the tag item is '@'.
* The element_value structure represents a "nested" annotation.
* @return a "nested" annotation
*/
public AnnotationJava getAnnotation() {
return annotation;
}
/**
* The value of the num_values item gives the number of elements in the
* array-typed value represented by this element_value structure.
* <p>
* Note that a maximum of 65535 elements are permitted in an array-typed element value.
* <p>
* Each value of the values table gives the value of an element of the
* array-typed value represented by this element_value structure.
* @return nested element value table
*/
public AnnotationElementValue [] getValues() {
return values;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "element_value" +"|" + tag + "|";
StructureDataType structure = new StructureDataType( name, 0 );
if ( tag == DescriptorDecoder.BASE_TYPE_BYTE ||
tag == DescriptorDecoder.BASE_TYPE_CHAR ||
tag == DescriptorDecoder.BASE_TYPE_INT ||
tag == DescriptorDecoder.BASE_TYPE_SHORT ||
tag == DescriptorDecoder.BASE_TYPE_LONG ||
tag == DescriptorDecoder.BASE_TYPE_FLOAT ||
tag == DescriptorDecoder.BASE_TYPE_DOUBLE ||
tag == DescriptorDecoder.BASE_TYPE_BOOLEAN ||
tag == DescriptorDecoder.BASE_TYPE_STRING ) {
}
else if ( tag == DescriptorDecoder.BASE_TYPE_ENUM ) {
}
else if ( tag == DescriptorDecoder.BASE_TYPE_CLASS ) {
}
else if ( tag == DescriptorDecoder.BASE_TYPE_ANNOTATION ) {
}
else if ( tag == DescriptorDecoder.BASE_TYPE_ARRAY ) {
}
return structure;
}
}

View file

@ -0,0 +1,79 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Each value of the element_value_pairs table represents a single element value
* pair in the annotation represented by this annotation structure.
* <p>
* Each element_value_pairs entry contains the following two items:
* <pre>
* element_value_pair {
* u2 element_name_index;
* element_value value;
* }
* </pre>
*/
public class AnnotationElementValuePair implements StructConverter {
private short elementNameIndex;
private AnnotationElementValue value;
public AnnotationElementValuePair( BinaryReader reader ) throws IOException {
elementNameIndex = reader.readNextShort();
value = new AnnotationElementValue( reader );
}
/**
* The value of the element_name_index item must be a valid index
* into the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Utf8_info (?4.4.7) structure representing a valid
* field descriptor that denotes the name of the annotation type
* element represented by this element_value_pairs entry.
* @return a valid index into the constant_pool table
*/
public short getElementNameIndex() {
return elementNameIndex;
}
/**
* The value of the value item represents the value of the element-value
* pair represented by this element_value_pairs entry.
* @return the value of the element-value pair
*/
public AnnotationElementValue getValue() {
return value;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = new StructureDataType( "element_value_pair", 0 );
structure.add( WORD, "element_name_index", null );
structure.add( value.toDataType(), "element_value_pair", null );
return structure;
}
}

View file

@ -0,0 +1,104 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Each value of the annotations table represents a single runtime-visible
* annotation on a program element.
* <p>
* The annotation structure has the following format:
* <pre>
* annotation {
* u2 type_index;
* u2 num_element_value_pairs;
* {
* u2 element_name_index;
* element_value value;
* } element_value_pair[num_element_value_pairs];
* }
* </pre>
*/
public class AnnotationJava implements StructConverter {
private short typeIndex;
private short numberOfElementValuePairs;
private AnnotationElementValuePair [] elementValuePairs;
public AnnotationJava( BinaryReader reader ) throws IOException {
typeIndex = reader.readNextShort();
numberOfElementValuePairs = reader.readNextShort();
elementValuePairs = new AnnotationElementValuePair[ numberOfElementValuePairs ];
for ( int i = 0 ; i < numberOfElementValuePairs ; ++i ) {
elementValuePairs[ i ] = new AnnotationElementValuePair( reader );
}
}
/**
* The value of the type_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Utf8_info structure representing a field
* descriptor representing the annotation type corresponding to the annotation
* represented by this annotation structure.
* @return valid index into the constant_pool table
*/
public short getTypeIndex() {
return typeIndex;
}
/**
* The value of the num_element_value_pairs item gives the number of
* element-value pairs of the annotation represented by this annotation
* structure.
* <p>
* Note that a maximum of 65535 element-value pairs may be contained in a single
* annotation.
* @return the number of element-value pairs of the annotation
*/
public short getNumberOfElementValuePairs() {
return numberOfElementValuePairs;
}
/**
* Returns the element value pair table for this annotation.
* @return the element value pair table
*/
public AnnotationElementValuePair [] getElementValuePairs() {
return elementValuePairs;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "annotation" +"|" + numberOfElementValuePairs + "|";
StructureDataType structure = new StructureDataType( name, 0 );
structure.add( WORD, "type_index", null );
structure.add( WORD, "num_element_value_pairs", null );
for ( int i = 0 ; i < elementValuePairs.length ; ++i ) {
structure.add( elementValuePairs[ i ].toDataType(), "element_value_pair_" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,104 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.javaclass.format.constantpool.ConstantPoolUtf8Info;
import java.io.IOException;
public class AttributeFactory {
public static AbstractAttributeInfo get( BinaryReader reader, AbstractConstantPoolInfoJava [] constantPool ) throws IOException {
int attributeNameIndex = reader.readShort( reader.getPointerIndex() );
if ( attributeNameIndex < 1 || attributeNameIndex >= constantPool.length ) {
throw new RuntimeException( "invalid index");
}
if ( !( constantPool[ attributeNameIndex ] instanceof ConstantPoolUtf8Info ) ) {
throw new RuntimeException();
}
ConstantPoolUtf8Info utf8 = (ConstantPoolUtf8Info) constantPool[ attributeNameIndex ];
if ( utf8.getString().equals( AttributesConstants.ConstantValue ) ) {
return new ConstantValueAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.Code ) ) {
return new CodeAttribute( reader, constantPool );
}
else if ( utf8.getString().equals( AttributesConstants.StackMapTable ) ) {
return new StackMapTableAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.Exceptions ) ) {
return new ExceptionsAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.InnerClasses ) ) {
return new InnerClassesAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.EnclosingMethod ) ) {
return new EnclosingMethodAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.Synthetic ) ) {
return new SyntheticAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.Signature ) ) {
return new SignatureAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.SourceFile ) ) {
return new SourceFileAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.SourceDebugExtension ) ) {
return new SourceDebugExtensionAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.LineNumberTable ) ) {
return new LineNumberTableAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.LocalVariableTable ) ) {
return new LocalVariableTableAttribute( reader, constantPool );
}
else if ( utf8.getString().equals( AttributesConstants.LocalVariableTypeTable ) ) {
return new LocalVariableTypeTableAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.Deprecated ) ) {
return new DeprecatedAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.RuntimeVisibleAnnotations ) ) {
return new RuntimeVisibleAnnotationsAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.RuntimeInvisibleAnnotations ) ) {
return new RuntimeInvisibleAnnotationsAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.RuntimeVisibleParameterAnnotations ) ) {
return new RuntimeParameterAnnotationsAttribute( reader, true /*visible*/ );
}
else if ( utf8.getString().equals( AttributesConstants.RuntimeInvisibleParameterAnnotations ) ) {
return new RuntimeParameterAnnotationsAttribute( reader, false /*invisible*/ );
}
else if ( utf8.getString().equals( AttributesConstants.AnnotationDefault ) ) {
return new AnnotationDefaultAttribute( reader );
}
else if ( utf8.getString().equals( AttributesConstants.BootstrapMethods ) ) {
return new BootstrapMethodsAttribute( reader );
}
throw new RuntimeException( "Unknown attribute type: " + utf8.getString() );
}
}

View file

@ -0,0 +1,42 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
public final class AttributesConstants {
public final static String ConstantValue = "ConstantValue";
public final static String Code = "Code";
public final static String StackMapTable = "StackMapTable";
public final static String Exceptions = "Exceptions";
public final static String InnerClasses = "InnerClasses";
public final static String EnclosingMethod = "EnclosingMethod";
public final static String Synthetic = "Synthetic";
public final static String Signature = "Signature";
public final static String SourceFile = "SourceFile";
public final static String SourceDebugExtension = "SourceDebugExtension";
public final static String LineNumberTable = "LineNumberTable";
public final static String LocalVariableTable = "LocalVariableTable";
public final static String LocalVariableTypeTable = "LocalVariableTypeTable";
public final static String Deprecated = "Deprecated";
public final static String RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations";
public final static String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";
public final static String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations";
public final static String RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations";
public final static String AnnotationDefault = "AnnotationDefault";
public final static String BootstrapMethods = "BootstrapMethods";
}

View file

@ -0,0 +1,100 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
*
* NOTE: THE COMMENT TEXT EXTRACTED FROM JVMS7.PDF
* <p>
*/
public class BootstrapMethods implements StructConverter {
private short bootstrapMethodsReference;
private short numberOfBootstrapArguments;
private short [] bootstrapArguments;
public BootstrapMethods( BinaryReader reader ) throws IOException {
bootstrapMethodsReference = reader.readNextShort();
numberOfBootstrapArguments = reader.readNextShort();
bootstrapArguments = reader.readNextShortArray( numberOfBootstrapArguments & 0xffff );
}
/**
* The value of the bootstrap_method_ref item must be a valid index into
* the constant_pool table. The constant_pool entry at that index must be
* a CONSTANT_MethodHandle_info structure.
*
* Commentary: The reference_kind item of the CONSTANT_MethodHandle_info
* structure should have the value 6 (REF_invokeStatic) or 8 (REF_newInvokeSpecial)
* or else invocation of the bootstrap method
* handle during call site specifier resolution for an invokedynamic instruction will
* complete abruptly.
*
* @return a valid index into the constant_pool table
*/
public short getBootstrapMethodsReference() {
return bootstrapMethodsReference;
}
/**
* The value of the num_bootstrap_arguments item gives the number of
* items in the bootstrap_arguments array.
* @return the number of items in the bootstrap_arguments array
*/
public short getNumberOfBootstrapArguments() {
return numberOfBootstrapArguments;
}
/**
* Each entry in the bootstrap_arguments array must be a valid index into
* the constant_pool table.
* The constant_pool entry at that index must be:
* CONSTANT_String_info,
* CONSTANT_Class_info,
* CONSTANT_Integer_info,
* CONSTANT_Long_info,
* CONSTANT_Float_info,
* CONSTANT_Double_info,
* CONSTANT_MethodHandle_info, or
* CONSTANT_MethodType_info structure.
* @return
*/
public short [] getBootstrapArguments() {
return bootstrapArguments;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = new StructureDataType( "bootstrap_methods", 0 );
structure.add( WORD, "bootstrap_method_ref", null );
structure.add( WORD, "num_bootstrap_arguments", null );
if ( numberOfBootstrapArguments > 0 ) {
DataType array = new ArrayDataType( WORD, numberOfBootstrapArguments, WORD.getLength() );
structure.add( array, "bootstrapArguments", null );
}
return structure;
}
}

View file

@ -0,0 +1,89 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The BootstrapMethods attribute is a variable-length attribute in the attributes
* table of a ClassFile structure.
*
* The BootstrapMethods attribute records bootstrap method specifiers
* referenced by invokedynamic instructions.
*
* There must be exactly one BootstrapMethods attribute in the attributes table of
* a given ClassFile structure if the constant_pool table of the ClassFile structure
* has at least one CONSTANT_InvokeDynamic_info entry.
*
* There can be no more than one BootstrapMethods attribute in the attributes table of a given
* ClassFile structure.
*
* The BootstrapMethods attribute has the following format:
* <pre>
* BootstrapMethods_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 num_bootstrap_methods;
* {
* u2 bootstrap_method_ref;
* u2 num_bootstrap_arguments;
* u2 bootstrap_arguments[num_bootstrap_arguments];
* } bootstrap_methods[num_bootstrap_methods];
* }
* </pre>
*/
public class BootstrapMethodsAttribute extends AbstractAttributeInfo {
private short numberOfBootstrapMethods;
private BootstrapMethods [] bootstrapMethods;
public BootstrapMethodsAttribute( BinaryReader reader ) throws IOException {
super(reader);
numberOfBootstrapMethods = reader.readNextShort();
bootstrapMethods = new BootstrapMethods[ numberOfBootstrapMethods ];
for ( int i = 0 ; i < numberOfBootstrapMethods ; ++i ) {
bootstrapMethods[ i ] = new BootstrapMethods( reader );
}
}
public short getNumberOfBootstrapMethods() {
return numberOfBootstrapMethods;
}
public BootstrapMethods[] getBootstrapMethods(){
return bootstrapMethods;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "BootstrapMethods_attribute" );
structure.add( WORD, "num_bootstrap_methods", null );
for ( int i = 0 ; i < bootstrapMethods.length ; ++i ) {
structure.add( bootstrapMethods[ i ].toDataType(), "bootstrap_methods" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,235 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The Code attribute is a variable-length attribute in the attributes table of a
* method_info (?4.6) structure.
* <p>
* A Code attribute contains the Java virtual machine
* instructions and auxiliary information for a single method, instance initialization
* method (?2.9), or class or interface initialization method (?2.9). Every Java virtual
* machine implementation must recognize Code attributes.
* <p>
* If the method is either
* native or abstract, its method_info structure must not have a Code attribute.
* Otherwise, its method_info structure must have exactly one Code attribute.
* <p>
* The Code attribute has the following format:
* <pre>
* Code_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 max_stack;
* u2 max_locals;
* u4 code_length;
* u1 code[code_length];
* u2 exception_table_length;
* {
* u2 start_pc;
* u2 end_pc;
* u2 handler_pc;
* u2 catch_type;
* } exception_table[exception_table_length];
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* </pre>
*/
public class CodeAttribute extends AbstractAttributeInfo {
private long _codeOffset;
private short maxStack;
private short maxLocals;
private int codeLength;
private byte[] code;
private short exceptionTableLength;
private ExceptionHandlerJava[] exceptionTable;
private short attributesCount;
private AbstractAttributeInfo[] attributes;
public CodeAttribute(BinaryReader reader, AbstractConstantPoolInfoJava[] constantPool)
throws IOException {
super(reader);
maxStack = reader.readNextShort();
maxLocals = reader.readNextShort();
codeLength = reader.readNextInt();
_codeOffset = reader.getPointerIndex();
code = reader.readNextByteArray(codeLength);
exceptionTableLength = reader.readNextShort();
exceptionTable = new ExceptionHandlerJava[exceptionTableLength];
for (int i = 0; i < exceptionTableLength; i++) {
exceptionTable[i] = new ExceptionHandlerJava(reader);
}
attributesCount = reader.readNextShort();
attributes = new AbstractAttributeInfo[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = AttributeFactory.get(reader, constantPool);
}
}
/**
* The value of the max_stack item gives the maximum depth of the
* operand stack of this method at any point during execution of the method.
* @return the maximum depth of the operand stack
*/
public short getMaxStack() {
return maxStack;
}
/**
* The value of the max_locals item gives the number of local variables in the
* local variable array allocated upon invocation of this method, including the
* local variables used to pass parameters to the method on its invocation.
* <p>
* The greatest local variable index for a value of type long or double is
* max_locals - 2. The greatest local variable index for a value of any other
* type is max_locals - 1.
* @return the number of local variables in the
* local variable array allocated upon invocation of this method
*/
public short getMaxLocals() {
return maxLocals;
}
/**
* The value of the code_length item gives the number of bytes in the code array
* for this method. The value of code_length must be greater than zero; the code
* array must not be empty.
* @return the number of bytes in the code array for this method
*/
public int getCodeLength() {
return codeLength;
}
/**
* The code array gives the actual bytes of Java virtual machine code that
* implement the method.
* <p>
* When the code array is read into memory on a byte-addressable machine, if
* the first byte of the array is aligned on a 4-byte boundary, the tableswitch and
* lookupswitch 32-bit offsets will be 4-byte aligned. (Refer to the descriptions
* of those instructions for more information on the consequences of code array
* alignment.)
* <p>
* The detailed constraints on the contents of the code array are extensive and are
* given in a separate section.
* @return he actual bytes of Java virtual machine code that implement the method
*/
public byte[] getCode() {
return code;
}
/**
* The value of the exception_table_length item gives the number of entries
* in the exception_table table.
* @return the number of entries in the exception_table table
*/
public short getExceptionTableLength() {
return exceptionTableLength;
}
/**
* Each entry in the exception_table array describes one exception handler in
* the code array.
* <p>
* The order of the handlers in the exception_table array is significant
* @return the exception_table array
*/
public ExceptionHandlerJava[] getExceptionTable() {
return exceptionTable;
}
/**
* The value of the attributes_count item indicates the number of attributes of
* the Code attribute.
* @return the number of attributes of the Code attribute
*/
public short getAttributesCount() {
return attributesCount;
}
/**
* Each value of the attributes table must be an attribute structure. A
* Code attribute can have any number of optional attributes associated with it.
* <p>
* The only attributes defined by this specification as appearing in the
* attributes table of a Code attribute are the LineNumberTable,
* LocalVariableTable, LocalVariableTypeTable, and
* StackMapTable attributes.
* <p>
* If a Java virtual machine implementation recognizes class files whose version
* number is 50.0 or above, it must recognize and correctly read StackMapTable
* attributes found in the attributes table of a Code attribute of a class
* file whose version number is 50.0 or above.
* <p>
* A Java virtual machine implementation is required to silently ignore any or
* all attributes in the attributes table of a Code attribute that it does not
* recognize. Attributes not defined in this specification are not allowed to affect
* the semantics of the class file, but only to provide additional descriptive
* information.
* @return the attributes table
*/
public AbstractAttributeInfo[] getAttributes() {
return attributes;
}
public LocalVariableTableAttribute getLocalVariableTableAttribute() {
for (AbstractAttributeInfo attributeInfo : attributes) {
if (attributeInfo instanceof LocalVariableTableAttribute) {
return (LocalVariableTableAttribute) attributeInfo;
}
}
return null;
}
public long getCodeOffset() {
return _codeOffset;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "Code_attribute" + "|" + exceptionTableLength + "|" + attributesCount + "|";
StructureDataType structure = getBaseStructure(name);
structure.add(WORD, "max_stack", null);
structure.add(WORD, "max_locals", null);
structure.add(DWORD, "code_length", null);
if (code.length > 0) {
DataType array = new ArrayDataType(BYTE, code.length, BYTE.getLength());
structure.add(array, "code", null);
}
structure.add(WORD, "exception_table_length", null);
for (int i = 0; i < exceptionTable.length; ++i) {
structure.add(exceptionTable[i].toDataType(), "exception_table_" + i, null);
}
structure.add(WORD, "attributes_count", null);
for (int i = 0; i < attributes.length; ++i) {
structure.add(attributes[i].toDataType(), "attributes_" + i, null);
}
return structure;
}
}

View file

@ -0,0 +1,94 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The ConstantValue attribute is a fixed-length attribute in the attributes table
* of a field_info structure.
* <p>
* A ConstantValue attribute represents the value
* of a constant field. There can be no more than one ConstantValue attribute in the
* attributes table of a given field_info structure. If the field is static (that is,
* the ACC_STATIC flag (Table 4.19) in the access_flags item of the field_info
* structure is set) then the constant field represented by the field_info structure
* is assigned the value referenced by its ConstantValue attribute as part of the
* initialization of the class or interface declaring the constant field (?5.5). This occurs
* prior to the invocation of the class or interface initialization method (?2.9) of that
* class or interface.
* <p>
* If a field_info structure representing a non-static field has a ConstantValue
* attribute, then that attribute must silently be ignored. Every Java virtual machine
* implementation must recognize ConstantValue attributes.
* <p>
* The ConstantValue attribute has the following format:
* <pre>
* ConstantValue_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 constantvalue_index;
* }
* </pre>
*/
public class ConstantValueAttribute extends AbstractAttributeInfo {
private short constantValueIndex;
public ConstantValueAttribute( BinaryReader reader ) throws IOException {
super( reader );
constantValueIndex = reader.readNextShort();
}
/**
* The value of the constantvalue_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index gives the
* constant value represented by this attribute. The constant_pool entry must be
* of a type appropriate to the field, as shown by Table 4.22.
* <pre>
* Table 4.22. Constant value attribute types
* --------------------------------------------------
* Field Type Entry Type
* --------------------------------------------------
* long CONSTANT_Long
* float CONSTANT_Float
* double CONSTANT_Double
* int, short, char, byte, boolean CONSTANT_Integer
* String CONSTANT_String
* --------------------------------------------------
* </pre>
* @return a valid index into the constant_pool table
*/
public short getConstantValueIndex() {
return constantValueIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "ConstantValue_attribute" );
structure.add( WORD, "constantvalue_index", null );
return structure;
}
}

View file

@ -0,0 +1,59 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The Deprecated attribute is an optional fixed-length attribute in the attributes
* table of a ClassFile, field_info or method_info structure.
* A class, interface, method, or field may be marked using a Deprecated attribute to
* indicate that the class, interface, method, or field has been superseded.
*
* A runtime interpreter or tool that reads the class file format, such as a compiler,
* can use this marking to advise the user that a superceded class, interface, method,
* or field is being referred to. The presence of a Deprecated attribute does not alter
* the semantics of a class or interface.
*
* The Deprecated attribute has the following format:
* <pre>
* Deprecated_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* }
* </pre>
*/
public class DeprecatedAttribute extends AbstractAttributeInfo {
public DeprecatedAttribute( BinaryReader reader ) throws IOException {
super( reader );
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "Deprecated_attribute" );
return structure;
}
}

View file

@ -0,0 +1,89 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The EnclosingMethod attribute is an optional fixed-length attribute in the
* attributes table of a ClassFile (?4.1) structure. A class must have an
* EnclosingMethod attribute if and only if it is a local class or an anonymous class.
* A class may have no more than one EnclosingMethod attribute.
* <p>
* The EnclosingMethod attribute has the following format:
* <pre>
* EnclosingMethod_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 class_index;
* u2 method_index;
* }
* </pre>
*/
public class EnclosingMethodAttribute extends AbstractAttributeInfo {
private short classIndex;
private short methodIndex;
public EnclosingMethodAttribute( BinaryReader reader ) throws IOException {
super( reader );
classIndex = reader.readNextShort();
methodIndex = reader.readNextShort();
}
/**
* The value of the class_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Class_info structure representing the innermost class that
* encloses the declaration of the current class.
* @return a valid index into the constant_pool table
*/
public short getClassIndex() {
return classIndex;
}
/**
* If the current class is not immediately enclosed by a method or constructor,
* then the value of the method_index item must be zero.
* <p>
* Otherwise, the value of the method_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_NameAndType_info structure representing the name and
* type of a method in the class referenced by the class_index attribute above.
* @return a valid index into the constant_pool table, or zero
*/
public short getMethodIndex() {
return methodIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "EnclosingMethod_attribute" );
structure.add( WORD, "class_index", null );
structure.add( WORD, "method_index", null );
return structure;
}
}

View file

@ -0,0 +1,112 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
*
*/
public class ExceptionHandlerJava implements StructConverter {
private short startPC;
private short endPC;
private short handlerPC;
private short catchType;
public ExceptionHandlerJava( BinaryReader reader ) throws IOException {
startPC = reader.readNextShort();
endPC = reader.readNextShort();
handlerPC = reader.readNextShort();
catchType = reader.readNextShort();
}
/**
* The values of the two items start_pc and end_pc indicate the ranges in the
* code array at which the exception handler is active.
* <p>
* The value of start_pc must be a valid index into the code array
* of the opcode of an instruction.
* <p>
* The value of start_pc must be less than the value of end_pc.
* <p>
* The start_pc is inclusive and end_pc is exclusive; that is, the exception
* handler must be active while the program counter is within the interval
* [start_pc, end_pc].
* @return a valid index into the code array
*/
public short getStartPC() {
return startPC;
}
/**
* The values of the two items start_pc and end_pc indicate the ranges in the
* code array at which the exception handler is active.
* <p>
* The value of end_pc either must be a valid index into the code array of the
* opcode of an instruction or must be equal to code_length, the length of the
* code array.
* <p>
* The value of start_pc must be less than the value of end_pc.
* <p>
* The start_pc is inclusive and end_pc is exclusive; that is, the exception
* handler must be active while the program counter is within the interval
* [start_pc, end_pc].
* @return a valid index into the code array
*/
public short getEndPC() {
return endPC;
}
/**
* The value of the handler_pc item indicates the start of the exception
* handler. The value of the item must be a valid index into the code array
* and must be the index of the opcode of an instruction.
* @return the start of the exception handler
*/
public short getHandlerPC() {
return handlerPC;
}
/**
* If the value of the catch_type item is nonzero, it must be a valid index
* into the constant_pool table. The constant_pool entry at that index
* must be a CONSTANT_Class_info (?4.4.1) structure representing a class of
* exceptions that this exception handler is designated to catch. The exception
* handler will be called only if the thrown exception is an instance of the
* given class or one of its subclasses.
* <p>
* If the value of the catch_type item is zero, this exception handler is called
* for all exceptions. This is used to implement finally (?3.13).
* @return the value of the catch_type item
*/
public short getCatchType() {
return catchType;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType( "exception_handler", 0 );
structure.add( WORD, "start_pc", null );
structure.add( WORD, "end_pc", null );
structure.add( WORD, "handler_pc", null );
structure.add( WORD, "catch_type", null );
return structure;
}
}

View file

@ -0,0 +1,86 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The Exceptions attribute is a variable-length attribute in the attributes table of a
* method_info structure. The Exceptions attribute indicates which checked
* exceptions a method may throw. There may be at most one Exceptions attribute
* in each method_info structure.
* <p>
* The Exceptions attribute has the following format:
* <pre>
* Exceptions_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_exceptions;
* u2 exception_index_table[number_of_exceptions];
* }
* </pre>
*/
public class ExceptionsAttribute extends AbstractAttributeInfo {
private short numberOfExceptions;
private short[] exceptionIndexTable;
public ExceptionsAttribute(BinaryReader reader) throws IOException {
super(reader);
numberOfExceptions = reader.readNextShort();
exceptionIndexTable = reader.readNextShortArray(numberOfExceptions);
}
/**
* The value of the number_of_exceptions item indicates the number of entries
* in the exception_index_table.
* @return the number of entries in the exception_index_table
*/
public short getNumberOfExceptions() {
return numberOfExceptions;
}
/**
* Each value in the exception_index_table array must be a valid index into
* the constant_pool table. The constant_pool entry referenced by each table
* item must be a CONSTANT_Class_info structure representing a class
* type that this method is declared to throw.
* @return the exception_index_table array
*/
public short[] getExceptionIndexTable() {
return exceptionIndexTable;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure("Exceptions_attribute");
structure.add(WORD, "number_of_exceptions", null);
if (exceptionIndexTable.length > 0) {
DataType array = new ArrayDataType(WORD, exceptionIndexTable.length, WORD.getLength());
structure.add(array, "exception_index_table", null);
}
return structure;
}
}

View file

@ -0,0 +1,118 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Each classes array entry contains the following four items:
* <pre>
* InnerClass {
* u2 inner_class_info_index;
* u2 outer_class_info_index;
* u2 inner_name_index;
* u2 inner_class_access_flags;
* }
* </pre>
*/
public class InnerClass implements StructConverter {
private short innerClassInfoIndex;
private short outerClassInfoIndex;
private short innerNameIndex;
private short innerClassAccessFlags;
public InnerClass( BinaryReader reader ) throws IOException {
innerClassInfoIndex = reader.readNextShort();
outerClassInfoIndex = reader.readNextShort();
innerNameIndex = reader.readNextShort();
innerClassAccessFlags = reader.readNextShort();
}
/**
* The value of the inner_class_info_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index must be
* a CONSTANT_Class_info structure representing C. The remaining
* items in the classes array entry give information about C.
* @return a valid index into the constant_pool table
*/
public short getInnerClassInfoIndex() {
return innerClassInfoIndex;
}
/**
* If C is not a member of a class or an interface (that is, if C is a top-level
* class or interface (JLS ?7.6) or a local class or an anonymous
* class, the value of the outer_class_info_index item must
* be zero.
* <p>
* Otherwise, the value of the outer_class_info_index item must be a
* valid index into the constant_pool table, and the entry at that index must
* be a CONSTANT_Class_info structure representing the class or
* interface of which C is a member.
* @return a valid index into the constant_pool table, or zero
*/
public short getOuterClassInfoIndex() {
return outerClassInfoIndex;
}
/**
* If C is anonymous (JLS ?15.9.5), the value of the inner_name_index item must be zero.
* <p>
* Otherwise, the value of the inner_name_index item must be a valid index
* into the constant_pool table, and the entry at that index must be a
* CONSTANT_Utf8_info (?4.4.7) structure that represents the original simple
* name of C, as given in the source code from which this class file was
* compiled.
* @return a valid index into the constant_pool table, or zero
*/
public short getInnerNameIndex() {
return innerNameIndex;
}
/**
* The value of the inner_class_access_flags item is a mask of flags used
* to denote access permissions to and properties of class or interface C as
* declared in the source code from which this class file was compiled. It is
* used by compilers to recover the original information when source code is
* not available.
* The flags are shown in Table 4.23.
* @return a mask of flags used to denote access permissions to and properties of class or interface
*/
public short getInnerClassAccessFlags() {
return innerClassAccessFlags;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = new StructureDataType( "inner_class", 0 );
structure.add( WORD, "inner_class_info_index", null );
structure.add( WORD, "outer_class_info_index", null );
structure.add( WORD, "inner_name_index", null );
structure.add( WORD, "inner_class_access_flags", null );
return structure;
}
}

View file

@ -0,0 +1,93 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The InnerClasses attribute is a variable-length attribute in the attributes table
* of a ClassFile (?4.1) structure. If the constant pool of a class or interface C
* contains a CONSTANT_Class_info entry which represents a class or interface that
* is not a member of a package, then C's ClassFile structure must have exactly one
* InnerClasses attribute in its attributes table.
* <p>
* The InnerClasses attribute has the following format:
* <pre>
* InnerClasses_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_classes;
* {
* u2 inner_class_info_index;
* u2 outer_class_info_index;
* u2 inner_name_index;
* u2 inner_class_access_flags;
* } classes[number_of_classes];
* }
* </pre>
*/
public class InnerClassesAttribute extends AbstractAttributeInfo {
private short numberOfInnerClasses;
private InnerClass [] innerClasses;
public InnerClassesAttribute( BinaryReader reader ) throws IOException {
super( reader );
numberOfInnerClasses = reader.readNextShort();
innerClasses = new InnerClass[ numberOfInnerClasses ];
for ( int i = 0 ; i < numberOfInnerClasses ; i++ ) {
innerClasses[ i ] = new InnerClass( reader );
}
}
/**
* The value of the number_of_classes item indicates the number of entries in
* the classes array.
* @return the number of entries in the classes array
*/
public short getNumberOfInnerClasses() {
return numberOfInnerClasses;
}
/**
* Returns array of inner classes.
* @return array of inner classes.
*/
public InnerClass [] getInnerClasses() {
return innerClasses;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "InnerClasses_attribute" + "|" + numberOfInnerClasses + "|" );
structure.add( WORD, "number_of_classes", null );
for ( int i = 0 ; i < innerClasses.length ; ++i ) {
structure.add( innerClasses[ i ].toDataType(), "inner_class_" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Each line_number_table entry must contain the following two items:
* <pre>
* LineNumber {
* u2 start_pc;
* u2 line_number;
* }
* </pre>
*/
public class LineNumber implements StructConverter {
private short startPC;
private short lineNumber;
public LineNumber( BinaryReader reader ) throws IOException {
startPC = reader.readNextShort();
lineNumber = reader.readNextShort();
}
/**
* The value of the start_pc item must indicate the index into the code array
* at which the code for a new line in the original source file begins.
* <p>
* The value of start_pc must be less than the value of the code_length
* item of the Code attribute of which this LineNumberTable is an attribute.
* @return index into the code array at which the code for a new line in the original source file begins
*/
public short getStartPC() {
return startPC;
}
/**
* The value of the line_number item must give the corresponding line number in the original source file.
* @return the corresponding line number in the original source file
*/
public short getLineNumber() {
return lineNumber;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = new StructureDataType( "line_number", 0 );
structure.add( WORD, "start_pc", null );
structure.add( WORD, "line_number", null );
return structure;
}
}

View file

@ -0,0 +1,78 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The LineNumberTable attribute is an optional variable-length attribute in the
* attributes table of a Code attribute. It may be used by debuggers to
* determine which part of the Java virtual machine code array corresponds to a given
* line number in the original source file.
*
* If LineNumberTable attributes are present in the attributes table of a given
* Code attribute, then they may appear in any order. Furthermore, multiple
* LineNumberTable attributes may together represent a given line of a source file;
* that is, LineNumberTable attributes need not be one-to-one with source lines.
*
* The LineNumberTable attribute has the following format:
* <pre>
* LineNumberTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 line_number_table_length;
* {
* u2 start_pc;
* u2 line_number;
* } line_number_table[line_number_table_length];
* }
* </pre>
*/
public class LineNumberTableAttribute extends AbstractAttributeInfo {
private short lineNumberTableLength;
private LineNumber [] lineNumberTable;
public LineNumberTableAttribute( BinaryReader reader ) throws IOException {
super( reader );
lineNumberTableLength = reader.readNextShort();
lineNumberTable = new LineNumber[ lineNumberTableLength ];
for ( int i = 0 ; i < lineNumberTableLength ; i++ ) {
lineNumberTable[ i ] = new LineNumber( reader );
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "LineNumberTable_attribute" + "|" + lineNumberTableLength + "|";
StructureDataType structure = getBaseStructure( name );
structure.add( WORD, "line_number_table_length", null );
for ( int i = 0 ; i < lineNumberTable.length ; ++i ) {
structure.add( lineNumberTable[ i ].toDataType(), "line_number_" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,171 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Each frame (2.6) contains an array of variables known as its local variables. The
* length of the local variable array of a frame is determined at compile-time and
* supplied in the binary representation of a class or interface along with the code for
* the method associated with the frame (4.7.3).
* <p>
* A single local variable can hold a value of type:
* <ol>
* <li>boolean</li>
* <li>byte</li>
* <li>char</li>
* <li>short</li>
* <li>int</li>
* <li>float</li>
* <li>reference</li>
* <li>returnAddress</li>
* </ol>
* A pair of local variables can hold a value of type:
* <ol>
* <li>long</li>
* <li>double</li>
* </ol>
* <p>
* Local variables are addressed by indexing. The index of the first local variable is
* zero. An integer is considered to be an index into the local variable array if and only
* if that integer is between zero and one less than the size of the local variable array.
* <p>
* A value of type long or type double occupies two consecutive local variables.
* Such a value may only be addressed using the lesser index. For example, a value of
* type double stored in the local variable array at index n actually occupies the local
* variables with indices n and n+1; however, the local variable at index n+1 cannot
* be loaded from. It can be stored into. However, doing so invalidates the contents
* of local variable n.
* <p>
* The Java virtual machine does not require n to be even. In intuitive terms, values
* of types long and double need not be 64-bit aligned in the local variables array.
* Implementors are free to decide the appropriate way to represent such values using
* the two local variables reserved for the value.
* <p>
* The Java virtual machine uses local variables to pass parameters on method
* invocation. On class method invocation, any parameters are passed in consecutive
* local variables starting from local variable 0. On instance method invocation,
* local variable 0 is always used to pass a reference to the object on which the
* instance method is being invoked (this in the Java programming language). Any
* parameters are subsequently passed in consecutive local variables starting from
* local variable 1.
* <p>
* Each classes array entry contains the following four items:
* <pre>
* local_variable {
* u2 start_pc;
* u2 length;
* u2 name_index;
* u2 descriptor_index;
* u2 index;
* }
* </pre>
*/
public class LocalVariableJava implements StructConverter {
private short startPC;
private short length;
private short nameIndex;
private short descriptorIndex;
private short index;
public LocalVariableJava( BinaryReader reader ) throws IOException {
startPC = reader.readNextShort();
length = reader.readNextShort();
nameIndex = reader.readNextShort();
descriptorIndex = reader.readNextShort();
index = reader.readNextShort();
}
/**
* The given local variable must have a value at indices into the code array in
* the interval [start_pc, start_pc + length), that is, between start_pc
* inclusive and start_pc + length exclusive.
* <p>
* The value of start_pc must be a valid index into the code array of this
* Code attribute and must be the index of the opcode of an instruction.
* <p>
* The value of start_pc + length must either be a valid index into the code
* array of this Code attribute and be the index of the opcode of an instruction,
* or it must be the first index beyond the end of that code array.
* @return the start PC
*/
public short getStartPC() {
return startPC;
}
/**
* Returns the length of this local variable in bytes.
* @return the length of this local variable in bytes
*/
public short getLength() {
return length;
}
/**
* The value of the name_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must contain
* a CONSTANT_Utf8_info structure representing a valid unqualified
* name denoting a local variable.
* @return a valid index into the constant_pool table
*/
public short getNameIndex() {
return nameIndex;
}
/**
* The value of the descriptor_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must contain
* a CONSTANT_Utf8_info structure representing a field descriptor
* encoding the type of a local variable in the source program.
* @return a valid index into the constant_pool table
*/
public short getDescriptorIndex() {
return descriptorIndex;
}
/**
* The given local variable must be at index in the local variable array of the
* current frame.
* <p>
* If the local variable at index is of type double or long, it occupies both
* index and index + 1.
* @return index in the local variable array
*/
public short getIndex() {
return index;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType( "local_variable", 0 );
structure.add( WORD, "start_pc", null );
structure.add( WORD, "length", null );
structure.add( WORD, "name_index", null );
structure.add( WORD, "descriptor_index", null );
structure.add( WORD, "index", null );
return structure;
}
}

View file

@ -0,0 +1,82 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The LocalVariableTable attribute is an optional variable-length attribute in the
* attributes table of a Code attribute. It may be used by debuggers to
* determine the value of a given local variable during the execution of a method.
* <p>
* If LocalVariableTable attributes are present in the attributes table of a given
* Code attribute, then they may appear in any order. There may be no more than one
* LocalVariableTable attribute per local variable in the Code attribute.
* <p>
* The LocalVariableTable attribute has the following format:
* <pre>
* LocalVariableTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 local_variable_table_length;
* {
* u2 start_pc;
* u2 length;
* u2 name_index;
* u2 descriptor_index;
* u2 index;
* } local_variable_table[local_variable_table_length];
* }
* </pre>
*/
public class LocalVariableTableAttribute extends AbstractAttributeInfo {
private short localVariableTableLength;
private LocalVariableJava [] localVariableTable;
public LocalVariableTableAttribute( BinaryReader reader, AbstractConstantPoolInfoJava [] constantPool ) throws IOException {
super( reader );
localVariableTableLength = reader.readNextShort();
localVariableTable = new LocalVariableJava[ localVariableTableLength ];
for ( int i = 0 ; i < localVariableTableLength ; i++ ) {
localVariableTable[ i ] = new LocalVariableJava( reader );
}
}
public LocalVariableJava [] getLocalVariables() {
return localVariableTable;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "LocalVariableTable_attribute" );
structure.add( WORD, "local_variable_table_length", null );
for ( int i = 0 ; i < localVariableTable.length ; ++i ) {
structure.add( localVariableTable[ i ].toDataType(), "local_variable_" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,80 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The LocalVariableTypeTable attribute is an optional variable-length attribute in
* the attributes table of a Code attribute. It may be used by debuggers to
* determine the value of a given local variable during the execution of a method.
* <p>
* If LocalVariableTypeTable attributes are present in the attributes table of a
* given Code attribute, then they may appear in any order. There may be no more than
* one LocalVariableTypeTable attribute per local variable in the Code attribute.
* <p>
* The LocalVariableTypeTable attribute differs from the LocalVariableTable
* attribute in that it provides signature information rather than descriptor information.
* This difference is only significant for variables whose type is a generic reference
* type. Such variables will appear in both tables, while variables of other types will
* appear only in LocalVariableTable.
* <p>
* The LocalVariableTypeTable attribute has the following format:
* <pre>
* LocalVariableTypeTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 local_variable_type_table_length;
* {
* u2 start_pc;
* u2 length;
* u2 name_index;
* u2 signature_index;
* u2 index;
* } local_variable_type_table[local_variable_type_table_length];
* }
* </pre>
*/
public class LocalVariableTypeTableAttribute extends AbstractAttributeInfo {
private byte [] infoBytes;
public LocalVariableTypeTableAttribute( BinaryReader reader ) throws IOException {
super( reader );
infoBytes = reader.readNextByteArray( getAttributeLength() );
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "LocalVariableTypeTable_attribute" );
if ( infoBytes.length > 0 ) {
DataType array = new ArrayDataType( BYTE, infoBytes.length, BYTE.getLength() );
structure.add( array, "info", null );
}
return structure;
}
}

View file

@ -0,0 +1,101 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The RuntimeVisibleAnnotations attribute is a variable-length attribute in the
* attributes table of a ClassFile, field_info or method_info structure.
* <p>
* The RuntimeVisibleAnnotations attribute records runtimevisible
* Java programming language annotations on the corresponding class, field,
* or method.
* <p>
* Each ClassFile, field_info, and method_info structure may contain at most
* one RuntimeVisibleAnnotations attribute, which records all the runtime-visible
* Java programming language annotations on the corresponding program element.
* <p>
* The Java virtual machine must make these annotations available so they can be
* returned by the appropriate reflective APIs.
* <p>
* The RuntimeVisibleAnnotations attribute has the following format:
* <pre>
* RuntimeVisibleAnnotations_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 num_annotations;
* annotation annotations[num_annotations];
* }
* </pre>
*/
public class RuntimeInvisibleAnnotationsAttribute extends AbstractAttributeInfo {
private short numberOfAnnotations;
private AnnotationJava [] annotations;
public RuntimeInvisibleAnnotationsAttribute( BinaryReader reader ) throws IOException {
super( reader );
numberOfAnnotations = reader.readNextShort();
annotations = new AnnotationJava[ numberOfAnnotations ];
for ( int i = 0 ; i < numberOfAnnotations ; ++i ) {
annotations[ i ] = new AnnotationJava( reader );
}
}
/**
* The value of the num_annotations item gives the number of runtime-visible
* annotations represented by the structure.
* <p>
* Note that a maximum of 65535 runtime-visible Java programming language annotations
* may be directly attached to a program element.
* @return the number of runtime-visible annotations
*/
public short getNumberOfAnnotations() {
return numberOfAnnotations;
}
/**
* Returns the annotations table of a single runtime-visible
* annotation on a program element.
* @return the annotations table
*/
public AnnotationJava [] getAnnotations() {
return annotations;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "RuntimeInvisibleAnnotations_attribute" +"|" + numberOfAnnotations + "|";
StructureDataType structure = getBaseStructure( name );
structure.add( WORD, "num_annotations", null );
for ( int i = 0 ; i < annotations.length ; ++i ) {
structure.add( annotations[ i ].toDataType(), "annotation_" + i, null );
}
return structure;
}
}

View file

@ -0,0 +1,146 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The RuntimeVisibleParameterAnnotations attribute is a variable-length
* attribute in the attributes table of the method_info structure. The
* RuntimeVisibleParameterAnnotations attribute records runtime-visible Java
* programming language annotations on the parameters of the corresponding
* method.
* <p>
* The RuntimeInvisibleParameterAnnotations attribute is similar to the
* RuntimeVisibleParameterAnnotations attribute, except that the annotations
* represented by a RuntimeInvisibleParameterAnnotations attribute must not
* be made available for return by reflective APIs, unless the the Java virtual
* machine has specifically been instructed to retain these annotations via some
* implementation-specific mechanism such as a command line flag. In the absence
* of such instructions, the Java virtual machine ignores this attribute.
* <p>
* Each method_info structure may contain at most one
* RuntimeVisibleParameterAnnotations attribute, which records all the runtimevisible
* Java programming language annotations on the parameters of the
* corresponding method. The Java virtual machine must make these annotations
* available so they can be returned by the appropriate reflective APIs.
* <p>
* The Runtime(In)VisibleParameterAnnotations attribute has the following format:
* <pre>
* Runtime(In)VisibleParameterAnnotations_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u1 num_parameters;
* {
* u2 num_annotations;
* annotation annotations[num_annotations];
* } parameter_annotations[num_parameters];
* }
* </pre>
*/
public class RuntimeParameterAnnotationsAttribute extends AbstractAttributeInfo {
private boolean _isVisible;
private byte numberOfParameters;
private Map<Integer, AnnotationJava []> parameterAnnotations = new HashMap<Integer, AnnotationJava []>();
public RuntimeParameterAnnotationsAttribute( BinaryReader reader, boolean isVisible ) throws IOException {
super( reader );
_isVisible = isVisible;
numberOfParameters = reader.readNextByte();
for ( int i = 0 ; i < numberOfParameters ; ++i ) {
short numberOfAnnotations = reader.readNextShort();
AnnotationJava [] annotations = new AnnotationJava[ numberOfAnnotations ];
for ( int a = 0 ; a < numberOfAnnotations ; ++a ) {
annotations[ a ] = new AnnotationJava( reader );
}
parameterAnnotations.put( i, annotations );
}
}
/**
* If true, these parameters are "RuntimeVisibleParameterAnnotations".
* Otherwise, these parameters are "RuntimeInvisibleParameterAnnotations".
* @return true if visible parameters
*/
public boolean isVisible() {
return _isVisible;
}
/**
* The value of the num_parameters item gives the number of parameters of
* the method represented by the method_info structure on which the annotation
* occurs.
* (This duplicates information that could be extracted from the method descriptor.)
* @return the number of parameters for this method
*/
public byte getNumberOfParameters() {
return numberOfParameters;
}
/**
* Each value of the parameter_annotations table represents all of the runtimevisible
* annotations on a single parameter. The sequence of values in the table
* corresponds to the sequence of parameters in the method descriptor. Each
* parameter_annotations entry contains the following two items:
* num_annotations
* The value of the num_annotations item indicates the number of runtimevisible
* annotations on the parameter corresponding to the sequence number
* of this parameter_annotations element.
* annotations
* Each value of the annotations table represents a single runtime-visible
* annotation on the parameter corresponding to the sequence number of this
* parameter_annotations element.
* @param parameter the parameter index
* @return the annotations for the given parameter
*/
public AnnotationJava [] getParameterAnnotations( int parameter ) {
return parameterAnnotations.get( parameter );
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = _isVisible ?
"RuntimeVisibleParameterAnnotations_attribute" + "|" + numberOfParameters + "|" :
"RuntimeInvisibleParameterAnnotations_attribute" + "|" + numberOfParameters + "|";
StructureDataType structure = getBaseStructure( name );
structure.add( BYTE, "num_parameters", null );
for ( int i = 0 ; i < numberOfParameters ; ++i ) {
structure.add( WORD, "num_annotations_" + i, null );
AnnotationJava [] annotations = parameterAnnotations.get( i );
for ( int a = 0 ; a < annotations.length ; ++a ) {
structure.add( annotations[ a ].toDataType(), "annotations_" + i + "_" + a, null );
}
}
return structure;
}
}

View file

@ -0,0 +1,47 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class RuntimeVisibleAnnotationsAttribute extends AbstractAttributeInfo {
private byte [] infoBytes;
public RuntimeVisibleAnnotationsAttribute( BinaryReader reader ) throws IOException {
super( reader );
infoBytes = reader.readNextByteArray( getAttributeLength() );
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "RuntimeVisibleAnnotations_attribute" );
if ( infoBytes.length > 0 ) {
DataType array = new ArrayDataType( BYTE, infoBytes.length, BYTE.getLength() );
structure.add( array, "info", null );
}
return structure;
}
}

View file

@ -0,0 +1,74 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The Signature attribute is an optional fixed-length attribute in the attributes
* table of a ClassFile, field_info or method_info structure.
* The Signature attribute records generic signature information for any class,
* interface, constructor or member whose generic signature in the Java programming
* language would include references to type variables or parameterized types.
* <p>
* The Signature attribute has the following format:
* <pre>
* Signature_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 signature_index;
* }
* </pre>
*/
public class SignatureAttribute extends AbstractAttributeInfo {
private short signatureIndex;
public SignatureAttribute( BinaryReader reader ) throws IOException {
super( reader );
signatureIndex = reader.readNextShort();
}
/**
* The value of the signature_index item must be a valid index into the
* constant_pool table. The constant pool entry at that index must be a
* CONSTANT_Utf8_info structure representing either a class signature,
* if this signature attribute is an attribute of a ClassFile structure, a method type
* signature, if this signature is an attribute of a method_info structure, or a field
* type signature otherwise.
* @return a valid index into the constant_pool table
*/
public short getSignatureIndex() {
return signatureIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "Signature_attribute" );
structure.add( WORD, "signature_index", null );
return structure;
}
}

View file

@ -0,0 +1,75 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The SourceDebugExtension attribute is an optional attribute in the attributes
* table of a ClassFile structure. There can be no more than one
* SourceDebugExtension attribute in the attributes table of a given ClassFile
* structure.
* <p>
* The SourceDebugExtension attribute has the following format:
* <pre>
* SourceDebugExtension_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u1 debug_extension[attribute_length];
* }
* </pre>
*/
public class SourceDebugExtensionAttribute extends AbstractAttributeInfo {
private byte [] debugExtension;
public SourceDebugExtensionAttribute( BinaryReader reader ) throws IOException {
super( reader );
debugExtension = reader.readNextByteArray( getAttributeLength() );
}
/**
* The debug_extension array holds extended debugging information which has
* no semantic effect on the Java virtual machine. The information is represented
* using a modified UTF-8 string with no terminating zero byte.
* <p>
* Note that the debug_extension array may denote a string longer than that which can be
* represented with an instance of class String.
* @return an array of extended debugging information
*/
public byte [] getDebugExtension() {
return debugExtension;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "SourceDebugExtension_attribute" );
if ( debugExtension.length > 0 ) {
DataType array = new ArrayDataType( BYTE, debugExtension.length, BYTE.getLength() );
structure.add( array, "debug_extension", null );
}
return structure;
}
}

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The SourceFile attribute is an optional fixed-length attribute in the attributes
* table of a ClassFile structure. There can be no more than one SourceFile
* attribute in the attributes table of a given ClassFile structure.
* <p>
* The SourceFile attribute has the following format:
* <pre>
* SourceFile_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 sourcefile_index;
* }
* </pre>
*/
public class SourceFileAttribute extends AbstractAttributeInfo {
private short sourceFileIndex;
public SourceFileAttribute( BinaryReader reader ) throws IOException {
super( reader );
sourceFileIndex = reader.readNextShort();
}
/**
* The value of the sourcefile_index item must be a valid index into the
* constant_pool table. The constant pool entry at that index must be a
* CONSTANT_Utf8_info structure representing a string.
* <p>
* The string referenced by the sourcefile_index item will be interpreted as
* indicating the name of the source file from which this class file was compiled.
* It will not be interpreted as indicating the name of a directory containing the
* file or an absolute path name for the file; such platform-specific additional
* information must be supplied by the runtime interpreter or development tool
* at the time the file name is actually used.
* @return a valid index into the constant_pool table
*/
public short getSourceFileIndex() {
return sourceFileIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "SourceFile_attribute" );
structure.add( WORD, "sourcefile_index", null );
return structure;
}
}

View file

@ -0,0 +1,108 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The StackMapTable attribute is a variable-length attribute in the attributes
* table of a Code attribute. This attribute is used during the process of
* verification by typechecking. A method's Code attribute may have at most
* one StackMapTable attribute.
* <p>
* A StackMapTable attribute consists of zero or more stack map frames. Each
* stack map frame specifies (either explicitly or implicitly) a bytecode offset, the
* verification types for the local variables, and the verification types for
* the operand stack.
* <p>
* The type checker deals with and manipulates the expected types of a method's local
* variables and operand stack. Throughout this section, a location refers to either a
* single local variable or to a single operand stack entry.
* <p>
* We will use the terms stack map frame and type state interchangeably to describe
* a mapping from locations in the operand stack and local variables of a method
* to verification types. We will usually use the term stack map frame when such a
* mapping is provided in the class file, and the term type state when the mapping
* is used by the type checker.
* <p>
* In a class file whose version number is greater than or equal to 50.0, if a method's
* Code attribute does not have a StackMapTable attribute, it has an implicit stack
* map attribute. This implicit stack map attribute is equivalent to a StackMapTable
* attribute with number_of_entries equal to zero.
* <p>
* The StackMapTable attribute has the following format:
* <pre.
* StackMapTable_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 number_of_entries;
* stack_map_frame entries[number_of_entries];
* }
* </pre>
*/
public class StackMapTableAttribute extends AbstractAttributeInfo {
// private short numberOfEntries;
// private StackMapFrame [] entries;
private byte [] infoBytes;
public StackMapTableAttribute( BinaryReader reader ) throws IOException {
super( reader );
// numberOfEntries = reader.readNextShort();
// entries = new StackMapFrame[ numberOfEntries ];
// for ( int i = 0 ; i < numberOfEntries ; i++ ) {
// entries[ i ] = new StackMapFrame( reader );
// }
infoBytes = reader.readNextByteArray( getAttributeLength() );
}
// public short getNumberOfEntries() {
// return numberOfEntries;
// }
//
// public StackMapFrame[] getEntries() {
// return entries;
// }
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
// String name = "StackMapTable_attribute_" + entries;
// StructureDataType structure = getBaseStructure( name );
// structure.add( WORD, "number_of_entries", null );
// for ( int i = 0 ; i < entries.length ; ++i ) {
// structure.add( entries[ i ].toDataType(), "entries_" + i, null );
// }
// return structure;
StructureDataType structure = getBaseStructure( "StackMapTable_attribute" );
if ( infoBytes.length > 0 ) {
DataType array = new ArrayDataType( BYTE, infoBytes.length, BYTE.getLength() );
structure.add( array, "info", null );
}
return structure;
}
}

View file

@ -0,0 +1,54 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.attributes;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The Synthetic attribute is a fixed-length attribute in the attributes table of a
* ClassFile, field_info or method_info structure.
* A class member that does not appear in the source code must be marked using a Synthetic
* attribute, or else it must have its ACC_SYNTHETIC flag set. The only exceptions
* to this requirement are compiler-generated methods which are not considered
* implementation artifacts, namely the instance initialization method representing a
* default constructor of the Java programming language, the class initialization
* method, and the Enum.values() and Enum.valueOf() methods.
* <p>
* The value of the attribute_name_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing the string "Synthetic".
*/
public class SyntheticAttribute extends AbstractAttributeInfo {
public SyntheticAttribute( BinaryReader reader ) throws IOException {
super( reader );
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = getBaseStructure( "Synthetic_attribute" );
return structure;
}
}

View file

@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* Java virtual machine instructions do not rely on the runtime layout of classes,
* interfaces, class instances, or arrays. Instead, instructions refer to symbolic
* information in the constant_pool table.
* <p>
* All constant_pool table entries have the following general format:
* <pre>
* cp_info {
* u1 tag;
* u1 info[];
* }
* </pre>
* Each item in the constant_pool table must begin with a 1-byte tag indicating
* the kind of cp_info entry. The contents of the info array vary with the value of
* tag. The valid tags and their values are listed in Table 4.3. Each tag byte must be
* followed by two or more bytes giving information about the specific constant. The
* format of the additional information varies with the tag value.
*
*/
public abstract class AbstractConstantPoolInfoJava implements StructConverter {
private long _offset;
private byte tag;
protected AbstractConstantPoolInfoJava( BinaryReader reader ) throws IOException {
_offset = reader.getPointerIndex();
tag = reader.readNextByte();
}
public long getOffset() {
return _offset;
}
public byte getTag() {
return tag;
}
}

View file

@ -0,0 +1,94 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
*
*/
public abstract class AbstractConstantPoolReferenceInfo extends AbstractConstantPoolInfoJava {
private short classIndex;
private short nameAndTypeIndex;
protected AbstractConstantPoolReferenceInfo(BinaryReader reader) throws IOException {
super(reader);
classIndex = reader.readNextShort();
nameAndTypeIndex = reader.readNextShort();
}
/**
* The value of the class_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Class_info structure representing a class or interface type
* that has the field or method as a member.
* <p>
* The class_index item of a CONSTANT_Methodref_info structure must be a
* class type, not an interface type.
* <p>
* The class_index item of a CONSTANT_InterfaceMethodref_info structure
* must be an interface type.
* <p>
* The class_index item of a CONSTANT_Fieldref_info structure may be either
* a class type or an interface type.
* <p>
* @return a valid index into the constant_pool table
*/
public short getClassIndex() {
return classIndex;
}
/**
* The value of the name_and_type_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_NameAndType_info structure. This constant_pool entry
* indicates the name and descriptor of the field or method.
* <p>
* In a CONSTANT_Fieldref_info, the indicated descriptor must be a field
* descriptor (?4.3.2). Otherwise, the indicated descriptor must be a method
* descriptor (?4.3.3).
* <p>
* If the name of the method of a CONSTANT_Methodref_info structure begins
* with a '<' ('\u003c'), then the name must be the special name <init>,
* representing an instance initialization method. The return type of such
* a method must be void.
* <p>
* @return a valid index into the constant_pool table
*/
public short getNameAndTypeIndex() {
return nameAndTypeIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "unnamed";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "class_index", null );
structure.add( WORD, "name_and_type_index", null );
return structure;
}
}

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_Class_info structure is used to represent a class or an interface:
* <pre>
* CONSTANT_Class_info {
* u1 tag;
* u2 name_index;
* }
* </pre>
* The items of the CONSTANT_Class_info structure are the following:
* tag
* The tag item has the value CONSTANT_Class (7).
*
* name_index
* The value of the name_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info (?4.4.7) structure representing a valid binary class or
* interface name encoded in internal form (?4.2.1).
*/
public class ConstantPoolClassInfo extends AbstractConstantPoolInfoJava {
private short nameIndex;
public ConstantPoolClassInfo( BinaryReader reader ) throws IOException {
super( reader );
nameIndex = reader.readNextShort();
}
/**
* The value of the name_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info (?4.4.7) structure representing a valid binary class or
* interface name encoded in internal form (?4.2.1).
* @return a valid index into the constant_pool table
*/
public short getNameIndex() {
return nameIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_Class_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "name_index", null );
return structure;
}
}

View file

@ -0,0 +1,105 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_Double_info represent 8-byte numeric (double) constants:
* <pre>
* CONSTANT_Double_info {
* u1 tag;
* u4 high_bytes;
* u4 low_bytes;
* }
* </pre>
* All 8-byte constants take up two entries in the constant_pool table of the class
* file. If a CONSTANT_Double_info structure is the item
* in the constant_pool table at index n, then the next usable item in the pool is
* located at index n+2. The constant_pool index n+1 must be valid but is considered
* unusable.
* <p>
* In retrospect, making 8-byte constants take two constant pool entries was a poor choice.
*/
public class ConstantPoolDoubleInfo extends AbstractConstantPoolInfoJava {
private int highBytes;
private int lowBytes;
public ConstantPoolDoubleInfo( BinaryReader reader ) throws IOException {
super( reader );
highBytes = reader.readNextInt();
lowBytes = reader.readNextInt();
}
/**
* The value represented by the CONSTANT_Double_info structure is determined
* as follows. The high_bytes and low_bytes items are converted into the long
* constant bits, which is equal to
* ((long) high_bytes << 32) + low_bytes
* Then:
* If bits is 0x7ff0000000000000L, the double value will be positive infinity.
*
* If bits is 0xfff0000000000000L, the double value will be negative infinity.
*
* If bits is in the range 0x7ff0000000000001L through 0x7fffffffffffffffL
* or in the range 0xfff0000000000001L through 0xffffffffffffffffL, the
* double value will be NaN.
*
* In all other cases, let s, e, and m be three values that might be computed from bits:
*
* int s = ((bits >> 63) == 0) ? 1 : -1;
* int e = (int)((bits >> 52) & 0x7ffL);
* long m = (e == 0) ?
* (bits & 0xfffffffffffffL) << 1 :
* (bits & 0xfffffffffffffL) | 0x10000000000000L;
*
* Then the floating-point value equals the double value of the mathematical
* expression s ? m ? 2e-1075.
* @return the double value
*/
public double getValue() {
return Double.longBitsToDouble( ( ( (long)highBytes ) << 32 ) + ( lowBytes & 0xffffffffL ) );
}
public long getRawBytes() {
return (((long)highBytes) << 32 ) + (lowBytes & 0xffffffffL);
}
@Override
public String toString() {
return "" + getValue();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_Long_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( DWORD, "high_bytes", null );
structure.add( DWORD, "low_bytes", null );
return structure;
}
}

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import java.io.IOException;
public class ConstantPoolFactory {
public static AbstractConstantPoolInfoJava get( BinaryReader reader ) throws IOException {
switch ( reader.peekNextByte() ) {
case ConstantPoolTagsJava.CONSTANT_Class:
return new ConstantPoolClassInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Double:
return new ConstantPoolDoubleInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Fieldref:
return new ConstantPoolFieldReferenceInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Float:
return new ConstantPoolFloatInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Integer:
return new ConstantPoolIntegerInfo( reader );
case ConstantPoolTagsJava.CONSTANT_InterfaceMethodref:
return new ConstantPoolInterfaceMethodReferenceInfo( reader );
case ConstantPoolTagsJava.CONSTANT_InvokeDynamic:
return new ConstantPoolInvokeDynamicInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Long:
return new ConstantPoolLongInfo( reader );
case ConstantPoolTagsJava.CONSTANT_MethodHandle:
return new ConstantPoolMethodHandleInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Methodref:
return new ConstantPoolMethodReferenceInfo( reader );
case ConstantPoolTagsJava.CONSTANT_MethodType:
return new ConstantPoolMethodTypeInfo( reader );
case ConstantPoolTagsJava.CONSTANT_NameAndType:
return new ConstantPoolNameAndTypeInfo( reader );
case ConstantPoolTagsJava.CONSTANT_String:
return new ConstantPoolStringInfo( reader );
case ConstantPoolTagsJava.CONSTANT_Utf8:
return new ConstantPoolUtf8Info( reader );
case 0:
return null;
}
throw new RuntimeException();
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class ConstantPoolFieldReferenceInfo extends AbstractConstantPoolReferenceInfo {
public ConstantPoolFieldReferenceInfo(BinaryReader reader) throws IOException {
super(reader);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = (StructureDataType) super.toDataType();
try {
structure.setName( "CONSTANT_Fieldref_info" );
}
catch (InvalidNameException e) {
throw new IOException( e );
}
return structure;
}
}

View file

@ -0,0 +1,97 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_Float_info structures represent 4-byte numeric (float) constants:
* <pre>
* CONSTANT_Float_info {
* u1 tag;
* u4 bytes;
* }
* </pre>
*/
public class ConstantPoolFloatInfo extends AbstractConstantPoolInfoJava {
private int bytes;
public ConstantPoolFloatInfo( BinaryReader reader ) throws IOException {
super( reader );
bytes = reader.readNextInt();
}
/**
* The bytes item of the CONSTANT_Float_info structure represents the value
* of the float constant in IEEE 754 floating-point single format (?2.3.2). The
* bytes of the single format representation are stored in big-endian (high byte
* first) order.
*
* The value represented by the CONSTANT_Float_info structure is determined
* as follows. The bytes of the value are first converted into an int constant bits.
* Then:
* If bits is 0x7f800000, the float value will be positive infinity.
*
* If bits is 0xff800000, the float value will be negative infinity.
*
* If bits is in the range 0x7f800001 through 0x7fffffff or in the range
* 0xff800001 through 0xffffffff, the float value will be NaN.
*
* In all other cases, let s, e, and m be three values that might be computed from
* bits:
* int s = ((bits >> 31) == 0) ? 1 : -1;
* int e = ((bits >> 23) & 0xff);
* int m = (e == 0) ?
* (bits & 0x7fffff) << 1 :
* (bits & 0x7fffff) | 0x800000;
* Then the float value equals the result of the mathematical expression
* s x m x 2e-150.
*
* @return the value of the float constant
*/
public int getRawBytes() {
return bytes;
}
public float getValue() {
return Float.intBitsToFloat( bytes );
}
@Override
public String toString() {
return "" + getValue();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_Float_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( DWORD, "bytes", null );
return structure;
}
}

View file

@ -0,0 +1,70 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_Integer_info structures represent 4-byte numeric (int) constants:
* <pre>
* CONSTANT_Integer_info {
* u1 tag;
* u4 bytes;
* }
* </pre>
*/
public class ConstantPoolIntegerInfo extends AbstractConstantPoolInfoJava {
private int bytes;
public ConstantPoolIntegerInfo( BinaryReader reader ) throws IOException {
super( reader );
bytes = reader.readNextInt();
}
/**
* The bytes item of the CONSTANT_Integer_info structure represents the value
* of the int constant. The bytes of the value are stored in big-endian (high byte
* first) order.
* @return the value of the integer constant
*/
public int getValue() {
return bytes;
}
@Override
public String toString() {
return "" + getValue();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_Integer_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( DWORD, "bytes", null );
return structure;
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class ConstantPoolInterfaceMethodReferenceInfo extends AbstractConstantPoolReferenceInfo {
public ConstantPoolInterfaceMethodReferenceInfo(BinaryReader reader) throws IOException {
super(reader);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = (StructureDataType) super.toDataType();
try {
structure.setName("CONSTANT_InterfaceMethodref_info");
}
catch (InvalidNameException e) {
throw new IOException(e);
}
return structure;
}
}

View file

@ -0,0 +1,83 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_InvokeDynamic_info structure is used by an invokedynamic
* instruction (?invokedynamic) to specify a bootstrap method, the dynamic
* invocation name, the argument and return types of the call, and optionally, a
* sequence of additional constants called static arguments to the bootstrap method.
* <pre>
* CONSTANT_InvokeDynamic_info {
* u1 tag;
* u2 bootstrap_method_attr_index;
* u2 name_and_type_index;
* }
* </pre>
*/
public class ConstantPoolInvokeDynamicInfo extends AbstractConstantPoolInfoJava {
private short bootstrapMethodAttrIndex;
private short nameAndTypeIndex;
public ConstantPoolInvokeDynamicInfo( BinaryReader reader ) throws IOException {
super( reader );
bootstrapMethodAttrIndex = reader.readNextShort();
nameAndTypeIndex = reader.readNextShort();
}
/**
* The value of the bootstrap_method_attr_index item must be a valid index
* into the bootstrap_methods array of the bootstrap method table of
* this class file.
* @return a valid index into the bootstrap_methods array
*/
public short getBootstrapMethodAttrIndex() {
return bootstrapMethodAttrIndex;
}
/**
* The value of the name_and_type_index item must be a valid index into
* the constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_NameAndType_info structure representing a method name
* and method descriptor.
* @return a valid index into the constant_pool table
*/
public short getNameAndTypeIndex() {
return nameAndTypeIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_InvokeDynamic_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "bootstrap_method_attr_index", null );
structure.add( WORD, "name_and_type_index", null );
return structure;
}
}

View file

@ -0,0 +1,85 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_Long_info represent 8-byte numeric (long) constants:
* <pre>
* CONSTANT_Long_info {
* u1 tag;
* u4 high_bytes;
* u4 low_bytes;
* }
* </pre>
* All 8-byte constants take up two entries in the constant_pool table of the class
* file. If a CONSTANT_Long_info structure is the item
* in the constant_pool table at index n, then the next usable item in the pool is
* located at index n+2. The constant_pool index n+1 must be valid but is considered
* unusable.
* <p>
* In retrospect, making 8-byte constants take two constant pool entries was a poor choice.
*/
public class ConstantPoolLongInfo extends AbstractConstantPoolInfoJava {
private int highBytes;
private int lowBytes;
public ConstantPoolLongInfo( BinaryReader reader ) throws IOException {
super( reader );
highBytes = reader.readNextInt();
lowBytes = reader.readNextInt();
}
/**
* The unsigned high_bytes and low_bytes items of the CONSTANT_Long_info
* structure together represent the value of the long constant
* <pre>
* ((long) high_bytes << 32) + low_bytes
* </pre>
* where the bytes of each of high_bytes and low_bytes are stored in big-endian
* (high byte first) order.
* @return the long value
*/
public long getValue() {
return ( ( (long)highBytes ) << 32 ) + ( lowBytes & 0xffffffffL );
}
@Override
public String toString() {
return "" + getValue();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_Long_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( DWORD, "high_bytes", null );
structure.add( DWORD, "low_bytes", null );
return structure;
}
}

View file

@ -0,0 +1,117 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_MethodHandle_info structure is used to represent a method handle:
* <pre>
* CONSTANT_MethodHandle_info {
* u1 tag;
* u1 reference_kind;
* u2 reference_index;
* }
* </pre>
*/
public class ConstantPoolMethodHandleInfo extends AbstractConstantPoolInfoJava {
private byte referenceKind;
private int referenceIndex;
public ConstantPoolMethodHandleInfo( BinaryReader reader ) throws IOException {
super( reader );
referenceKind = reader.readNextByte();
referenceIndex = reader.readNextShort();
}
/**
* The value of the reference_kind item must be in the range 1 to 9. The
* value denotes the kind of this method handle, which characterizes its bytecode
* behavior.
* @return this method handle bytecode behavior
*/
public byte getReferenceKind() {
return referenceKind;
}
/**
* The value of the reference_index item must be a valid index into the
* constant_pool table.
* <p>
* If the value of the reference_kind item is
* 1 (REF_getField),
* 2 (REF_getStatic),
* 3 (REF_putField), or
* 4 (REF_putStatic),
* then the constant_pool entry at that index must be a CONSTANT_Fieldref_info
* structure representing a field for which a method handle is to be
* created.
* <p>
* If the value of the reference_kind item is
* 5 (REF_invokeVirtual),
* 6 (REF_invokeStatic),
* 7 (REF_invokeSpecial), or
* 8 (REF_newInvokeSpecial),
* then the constant_pool entry at that index must be
* a CONSTANT_Methodref_info structure representing a class's method
* or constructor for which a method handle is to be created.
* <p>
* If the value of the reference_kind item is
* 9 (REF_invokeInterface),
* then the constant_pool entry at that index must be a
* CONSTANT_InterfaceMethodref_info structure representing an
* interface's method for which a method handle is to be created.
* <p>
* If the value of the reference_kind item is
* 5 (REF_invokeVirtual),
* 6 (REF_invokeStatic),
* 7 (REF_invokeSpecial), or
* 9 (REF_invokeInterface),
* the name of the method represented by a CONSTANT_Methodref_info structure
* must not be <init> or <clinit>.
* <p>
* If the value is
* 8 (REF_newInvokeSpecial),
* the name of the method represented by a
* CONSTANT_Methodref_info structure must be <init>.
*
* @return a valid index into the constant_pool table
*/
public int getReferenceIndex() {
return referenceIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_MethodHandle_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( BYTE, "reference_kind", null );
structure.add( WORD, "reference_index", null );
return structure;
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class ConstantPoolMethodReferenceInfo extends AbstractConstantPoolReferenceInfo {
public ConstantPoolMethodReferenceInfo(BinaryReader reader) throws IOException {
super(reader);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType structure = (StructureDataType) super.toDataType();
try {
structure.setName("CONSTANT_Methodref_info");
}
catch (InvalidNameException e) {
throw new IOException(e);
}
return structure;
}
}

View file

@ -0,0 +1,65 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_MethodType_info structure is used to represent a method type:
* <pre>
* CONSTANT_MethodType_info {
* u1 tag;
* u2 descriptor_index;
* }
* </pre>
*/
public class ConstantPoolMethodTypeInfo extends AbstractConstantPoolInfoJava {
private short descriptorIndex;
public ConstantPoolMethodTypeInfo( BinaryReader reader ) throws IOException {
super( reader );
descriptorIndex = reader.readNextShort();
}
/**
* The value of the descriptor_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing a method descriptor.
* @return a valid index into the constant_pool table
*/
public short getDescriptorIndex() {
return descriptorIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_MethodType_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "descriptor_index", null );
return structure;
}
}

View file

@ -0,0 +1,83 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_NameAndType_info structure is used to represent a field or method,
* without indicating which class or interface type it belongs to:
* <pre>
* CONSTANT_NameAndType_info {
* u1 tag;
* u2 name_index;
* u2 descriptor_index;
* }
* </pre>
*/
public class ConstantPoolNameAndTypeInfo extends AbstractConstantPoolInfoJava {
private short nameIndex;
private short descriptorIndex;
public ConstantPoolNameAndTypeInfo( BinaryReader reader ) throws IOException {
super( reader );
nameIndex = reader.readNextShort();
descriptorIndex = reader.readNextShort();
}
/**
* The value of the name_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing either the special method
* name <init> or a valid unqualified name denoting a field or
* method.
* @return a valid index into the constant_pool table to the name
*/
public short getNameIndex() {
return nameIndex;
}
/**
* The value of the descriptor_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing a valid field descriptor
* or method descriptor.
* @return a valid index into the constant_pool table to the descriptor
*/
public short getDescriptorIndex() {
return descriptorIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_NameAndType_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "name_index", null );
structure.add( WORD, "descriptor_index", null );
return structure;
}
}

View file

@ -0,0 +1,67 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_String_info structure is used to represent constant objects of the
* type String:
* <pre>
* CONSTANT_String_info {
* u1 tag;
* u2 string_index
* }
* </pre>
*/
public class ConstantPoolStringInfo extends AbstractConstantPoolInfoJava {
private short stringIndex;
public ConstantPoolStringInfo( BinaryReader reader ) throws IOException {
super(reader);
stringIndex = reader.readNextShort();
}
/**
* The value of the string_index item must be a valid index into the
* constant_pool table. The constant_pool entry at that index must be a
* CONSTANT_Utf8_info structure representing the sequence of Unicode
* code points to which the String object is to be initialized.
* @return a valid index into the constant_pool table
*/
public short getStringIndex() {
return stringIndex;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_String_info";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "string_index", null );
return structure;
}
}

View file

@ -0,0 +1,35 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
public final class ConstantPoolTagsJava {
public final static byte CONSTANT_Class = 7;
public final static byte CONSTANT_Fieldref = 9;
public final static byte CONSTANT_Methodref = 10;
public final static byte CONSTANT_InterfaceMethodref = 11;
public final static byte CONSTANT_String = 8;
public final static byte CONSTANT_Integer = 3;
public final static byte CONSTANT_Float = 4;
public final static byte CONSTANT_Long = 5;
public final static byte CONSTANT_Double = 6;
public final static byte CONSTANT_NameAndType = 12;
public final static byte CONSTANT_Utf8 = 1;
public final static byte CONSTANT_MethodHandle = 15;
public final static byte CONSTANT_MethodType = 16;
public final static byte CONSTANT_InvokeDynamic = 18;
}

View file

@ -0,0 +1,97 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.format.constantpool;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* NOTE: THE FOLLOWING TEXT EXTRACTED FROM JVMS7.PDF
* <p>
* The CONSTANT_Utf8_info structure is used to represent constant string values:
* <pre>
* CONSTANT_Utf8_info {
* u1 tag;
* u2 length;
* u1 bytes[length];
* }
* </pre>
* <p>
* String content is encoded in modified UTF-8. Modified UTF-8 strings are encoded
* so that code point sequences that contain only non-null ASCII characters can be
* represented using only 1 byte per code point, but all code points in the Unicode
* codespace can be represented.
*/
public class ConstantPoolUtf8Info extends AbstractConstantPoolInfoJava {
private short length;
private byte [] bytes;
public ConstantPoolUtf8Info( BinaryReader reader ) throws IOException {
super( reader );
length = reader.readNextShort();
bytes = reader.readNextByteArray( length );
}
/**
* The value of the length item gives the number of bytes in the bytes array
* (not the length of the resulting string). The strings in the CONSTANT_Utf8_info
* structure are not null-terminated.
* @return the number of bytes in the bytes array
*/
public short getLength() {
return length;
}
/**
* The bytes array contains the bytes of the string.
* No byte may have the value (byte)0 or lie in the
* range (byte)0xf0 - (byte)0xff.
* @return the bytes of the string
*/
public byte [] getBytes() {
return bytes;
}
/**
* TODO
* Returns the byte array translated into a string.
* @return the byte array translated into a string
*/
public String getString() {
return new String( bytes );
}
@Override
public String toString() {
return getString();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "CONSTANT_Utf8_info" + "|" + length + "|";
Structure structure = new StructureDataType( name, 0 );
structure.add( BYTE, "tag", null );
structure.add( WORD, "length", null );
if ( length > 0 ) {
structure.add(UTF8, length, "data", null);
}
return structure;
}
}

View file

@ -0,0 +1,191 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import ghidra.javaclass.format.DescriptorDecoder;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
public class InvokeMethodsTest {
@Test
public void testEmitPcodeToResolveMethodReferenceInvokeDynamic() throws IOException{
short bootstrap = 0;
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 5);
TestClassFileCreator.appendInvokeDynamicInfo(classFile, bootstrap, (short) 2); //1
TestClassFileCreator.appendNameAndType(classFile, (short) 3, (short) 4); //2
TestClassFileCreator.appendUtf8(classFile, "dynamicMethodName"); //3
TestClassFileCreator.appendUtf8(classFile,"(I)I"); //4 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
StringBuilder pCode = new StringBuilder();
InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_DYNAMIC);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_INVOKEDYNAMIC);
assertEquals("incorrect pcode for dynamic invocation", expected.toString(), pCode.toString());
}
@Test
public void testEmitPcodeToResolveMethodReferenceInvokeInterface() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendInterfaceMethodRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "interfaceMethodName"); //5
TestClassFileCreator.appendUtf8(classFile,"(I)I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
StringBuilder pCode = new StringBuilder();
InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_INTERFACE);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, InvokeMethods.THIS, "1", ConstantPoolJava.CPOOL_INVOKEINTERFACE);
assertEquals("incorrect pcode for interface method invocation", expected.toString(), pCode.toString());
}
@Test
public void testEmitPcodeToResolveMethodReferenceInvokeSpecial() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendMethodRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "methodName"); //5
TestClassFileCreator.appendUtf8(classFile,"(I)I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
StringBuilder pCode = new StringBuilder();
InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_SPECIAL);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, InvokeMethods.THIS, "1", ConstantPoolJava.CPOOL_INVOKESPECIAL);
assertEquals("incorrect pcode for special method invocation", expected.toString(), pCode.toString());
}
@Test
public void testEmitPcodeToResolveMethodReferenceInvokeStatic() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendMethodRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "methodName"); //5
TestClassFileCreator.appendUtf8(classFile,"(I)I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
StringBuilder pCode = new StringBuilder();
InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_STATIC);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_INVOKESTATIC);
assertEquals("incorrect pcode for static method invocation", expected.toString(), pCode.toString());
}
@Test
public void testEmitPcodeToResolveMethodReferenceInvokeVirtual() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendMethodRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "methodName"); //5
TestClassFileCreator.appendUtf8(classFile,"(I)I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
StringBuilder pCode = new StringBuilder();
InvokeMethods.emitPcodeToResolveMethodReference(pCode, 1, constantPool, JavaInvocationType.INVOKE_VIRTUAL);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(expected, InvokeMethods.CALL_TARGET, ConstantPoolJava.CPOOL_OP, InvokeMethods.THIS, "1", ConstantPoolJava.CPOOL_INVOKEVIRTUAL);
assertEquals("incorrect pcode for static method invocation", expected.toString(), pCode.toString());
}
@Test
public void testEmitPcodeToReverseStackNoParamsNoThis(){
StringBuilder pCode = new StringBuilder();
//InvokeMethods.emitPcodeToReverseStack(pCode, new ArrayList<JavaComputationalCategory>(), false);
StringBuilder expected = new StringBuilder();
assertEquals("incorrect pcode reversing stack: no params no this", expected.toString(), pCode.toString());
}
//test bad category
@Test
public void testGetPcodeForInvoke() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 5);
TestClassFileCreator.appendInvokeDynamicInfo(classFile, (short) 0, (short)2); //1
TestClassFileCreator.appendNameAndType(classFile, (short) 3, (short) 4); //2
TestClassFileCreator.appendUtf8(classFile, "dynamicMethodName"); //3
TestClassFileCreator.appendUtf8(classFile,"(JJII)I"); //4 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = InvokeMethods.getPcodeForInvoke(1, constantPool, JavaInvocationType.INVOKE_DYNAMIC);
StringBuilder expected = new StringBuilder();
String descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool, JavaInvocationType.INVOKE_DYNAMIC);
List<JavaComputationalCategory> categories = DescriptorDecoder.getParameterCategories(descriptor);
InvokeMethods.emitPcodeToMoveParams(expected, categories, false, 24);
InvokeMethods.emitPcodeToResolveMethodReference(expected, 1, constantPool, JavaInvocationType.INVOKE_DYNAMIC);
PcodeTextEmitter.emitIndirectCall(expected, InvokeMethods.CALL_TARGET);
PcodeTextEmitter.emitPushCat1Value(expected, InvokeMethods.CAT_1_RETURN);
System.out.println(pCode);
System.out.println(expected);
assertEquals("incorrect pcode for invoke dynamic: (JJII)I", expected.toString(), pCode);
}
}

View file

@ -0,0 +1,264 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import org.junit.Test;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
/**
*
* size of constant pool structures in bytes:
* Class: 3
* MethodRef: 5
* String: 3
* Integer: 5
* Float: 5
* Long: 9
* Double: 9
* Utf8:
*
* Constant pool begins at offset 0xa of the class file
*/
public class LdcMethodsTest {
private static final int COUNT_LOW_BYTE = 9;
@Test
public void testLdcInteger() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 2);
TestClassFileCreator.appendInteger(classFile, 0x12345678);
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0","1", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append an additional integer to the end of the constant pool and generate a reference to it
classFile.set(COUNT_LOW_BYTE, (byte) 3);
TestClassFileCreator.appendInteger(classFile, 0x11111111);
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(2, constantPool);
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0","2", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
@Test
public void testLdcFloat() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 2);
TestClassFileCreator.appendFloat(classFile, 2.0f);
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
//+1 to skip over the tag
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append an additional float to the end of the constant pool and generate a reference to it
classFile.set(COUNT_LOW_BYTE, (byte) 3);
TestClassFileCreator.appendFloat(classFile, 4.0f);
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(2, constantPool);
//+1 for tag of first float, +4 for data of first float, +1 for tag of 2nd float
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "2", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
@Test
public void testLdcDouble() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 3);
TestClassFileCreator.appendDouble(classFile, 2.0);
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
//+1 to skip over the tag
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_LDC2_W);
PcodeTextEmitter.emitPushCat2Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append an additional double to the end of the constant pool and generate a reference to it
//doubles count as two elements in the constant pool!
classFile.set(COUNT_LOW_BYTE, (byte) 5);
TestClassFileCreator.appendDouble(classFile, 4.0);
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(3, constantPool);
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", "3", ConstantPoolJava.CPOOL_LDC2_W);
PcodeTextEmitter.emitPushCat2Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
@Test
public void testLdcLong() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 3);
TestClassFileCreator.appendLong(classFile, 0x123456789l);
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_LDC2_W);
PcodeTextEmitter.emitPushCat2Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append an additional long to the end of the constant pool and generate a reference to it
//longs count as two elements in the constant pool!
classFile.set(COUNT_LOW_BYTE, (byte) 5);
TestClassFileCreator.appendLong(classFile, 0x1111111111l);
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(3, constantPool);
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", "3", ConstantPoolJava.CPOOL_LDC2_W);
PcodeTextEmitter.emitPushCat2Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
@Test
public void testLdcString() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 3);
TestClassFileCreator.appendString(classFile, (short) 2);
TestClassFileCreator.appendUtf8(classFile, "input1");
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append additional string, utf8 element to the end of the constant pool and generate a reference
//character string
classFile.set(COUNT_LOW_BYTE, (byte) 5);
TestClassFileCreator.appendString(classFile, (short) 4);
TestClassFileCreator.appendUtf8(classFile, "input2");
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(3, constantPool);
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "3", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
@Test
public void testLdcClass() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile,(short) 3);
TestClassFileCreator.appendClass(classFile, (short) 2);
TestClassFileCreator.appendUtf8(classFile, "Ljava/lang/Integer;");
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append additional class, utf8 element to the end of the constant pool and generate a reference
//character string
classFile.set(COUNT_LOW_BYTE, (byte) 5);
TestClassFileCreator.appendClass(classFile, (short) 4);
TestClassFileCreator.appendUtf8(classFile, "Ljava/lang/String;");
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(3, constantPool);
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "3", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
@Test
public void testLdcMethodType() throws IOException{
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 3);
TestClassFileCreator.appendMethodType(classFile, (short) 2);
TestClassFileCreator.appendUtf8(classFile, "(I)Ljava/lang/Integer;");
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = LdcMethods.getPcodeForLdc(1, constantPool);
//+1 to skip over the tag of the MethodType element
//+2 to skip over data of MethodType element (2-byte ref to utf8 element)
//+1 to skip over tag of utf8 element
//+2 to skip over length of utf8 element
StringBuilder expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
//append additional MethodType, utf8 element to the end of the constant pool and generate a reference
//character string
classFile.set(COUNT_LOW_BYTE, (byte) 5);
TestClassFileCreator.appendMethodType(classFile, (short) 4);
TestClassFileCreator.appendUtf8(classFile, "(I)Ljava/lang/Integer;");
classFileBytes = TestClassFileCreator.getByteArray(classFile);
constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
pCode = LdcMethods.getPcodeForLdc(3, constantPool);
expectedPcode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expectedPcode, LdcMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP, "0", "3", ConstantPoolJava.CPOOL_LDC);
PcodeTextEmitter.emitPushCat1Value(expectedPcode, LdcMethods.VALUE);
assertTrue(pCode.equals(expectedPcode.toString()));
}
}

View file

@ -0,0 +1,159 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import static org.junit.Assert.*;
import org.junit.Test;
public class PcodeTextEmitterTest {
@Test
public void testEmitPushType1Value(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitPushCat1Value(pCode, "x");
assertTrue(pCode.toString().equals("SP = SP - 4;\n*:4 SP = x;\n"));
}
@Test
public void testEmitPushType2Value(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitPushCat2Value(pCode, "x");
assertTrue(pCode.toString().equals("SP = SP - 8;\n*:8 SP = x;\n"));
}
@Test
public void testEmitPopType1Value(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitPopCat1Value(pCode, "x");
assertTrue(pCode.toString().equals("x:4 = *:4 SP;\nSP = SP + 4;\n"));
}
@Test
public void testEmitPopType2Value(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitPopCat2Value(pCode, "x");
assertTrue(pCode.toString().equals("x:8 = *:8 SP;\nSP = SP + 8;\n"));
}
@Test
public void testEmitVarnodeBytesFromPcodeOpCall(){
StringBuilder pCode = new StringBuilder();
//test no args
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, "LHS", 4, "PCODEOP");
assertTrue(pCode.toString().equals("LHS:4 = PCODEOP();\n"));
//one arg
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, "LHS", 4, "PCODEOP", "ARG1");
assertTrue(pCode.toString().equals("LHS:4 = PCODEOP(ARG1);\n"));
//two args
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, "LHS", 4, "PCODEOP", "ARG1", "ARG2");
assertTrue(pCode.toString().equals("LHS:4 = PCODEOP(ARG1,ARG2);\n"));
//test no args
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, "LHS", 8, "PCODEOP");
assertTrue(pCode.toString().equals("LHS:8 = PCODEOP();\n"));
//one arg
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, "LHS", 8, "PCODEOP", "ARG1");
assertTrue(pCode.toString().equals("LHS:8 = PCODEOP(ARG1);\n"));
//two args
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(pCode, "LHS", 8, "PCODEOP", "ARG1", "ARG2");
assertTrue(pCode.toString().equals("LHS:8 = PCODEOP(ARG1,ARG2);\n"));
}
@Test
public void testEmitVoidPcodeOpCall(){
StringBuilder pCode = new StringBuilder();
//test no args
PcodeTextEmitter.emitVoidPcodeOpCall(pCode, "PCODEOP");
assertTrue(pCode.toString().equals("PCODEOP();\n"));
//one arg
pCode = new StringBuilder();
PcodeTextEmitter.emitVoidPcodeOpCall(pCode,"PCODEOP", "ARG1");
assertTrue(pCode.toString().equals("PCODEOP(ARG1);\n"));
//two args
pCode = new StringBuilder();
PcodeTextEmitter.emitVoidPcodeOpCall(pCode,"PCODEOP", "ARG1", "ARG2");
assertTrue(pCode.toString().equals("PCODEOP(ARG1,ARG2);\n"));
}
@Test
public void testEmitAssignRegisterFromPcodeOpCall(){
//void call
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, "REG", "TEST");
assertTrue(pCode.toString().equals("REG = TEST();\n"));
//one-param call
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, "REG", "TEST", "ARG1");
assertTrue(pCode.toString().equals("REG = TEST(ARG1);\n"));
//two-param call
pCode = new StringBuilder();
PcodeTextEmitter.emitAssignRegisterFromPcodeOpCall(pCode, "REG", "TEST", "ARG1", "ARG2");
assertTrue(pCode.toString().equals("REG = TEST(ARG1,ARG2);\n"));
}
@Test
public void testEmitAssignConstantToRegister(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitAssignConstantToRegister(pCode, "REGISTER", 0);
assertTrue(pCode.toString().equals("REGISTER = 0x0;\n"));
}
@Test
public void testEmitLabelDef(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitLabelDefinition(pCode, "LABEL");
assertEquals("bad label definition emitted", "<LABEL>\n", pCode.toString());
}
@Test
public void testEmitAddToStackPointer(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitAddToStackPointer(pCode, 4);
assertEquals("SP = SP + 4;\n", pCode.toString());
}
@Test
public void testEmitIndirectCall(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitIndirectCall(pCode, "call_target");
assertEquals("call [call_target];\n", pCode.toString());
}
@Test
public void testEmitWriteToMemory(){
StringBuilder pCode = new StringBuilder();
PcodeTextEmitter.emitWriteToMemory(pCode, "ram", 4, "offset", "test");
assertEquals("*[ram]:4 offset = test;\n", pCode.toString());
}
}

View file

@ -0,0 +1,243 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import org.junit.Test;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
public class ReferenceMethodsTest {
public ReferenceMethodsTest(){
}
@Test
public void testGetStatic1() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForGetStatic(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP , "0", "1", ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitPushCat1Value(expected, ReferenceMethods.VALUE);
assertTrue(pCode.equals(expected.toString()));
}
@Test
public void testGetStatic2() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"J"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForGetStatic(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.VALUE, 8, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_GETSTATIC);
PcodeTextEmitter.emitPushCat2Value(expected, ReferenceMethods.VALUE);
assertEquals(pCode,expected.toString());
}
@Test
public void testPutStatic1() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForPutStatic(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitPopCat1Value(expected, ReferenceMethods.NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitWriteToMemory(expected, PcodeTextEmitter.RAM, 4, ReferenceMethods.STATIC_OFFSET, ReferenceMethods.NEW_VALUE);
assertEquals(pCode,expected.toString());
}
@Test
public void testPutStatic2() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"J"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForPutStatic(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitPopCat2Value(expected, ReferenceMethods.NEW_VALUE);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.STATIC_OFFSET, 4, ConstantPoolJava.CPOOL_OP, "0", "1", ConstantPoolJava.CPOOL_PUTSTATIC);
PcodeTextEmitter.emitWriteToMemory(expected, PcodeTextEmitter.RAM, 8, ReferenceMethods.STATIC_OFFSET, ReferenceMethods.NEW_VALUE);
assertEquals(pCode,expected.toString());
}
@Test
public void testGetField1() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForGetField(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitPopCat1Value(expected, ReferenceMethods.OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.VALUE, 4, ConstantPoolJava.CPOOL_OP , ReferenceMethods.OBJECT_REF, "1", ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitPushCat1Value(expected, ReferenceMethods.VALUE);
assertEquals(pCode,expected.toString());
}
@Test
public void testGetField2() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"J"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForGetField(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitPopCat1Value(expected, ReferenceMethods.OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.VALUE, 8, ConstantPoolJava.CPOOL_OP , ReferenceMethods.OBJECT_REF, "1", ConstantPoolJava.CPOOL_GETFIELD);
PcodeTextEmitter.emitPushCat2Value(expected, ReferenceMethods.VALUE);
assertEquals(pCode,expected.toString());
}
@Test
public void testPutField1() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForPutField(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitPopCat1Value(expected, ReferenceMethods.NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(expected, ReferenceMethods.OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, ReferenceMethods.OBJECT_REF, "1", ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitWriteToMemory(expected, PcodeTextEmitter.RAM, 4, ReferenceMethods.FIELD_OFFSET, ReferenceMethods.NEW_VALUE);
assertEquals(pCode,expected.toString());
}
@Test
public void testPutField2() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendFieldRef(classFile, (short) 2, (short)3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "fieldName"); //5
TestClassFileCreator.appendUtf8(classFile,"J"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool = TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String pCode = ReferenceMethods.getPcodeForPutField(1, constantPool);
StringBuilder expected = new StringBuilder();
PcodeTextEmitter.emitPopCat2Value(expected, ReferenceMethods.NEW_VALUE);
PcodeTextEmitter.emitPopCat1Value(expected, ReferenceMethods.OBJECT_REF);
PcodeTextEmitter.emitAssignVarnodeFromPcodeOpCall(expected, ReferenceMethods.FIELD_OFFSET, 4, ConstantPoolJava.CPOOL_OP, ReferenceMethods.OBJECT_REF, "1", ConstantPoolJava.CPOOL_PUTFIELD);
PcodeTextEmitter.emitWriteToMemory(expected, PcodeTextEmitter.RAM, 8, ReferenceMethods.FIELD_OFFSET, ReferenceMethods.NEW_VALUE);
assertEquals(pCode,expected.toString());
}
}

View file

@ -0,0 +1,231 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pcodeInject;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import generic.jar.ResourceFile;
import ghidra.GhidraApplicationLayout;
import ghidra.app.util.bin.*;
import ghidra.framework.Application;
import ghidra.framework.ApplicationConfiguration;
import ghidra.javaclass.format.JavaClassConstants;
import ghidra.javaclass.format.constantpool.*;
public class TestClassFileCreator {
private static final String RESOURCE_DIRECTORY = File.separator + "resources" + File.separator;
public static final int CLASS_INFO_SIZE = 3;
public static final int METHODREF_INFO_SIZE = 5;
public static final int STRING_INFO_SIZE = 3;
public static final int INTEGER_INFO_SIZE = 5;
public static final int FLOAT_INFO_SIZE = 5;
public static final int LONG_INFO_SIZE = 9;
public static final int DOUBLE_INFO_SIZE = 9;
public static final int FIELDREF_INFO_SIZE = 5;
public static final int INTERFACEMETHODREF_INFO_SIZE = 5;
public static final int INVOKEDYNAMIC_INFO_SIZE = 5;
public static final int NAMEANDTYPE_INFO_SIZE = 5;
private TestClassFileCreator(){
//private constructor to enforce noninstantiability
}
public static AbstractConstantPoolInfoJava[] getConstantPoolFromBytes(byte[] bytes) throws IOException{
ByteProvider provider = new ByteArrayProvider(bytes);
return getConstantPoolFromByteProvider(provider);
}
public static AbstractConstantPoolInfoJava[] getConstantPoolFromFile(String testFileName) throws IOException{
Application.initializeApplication(new GhidraApplicationLayout(),
new ApplicationConfiguration());
ResourceFile moduleRoot = Application.getMyModuleRootDirectory();
File testFile = new File(moduleRoot.getAbsolutePath() + RESOURCE_DIRECTORY + testFileName);
ByteProvider provider = new RandomAccessByteProvider(testFile);
return getConstantPoolFromByteProvider(provider);
}
public static AbstractConstantPoolInfoJava[] getConstantPoolFromByteProvider(ByteProvider provider) throws IOException{
//java is big endian
BinaryReader reader = new BinaryReader(provider, false);
int magic = reader.readNextInt();
if (magic != JavaClassConstants.MAGIC) {
throw new IOException("Invalid Java Class File.");
}
@SuppressWarnings("unused") //just advance past it
short minorVersion = reader.readNextShort();
@SuppressWarnings("unused") //just advance past it
short majorVersion = reader.readNextShort();
short constantPoolCount = reader.readNextShort();
AbstractConstantPoolInfoJava[] constantPool = new AbstractConstantPoolInfoJava[constantPoolCount+1];
//NOTE: start at index 1 per JVM specification!!!
for (int i = 1; i < constantPoolCount; i++) {
constantPool[i] = ConstantPoolFactory.get(reader);
if (constantPool[i] instanceof ConstantPoolLongInfo ||
constantPool[i] instanceof ConstantPoolDoubleInfo) {
++i;
}
}
provider.close();
return constantPool;
}
public static void appendMagic(ArrayList<Byte> classFile) {
classFile.add((byte)0xca);
classFile.add((byte)0xfe);
classFile.add((byte)0xba);
classFile.add((byte)0xbe);
}
public static void appendVersions(ArrayList<Byte> classFile) {
classFile.add((byte) 0);
classFile.add((byte) 0);
classFile.add((byte) 0);
classFile.add((byte) 0x34);
}
public static void appendCount(ArrayList<Byte> classFile, short s) {
appendShort(classFile,s);
}
public static void appendInteger(ArrayList<Byte> classFile, int i){
classFile.add(ConstantPoolTagsJava.CONSTANT_Integer);
appendInt(classFile,i);
}
public static byte[] getByteArray(ArrayList<Byte> classFile) {
int length = classFile.size();
byte[] bytes = new byte[length];
for (int i = 0; i < length; ++i){
bytes[i] = classFile.get(i);
}
return bytes;
}
public static void appendFloat(ArrayList<Byte> classFile, float input) {
int i = (int) input;
classFile.add(ConstantPoolTagsJava.CONSTANT_Float);
appendInt(classFile,i);
}
public static void appendDouble(ArrayList<Byte> classFile, double input){
long l = (long) input;
classFile.add(ConstantPoolTagsJava.CONSTANT_Double);
appendLongValue(classFile, l);
}
public static void appendLong(ArrayList<Byte> classFile, long l) {
classFile.add(ConstantPoolTagsJava.CONSTANT_Long);
appendLongValue(classFile, l);
}
public static void appendString(ArrayList<Byte> classFile, short string_index) {
classFile.add(ConstantPoolTagsJava.CONSTANT_String);
appendShort(classFile, string_index);
}
public static void appendUtf8(ArrayList<Byte> classFile, String input){
classFile.add(ConstantPoolTagsJava.CONSTANT_Utf8);
byte[] bytes = input.getBytes();
classFile.add((byte) ((input.length() >> 8) & 0xff));
classFile.add((byte) (input.length() & 0xff));
for (byte b : bytes) {
classFile.add(b);
}
}
public static void appendClass(ArrayList<Byte> classFile, short name_index){
classFile.add(ConstantPoolTagsJava.CONSTANT_Class);
appendShort(classFile, name_index);
}
public static void appendMethodType(ArrayList<Byte> classFile, short s){
classFile.add(ConstantPoolTagsJava.CONSTANT_MethodType);
appendShort(classFile, s);
}
public static void appendFieldRef(ArrayList<Byte> classFile, short class_index, short name_and_type_index) {
classFile.add( ConstantPoolTagsJava.CONSTANT_Fieldref);
appendShort(classFile, class_index);
appendShort(classFile, name_and_type_index);
}
public static void appendMethodRef(ArrayList<Byte> classFile, short class_index, short name_and_type_index) {
classFile.add( ConstantPoolTagsJava.CONSTANT_Methodref);
appendShort(classFile, class_index);
appendShort(classFile, name_and_type_index);
}
public static void appendInterfaceMethodRef(ArrayList<Byte> classFile, short class_index, short name_and_type_index) {
classFile.add( ConstantPoolTagsJava.CONSTANT_InterfaceMethodref);
appendShort(classFile, class_index);
appendShort(classFile, name_and_type_index);
}
public static void appendInvokeDynamicInfo(ArrayList<Byte> classFile, short bootstrap_method_attr_index, short name_and_type_index){
classFile.add( ConstantPoolTagsJava.CONSTANT_InvokeDynamic);
appendShort(classFile, bootstrap_method_attr_index);
appendShort(classFile, name_and_type_index);
}
public static void appendNameAndType(ArrayList<Byte> classFile, short name_index, short descriptor_index){
classFile.add( ConstantPoolTagsJava.CONSTANT_NameAndType);
appendShort(classFile, name_index);
appendShort(classFile, descriptor_index);
}
private static void appendShort(ArrayList<Byte> classFile, short s){
classFile.add((byte) ((s >> 8) & 0xff));
classFile.add((byte) (s & 0xff));
}
private static void appendInt(ArrayList<Byte> classFile, int i){
classFile.add((byte) ((i >> 24) & 0xff));
classFile.add((byte) ((i >> 16) & 0xff));
classFile.add((byte) ((i >> 8) & 0xff));
classFile.add((byte) (i & 0xff));
}
private static void appendLongValue(ArrayList<Byte> classFile, long l){
classFile.add((byte) ((l >> 56) & 0xff));
classFile.add((byte) ((l >> 48) & 0xff));
classFile.add((byte) ((l >> 40) & 0xff));
classFile.add((byte) ((l >> 32) & 0xff));
classFile.add((byte) ((l >> 24) & 0xff));
classFile.add((byte) ((l >> 16) & 0xff));
classFile.add((byte) ((l >> 8) & 0xff));
classFile.add((byte) (l & 0xff));
}
public static void appendMethodHandleFieldRef(ArrayList<Byte> classFile, byte reference_kind, short reference_index ) {
classFile.add(ConstantPoolTagsJava.CONSTANT_MethodHandle);
classFile.add(reference_kind);
appendShort(classFile, reference_index);
}
}

View file

@ -0,0 +1,440 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.javaclass.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.app.util.pcodeInject.*;
import ghidra.javaclass.format.DescriptorDecoder;
import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava;
import ghidra.program.model.data.*;
public class DescriptorDecoderTest extends AbstractGenericTest {
DataTypeManager dtm;
DataType dtInteger;
@Before
public void setUp() {
dtm = new StandAloneDataTypeManager("");
int transactionID = dtm.startTransaction(null);
dtInteger = DescriptorDecoder.resolveClassForString("java/lang/Integer", dtm,
DWordDataType.dataType);
DescriptorDecoder.resolveClassForString("JVM_primitives/byte", dtm,
SignedByteDataType.dataType);
DescriptorDecoder.resolveClassForString("JVM_primitives/boolean", dtm,
BooleanDataType.dataType);
dtm.endTransaction(transactionID, true);
}
@Test
public void testGetComputationalCategoryOfDescriptor() {
//test type 1 descriptors
JavaComputationalCategory cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("B");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("C");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("F");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("I");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("L");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("Ljava/lang/Integer;");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("S");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("Z");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("[I");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("[[I");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("[Ljava/lang/Integer;");
assertTrue(cat.equals(JavaComputationalCategory.CAT_1));
//test type 2
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("D");
assertTrue(cat.equals(JavaComputationalCategory.CAT_2));
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("J");
assertTrue(cat.equals(JavaComputationalCategory.CAT_2));
//test void
cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("V");
assertTrue(cat.equals(JavaComputationalCategory.VOID));
}
@Test(expected = IllegalArgumentException.class)
public void testUnknownDescriptor() {
@SuppressWarnings("unused")
JavaComputationalCategory cat = DescriptorDecoder.getComputationalCategoryOfDescriptor("X");
}
@Test
public void testGetStackPurgeAndReturnCategory() {
String ItoInt = "(I)Ljava/lang/Integer;";
int expectedStackPurge = PcodeInjectLibraryJava.REFERENCE_SIZE;
JavaComputationalCategory expectedReturn = JavaComputationalCategory.CAT_1;
int computedStackPurge = DescriptorDecoder.getStackPurge(ItoInt);
assertTrue(computedStackPurge == expectedStackPurge);
JavaComputationalCategory returnCat =
DescriptorDecoder.getReturnCategoryOfMethodDescriptor(ItoInt);
assertTrue(returnCat.equals(expectedReturn));
String IntIntInttoInt =
"(Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;";
expectedStackPurge = 3 * PcodeInjectLibraryJava.REFERENCE_SIZE;
expectedReturn = JavaComputationalCategory.CAT_1;
computedStackPurge = DescriptorDecoder.getStackPurge(IntIntInttoInt);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(IntIntInttoInt);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
String voidTovoid = "()V";
expectedStackPurge = 0;
expectedReturn = JavaComputationalCategory.VOID;
computedStackPurge = DescriptorDecoder.getStackPurge(voidTovoid);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(voidTovoid);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
String ItoI = "(I)I";
expectedStackPurge = PcodeInjectLibraryJava.REFERENCE_SIZE;
expectedReturn = JavaComputationalCategory.CAT_1;
computedStackPurge = DescriptorDecoder.getStackPurge(ItoI);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(ItoI);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
String OneDIntTwoDInttoInt =
"([Ljava/lang/Integer;[[Ljava/lang/Integer;)Ljava/lang/Integer;";
expectedStackPurge = 2 * PcodeInjectLibraryJava.REFERENCE_SIZE;
expectedReturn = JavaComputationalCategory.CAT_1;
computedStackPurge = DescriptorDecoder.getStackPurge(OneDIntTwoDInttoInt);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(OneDIntTwoDInttoInt);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
String DDtoD = "(DD)D";
expectedStackPurge =
2 * PcodeInjectLibraryJava.REFERENCE_SIZE + 2 * PcodeInjectLibraryJava.REFERENCE_SIZE;
expectedReturn = JavaComputationalCategory.CAT_2;
computedStackPurge = DescriptorDecoder.getStackPurge(DDtoD);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(DDtoD);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
String crazy = "(DJLjava/lang/Integer;[[Ljava/lang/Integer;)[[Ljava/lang/Integer;";
expectedStackPurge = 6 * PcodeInjectLibraryJava.REFERENCE_SIZE;
expectedReturn = JavaComputationalCategory.CAT_1;
computedStackPurge = DescriptorDecoder.getStackPurge(crazy);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(crazy);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
String getClass = "()Ljava/lang/Class";
expectedStackPurge = 0;
expectedReturn = JavaComputationalCategory.CAT_1;
computedStackPurge = DescriptorDecoder.getStackPurge(getClass);
returnCat = DescriptorDecoder.getReturnCategoryOfMethodDescriptor(getClass);
assertTrue(computedStackPurge == expectedStackPurge);
assertTrue(returnCat.equals(expectedReturn));
}
@Test
public void testGetParameterCategories() {
String ItoInt = "(I)Ljava/lang/Integer;";
List<JavaComputationalCategory> expectedList = new ArrayList<>();
expectedList.add(JavaComputationalCategory.CAT_1);
List<JavaComputationalCategory> computedList =
DescriptorDecoder.getParameterCategories(ItoInt);
assertTrue(expectedList.equals(computedList));
String IntIntInttoInt =
"(Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;";
expectedList.removeAll(expectedList);
expectedList.add(JavaComputationalCategory.CAT_1);
expectedList.add(JavaComputationalCategory.CAT_1);
expectedList.add(JavaComputationalCategory.CAT_1);
computedList = DescriptorDecoder.getParameterCategories(IntIntInttoInt);
assertTrue(expectedList.equals(computedList));
String voidTovoid = "()V";
expectedList.removeAll(expectedList);
computedList = DescriptorDecoder.getParameterCategories(voidTovoid);
assertTrue(expectedList.equals(computedList));
String IDItoI = "(IDI)I";
expectedList.removeAll(expectedList);
expectedList.add(JavaComputationalCategory.CAT_1);
expectedList.add(JavaComputationalCategory.CAT_2);
expectedList.add(JavaComputationalCategory.CAT_1);
computedList = DescriptorDecoder.getParameterCategories(IDItoI);
assertTrue(expectedList.equals(computedList));
String OneDIntTwoDInttoInt =
"([Ljava/lang/Integer;[[Ljava/lang/Integer;)Ljava/lang/Integer;";
expectedList.removeAll(expectedList);
expectedList.add(JavaComputationalCategory.CAT_1);
expectedList.add(JavaComputationalCategory.CAT_1);
computedList = DescriptorDecoder.getParameterCategories(OneDIntTwoDInttoInt);
assertTrue(expectedList.equals(computedList));
String DDtoD = "(DD)D";
expectedList.removeAll(expectedList);
expectedList.add(JavaComputationalCategory.CAT_2);
expectedList.add(JavaComputationalCategory.CAT_2);
computedList = DescriptorDecoder.getParameterCategories(DDtoD);
assertTrue(expectedList.equals(computedList));
String crazy = "(DJLjava/lang/Integer;[[Ljava/lang/Integer;)[[Ljava/lang/Integer;";
expectedList.removeAll(expectedList);
expectedList.add(JavaComputationalCategory.CAT_2);
expectedList.add(JavaComputationalCategory.CAT_2);
expectedList.add(JavaComputationalCategory.CAT_1);
expectedList.add(JavaComputationalCategory.CAT_1);
computedList = DescriptorDecoder.getParameterCategories(crazy);
assertTrue(expectedList.equals(computedList));
String getClass = "()Ljava/lang/Class";
computedList = DescriptorDecoder.getParameterCategories(getClass);
expectedList.removeAll(expectedList);
assertTrue(expectedList.equals(computedList));
}
@Test
public void testGetDataTypeOfDescriptor() {
DataType computedType = DescriptorDecoder.getDataTypeOfDescriptor("B", dtm);
DataType expectedType = SignedByteDataType.dataType;
assertTrue(computedType.equals(expectedType));
computedType = DescriptorDecoder.getDataTypeOfDescriptor("Z", dtm);
expectedType = BooleanDataType.dataType;
assertTrue(computedType.equals(expectedType));
computedType = DescriptorDecoder.getDataTypeOfDescriptor("C", dtm);
assertTrue(computedType.equals(CharDataType.dataType));
computedType = DescriptorDecoder.getDataTypeOfDescriptor("S", dtm);
assertTrue(computedType.equals(ShortDataType.dataType));
computedType = DescriptorDecoder.getDataTypeOfDescriptor("I", dtm);
assertTrue(computedType.equals(IntegerDataType.dataType));
String floatDescriptor = "F";
computedType = DescriptorDecoder.getDataTypeOfDescriptor(floatDescriptor, dtm);
assertTrue(computedType.equals(FloatDataType.dataType));
String arrayDescriptor = "[I";
computedType = DescriptorDecoder.getDataTypeOfDescriptor(arrayDescriptor, dtm);
assertTrue(computedType.equals(dtm.getPointer(IntegerDataType.dataType)));
String referenceDescriptor = "Ljava/lang/Integer;";
computedType = DescriptorDecoder.getDataTypeOfDescriptor(referenceDescriptor, dtm);
DataType intRef = new PointerDataType(dtInteger);
assertTrue(computedType.equals(intRef));
String doubleDescriptor = "D";
computedType = DescriptorDecoder.getDataTypeOfDescriptor(doubleDescriptor, dtm);
assertTrue(computedType.equals(DoubleDataType.dataType));
String longDescriptor = "J";
computedType = DescriptorDecoder.getDataTypeOfDescriptor(longDescriptor, dtm);
assertTrue(computedType.equals(LongDataType.dataType));
String voidDescriptor = "V";
computedType = DescriptorDecoder.getDataTypeOfDescriptor(voidDescriptor, dtm);
assertTrue(computedType.equals(DataType.VOID));
}
@Test
public void testGetDataTypeList() {
String comprehensive = "(BZCSIF[[ILjava/lang/Integer;DJ)Ljava/lang/Integer;";
List<DataType> computedList = DescriptorDecoder.getDataTypeList(comprehensive, dtm);
List<DataType> expectedList = new ArrayList<>();
expectedList.add(SignedByteDataType.dataType);
expectedList.add(BooleanDataType.dataType);
expectedList.add(CharDataType.dataType);
expectedList.add(ShortDataType.dataType);
expectedList.add(IntegerDataType.dataType);
expectedList.add(FloatDataType.dataType);
expectedList.add(dtm.getPointer(IntegerDataType.dataType));
expectedList.add(dtm.getPointer(dtInteger));
expectedList.add(DoubleDataType.dataType);
expectedList.add(LongDataType.dataType);
for (int i = 0, max = Integer.max(expectedList.size(), computedList.size()); i < max; ++i) {
assertTrue(computedList.get(i).isEquivalent(expectedList.get(i)));
}
String voidTovoid = "()V";
expectedList.clear();
computedList = DescriptorDecoder.getDataTypeList(voidTovoid, null);
assertTrue(expectedList.equals(computedList));
}
@Test
public void testGetDescriptorForInvokeMethodRef() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendMethodRef(classFile, (short) 2, (short) 3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "methodName"); //5
TestClassFileCreator.appendUtf8(classFile, "(I)I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool =
TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool,
JavaInvocationType.INVOKE_VIRTUAL);
String err = "returned " + descriptor + " instead of (I)I for " +
JavaInvocationType.INVOKE_VIRTUAL.name();
assertEquals(err, descriptor, "(I)I");
descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool,
JavaInvocationType.INVOKE_STATIC);
err = "returned " + descriptor + " instead of (I)I for " +
JavaInvocationType.INVOKE_STATIC.name();
assertEquals(err, descriptor, "(I)I");
descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool,
JavaInvocationType.INVOKE_SPECIAL);
err = "returned " + descriptor + " instead of (I)I for " +
JavaInvocationType.INVOKE_SPECIAL.name();
assertEquals(err, descriptor, "(I)I");
}
@Test
public void testGetDescriptorForInvokeInterfaceMethodRef() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 7);
TestClassFileCreator.appendInterfaceMethodRef(classFile, (short) 2, (short) 3); //1
TestClassFileCreator.appendClass(classFile, (short) 4); //2
TestClassFileCreator.appendNameAndType(classFile, (short) 5, (short) 6); //3
TestClassFileCreator.appendUtf8(classFile, "className"); //4
TestClassFileCreator.appendUtf8(classFile, "interfaceMethodName"); //5
TestClassFileCreator.appendUtf8(classFile, "(I)I"); //6 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool =
TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool,
JavaInvocationType.INVOKE_INTERFACE);
String err = "returned " + descriptor + " instead of (I)I for " +
JavaInvocationType.INVOKE_INTERFACE.name();
assertEquals(err, descriptor, "(I)I");
}
@Test
public void testGetDescriptorForInvokeDynamicRef() throws IOException {
ArrayList<Byte> classFile = new ArrayList<>();
TestClassFileCreator.appendMagic(classFile);
TestClassFileCreator.appendVersions(classFile);
TestClassFileCreator.appendCount(classFile, (short) 5);
TestClassFileCreator.appendInvokeDynamicInfo(classFile, (short) 0, (short) 2); //1
TestClassFileCreator.appendNameAndType(classFile, (short) 3, (short) 4); //2
TestClassFileCreator.appendUtf8(classFile, "dynamicMethodName"); //3
TestClassFileCreator.appendUtf8(classFile, "(I)I"); //4 (descriptor)
byte[] classFileBytes = TestClassFileCreator.getByteArray(classFile);
AbstractConstantPoolInfoJava[] constantPool =
TestClassFileCreator.getConstantPoolFromBytes(classFileBytes);
String descriptor = DescriptorDecoder.getDescriptorForInvoke(1, constantPool,
JavaInvocationType.INVOKE_DYNAMIC);
String err = "returned " + descriptor + " instead of (I)I for " +
JavaInvocationType.INVOKE_DYNAMIC.name();
assertEquals(err, descriptor, "(I)I");
}
@Test
public void testGetTypeNameForDescriptor() {
assertEquals("byte", DescriptorDecoder.getTypeNameFromDescriptor("B", false, true));
assertEquals("char", DescriptorDecoder.getTypeNameFromDescriptor("C", false, true));
assertEquals("float", DescriptorDecoder.getTypeNameFromDescriptor("F", false, true));
assertEquals("int", DescriptorDecoder.getTypeNameFromDescriptor("I", false, true));
assertEquals("short", DescriptorDecoder.getTypeNameFromDescriptor("S", false, true));
assertEquals("boolean", DescriptorDecoder.getTypeNameFromDescriptor("Z", false, true));
assertEquals("double", DescriptorDecoder.getTypeNameFromDescriptor("D", false, true));
assertEquals("long", DescriptorDecoder.getTypeNameFromDescriptor("J", false, true));
assertEquals("void", DescriptorDecoder.getTypeNameFromDescriptor("V", false, true));
assertEquals("java.lang.Integer",
DescriptorDecoder.getTypeNameFromDescriptor("Ljava/lang/Integer;", true, true));
assertEquals("Integer",
DescriptorDecoder.getTypeNameFromDescriptor("Ljava/lang/Integer;", false, true));
assertEquals("Integer[][][]",
DescriptorDecoder.getTypeNameFromDescriptor("[[[Ljava/lang/Integer;", false, true));
assertEquals("java.lang.Integer[][][]",
DescriptorDecoder.getTypeNameFromDescriptor("[[[Ljava/lang/Integer;", true, true));
assertEquals("java/lang/Integer",
DescriptorDecoder.getTypeNameFromDescriptor("Ljava/lang/Integer;", true, false));
assertEquals("Integer",
DescriptorDecoder.getTypeNameFromDescriptor("Ljava/lang/Integer;", false, false));
assertEquals("Integer[][][]",
DescriptorDecoder.getTypeNameFromDescriptor("[[[Ljava/lang/Integer;", false, false));
assertEquals("java/lang/Integer[][][]",
DescriptorDecoder.getTypeNameFromDescriptor("[[[Ljava/lang/Integer;", true, false));
}
@Test
public void testGetTypeNameList() {
String methodDescriptor = "(I)I";
List<String> typeNames = DescriptorDecoder.getTypeNameList(methodDescriptor, false, true);
assertEquals("int", typeNames.get(0));
assertEquals("int", typeNames.get(1));
methodDescriptor = "()V";
typeNames = DescriptorDecoder.getTypeNameList(methodDescriptor, false, true);
assertEquals("void", typeNames.get(0));
methodDescriptor = "(JLjava/lang/Integer;[[[I)[[[Ljava/lang/Integer;";
typeNames = DescriptorDecoder.getTypeNameList(methodDescriptor, true, true);
assertEquals("long", typeNames.get(0));
assertEquals("java.lang.Integer", typeNames.get(1));
assertEquals("int[][][]", typeNames.get(2));
assertEquals("java.lang.Integer[][][]", typeNames.get(3));
typeNames = DescriptorDecoder.getTypeNameList(methodDescriptor, false, true);
assertEquals("long", typeNames.get(0));
assertEquals("Integer", typeNames.get(1));
assertEquals("int[][][]", typeNames.get(2));
assertEquals("Integer[][][]", typeNames.get(3));
}
}