From d1d0e8832ba90dcdcc704dfccada95953750a791 Mon Sep 17 00:00:00 2001 From: jmlagor <73651947+jmlagor@users.noreply.github.com> Date: Tue, 16 Mar 2021 09:20:50 -0400 Subject: [PATCH] Correct processing for pointers, function pointers, custom modifiers, ValueTyes, static methods, MethodRefs, MethodDefs, and PInvokes found in mixed binaries --- .../format/pe/cli/blobs/CliAbstractSig.java | 363 +++++++++++++----- .../bin/format/pe/cli/blobs/CliSigField.java | 4 +- .../format/pe/cli/blobs/CliSigMethodDef.java | 10 +- .../format/pe/cli/blobs/CliSigMethodRef.java | 15 +- .../format/pe/cli/methods/CliMethodDef.java | 97 +++-- .../format/pe/cli/streams/CliStreamBlob.java | 4 +- .../pe/cli/streams/CliStreamMetadata.java | 44 +-- .../format/pe/cli/tables/CliTableField.java | 13 +- .../pe/cli/tables/CliTableMethodDef.java | 316 ++++++++++++--- .../format/pe/cli/tables/CliTableParam.java | 21 +- .../pe/cli/tables/CliTableStandAloneSig.java | 5 +- 11 files changed, 671 insertions(+), 221 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliAbstractSig.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliAbstractSig.java index e82c38b122..303b5138b7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliAbstractSig.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliAbstractSig.java @@ -4,9 +4,9 @@ * 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. @@ -107,12 +107,12 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable ELEMENT_TYPE_VALUETYPE(0x11), ELEMENT_TYPE_VAR(0x13), // "Class type variable VAR" (0x16), - + ELEMENT_TYPE_MVAR(0x1e), // Method type variable MVAR - + ELEMENT_TYPE_INTERNAL(0x21), // Internal (generated internally, "will not be persisted in any way") ELEMENT_TYPE_MAX(0x22), - + */ switch (typeCode) { @@ -431,32 +431,95 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public class CliTypeFnPtr extends CliSigType { - private CliAbstractSig sig; - private boolean isDefSig; // true => MethodDef, false => MethodRef + private long dataOffset; + + private CliRetType retType; + private CliParam params[]; + private int sizeOfCount; + private byte flags; + private int genericParamCount; + private int sizeOfGenericCount; + + private int sentinelIndex; // SENTINEL is before the parameter index in this field + + private final int FNPTR_FLAGS_DEFAULT = 0x00; + private final int FNPTR_FLAGS_VARARG = 0x05; + private final int FNPTR_FLAGS_GENERIC = 0x10; + private final int FNPTR_FLAGS_HASTHIS = 0x20; + private final int FNPTR_FLAGS_EXPLICITTHIS = 0x40; public CliTypeFnPtr(BinaryReader reader, CliElementType typeCode) throws IOException { super(typeCode); - // TODO: MethodDef and MethodRef sig need to have static isX(reader) methods so I can tell the difference - //sig = new CliSigMethodRef(blob); // MethodRef is just Def plus possible sentinel and minus potential XORed args in the first byte + + sentinelIndex = -1; + + dataOffset = reader.getPointerIndex(); + + // Flags is similar to a MethodDef unless vararg is used. + flags = reader.readNextByte(); + + if ((flags & FNPTR_FLAGS_GENERIC) == FNPTR_FLAGS_GENERIC) { + long origIndex = reader.getPointerIndex(); + genericParamCount = decodeCompressedUnsignedInt(reader); + sizeOfGenericCount = (int) (reader.getPointerIndex() - origIndex); + } + + long origIndex = reader.getPointerIndex(); + int paramCount = decodeCompressedUnsignedInt(reader); + this.sizeOfCount = (int) (reader.getPointerIndex() - origIndex); + + try { + retType = new CliRetType(reader); + } + catch (InvalidInputException e) { + retType = null; + } + params = new CliParam[paramCount]; + + for (int i = 0; i < paramCount; i++) { + if (reader.peekNextByte() == CliElementType.ELEMENT_TYPE_SENTINEL.id()) { + reader.readNextByte(); + sentinelIndex = i; + } + try { + params[i] = new CliParam(reader); + } + catch (InvalidInputException e) { + params[i] = null; + } + } } @Override public String getRepresentation() { - return "FnPtr " + sig.getRepresentation(); + return "FnPtr " /*+ sig.getRepresentation()*/; } @Override public String getShortRepresentation() { - return "FnPtr " + sig.getShortRepresentation(); + return "FnPtr " /*+ sig.getShortRepresentation()*/; } @Override public DataType getDefinitionDataType() { - StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "FnPtr", 0); - struct.add(CliTypeCodeDataType.dataType, "FnPtr", "FnPtr"); - struct.add(DWORD, "MethodDefOrRef", "index into blob heap"); + StructureDataType struct = + new StructureDataType(new CategoryPath(PATH), "FnPtr_" + dataOffset, 0); + struct.add(CliTypeCodeDataType.dataType, CliElementType.ELEMENT_TYPE_FNPTR.toString(), + ""); + struct.add(BYTE, "Flags", "ORed VARARG/GENERIC/HASTHIS/EXPLICITTHIS"); + + if (genericParamCount > 0) { + struct.add(getDataTypeForBytes(sizeOfGenericCount), "GenParamCount", + "Number of generic paramameters for the method"); + } + struct.add(getDataTypeForBytes(sizeOfCount), "ParamCount", + "Number of parameter types to follow RetType"); + struct.add(retType.getDefinitionDataType(), "RetType", null); + + for (int i = 0; i < params.length; i++) { + struct.add(params[i].getDefinitionDataType(), "Param" + i, null); + } return struct; - // TODO: Return the correct size of a signature reference (always 4B in this context perchance?) } } @@ -552,17 +615,18 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public class CliTypeVarOrMvar extends CliSigType { + private long dataOffset; private int number; private int numberBytes; public CliTypeVarOrMvar(BinaryReader reader, CliElementType typeCode) throws IOException { super(typeCode); - long origIndex = reader.getPointerIndex(); + dataOffset = reader.getPointerIndex(); number = decodeCompressedUnsignedInt(reader); long endIndex = reader.getPointerIndex(); - numberBytes = (int) (endIndex - origIndex); + numberBytes = (int) (endIndex - dataOffset); } @Override @@ -572,9 +636,9 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable @Override public DataType getDefinitionDataType() { - StructureDataType struct = - new StructureDataType(new CategoryPath(PATH), "VarOrMvar", 0); - struct.add(BYTE, "Type", "Var or Mvar"); + StructureDataType struct = new StructureDataType(new CategoryPath(PATH), + baseTypeCode.toString() + "_" + dataOffset, 0); + struct.add(CliTypeCodeDataType.dataType, baseTypeCode.toString(), ""); struct.add(getDataTypeForBytes(numberBytes), "number", null); return struct; } @@ -582,16 +646,24 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable public class CliTypePtr extends CliSigType { private List customMods = new ArrayList<>(); - private CliElementType typeCode; + private CliSigType type; + private long dataOffset; - public CliTypePtr(BinaryReader reader, CliElementType typeCode) throws IOException { + public CliTypePtr(BinaryReader reader, CliElementType typeCode) + throws IOException, InvalidInputException { super(typeCode); + dataOffset = reader.getPointerIndex(); + while (CliCustomMod.isCustomMod(reader)) { customMods.add(new CliCustomMod(reader)); } - typeCode = CliElementType.fromInt(reader.readNextByte()); + type = readCliType(reader); + } + + public CliSigType getType() { + return type; } @Override @@ -601,17 +673,22 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable modsRep += mod.toString() + ", "; } modsRep = modsRep.substring(0, modsRep.length() - 2); - return String.format("Ptr %s %s", modsRep, typeCode.toString()); + return String.format("Ptr %s %s", modsRep, type.toString()); } @Override public DataType getDefinitionDataType() { - StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "Ptr", 0); - struct.add(CliTypeCodeDataType.dataType, "TypeCode", "Ptr"); + StructureDataType struct = new StructureDataType(new CategoryPath(PATH), + CliElementType.ELEMENT_TYPE_PTR.toString() + "_" + dataOffset, 0); + struct.add(CliTypeCodeDataType.dataType, CliElementType.ELEMENT_TYPE_PTR.toString(), + ""); + for (CliCustomMod mod : customMods) { struct.add(mod.getDefinitionDataType()); } - struct.add(CliTypeCodeDataType.dataType, "Type", "type or void"); + + struct.add(type.getDefinitionDataType(), type.baseTypeCode.toString(), ""); + return struct; } } @@ -682,6 +759,19 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable typeBytes = (int) (endIndex - origIndex); } + public CliTypeTable getTable() { + try { + return CliIndexTypeDefOrRef.getTableName(encodedType); + } + catch (InvalidInputException e) { + return null; + } + } + + public int getRowIndex() { + return CliIndexTypeDefOrRef.getRowIndex(encodedType); + } + @Override public String getRepresentation() { return "ValueType " + Integer.toHexString(encodedType); @@ -714,12 +804,17 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable @Override public DataType getDefinitionDataType() { + String tableName = getTable().name(); + StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "ValueType", 0); struct.add(CliTypeCodeDataType.dataType, "ValueType", "ValueType"); - struct.add(getDataTypeForBytes(typeBytes), "Type", "TypeDefOrRefOrSpecEncoded"); + struct.add(getDataTypeForBytes(typeBytes), "TypeDefOrRefEncoded", + tableName + ": Row 0x" + Integer.toHexString(getRowIndex())); + return struct; } + } public CliSigType readCliType(BinaryReader reader) throws IOException, InvalidInputException { @@ -761,10 +856,11 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } } - // The CustomMod signature part contains a required CMOD option (CMOD_OPT or CMOD_REQD) then a compressed TypeDefOrRefOrSpecEncoded - public static class CliCustomMod { + // The CustomMod signature part contains a required CMOD + // option (CMOD_OPT or CMOD_REQD) then a compressed TypeDefOrRefOrSpecEncoded + public static class CliCustomMod implements CliRepresentable { private CliElementType cmod; - private int typeEncoded; + private int encodedType; private int sizeOfCount; public static boolean isCustomMod(BinaryReader reader) throws IOException { @@ -776,7 +872,7 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable cmod = CliElementType.fromInt(reader.readNextByte()); long origIndex = reader.getPointerIndex(); - typeEncoded = decodeCompressedUnsignedInt(reader); + encodedType = decodeCompressedUnsignedInt(reader); long endIndex = reader.getPointerIndex(); sizeOfCount = (int) (endIndex - origIndex); @@ -787,12 +883,12 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public int getTypeEncoded() { - return typeEncoded; + return encodedType; } public CliTypeTable getTable() { try { - return CliIndexTypeDefOrRef.getTableName(typeEncoded); + return CliIndexTypeDefOrRef.getTableName(encodedType); } catch (InvalidInputException e) { return null; @@ -800,7 +896,7 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public int getRowIndex() { - return CliIndexTypeDefOrRef.getRowIndex(typeEncoded); + return CliIndexTypeDefOrRef.getRowIndex(encodedType); } public CliAbstractTableRow getRow(CliStreamMetadata stream) { @@ -808,25 +904,40 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public DataType getDefinitionDataType() { - StructureDataType struct = new StructureDataType(new CategoryPath(PATH), - CliCustomMod.class.getSimpleName(), 0); - struct.add(BYTE, "CMOD", "CMOD_OPT or CMOD_REQD"); - struct.add(getDataTypeForBytes(this.sizeOfCount), "Type", - "TypeDefOrRefOrSpec encoded type"); + StructureDataType struct = + new StructureDataType(new CategoryPath(PATH), "CustomMod", 0); + + String tableName = getTable().name(); + + struct.add(CliTypeCodeDataType.dataType, cmod.toString(), null); + struct.add(getDataTypeForBytes(this.sizeOfCount), "TypeDefOrRefEncoded", + tableName + ": Row 0x" + Integer.toHexString(getRowIndex())); return struct; } + @Override public String getRepresentation(CliStreamMetadata stream) { return String.format("%s %s", cmod.toString(), getRow(stream)); } + @Override public String getRepresentation() { - return String.format("%s %x", cmod.toString(), typeEncoded); + return String.format("%s %x", cmod.toString(), encodedType); + } + + @Override + public String getShortRepresentation() { + return getRepresentation(); + } + + @Override + public String getShortRepresentation(CliStreamMetadata stream) { + return getRepresentation(stream); } } // The only possible constraint is ELEMENT_TYPE_PINNED (CliTypeCode.Pinned) - public static class CliConstraint { + public static class CliConstraint implements CliRepresentable { private CliElementType constraint; public static boolean isConstraint(BinaryReader reader) throws IOException { @@ -841,6 +952,7 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable return constraint; } + @Override public String getRepresentation() { if (constraint == CliElementType.ELEMENT_TYPE_PINNED) { return constraint.toString(); @@ -848,36 +960,96 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable return String.format("Invalid Constraint (%s - %x)", constraint.toString(), constraint.id()); } + + @Override + public String getShortRepresentation() { + return getRepresentation(); + } + + @Override + public String getRepresentation(CliStreamMetadata stream) { + return getRepresentation(); + } + + @Override + public String getShortRepresentation(CliStreamMetadata stream) { + return getRepresentation(); + } + } + + // The only possible result is ELEMENT_TYPE_BYREF + public static class CliByRef implements CliRepresentable { + private CliElementType byRef; + + public static boolean isByRef(BinaryReader reader) throws IOException { + return (reader.peekNextByte() == CliElementType.ELEMENT_TYPE_BYREF.id()); + } + + public CliByRef(BinaryReader reader) throws IOException { + byRef = CliElementType.fromInt(reader.readNextByte()); + } + + public CliElementType getByRef() { + return byRef; + } + + @Override + public String getRepresentation() { + if (byRef == CliElementType.ELEMENT_TYPE_BYREF) { + return byRef.toString(); + } + return String.format("Invalid ByRef (%s - %x)", byRef.toString(), byRef.id()); + } + + @Override + public String getShortRepresentation() { + return getRepresentation(); + } + + @Override + public String getRepresentation(CliStreamMetadata stream) { + return getRepresentation(); + } + + @Override + public String getShortRepresentation(CliStreamMetadata stream) { + return getRepresentation(); + } } public class CliTypeBase implements CliRepresentable { - private List customMods = new ArrayList<>(); - private boolean constraint = false; - private boolean byRef = false; + private List modifiers = new ArrayList<>(); + private CliSigType type; + private long dataOffset = 0; private boolean isVoidAllowed = false; public CliTypeBase(BinaryReader reader, boolean isRetType) throws IOException, InvalidInputException { + dataOffset = reader.getPointerIndex(); + this.isVoidAllowed = isRetType; - // Get any custom modifiers - while (CliCustomMod.isCustomMod(reader)) { - customMods.add(new CliCustomMod(reader)); - } + // Check for any of the modifiers, which can repeat out of order + // before encountering the base type + while (CliCustomMod.isCustomMod(reader) || CliConstraint.isConstraint(reader) || + CliByRef.isByRef(reader)) { - // Check to see if it's a constrained variable - if (CliConstraint.isConstraint(reader)) { - constraint = true; - reader.readNextByte(); - } + // Get any custom modifiers + while (CliCustomMod.isCustomMod(reader)) { + modifiers.add(new CliCustomMod(reader)); + } - // Check to see if it's a ByRef - byte byRefCheck = reader.peekNextByte(); - if (byRefCheck == CliElementType.ELEMENT_TYPE_BYREF.id()) { - byRef = true; - reader.readNextByte(); + // Check to see if it's a constrained variable + if (CliConstraint.isConstraint(reader)) { + modifiers.add(new CliConstraint(reader)); + } + + // Check to see if it's a ByRef + if (CliByRef.isByRef(reader)) { + modifiers.add(new CliByRef(reader)); + } } type = readCliType(reader); @@ -888,34 +1060,48 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public List getCustomMods() { + List customMods = new ArrayList(); + for (CliRepresentable mod : modifiers) { + if (mod instanceof CliCustomMod) { + customMods.add((CliCustomMod) mod); + } + } + return customMods; } public boolean isByRef() { - return byRef; + for (CliRepresentable mod : modifiers) { + if (mod instanceof CliByRef) { + return true; + } + } + return false; } public boolean isConstrained() { - return constraint; + for (CliRepresentable mod : modifiers) { + if (mod instanceof CliConstraint) { + return true; + } + } + return false; } private String getRepresentationCommon(CliStreamMetadata stream, boolean shortRep) { String rep = ""; - for (CliCustomMod mod : customMods) { - rep += mod.getRepresentation() + "; "; - } - - if (customMods.size() > 0) { - rep = rep.substring(0, rep.length() - 2) + " "; - } - - if (constraint) { - rep += "constrained "; - } - - if (byRef) { - rep += "byref "; + for (CliRepresentable mod : modifiers) { + if (mod instanceof CliCustomMod) { + CliCustomMod customMod = (CliCustomMod) mod; + rep += customMod.getRepresentation() + "; "; + } + else if (mod instanceof CliConstraint) { + rep += "constrained "; + } + else if (mod instanceof CliByRef) { + rep += "byref "; + } } // The one special case value we have is the SENTINEL, which @@ -952,20 +1138,25 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable } public DataType getDefinitionDataType() { - StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "Type", 0); + StructureDataType struct = + new StructureDataType(new CategoryPath(PATH), "Type_" + dataOffset, 0); - for (CliCustomMod mod : customMods) { - struct.add(mod.getDefinitionDataType(), "CustomMod", null); + for (CliRepresentable mod : modifiers) { + if (mod instanceof CliCustomMod) { + CliCustomMod customMod = (CliCustomMod) mod; + struct.add(customMod.getDefinitionDataType(), "CustomMod", null); + } + else if (mod instanceof CliConstraint) { + struct.add(CliTypeCodeDataType.dataType, + CliElementType.ELEMENT_TYPE_PINNED.toString(), "Constrained"); + } + else if (mod instanceof CliByRef) { + struct.add(CliTypeCodeDataType.dataType, + CliElementType.ELEMENT_TYPE_BYREF.toString(), "By Reference"); + } } - if (constraint) { - struct.add(BYTE, "CONSTRAINT", "Constrained"); - } - - if (byRef) { - struct.add(BYTE, "BYREF", "By reference"); - } - struct.add(type.getDefinitionDataType(), "Type", null); + struct.add(type.getDefinitionDataType(), type.baseTypeCode.toString(), null); return struct; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigField.java index 22a4331886..19f00c6ff2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigField.java @@ -26,12 +26,14 @@ import ghidra.util.exception.InvalidInputException; public class CliSigField extends CliAbstractSig { private CliParam type; + private long dataOffset; private static final byte CLISIGFIELD_PROLOG = 0x06; public CliSigField(CliBlob blob) throws IOException { super(blob); BinaryReader reader = getContentsReader(); + dataOffset = reader.getPointerIndex(); byte prolog = reader.readNextByte(); if (prolog != CLISIGFIELD_PROLOG) { @@ -67,7 +69,7 @@ public class CliSigField extends CliAbstractSig { public DataType getContentsDataType() { StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0); struct.add(BYTE, "FIELD", "Magic (0x06)"); - struct.add(type.getDefinitionDataType(), "Type", null); + struct.add(type.getDefinitionDataType(), type.getType().baseTypeCode.toString(), null); return struct; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodDef.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodDef.java index 6ffb69f5d9..687ba588f8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodDef.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodDef.java @@ -85,17 +85,19 @@ public class CliSigMethodDef extends CliAbstractSig { @Override public DataType getContentsDataType() { StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0); - struct.add(BYTE, "flags", "ORed calling convention and THIS presence"); // TODO: enum + struct.add(BYTE, "Flags", "ORed VARARG/GENERIC/HASTHIS/EXPLICITTHIS"); // TODO: enum if (genericParamCount > 0) { struct.add(getDataTypeForBytes(sizeOfGenericCount), "GenParamCount", "Number of generic paramameters for the method"); } struct.add(getDataTypeForBytes(sizeOfCount), "Count", - "Number of param types to follow RetType"); + "Number of parameter types to follow RetType"); struct.add(retType.getDefinitionDataType(), "RetType", null); - for (CliParam param : params) { - struct.add(param.getDefinitionDataType(), null, null); + + for (int i = 0; i < params.length; i++) { + struct.add(params[i].getDefinitionDataType(), "Param" + i, null); } + return struct; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodRef.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodRef.java index 7e423ff732..c5e3734dbb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodRef.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/blobs/CliSigMethodRef.java @@ -23,7 +23,7 @@ import ghidra.program.model.data.*; import ghidra.util.exception.InvalidInputException; public class CliSigMethodRef extends CliAbstractSig { - + private long dataOffset; private CliRetType retType; private CliParam params[]; private int sizeOfCount; @@ -45,6 +45,8 @@ public class CliSigMethodRef extends CliAbstractSig { // Flags is similar to a MethodDef unless vararg is used. BinaryReader reader = getContentsReader(); + dataOffset = reader.getPointerIndex(); + flags = reader.readNextByte(); if ((flags & METHODREFSIG_FLAGS_GENERIC) == METHODREFSIG_FLAGS_GENERIC) { @@ -90,16 +92,21 @@ public class CliSigMethodRef extends CliAbstractSig { @Override public DataType getContentsDataType() { StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0); - struct.add(BYTE, "FirstByte", "ORed VARARG and HASTHIS/EXPLICITTHIS"); + struct.add(BYTE, "Flags", "ORed VARARG/GENERIC/HASTHIS/EXPLICITTHIS"); if (genericParamCount > 0) { struct.add(getDataTypeForBytes(sizeOfGenericCount), "GenParamCount", "Number of generic paramameters for the method"); } struct.add(getDataTypeForBytes(sizeOfCount), "ParamCount", - "Number of param types to follow RetType"); + "Number of parameter types to follow RetType"); struct.add(retType.getDefinitionDataType(), "RetType", null); for (int i = 0; i < params.length; i++) { - struct.add(params[i].getDefinitionDataType(), "Type" + i, null); + if (sentinelIndex == i) { + struct.add(CliTypeCodeDataType.dataType, + CliElementType.ELEMENT_TYPE_SENTINEL.toString(), "SENTINEL"); + } + + struct.add(params[i].getDefinitionDataType(), "Param" + i, null); } return struct; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/methods/CliMethodDef.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/methods/CliMethodDef.java index a8186c6ecb..fb63c99122 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/methods/CliMethodDef.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/methods/CliMethodDef.java @@ -4,9 +4,9 @@ * 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. @@ -24,69 +24,81 @@ import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; public class CliMethodDef implements StructConverter { - + public static final String PATH = "/PE/CLI/Methods/MethodDefs"; private Address addr; - private boolean isFatHeader; - private boolean hasMoreSections; - private boolean initLocals; + public enum HeaderFormat { + Fat, Tiny + } + + private int headerFlags; + private HeaderFormat headerFormat; + private int headerSize; // Size of this header private int maxStack; // Max number of items on operand stack private int methodSize; // Size of method body (code) private int localVarSigTok; - + + private static final int CLIMETHODDEF_HEADER_FLAGS_SHIFT = 0x08; + private static final int CLIMETHODDEF_HEADER_FLAGS_MASK = 0x0FFF; + private static final int CLIMETHODDEF_HEADER_SIZE_SHIFT = 0x0C; + private static final int CLIMETHODDEF_HEADER_SIZE_FAT_MULTIPLIER = 0x04; + private static final byte CorILMethod_TinyFormat = 0x2; private static final byte CorILMethod_FatFormat = 0x3; private static final byte CorILMethod_MoreSects = 0x8; private static final byte CorILMethod_InitLocals = 0x10; - + public CliMethodDef(Address addr, BinaryReader reader) throws IOException { this.addr = addr; // Read first byte, see if tiny or fat. - byte one = reader.readNextByte(); - if ((one & CorILMethod_FatFormat) == CorILMethod_FatFormat) { - isFatHeader = true; - if ((one & CorILMethod_MoreSects) == CorILMethod_MoreSects) - hasMoreSections = true; - if ((one & CorILMethod_InitLocals) == CorILMethod_InitLocals) - initLocals = true; - byte two = reader.readNextByte(); // TODO: need to read byte two? Seems to only have header length (in the wrong order?? >_<) + int firstByte = reader.readNextUnsignedByte(); + if ((firstByte & CorILMethod_FatFormat) == CorILMethod_FatFormat) { + headerFormat = HeaderFormat.Fat; + + // The header flags are stored across 12 bits, the top 4 bits + // indicate the size of the header + headerFlags = + (firstByte << CLIMETHODDEF_HEADER_FLAGS_SHIFT) + reader.readNextUnsignedByte(); + headerSize = headerFlags >> CLIMETHODDEF_HEADER_SIZE_SHIFT; + headerFlags = (headerFlags & CLIMETHODDEF_HEADER_FLAGS_MASK); + + // The raw header size bits indicate: "Size of this header + // expressed as the count of 4-bytes integers occupied." + headerSize = headerSize * CLIMETHODDEF_HEADER_SIZE_FAT_MULTIPLIER; + maxStack = reader.readNextShort(); methodSize = reader.readNextInt(); localVarSigTok = reader.readNextInt(); } - else if ((one & CorILMethod_TinyFormat) == CorILMethod_TinyFormat) { - isFatHeader = false; - hasMoreSections = false; - initLocals = false; + else if ((firstByte & CorILMethod_TinyFormat) == CorILMethod_TinyFormat) { + headerFormat = HeaderFormat.Tiny; + headerSize = 1; + headerFlags = 0; maxStack = 8; - methodSize = (((one & ~0x3) & 0xff) >> 2); // Mask off first 2 bits, right shift to get 6 length bits. 0xff mask to convert to right sign. + methodSize = (((firstByte & ~0x3) & 0xff) >> 2); // Mask off first 2 bits, right shift to get 6 length bits. 0xff mask to convert to right sign. } } - - private void fillTinyHeaderType(Structure struct) { - struct.add(BYTE, "Size+Flags", "L.S. Bits 0:1 Flags, Bits 2:7 Size of method in Bytes"); - } - - private void fillFatHeaderType(Structure struct) { - struct.add( WORD, "Size+Flags", "L.S. Bits 0:3 Size of hdr in B, Bits 4:15 Flags"); - struct.add( WORD, "MaxStack", "Maximum number of items on the operand stack"); - struct.add(DWORD, "CodeSize", "Size of actual method body in B"); - struct.add(DWORD, "LocalVarSigTok", "Signature for the local variables of the method. 0 means no locals. References standalone signature in Metadata tables, which references #Blob heap."); - } - + @Override public DataType toDataType() throws DuplicateNameException, IOException { - StructureDataType struct = - new StructureDataType(new CategoryPath(PATH), "MethodDefHdr_" + addr, 0); - if (isFatHeader) { - fillFatHeaderType(struct); + StructureDataType struct; + + if (headerFormat == HeaderFormat.Fat) { + struct = new StructureDataType(new CategoryPath(PATH), "MethodDefHdr_Fat", 0); + struct.add(WORD, "Size+Flags", "L.S. Bits 0:3 Size of hdr in bytes, Bits 4:15 Flags"); + struct.add(WORD, "MaxStack", "Maximum number of items on the operand stack"); + struct.add(DWORD, "CodeSize", "Size of actual method body in bytes"); + struct.add(DWORD, "LocalVarSigTok", + "Signature for the local variables of the method. 0 means no locals. References standalone signature in Metadata tables, which references #Blob heap."); } else { - fillTinyHeaderType(struct); + struct = new StructureDataType(new CategoryPath(PATH), "MethodDefHdr_Tiny", 0); + struct.add(BYTE, "Size+Flags", "L.S. Bits 0:1 Flags, Bits 2:7 Size of method in Bytes"); } + return struct; } @@ -95,7 +107,14 @@ public class CliMethodDef implements StructConverter { } public boolean hasMoreSections() { - return hasMoreSections; + return (headerFlags & CorILMethod_MoreSects) == CorILMethod_MoreSects; } + public boolean hasLocals() { + return (headerFlags & CorILMethod_InitLocals) == CorILMethod_InitLocals; + } + + public HeaderFormat getHeaderFormat() { + return headerFormat; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamBlob.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamBlob.java index 7432ac7ed7..87b3ea13f2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamBlob.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamBlob.java @@ -4,9 +4,9 @@ * 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. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamMetadata.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamMetadata.java index 65d8faf138..5b6b437656 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamMetadata.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/streams/CliStreamMetadata.java @@ -35,7 +35,7 @@ import ghidra.util.task.TaskMonitor; /** * The Metadata stream is giant and complicated. It is made up of {@link CliAbstractTable}s. - * + * * @see CliTypeTable */ public class CliStreamMetadata extends CliAbstractStream { @@ -55,7 +55,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the name of this stream. - * + * * @return The name of this stream. */ public static String getName() { @@ -64,13 +64,13 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Creates a new Metadata stream. - * + * * @param header The stream header associated with this stream. * @param guidStream The GUID stream. * @param userStringsStream The user strings stream. * @param stringsStream The strings stream. * @param blobStream The blob stream. - * @param fileOffset The file offset where this stream starts. + * @param fileOffset The file offset where this stream starts. * @param rva The relative virtual address where this stream starts. * @param reader A reader that is set to the start of the stream. * @throws IOException if there is a problem reading the stream. @@ -133,7 +133,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the GUID stream. - * + * * @return The GUID stream. Could be null if one doesn't exist. */ public CliStreamGuid getGuidStream() { @@ -142,7 +142,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the user strings stream. - * + * * @return The user strings stream. Could be null if one doesn't exist. */ public CliStreamUserStrings getUserStringsStream() { @@ -151,7 +151,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the strings stream. - * + * * @return The strings stream. Could be null if one doesn't exist. */ public CliStreamStrings getStringsStream() { @@ -160,7 +160,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the blob stream. - * + * * @return The blob stream. Could be null if one doesn't exist. */ public CliStreamBlob getBlobStream() { @@ -170,7 +170,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Creates a new {@link CliAbstractTable} from the table at the current reader index * with the given table type. - * + * * @param tableType The type of table to create. * @return A new table with the given type. Could be null if we don't support the table type. * @throws IOException if there was an issue reading the new table. @@ -300,7 +300,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the major version. - * + * * @return The major version. */ public short getMajorVersion() { @@ -309,7 +309,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the minor version. - * + * * @return The minor version. */ public short getMinorVersion() { @@ -318,7 +318,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the sorted field. - * + * * @return The sorted field. */ public long getSorted() { @@ -327,7 +327,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the valid field. - * + * * @return The valid field. */ public long getValid() { @@ -336,7 +336,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the table with the provided table type from the metadata stream. - * + * * @param tableType The type of table to get. * @return The table with the provided table type. Could be null if it doesn't exist. */ @@ -360,7 +360,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the table with the provided table type id from the metadata stream. - * + * * @param tableId The id of the table type to get. * @return The table with the provided table id. Could be null if it doesn't exist. */ @@ -370,9 +370,9 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the number of rows in the table with the given table type. - * + * * @param tableType The type of table to get the number of rows of. - * @return The number of rows in the table with the given table type. Could be 0 if + * @return The number of rows in the table with the given table type. Could be 0 if * the table of the given type was not found. */ public int getNumberRowsForTable(CliTypeTable tableType) { @@ -383,7 +383,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the data type of the index into the string stream. Will be either * {@link DWordDataType} or {@link WordDataType}. - * + * * @return The data type of the index into the string stream. */ public DataType getStringIndexDataType() { @@ -393,7 +393,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the data type of the index into the GUID stream. Will be either * {@link DWordDataType} or {@link WordDataType}. - * + * * @return The data type of the index into the string stream. */ public DataType getGuidIndexDataType() { @@ -403,7 +403,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the data type of the index into the Blob stream. Will be either * {@link DWordDataType} or {@link WordDataType}. - * + * * @return The data type of the index into the string stream. */ public DataType getBlobIndexDataType() { @@ -413,7 +413,7 @@ public class CliStreamMetadata extends CliAbstractStream { /** * Gets the data type of the index into a metadata table. Will be either * {@link DWordDataType} or {@link WordDataType}. - * + * * @return The data type of the index into the string stream. */ public DataType getTableIndexDataType(CliTypeTable table) { @@ -435,7 +435,7 @@ public class CliStreamMetadata extends CliAbstractStream { table.markup(program, isBinary, monitor, log, ntHeader); } catch (Exception e) { - Msg.error(this, "Failed to markup " + table); + Msg.error(this, "Failed to markup " + table + ": " + e.getMessage()); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableField.java index 93f2102206..2122af05cc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableField.java @@ -30,6 +30,7 @@ import ghidra.program.model.data.CategoryPath; import ghidra.program.model.data.StructureDataType; import ghidra.program.model.listing.Program; import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; @@ -100,16 +101,24 @@ public class CliTableField extends CliAbstractTable { public void markup(Program program, boolean isBinary, TaskMonitor monitor, MessageLog log, NTHeader ntHeader) throws DuplicateNameException, CodeUnitInsertionException, IOException { + int fieldRowIndex = 0; for (CliAbstractTableRow row : rows) { CliFieldRow fieldRow = (CliFieldRow) row; + fieldRowIndex++; - // Create FieldSig object and bookmark it + // Create FieldSig object Address sigAddr = CliAbstractStream.getStreamMarkupAddress(program, isBinary, monitor, log, ntHeader, metadataStream.getBlobStream(), fieldRow.sigIndex); CliSigField fieldSig = new CliSigField(metadataStream.getBlobStream().getBlob(fieldRow.sigIndex)); - metadataStream.getBlobStream().updateBlob(fieldSig, sigAddr, program); + + if (!metadataStream.getBlobStream().updateBlob(fieldSig, sigAddr, program)) { + Msg.warn(CliTableField.class, + "Couldn't update FieldSig blob " + + metadataStream.getStringsStream().getString(fieldRow.nameIndex) + + " at Field table index " + fieldRowIndex); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableMethodDef.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableMethodDef.java index 4ea7aaf787..f6a841479a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableMethodDef.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableMethodDef.java @@ -4,9 +4,9 @@ * 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. @@ -16,14 +16,13 @@ package ghidra.app.util.bin.format.pe.cli.tables; import java.io.IOException; -import java.util.Arrays; -import java.util.List; +import java.util.*; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.MemoryByteProvider; import ghidra.app.util.bin.format.pe.NTHeader; import ghidra.app.util.bin.format.pe.PeUtils; -import ghidra.app.util.bin.format.pe.cli.blobs.CliAbstractSig.CliParam; +import ghidra.app.util.bin.format.pe.cli.blobs.CliAbstractSig.*; import ghidra.app.util.bin.format.pe.cli.blobs.CliBlob; import ghidra.app.util.bin.format.pe.cli.blobs.CliSigMethodDef; import ghidra.app.util.bin.format.pe.cli.methods.CliMethodDef; @@ -31,6 +30,8 @@ import ghidra.app.util.bin.format.pe.cli.methods.CliMethodExtraSections; import ghidra.app.util.bin.format.pe.cli.streams.CliAbstractStream; import ghidra.app.util.bin.format.pe.cli.streams.CliStreamMetadata; import ghidra.app.util.bin.format.pe.cli.tables.CliTableParam.CliParamRow; +import ghidra.app.util.bin.format.pe.cli.tables.CliTableTypeDef.CliTypeDefRow; +import ghidra.app.util.bin.format.pe.cli.tables.CliTableTypeRef.CliTypeRefRow; import ghidra.app.util.bin.format.pe.cli.tables.flags.CliFlags.CliEnumMethodAttributes; import ghidra.app.util.bin.format.pe.cli.tables.flags.CliFlags.CliEnumMethodImplAttributes; import ghidra.app.util.importer.MessageLog; @@ -40,6 +41,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.listing.*; import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.SymbolUtilities; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; @@ -52,6 +54,8 @@ import ghidra.util.task.TaskMonitor; */ public class CliTableMethodDef extends CliAbstractTable { + private static final int CLITABLEMETHODDEF_PINVOKE_JUMP_LENGTH = 0x06; + public class CliMethodDefRow extends CliAbstractTableRow { public int RVA; public short ImplFlags; // MethodImplAttributes @@ -63,6 +67,42 @@ public class CliTableMethodDef extends CliAbstractTable { private static final int NEXT_ROW_PARAM_INIT_VALUE = -1; private int nextRowParamIndex = NEXT_ROW_PARAM_INIT_VALUE; + private static final int METHODIMPLATTRIBUTES_CODETYPE_IL = 0x00; + private static final int METHODIMPLATTRIBUTES_CODETYPE_NATIVE = 0x01; + private static final int METHODIMPLATTRIBUTES_CODETYPE_OPTIL = 0x02; + private static final int METHODIMPLATTRIBUTES_CODETYPE_RUNTIME = 0x03; + private static final int METHODIMPLATTRIBUTES_MANAGED_MANAGED = 0x00; + private static final int METHODIMPLATTRIBUTES_MANAGED_UNMANAGED = 0x04; + private static final int METHODIMPLATTRIBUTES_FORWARDREF = 0x10; + private static final int METHODIMPLATTRIBUTES_PRESERVESIG = 0x80; + private static final int METHODIMPLATTRIBUTES_INTERNALCALL = 0x1000; + private static final int METHODIMPLATTRIBUTES_SYNCHRONIZED = 0x20; + private static final int METHODIMPLATTRIBUTES_NOINLINING = 0x08; + private static final int METHODIMPLATTRIBUTES_AGGRESSIVEINLINING = 0x1000; + private static final int METHODIMPLATTRIBUTES_MAXMETHODIMPLVAL = 0xffff; + + private static final int METHODATTRIBUTES_MEMBERACCESS_COMPILERCONTROLLED = 0x00; + private static final int METHODATTRIBUTES_MEMBERACCESS_PRIVATE = 0x01; + private static final int METHODATTRIBUTES_MEMBERACCESS_FAMANDASSEM = 0x02; + private static final int METHODATTRIBUTES_MEMBERACCESS_ASSEM = 0x03; + private static final int METHODATTRIBUTES_MEMBERACCESS_FAMILY = 0x04; + private static final int METHODATTRIBUTES_MEMBERACCESS_FAMORASSEM = 0x05; + private static final int METHODATTRIBUTES_MEMBERACCESS_PUBLIC = 0x06; + private static final int METHODATTRIBUTES_STATIC = 0x10; + private static final int METHODATTRIBUTES_FINAL = 0x20; + private static final int METHODATTRIBUTES_VIRTUAL = 0x40; + private static final int METHODATTRIBUTES_HIDEBYSIG = 0x80; + private static final int METHODATTRIBUTES_VTABLELAYOUT_REUSESLOT = 0x0000; + private static final int METHODATTRIBUTES_VTABLELAYOUT_NEWSLOT = 0x0100; + private static final int METHODATTRIBUTES_STRICT = 0x0200; + private static final int METHODATTRIBUTES_ABSTRACT = 0x0400; + private static final int METHODATTRIBUTES_SPECIALNAME = 0x0800; + private static final int METHODATTRIBUTES_PINVOKEIMPL = 0x2000; + private static final int METHODATTRIBUTES_UNMANAGEDEXPORT = 0x08; + private static final int METHODATTRIBUTES_RTSPECIALNAME = 0x1000; + private static final int METHODATTRIBUTES_HASSECURITY = 0x4000; + private static final int METHODATTRIBUTES_REQUIRESECOBJECT = 0x8000; + public CliMethodDefRow(int rva, short implFlags, short flags, int nameIndex, int sigIndex, int paramIndex) { this.RVA = rva; @@ -141,6 +181,28 @@ public class CliTableMethodDef extends CliAbstractTable { CliEnumMethodImplAttributes.dataType.getName(ImplFlags & 0xffff), CliEnumMethodAttributes.dataType.getName(Flags & 0xffff)); } + + // Static functions have four parameters but the first is an implied + // pointer to the object they're associated with. It's not in the + // ParameterTable and looking for the extra parameter will walk + // you into the parameter of another function. + boolean isStatic() { + return (Flags & METHODATTRIBUTES_STATIC) == METHODATTRIBUTES_STATIC; + } + + boolean isPInvokeImpl() { + return (Flags & METHODATTRIBUTES_PINVOKEIMPL) == METHODATTRIBUTES_PINVOKEIMPL; + } + + boolean isNative() { + return (ImplFlags & + METHODIMPLATTRIBUTES_CODETYPE_NATIVE) == METHODIMPLATTRIBUTES_CODETYPE_NATIVE; + } + + boolean isManaged() { + return (ImplFlags & + METHODIMPLATTRIBUTES_CODETYPE_IL) == METHODIMPLATTRIBUTES_CODETYPE_IL; + } } public CliTableMethodDef(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId) @@ -159,7 +221,7 @@ public class CliTableMethodDef extends CliAbstractTable { } lastRow = row; } - reader.setPointerIndex(this.readerOffset); // TODO: why do this, also elsewhere + reader.setPointerIndex(this.readerOffset); } @Override @@ -167,54 +229,64 @@ public class CliTableMethodDef extends CliAbstractTable { NTHeader ntHeader) throws DuplicateNameException, CodeUnitInsertionException, IOException { - int rvaZero = 0; - + int methodRowIndex = 0; for (CliAbstractTableRow method : rows) { + methodRowIndex++; + CliMethodDefRow methodRow = (CliMethodDefRow) method; // This indicates the method is abstract, runtime, or PInvokeImpl if (methodRow.RVA == 0) { - rvaZero++; continue; } Address addr = PeUtils.getMarkupAddress(program, isBinary, ntHeader, methodRow.RVA); + Address startAddr = addr; + Address endAddr = addr; - // Create MethodDef at this RVA - BinaryReader reader = - new BinaryReader(new MemoryByteProvider(program.getMemory(), addr), - !program.getMemory().isBigEndian()); - CliMethodDef methodDef = new CliMethodDef(addr, reader); - - PeUtils.createData(program, addr, methodDef.toDataType(), log); - - // Get the function's address space, default to zero-length just in case - Address startAddr = addr.add(methodDef.toDataType().getLength()); - Address endAddr = startAddr; - if (methodDef.getMethodSize() > 0) { - endAddr = startAddr.add(methodDef.getMethodSize() - 1); + if (methodRow.isPInvokeImpl() && methodRow.isNative()) { + endAddr = startAddr.add(CLITABLEMETHODDEF_PINVOKE_JUMP_LENGTH - 1); } + else { + // Create MethodDef at this RVA + BinaryReader reader = + new BinaryReader(new MemoryByteProvider(program.getMemory(), addr), + !program.getMemory().isBigEndian()); + CliMethodDef methodDef = new CliMethodDef(addr, reader); + + DataType methodDefDataType = methodDef.toDataType(); + PeUtils.createData(program, addr, methodDefDataType, log); + + // Get the function's address space, default to zero-length just in case + startAddr = addr.add(methodDefDataType.getLength()); + endAddr = startAddr; + if (methodDef.getMethodSize() > 0) { + endAddr = startAddr.add(methodDef.getMethodSize() - 1); + } + + // Do extra data sections in MethodDef + if (methodDef.hasMoreSections()) { + int extraSectionOffset = + methodDefDataType.getLength() + methodDef.getMethodSize(); + + // Round up to the next offset divisible by 4 + extraSectionOffset = ((extraSectionOffset + 3) / 4) * 4; + + reader.setPointerIndex(extraSectionOffset); + CliMethodExtraSections extraSections = new CliMethodExtraSections(reader); + Address extraSectionAddr = addr.add(extraSectionOffset); + PeUtils.createData(program, extraSectionAddr, extraSections.toDataType(), log); + } + } + AddressSetView funcAddrSet = new AddressSet(startAddr, endAddr); // Let Ghidra assign a default function name and then try to decode the // real one if it exists String funcName = null; if (methodRow.nameIndex > 0) { - funcName = metadataStream.getStringsStream().getString(methodRow.nameIndex); - } - - // Do extra data sections in MethodDef - if (methodDef.hasMoreSections()) { - int extraSectionOffset = - methodDef.toDataType().getLength() + methodDef.getMethodSize(); - - // Round up to the next offset divisible by 4 - extraSectionOffset = ((extraSectionOffset + 3) / 4) * 4; - - reader.setPointerIndex(extraSectionOffset); - CliMethodExtraSections extraSections = new CliMethodExtraSections(reader); - Address extraSectionAddr = addr.add(extraSectionOffset); - PeUtils.createData(program, extraSectionAddr, extraSections.toDataType(), log); + funcName = SymbolUtilities.replaceInvalidChars( + metadataStream.getStringsStream().getString(methodRow.nameIndex), true); } // Get the function signature blob @@ -227,27 +299,136 @@ public class CliTableMethodDef extends CliAbstractTable { metadataStream.getBlobStream().updateBlob(methodSig, sigAddr, program); DataType returnType = methodSig.getReturnType().getExecutionDataType(); - // Pull apart the function parameter names and types + int maxSequence = 0; int stackOffset = 0; CliParam paramTypes[] = methodSig.getParamTypes(); + int paramCount = paramTypes.length; CliTableParam paramTable = (CliTableParam) metadataStream.getTable(CliTypeTable.Param); - ParameterImpl parameters[] = new ParameterImpl[paramTypes.length]; - for (int i = 0; i < paramTypes.length; i++) { + // Store the parameters in a Hashtable because by the time processing + // finishes the number of actual parameters might change + HashMap parameterList = new HashMap(); + + // Some Static function first parameters being pointers to a ValueType + // have the same number of parameters specified, but one or more are implied + // pointers to the object they're associated with. It's not in the Parameter + // Table and looking for the extra parameter in the table will walk you + // into the parameter list of another function. + ParameterImpl staticParameter = null; + if (methodRow.isStatic() && paramCount > 0) { + CliParam staticParam = paramTypes[0]; + String paramName = ""; + + // Walk the path from the ELEMENT_TYPE_PTR to the ELEMENT_TYPE_VALUETYPE + if (staticParam.getType() instanceof CliTypePtr) { + CliTypePtr ptrToValueType = (CliTypePtr) staticParam.getType(); + if (ptrToValueType.getType() instanceof CliTypeValueType) { + CliTypeValueType valueType = (CliTypeValueType) ptrToValueType.getType(); + + // Get the table and row specifying the type name + CliTypeTable tableType = valueType.getTable(); + int rowIndex = valueType.getRowIndex(); + + int paramNameStringIndex = 0; + CliAbstractTable table = metadataStream.getTable(tableType); + CliAbstractTableRow row = table.getRow(rowIndex); + if (tableType.id() == tableType.TypeDef.id()) { + CliTypeDefRow typeDefRow = (CliTypeDefRow) row; + paramNameStringIndex = typeDefRow.typeNameIndex; + } + else if (tableType.id() == tableType.TypeRef.id()) { + CliTypeRefRow typeRefRow = (CliTypeRefRow) row; + paramNameStringIndex = typeRefRow.typeNameIndex; + } + + if (paramNameStringIndex > 0) { + paramName = + metadataStream.getStringsStream().getString(paramNameStringIndex); + paramName = SymbolUtilities.replaceInvalidChars(paramName, true); + + DataType dataType = staticParam.getExecutionDataType(); + + try { + staticParameter = + new ParameterImpl(paramName, dataType, stackOffset, program); + } + catch (InvalidInputException e) { + Msg.warn(this, "Error processing parameter \"" + paramName + + "\" in function \"" + funcName + "\": " + e.getMessage()); + } + + stackOffset += dataType.getLength(); + + paramCount--; + } + } + } + } + + // Pull apart the function's Param table entries + for (int i = 0; i < paramCount; i++) { CliParamRow paramRow = (CliParamRow) paramTable.getRow(methodRow.paramIndex + i); - String paramName = metadataStream.getStringsStream().getString(paramRow.nameIndex); + if (paramRow.sequence > maxSequence) { + maxSequence = paramRow.sequence; + } + + String paramName = SymbolUtilities.replaceInvalidChars( + metadataStream.getStringsStream().getString(paramRow.nameIndex), true); + DataType dataType = paramTypes[i].getExecutionDataType(); - try { - parameters[i] = new ParameterImpl(paramName, dataType, stackOffset, program); - } - catch (InvalidInputException e) { - Msg.warn(this, "Error processing parameter \"" + paramName + - "\" in function \"" + funcName + "\": " + e.getMessage()); + if (paramRow.sequence == 0) { + // Parameters with a 0 sequence number are the return type, + // reduce the size of the array and put any previously discovered + // parameters into it + returnType = dataType; } + else { + // Parameters are placed in the proper order based on the sequence + // field (1-based) to compensate for some static methods having an implied + // first parameter that won't be represented in the Parameter Table + // and some return types that are represented as parameters. + try { + parameterList.put((int) paramRow.sequence, + new ParameterImpl(paramName, dataType, stackOffset, program)); + } + catch (InvalidInputException e) { + Msg.warn(this, "Error processing parameter \"" + paramName + + "\" in function \"" + funcName + "\": " + e.getMessage()); + } - stackOffset += dataType.getLength(); + stackOffset += dataType.getLength(); + } + } + + ParameterImpl[] parameters = new ParameterImpl[maxSequence]; + parameterList.forEach((key, value) -> + // Sequences are 1-based + parameters[key - 1] = value); + + // For static functions, fill in the pointer to ValueType + // parameters that are implied before the actual parameters + if (methodRow.isStatic()) { + if (staticParameter != null) { + for (int i = 0; i < parameters.length; i++) { + if (parameters[i] == null) { + ParameterImpl param = null; + try { + param = new ParameterImpl(staticParameter.getName() + i, + staticParameter.getDataType(), staticParameter.getStackOffset(), + staticParameter.getProgram()); + } + catch (InvalidInputException e1) { + Msg.warn(this, + "Couldn't clone " + staticParameter.getName() + + " implied static function parameter in function : " + + funcName + "in position " + i); + } + parameters[i] = param; + } + } + } } try { @@ -257,15 +438,44 @@ public class CliTableMethodDef extends CliAbstractTable { newFunc.updateFunction(null, null, FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS, parameters); } + catch (NullPointerException e) { + Msg.warn(this, "Error processing function \"" + funcName + "\" (" + methodRowIndex + + "): Bad parameters provided"); + } catch (InvalidInputException e) { - Msg.warn(this, "Error processing function \"" + funcName + "\""); + Msg.warn(this, "Error processing function \" (\" + methodRowIndex + \")" + + funcName + "\": Invalid function"); } catch (OverlappingFunctionException e) { - Msg.warn(this, "Error processing function \"" + funcName + "\""); + String err = "Error processing function \" (\" + methodRowIndex + \")" + funcName + + "\": Overlapping function (" + startAddr + ", " + endAddr + ": "; + + Function existingFuncA = program.getFunctionManager().getFunctionAt(startAddr); + Function existingFuncB = program.getFunctionManager().getFunctionAt(endAddr); + + if (existingFuncA != null && existingFuncB == null) { + err = err + existingFuncA.getName(); + } + else if (existingFuncA == null && existingFuncB != null) { + err = err + existingFuncB.getName(); + } + else if (existingFuncA != null && existingFuncA == existingFuncB) { + err = err + existingFuncA.getName(); + } + + err = err + ")"; + + Msg.warn(this, err); + } + catch (DuplicateNameException e) { + String paramNames = ""; + for (int i = 0; i < parameters.length - 1; i++) { + paramNames += parameters[i].getName() + ", "; + } + paramNames += parameters[parameters.length - 1].getName(); + Msg.warn(this, "Error processing function \"" + funcName + "\" (" + methodRowIndex + + "): Duplicate parameter name (" + paramNames + ")"); } - } - if (rvaZero > 0) { - Msg.warn(this, rvaZero + " methods with RVA 0"); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableParam.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableParam.java index 097258ec86..875acf1a9b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableParam.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableParam.java @@ -31,7 +31,14 @@ public class CliTableParam extends CliAbstractTable { public short flags; public short sequence; public int nameIndex; - + + private static final int PARAMATTRIBUTES_IN = 0x1; + private static final int PARAMATTRIBUTES_OUT = 0x2; + private static final int PARAMATTRIBUTES_OPTIONAL = 0x10; + private static final int PARAMATTRIBUTES_HASDEFAULT = 0x1000; + private static final int PARAMATTRIBUTES_HASFIELDMARSHAL = 0x2000; + private static final int PARAMATTRIBUTES_UNUSED = 0xCFE0; + public CliParamRow(short flags, short sequence, int nameIndex) { super(); this.flags = flags; @@ -46,11 +53,13 @@ public class CliTableParam extends CliAbstractTable { CliEnumParamAttributes.dataType.getName(flags & 0xffff), sequence); } } - - public CliTableParam(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId) throws IOException { + + public CliTableParam(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId) + throws IOException { super(reader, stream, tableId); for (int i = 0; i < this.numRows; i++) { - CliParamRow row = new CliParamRow(reader.readNextShort(), reader.readNextShort(), readStringIndex(reader)); + CliParamRow row = new CliParamRow(reader.readNextShort(), reader.readNextShort(), + readStringIndex(reader)); rows.add(row); strings.add(row.nameIndex); } @@ -59,9 +68,9 @@ public class CliTableParam extends CliAbstractTable { @Override public StructureDataType getRowDataType() { - StructureDataType rowDt = new StructureDataType(new CategoryPath(PATH), "ParamRow",0); + StructureDataType rowDt = new StructureDataType(new CategoryPath(PATH), "ParamRow", 0); rowDt.add(CliEnumParamAttributes.dataType, "Flags", "bitmask of type ParamAttributes"); - rowDt.add( WORD, "Sequence", "constant"); + rowDt.add(WORD, "Sequence", "constant"); rowDt.add(metadataStream.getStringIndexDataType(), "Name", "index into String heap"); return rowDt; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableStandAloneSig.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableStandAloneSig.java index 025eae5acf..aefe83d791 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableStandAloneSig.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/cli/tables/CliTableStandAloneSig.java @@ -4,9 +4,9 @@ * 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. @@ -90,6 +90,7 @@ public class CliTableStandAloneSig extends CliAbstractTable { throws DuplicateNameException, CodeUnitInsertionException, IOException { for (CliAbstractTableRow row : rows) { Integer sigIndex = ((CliStandAloneSigRow) row).signatureIndex; + CliBlob blob = metadataStream.getBlobStream().getBlob(((CliStandAloneSigRow) row).signatureIndex); Address sigAddr = CliAbstractStream.getStreamMarkupAddress(program, isBinary, monitor,