Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix of .NET analysis differences #997

Merged
merged 15 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions include/retdec/fileformat/types/dotnet_headers/blob_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef RETDEC_FILEFORMAT_TYPES_DOTNET_HEADERS_BLOB_STREAM_H
#define RETDEC_FILEFORMAT_TYPES_DOTNET_HEADERS_BLOB_STREAM_H

#include <cstdint>
#include <unordered_map>

#include "retdec/fileformat/types/dotnet_headers/stream.h"
Expand All @@ -17,19 +18,11 @@ namespace fileformat {
class BlobStream : public Stream
{
private:
std::unordered_map<std::size_t, std::vector<std::uint8_t>> elements;
std::vector<std::uint8_t> data;
public:
BlobStream(std::uint64_t streamOffset, std::uint64_t streamSize);
BlobStream(std::vector<std::uint8_t> data, std::uint64_t streamOffset, std::uint64_t streamSize);
HoundThe marked this conversation as resolved.
Show resolved Hide resolved

/// @name Getters
/// @{
std::vector<std::uint8_t> getElement(std::size_t offset) const;
/// @}

/// @name Element methods
/// @{
void addElement(std::size_t offset, const std::vector<std::uint8_t>& data);
/// @}
};

} // namespace fileformat
Expand Down
10 changes: 10 additions & 0 deletions include/retdec/fileformat/types/dotnet_headers/metadata_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,9 @@ struct TypeDef : public BaseRecord
bool isNestedPublic() const { return (flags & TypeVisibilityMask) == TypeNestedPublic; }
bool isNestedPrivate() const { return (flags & TypeVisibilityMask) == TypeNestedPrivate; }
bool isNestedProtected() const { return (flags & TypeVisibilityMask) == TypeNestedFamily; }
bool isNestedInternal() const { return (flags & TypeVisibilityMask) == TypeNestedAssembly; }
bool isNestedFamOrAssem() const { return (flags & TypeVisibilityMask) == TypeNestedFamORAssem; }
bool isNestedFamAndAssem() const { return (flags & TypeVisibilityMask) == TypeNestedFamANDAssem; }
bool isClass() const { return (flags & TypeClassSemanticsMask) == TypeClass; }
bool isInterface() const { return (flags & TypeClassSemanticsMask) == TypeInterface; }
bool isAbstract() const { return flags & TypeClassAbstract; }
Expand Down Expand Up @@ -688,6 +691,9 @@ struct Field : public BaseRecord
bool isPublic() const { return (flags & FieldAccessMask) == FieldPublic; }
bool isProtected() const { return (flags & FieldAccessMask) == FieldFamily; }
bool isPrivate() const { return (flags & FieldAccessMask) == FieldPrivate; }
bool isInternal() const { return (flags & FieldAccessMask) == FieldAssembly; }
bool isFamOrAssem() const { return (flags & FieldAccessMask) == FieldFamORAssem; }
bool isFamAndAssem() const { return (flags & FieldAccessMask) == FieldFamANDAssem; }
bool isStatic() const { return flags & FieldStatic; }

virtual void load(const FileFormat* file, const MetadataStream* stream, std::uint64_t& address) override
Expand Down Expand Up @@ -720,6 +726,10 @@ struct MethodDef : public BaseRecord
bool isPublic() const { return (flags & MethodMemberAccessMask) == MethodPublic; }
bool isPrivate() const { return (flags & MethodMemberAccessMask) == MethodPrivate; }
bool isProtected() const { return (flags & MethodMemberAccessMask) == MethodFamily; }
bool isInternal() const { return (flags & MethodMemberAccessMask) == MethodAssem; }
bool isFamOrAssem() const { return (flags & MethodMemberAccessMask) == MethodFamORAssem; }
bool isFamAndAssem() const { return (flags & MethodMemberAccessMask) == MethodFamANDAssem; }

bool isStatic() const { return flags & MethodStatic; }
bool isVirtual() const { return flags & MethodVirtual; }
bool isFinal() const { return flags & MethodFinal; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class DotnetClass : public DotnetType
bool isInterface() const;
bool isAbstract() const;
bool isSealed() const;
bool isNested() const;
/// @}

/// @name Additions
Expand Down
8 changes: 7 additions & 1 deletion include/retdec/fileformat/types/dotnet_types/dotnet_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ namespace fileformat {
enum class DotnetTypeVisibility
{
Public,
Internal,
Private,
Protected,
Private
ProtectedInternal,
PrivateProtected
};

/**
Expand Down Expand Up @@ -56,6 +59,9 @@ class DotnetType
bool isPublic() const { return visibility == DotnetTypeVisibility::Public; }
bool isProtected() const { return visibility == DotnetTypeVisibility::Protected; }
bool isPrivate() const { return visibility == DotnetTypeVisibility::Private; }
bool isInternal() const { return visibility == DotnetTypeVisibility::Internal; }
bool isProtectedInternal() const { return visibility == DotnetTypeVisibility::ProtectedInternal; }
bool isPrivateProtected() const { return visibility == DotnetTypeVisibility::PrivateProtected; }
/// @}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class DotnetTypeReconstructor
std::unique_ptr<DotnetField> createField(const Field* field, const DotnetClass* ownerClass);
std::unique_ptr<DotnetProperty> createProperty(const Property* property, const DotnetClass* ownerClass);
std::unique_ptr<DotnetMethod> createMethod(const MethodDef* methodDef, const DotnetClass* ownerClass);
std::unique_ptr<DotnetParameter> createMethodParameter(const Param* param, const DotnetClass* ownerClass, const DotnetMethod* ownerMethod, std::vector<std::uint8_t>& signature);
std::unique_ptr<DotnetParameter> createMethodParameter(std::size_t paramIdx, std::size_t startIdx, const DotnetClass* ownerClass, const DotnetMethod* ownerMethod, std::vector<std::uint8_t>& signature);

template <typename T> std::unique_ptr<T> createDataTypeFollowedByReference(std::vector<std::uint8_t>& data);
template <typename T> std::unique_ptr<T> createDataTypeFollowedByType(std::vector<std::uint8_t>& data, const DotnetClass* ownerClass, const DotnetMethod* ownerMethod);
Expand Down
113 changes: 53 additions & 60 deletions src/fileformat/file_format/pe/pe_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1924,15 +1924,15 @@ void PeFormat::loadDotnetHeaders()
return;
}

if (streamName == "#~" || streamName == "#-")
if ((streamName == "#~" || streamName == "#-") && !metadataStream)
parseMetadataStream(metadataHeaderAddress, streamOffset, streamSize);
else if (streamName == "#Blob")
else if (streamName == "#Blob" && !blobStream)
parseBlobStream(metadataHeaderAddress, streamOffset, streamSize);
else if (streamName == "#GUID")
else if (streamName == "#GUID" && !guidStream)
parseGuidStream(metadataHeaderAddress, streamOffset, streamSize);
else if (streamName == "#Strings")
else if (streamName == "#Strings" && !stringStream)
parseStringStream(metadataHeaderAddress, streamOffset, streamSize);
else if (streamName == "#US")
else if (streamName == "#US" && !userStringStream)
parseUserStringStream(metadataHeaderAddress, streamOffset, streamSize);

// Round-up to the nearest higher multiple of 4
Expand Down Expand Up @@ -2060,6 +2060,13 @@ void PeFormat::parseMetadataStream(std::uint64_t baseAddress, std::uint64_t offs
currentAddress += 4;
}
}
// ExtraData flags means there is extra 4 bytes at the end Rows array that contaisn the rows sizes
// I don't see anything about in at ECMA-335, but I can see in real samples and in IlSpy source
// that understands it and correctly decompiles, sample: 5b5817fe2d4f0989501802b0e2bb4451583ff27fd0723f40bb7f8b0417dd7c58
if (heapOffsetSizes & 0x40)
{
currentAddress += 4;
}

for (std::size_t i = 0; i < 64; ++i)
{
Expand Down Expand Up @@ -2214,55 +2221,11 @@ void PeFormat::parseMetadataStream(std::uint64_t baseAddress, std::uint64_t offs
*/
void PeFormat::parseBlobStream(std::uint64_t baseAddress, std::uint64_t offset, std::uint64_t size)
{
blobStream = std::make_unique<BlobStream>(offset, size);
std::vector<std::uint8_t> data;
auto address = baseAddress + offset;
getXBytes(address, size, data);
blobStream = std::make_unique<BlobStream>(data, offset, size);

std::vector<std::uint8_t> elementData;
std::uint64_t length, lengthSize;

std::size_t inStreamOffset = 0;
while (inStreamOffset < size)
{
// First std::uint8_t is length of next element in the blob
lengthSize = 1;
if (!get1Byte(address + inStreamOffset, length))
{
return;
}

// 2-std::uint8_t length encoding if the length is 10xxxxxx
if ((length & 0xC0) == 0x80)
{
if (!get2Byte(address + inStreamOffset, length, Endianness::BIG))
{
return;
}

length &= ~0xC000;
lengthSize = 2;
}
// 4-std::uint8_t length encoding if the length is 110xxxxx
else if ((length & 0xE0) == 0xC0)
{
if (!get4Byte(address + inStreamOffset, length, Endianness::BIG))
{
return;
}

length &= ~0xE0000000;
lengthSize = 4;
}

// Read only if length is greater than 0
elementData.clear();
if (length > 0 && !getXBytes(address + inStreamOffset + lengthSize, length, elementData))
{
return;
}

blobStream->addElement(inStreamOffset, elementData);
inStreamOffset += lengthSize + length;
}
}

/**
Expand Down Expand Up @@ -2299,14 +2262,8 @@ void PeFormat::parseStringStream(std::uint64_t baseAddress, std::uint64_t offset
while (currentOffset < size)
{
std::string string;
if (!getNTBS(address + currentOffset, string))
{
currentOffset += 1;
continue;
}

getNTBS(address + currentOffset, string);
stringStream->addString(currentOffset, string);

// +1 for null-terminator
currentOffset += 1 + string.length();
}
Expand Down Expand Up @@ -3191,7 +3148,43 @@ bool PeFormat::initDllList(const std::string & dllListFile)
*/
bool PeFormat::isDotNet() const
{
return clrHeader != nullptr || metadataHeader != nullptr;
if (!clrHeader || !metadataHeader) {
return false;
}

unsigned correctHdrSize = 72;
HoundThe marked this conversation as resolved.
Show resolved Hide resolved
if (clrHeader->getHeaderSize() != correctHdrSize)
{
return false;
}

std::uint32_t numberOfRvaAndSizes = getImageLoader().getOptionalHeader().NumberOfRvaAndSizes;
// If the binary is 64bit, check NumberOfRvaAndSizes, otherwise don't
if (getImageBitability() == 64 &&
numberOfRvaAndSizes < PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
{
return false;
}
else if (!isDll())
{ // If 32 bit check if first 2 bytes at entry point are 0xFF 0x25

unsigned long long entryAddr = 0;
if (!getEpAddress(entryAddr))
{
return false;
}
std::uint64_t bytes[2];
if (!get1Byte(entryAddr, bytes[0]) || !get1Byte(entryAddr + 1, bytes[1]))
{
return false;
}
if (bytes[0] != 0xFF || bytes[1] != 0x25)
{
return false;
}
}

return true;
}

/**
Expand Down
71 changes: 51 additions & 20 deletions src/fileformat/types/dotnet_headers/blob_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@
*/

#include "retdec/fileformat/types/dotnet_headers/blob_stream.h"
#include <cstdint>

namespace retdec {
namespace fileformat {

/**
* Constructor.
* @param streamOffset Stream offset.
* @param streamSize Stream size.
*/
BlobStream::BlobStream(std::uint64_t streamOffset, std::uint64_t streamSize) : Stream(StreamType::Blob, streamOffset, streamSize)
BlobStream::BlobStream(std::vector<std::uint8_t> data, std::uint64_t streamOffset, std::uint64_t streamSize)
: Stream(StreamType::Blob, streamOffset, streamSize), data(data)
{
}

Expand All @@ -25,22 +22,56 @@ BlobStream::BlobStream(std::uint64_t streamOffset, std::uint64_t streamSize) : S
*/
std::vector<std::uint8_t> BlobStream::getElement(std::size_t offset) const
{
auto itr = elements.find(offset);
if (itr == elements.end())
// Adapted from YARA
// https://github.com/VirusTotal/yara/blob/v4.1.2/libyara/modules/dotnet/dotnet.c#L130
std::uint32_t len = 0;
const unsigned char* ptr = data.data() + offset;
if (offset >= data.size())
{
return {};
}
else if ((*ptr & 0x80) == 0x00)
{
len = *ptr;
offset += 1;
if (offset + len <= data.size())
PeterMatula marked this conversation as resolved.
Show resolved Hide resolved
{
return { data.begin() + offset, data.begin() + offset + len };
}
}
else if ((*ptr & 0xC0) == 0x80)
PeterMatula marked this conversation as resolved.
Show resolved Hide resolved
{
// Make sure we have one more byte.
if (offset + 1 < data.size())
{
// Shift remaining 6 bits left by 8 and OR in the remaining byte.
len = ((*ptr & 0x3F) << 8) | *(ptr + 1);
offset += 2;
}
if (offset + len <= data.size())
{
return { data.begin() + offset, data.begin() + offset + len };
}
}
else if ((*ptr & 0xE0) == 0xC0)
{
// Make sure we have 3 more bytes.
if (offset + 3 < data.size())
{
// Shift remaining 6 bits left by 8 and OR in the remaining byte.
len = ((*ptr & 0x1F) << 24) |
(*(ptr + 1) << 16) |
(*(ptr + 2) << 8) |
*(ptr + 3);
offset += 4;
}
if (offset + len <= data.size())
{
return { data.begin() + offset, data.begin() + offset + len };
}
}

return itr->second;
return {};
}

/**
* Adds new element at the specified offset.
* @param offset Offset of the element.
* @param data Data of the element.
*/
void BlobStream::addElement(std::size_t offset, const std::vector<std::uint8_t>& data)
{
elements.emplace(offset, data);
}

} // namespace fileformat
} // namespace retdec
13 changes: 13 additions & 0 deletions src/fileformat/types/dotnet_types/dotnet_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "retdec/utils/string.h"
#include "retdec/fileformat/types/dotnet_types/dotnet_class.h"
#include <cstdint>

using namespace retdec::utils;

Expand Down Expand Up @@ -412,6 +413,18 @@ bool DotnetClass::isSealed() const
return sealed;
}

bool DotnetClass::isNested() const
{
const TypeDef* row = getRawTypeDef();
if (!row)
{
return false;
}
std::uint32_t flags = getRawTypeDef()->flags;
return (flags & TypeVisibilityMask) != TypeNotPublic &&
(flags & TypeVisibilityMask) != TypePublic;
}

/**
* Adds the field to the class.
* @param field Field to add.
Expand Down
5 changes: 4 additions & 1 deletion src/fileformat/types/dotnet_types/dotnet_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const std::unordered_map<DotnetTypeVisibility, std::string, retdec::utils::EnumC
{
{ DotnetTypeVisibility::Public, "public" },
{ DotnetTypeVisibility::Protected, "protected" },
{ DotnetTypeVisibility::Private, "private" }
{ DotnetTypeVisibility::Private, "private" },
{ DotnetTypeVisibility::Internal, "internal" },
{ DotnetTypeVisibility::ProtectedInternal, "protected internal" },
{ DotnetTypeVisibility::PrivateProtected, "private protected" },
};

}
Expand Down
Loading