From 7d8b2221831628acc2d5becefa7602371af97d9e Mon Sep 17 00:00:00 2001 From: James <49045138+ghidracadabra@users.noreply.github.com> Date: Tue, 10 May 2022 18:22:43 +0000 Subject: [PATCH] GP-2012_James_java_attribute_improvements --- .../javaclass/analyzers/JavaAnalyzer.java | 20 ++++ .../javaclass/format/MethodInfoJava.java | 19 +++- .../format/attributes/AttributeFactory.java | 12 +- .../attributes/AttributesConstants.java | 1 + .../format/attributes/MethodParameters.java | 104 ++++++++++++++++++ .../attributes/MethodParametersAttribute.java | 68 ++++++++++++ .../attributes/UnsupportedAttributeInfo.java | 49 +++++++++ 7 files changed, 266 insertions(+), 7 deletions(-) create mode 100644 Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParameters.java create mode 100644 Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParametersAttribute.java create mode 100644 Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/UnsupportedAttributeInfo.java diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JavaAnalyzer.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JavaAnalyzer.java index c439d86f4e..e9a8aa119f 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JavaAnalyzer.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JavaAnalyzer.java @@ -720,6 +720,26 @@ public class JavaAnalyzer extends AbstractJavaAnalyzer implements AnalysisWorker explicitParams.get(i), function.getProgram()); params.add(currentParam); } + MethodParametersAttribute methodParamsAttr = methodInfo.getMethodParameters(); + if (methodParamsAttr != null) { + MethodParameters[] methodParams = methodParamsAttr.getMethodParameters(); + int indexAdjust = methodInfo.isStatic() ? 0 : 1; + if (methodParams.length == (params.size() - indexAdjust)) { + for (int i = 0; i < methodParams.length; ++i) { + int nameIndex = methodParams[i].getNameIndex(); + if (nameIndex == 0) { + continue; // no name + } + String name = ((ConstantPoolUtf8Info) constantPool[nameIndex]).getString(); + params.get(i + indexAdjust).setName(name, SourceType.ANALYSIS); + } + } + else { + Msg.warn(this, "methodParams/params size mismatch for " + function.getName()); + Msg.warn(this, + "methodParams: " + methodParams.length + "; params: " + params.size()); + } + } function.replaceParameters(params, FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS); diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/MethodInfoJava.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/MethodInfoJava.java index 4e1d9cc81b..ad24bbdc2f 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/MethodInfoJava.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/MethodInfoJava.java @@ -223,9 +223,11 @@ public class MethodInfoJava implements StructConverter { stringBuffer.append(", "); } ConstantPoolUtf8Info parameterName = - (ConstantPoolUtf8Info) constantPool[localVariables[i].getNameIndex()]; + (ConstantPoolUtf8Info) constantPool[localVariables[i] + .getNameIndex()]; ConstantPoolUtf8Info parameterDescriptor = - (ConstantPoolUtf8Info) constantPool[localVariables[i].getDescriptorIndex()]; + (ConstantPoolUtf8Info) constantPool[localVariables[i] + .getDescriptorIndex()]; stringBuffer.append(DescriptorDecoder.getTypeNameFromDescriptor( parameterDescriptor.getString(), false, true)); @@ -247,8 +249,8 @@ public class MethodInfoJava implements StructConverter { int i = 0; for (int k = 0; k < exceptionsAttribute.getNumberOfExceptions(); k++) { ConstantPoolClassInfo exceptionClass = - (ConstantPoolClassInfo) constantPool[exceptionsAttribute.getExceptionIndexTableEntry( - k)]; + (ConstantPoolClassInfo) constantPool[exceptionsAttribute + .getExceptionIndexTableEntry(k)]; ConstantPoolUtf8Info exceptionClassName = (ConstantPoolUtf8Info) constantPool[exceptionClass.getNameIndex()]; String className = exceptionClassName.getString(); @@ -306,6 +308,15 @@ public class MethodInfoJava implements StructConverter { return null; } + public MethodParametersAttribute getMethodParameters() { + for (AbstractAttributeInfo attributeInfo : attributes) { + if (attributeInfo instanceof MethodParametersAttribute) { + return (MethodParametersAttribute) attributeInfo; + } + } + return null; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { String name = diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributeFactory.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributeFactory.java index 48b56b0a31..0a39104e55 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributeFactory.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributeFactory.java @@ -20,6 +20,7 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava; import ghidra.javaclass.format.constantpool.ConstantPoolUtf8Info; +import ghidra.util.Msg; public class AttributeFactory { @@ -61,6 +62,10 @@ public class AttributeFactory { return new LocalVariableTableAttribute(reader, constantPool); case AttributesConstants.LocalVariableTypeTable: return new LocalVariableTypeTableAttribute(reader); + case AttributesConstants.MethodParameters: + return new MethodParametersAttribute(reader); + case AttributesConstants.Module: + return new ModuleAttribute(reader); case AttributesConstants.ModuleMainClass: return new ModuleMainClassAttribute(reader); case AttributesConstants.ModulePackages: @@ -87,10 +92,11 @@ public class AttributeFactory { return new StackMapTableAttribute(reader); case AttributesConstants.Synthetic: return new SyntheticAttribute(reader); - case AttributesConstants.Module: - return new ModuleAttribute(reader); + default: - throw new RuntimeException("Unknown attribute type: " + utf8.getString()); + Msg.warn(AttributeFactory.class, "Unknown attribute type: " + utf8.getString() + + " at index " + (reader.getPointerIndex() - 2)); + return new UnsupportedAttributeInfo(reader); } } } diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributesConstants.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributesConstants.java index 3d748dc820..abb8298215 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributesConstants.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/AttributesConstants.java @@ -39,6 +39,7 @@ public final class AttributesConstants { "RuntimeInvisibleParameterAnnotations"; public final static String AnnotationDefault = "AnnotationDefault"; public final static String BootstrapMethods = "BootstrapMethods"; + public final static String MethodParameters = "MethodParameters"; public final static String Module = "Module"; public final static String ModulePackages = "ModulePackages"; public final static String ModuleMainClass = "ModuleMainClass"; diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParameters.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParameters.java new file mode 100644 index 0000000000..3c8da0283b --- /dev/null +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParameters.java @@ -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 java.io.IOException; + +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; + +/** + * This class stores information about a single method parameter + */ +public class MethodParameters implements StructConverter { + + private final static int ACC_FINAL_INDEX = 0x10; + private final static int ACC_SYNTHETIC_INDEX = 0x1000; + private final static int ACC_MANDATED_INDEX = 0x8000; + + private int name_index; + private int access_flags; + + /** + * Creates a new {@code MethodParameters} object from the current index of {@code reader} + * and advances the index. + * @param reader source of bytes + * @throws IOException thrown if problem reading bytes + */ + public MethodParameters(BinaryReader reader) throws IOException { + name_index = Short.toUnsignedInt(reader.readNextShort()); + access_flags = Short.toUnsignedInt(reader.readNextShort()); + } + + /** + * Returns the name index. If the index is 0, then this formal parameter + * does not have a name. Otherwise, the constant pool entry at the index + * is a {@code CONSTANT_Utf8_Info} structure encoding the name of the + * parameter. + * @return index or 0 + */ + public int getNameIndex() { + return name_index; + } + + /** + * Returns a integer whose bits are treated as flags to encode various properties + * of the parameter + * @return access flags + */ + public int getAcccessFlags() { + return access_flags; + } + + /** + * Returns a {@code boolean} representing whether or not the parameter was declared + * {@code final} + * @return true if final + */ + public boolean isFinal() { + return (access_flags & ACC_FINAL_INDEX) != 0; + } + + /** + * Returns a {@code boolean} representing whether or not the parameter is synthetic, i.e., + * a compiler artifact rather than being explicitly or implicitly declared in the source. + * @return true if synthetic + */ + public boolean isSynthetic() { + return (access_flags & ACC_SYNTHETIC_INDEX) != 0; + } + + /** + * Returns a {@code boolean} representing whether or not the parameter is mandated, i.e., + * implicitly declared in source code and forced to be emitted by all compilers. + * @return true if synthetic + */ + public boolean isMandated() { + return (access_flags & ACC_MANDATED_INDEX) != 0; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType structure = new StructureDataType("method_parameters", 0); + structure.add(WORD, "name_index", null); + structure.add(WORD, "access_flags", null); + return structure; + } + +} diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParametersAttribute.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParametersAttribute.java new file mode 100644 index 0000000000..e45b4f7872 --- /dev/null +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/MethodParametersAttribute.java @@ -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.javaclass.format.attributes; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.StructureDataType; +import ghidra.util.exception.DuplicateNameException; + +/** + * This attribute records info about the (formal) parameters of a method, + * such as parameter names. + */ +public class MethodParametersAttribute extends AbstractAttributeInfo { + + private byte parameters_count; + private MethodParameters[] parameters; + + /** + * Creates a {@code MethodParametersAttribute} object from the current index of + * {@code reader} and advances index. + * @param reader source of bytes + * @throws IOException if problem reading bytes + */ + protected MethodParametersAttribute(BinaryReader reader) throws IOException { + super(reader); + parameters_count = reader.readNextByte(); + int size = Byte.toUnsignedInt(parameters_count); + parameters = new MethodParameters[size]; + for (int i = 0; i < size; ++i) { + parameters[i] = new MethodParameters(reader); + } + } + + /** + * Returns information about the parameters of the method + * @return parameter info + */ + public MethodParameters[] getMethodParameters() { + return parameters; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType structure = getBaseStructure("MethodParameters_attribute"); + structure.add(BYTE, "num_parameters", null); + for (int i = 0; i < parameters.length; ++i) { + structure.add(parameters[i].toDataType(), "parameter" + i, null); + } + return structure; + } + +} diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/UnsupportedAttributeInfo.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/UnsupportedAttributeInfo.java new file mode 100644 index 0000000000..355fcf87a3 --- /dev/null +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/format/attributes/UnsupportedAttributeInfo.java @@ -0,0 +1,49 @@ +/* ### + * 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 java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +/** + * This class in an artificial attribute (i.e., not defined in the JVM specification) + * whose purpose is to consume attributes that are not (yet) supported by Ghidra + * so that class file parsing can proceed. + */ +public class UnsupportedAttributeInfo extends AbstractAttributeInfo { + + /** + * Creates a {@code UnsupportedAttributeInfo} object from the current index of + * {@code reader} and advances the index. + * @param reader source of bytes + * @throws IOException thrown if problem reading bytes + */ + protected UnsupportedAttributeInfo(BinaryReader reader) throws IOException { + super(reader); + reader.setPointerIndex(reader.getPointerIndex() + getAttributeLength()); + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType structure = getBaseStructure("Unsupported_attribute"); + structure.add(new ArrayDataType(BYTE, getAttributeLength(), BYTE.getLength())); + return structure; + } + +}