
com.github.katjahahn.tools.anomalies.SectionTableScanning.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of portex_2.12 Show documentation
Show all versions of portex_2.12 Show documentation
Java library to parse Portable Executable files
The newest version!
/**
* *****************************************************************************
* Copyright 2014 Karsten Hahn
*
* 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 com.github.katjahahn.tools.anomalies
import com.github.katjahahn.parser.IOUtil.NL
import com.github.katjahahn.parser.PhysicalLocation
import com.github.katjahahn.parser.optheader.WindowsEntryKey
import com.github.katjahahn.parser.sections.SectionCharacteristic._
import com.github.katjahahn.parser.sections.{SectionCharacteristic, SectionHeader, SectionHeaderKey, SectionLoader}
import com.github.katjahahn.tools.Overlay
import com.github.katjahahn.tools.anomalies.AnomalySubType._
import scala.collection.JavaConverters._
import scala.collection.immutable.HashMap
import scala.collection.mutable.ListBuffer
/**
* Scans the Section Table for anomalies.
*
* @author Karsten Hahn
*/
trait SectionTableScanning extends AnomalyScanner {
private val packerNames = HashMap(
//Aspack
".aspack" -> "Aspack packer", ".adata" -> "Aspack/Armadillo packer",
"ASPack" -> "Aspack packer", ".ASPack" -> "Aspack packer",
".asspck" -> "Aspack packer",
//common
".arch" -> "Alpha-architecture section",
".bindat" -> "Binary data, e.g., by downware installers",
".cormeta" -> "CLR Metadata section",
".complua" -> "LUA compiler",
".fasm" -> "Flat Assembler", ".flat" -> "Flat Assembler",
".idlsym" -> "IDL Attributes (registered SEH)",
".impdata" -> "Alternative import section",
".orpc" -> "Code section inside rpcrt4.dll",
".rodata" -> "Read-only data section",
".script" -> "Section containing script",
".stab" -> "GHC (Haskell)", ".stabstr" -> "GHC (Haskell)",
".sxdata" -> "Registered Exception Handlers section",
".xdata" -> "Exception information section",
"DGROUP" -> "Legacy data group section",
"BSS" -> "Uninitialized Data section (Borland)",
"CODE" -> "Code section (Borland)",
"DATA" -> "Data section (Borland)",
"INIT" -> "INIT section of drivers",
"IPPCode" -> "OpenCV", "IPPDATA" -> "OpenCV",
"_NVTEXT3" -> "NVidia",
"PAGE" -> "PAGE section of drivers",
"t.Policy" -> "Trustlet section with metadata for secure kernel policy",
"TulipLog" -> "Hewlet-Packard test/verification tools",
//Other
"BitArts" -> "Crunch 2.0 Packer",
".boom" -> "The Boomerang List Builder ((config+exe xored with a single byte key 0x77))",
".ccg" -> "CCG Packer (Chinese)",
".charmve" -> "Added by the PIN tool",
"DAStub" -> "DAStub Dragon Armor protector",
// Enigma Virtual Box
".enigma1" -> "Enigma Virtual Box protector",
".enigma2" -> "Enigma Virtual Box protector",
//Other
".ecode" -> "Easy Programming Language",
"!EPack" -> "EPack packer",
".gentee" -> "Gentee installer",
".imrsv" -> "Windows desktop bands application",
"kkrunchy" -> "kkrunchy packer",
"lz32.dll" -> "Crinkler",
".mackt" -> "ImpRec-created section, this file was patched/cracked",
".MaskPE" -> "MaskPE Packer",
"MEW" -> "MEW packer",
//Firseria
".mnbvcx1" -> "Firseria PUP downloader", ".mnbvcx2" -> "Firseria PUP downloader",
//MPRESS
".MPRESS1" -> "MPRESS Packer", ".MPRESS2" -> "MPRESS Packer",
//Neolite
".neolite" -> "Neolite Packer", ".neolit" -> "Neolite Packer",
//NSIS
".ndata" -> "Nullsoft Installer",
//NS Pack
".nsp0" -> "NsPack packer",".nsp1" -> "NsPack packer",".nsp2" -> "NsPack packer",
"nsp0" -> "NsPack packer","nsp0" -> "NsPack packer","nsp0" -> "NsPack packer",
//Other
".packed" -> "RLPack Packer", //first section only
//PEBundle
"pebundle" -> "PEBundle Packer", "PEBundle" -> "PEBundle Packer",
//PECompact
"PEC2TO" -> "PECompact packer","PEC2" -> "PECompact packer",
"pec1" -> "PECompact packer","pec2" -> "PECompact packer",
"pec3" -> "PECompact packer","pec4" -> "PECompact packer",
"pec5" -> "PECompact packer","pec6" -> "PECompact packer",
"pec" -> "PECompact packer",
"PEC2MO" -> "PECompact packer", "PEC2TO" -> "PECompact packer",
"PECompact2" -> "PECompact packer",
//Other
"PELOCKnt" -> "PELock Protector",
"PEPACK!!" -> "Pepack",
".perplex" -> "Perplex PE-Protector",
"PESHiELD" -> "PEShield Packer",
".petite" -> "Petite Packer",
".pinclie" -> "Added by the PIN tool",
"ProCrypt" -> "ProCrypt Packer",
".RLPack" -> "RLPack Packer", //second section
".rmnet" -> "Ramnit virus marker",
//RPCrypt
"RCryptor" -> "RPCrypt Packer", ".RPCrypt" -> "RPCrypt Packer",
//Other
".seau" -> "SeauSFX Packer",
".sforce3" -> "StarForce Protection",
// Shrinker
".shrink1" -> "Shrinker", ".shrink2" -> "Shrinker",
".shrink3" -> "Shrinker",
//Other
".spack" -> "Simple Pack (by bagie)",
".svkp" -> "SVKP packer",
".taz" -> "PESpin",
//Themida
".Themida" -> "Themida","Themida" -> "Themida",
//TSULoader
".tsuarch" -> "TSULoader", ".tsustub" -> "TSULoader",
//Upack
".Upack" -> "Upack packer",
".ByDwing" -> "Upack packer",
//UPX
"UPX0" -> "UPX packer", "UPX1" -> "UPX packer", "UPX2" -> "UPX packer",
"UPX!" -> "UPX packer", ".UPX0" -> "UPX packer", ".UPX1" -> "UPX packer",
".UPX2" -> "UPX packer",
//VMProtect packer
".vmp0" -> "VMProtect packer",".vmp1" -> "VMProtect packer",".vmp2" -> "VMProtect packer",
//Other
"VProtect" -> "Vprotect Packer",
".winapi" -> "API Override tool",
"__wibu00" -> "Wibu CodeMeter", "__wibu01" -> "Wibu CodeMeter",
"WinLicen" -> "WinLicense (Themida) Protector",
"_winzip_" -> "WinZip Self-Extractor",
//WWPACK
".WWPACK" -> "WWPACK Packer", ".WWP32" -> "WWPACK Packer",
//y0da
".yP" -> "Y0da Protector", ".y0da" -> "Y0da Protector"
)
type SectionRange = (Long, Long)
abstract override def scanReport(): String =
"Applied Section Table Scanning" + NL + super.scanReport
abstract override def scan(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
anomalyList ++= checkVirtualSecTable
anomalyList ++= checkFileAlignmentConstrains
anomalyList ++= checkZeroValues
anomalyList ++= checkDeprecated
anomalyList ++= checkReserved
anomalyList ++= checkAscendingVA
anomalyList ++= checkExtendedReloc
anomalyList ++= checkTooLargeSizes
anomalyList ++= checkSectionNames
anomalyList ++= checkOverlappingOrShuffledSections
anomalyList ++= checkSectionCharacteristics
anomalyList ++= sectionTableInOverlay
super.scan ::: anomalyList.toList
}
private def checkVirtualSecTable(): List[Anomaly] = {
val table = data.getSectionTable
if (table.getOffset > data.getFile.length()) {
val description = s"Section Table (offset: ${table.getOffset}) is in virtual space"
val locations = List(new PhysicalLocation(table.getOffset, table.getSize))
List(StructureAnomaly(PEStructureKey.SECTION_TABLE, description,
SEC_TABLE_IN_OVERLAY, locations))
} else Nil
}
private def checkSectionCharacteristics(): List[Anomaly] = {
def isLastSection(header: SectionHeader): Boolean =
header.getNumber == data.getSectionTable.getNumberOfSections
val charSecNameMap = Map(
".bss" -> List(IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".cormeta" -> List(IMAGE_SCN_LNK_INFO),
".data" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".debug" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_DISCARDABLE),
".drective" -> List(IMAGE_SCN_LNK_INFO),
".edata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ),
".idata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE),
".idlsym" -> List(IMAGE_SCN_LNK_INFO),
".pdata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ),
".rdata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ),
".reloc" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_DISCARDABLE),
".rsrc" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ),
".sbss" -> List(IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".sdata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".srdata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ),
".sxdata" -> List(IMAGE_SCN_LNK_INFO),
".text" -> List(IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ),
".tls" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".tls$" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".vsdata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ,
IMAGE_SCN_MEM_WRITE),
".xdata" -> List(IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ))
val anomalyList = ListBuffer[Anomaly]()
val headers = data.getSectionTable.getSectionHeaders.asScala
val loader = new SectionLoader(data)
for (header <- headers) {
val sectionName = header.getName
val characs = header.getCharacteristics.asScala.toList
val entry = header.getField(SectionHeaderKey.CHARACTERISTICS)
if (characs.contains(SectionCharacteristic.IMAGE_SCN_MEM_WRITE) && characs.contains(SectionCharacteristic.IMAGE_SCN_MEM_EXECUTE)) {
val description = s"Section ${header.getNumber} with name $sectionName has write and execute characteristics."
anomalyList += FieldAnomaly(entry, description, WRITE_AND_EXECUTE_SECTION)
}
if (characs.size == 1 && characs.contains(SectionCharacteristic.IMAGE_SCN_MEM_WRITE)) {
val description = s"Section ${header.getNumber} with name $sectionName has write as only characteristic"
anomalyList += FieldAnomaly(entry, description, WRITEABLE_ONLY_SECTION)
}
if (characs.isEmpty) {
val description = s"Section ${header.getNumber} with name $sectionName has no characteristics"
anomalyList += FieldAnomaly(entry, description, CHARACTERLESS_SECTION)
}
if (loader.containsEntryPoint(header)) {
if (characs.contains(IMAGE_SCN_MEM_WRITE)) {
val description = s"Entry point is in writeable section ${header.getNumber} with name $sectionName"
anomalyList += FieldAnomaly(entry, description, EP_IN_WRITEABLE_SEC)
}
if (isLastSection(header)) {
val description = s"Entry point is in the last section ${header.getNumber} with name $sectionName"
anomalyList += FieldAnomaly(entry, description, EP_IN_LAST_SECTION)
}
}
if (charSecNameMap.contains(header.getName)) {
val mustHaveCharac = charSecNameMap(header.getName)
//Note: Almost all files don't have IMAGE_SCN_MEM_READ activated, so
//this is not an indicator for anything
val notContainedCharac = mustHaveCharac.filterNot(c => c == IMAGE_SCN_MEM_READ || characs.contains(c))
val superfluousCharac = characs.filterNot(mustHaveCharac.contains(_))
if (notContainedCharac.nonEmpty) {
val description = s"Section Header ${header.getNumber} with name $sectionName should (but doesn't) contain the characteristics: ${notContainedCharac.map(_.shortName).mkString(", ")}"
anomalyList += FieldAnomaly(entry, description, UNUSUAL_SEC_CHARACTERISTICS)
}
if (superfluousCharac.nonEmpty) {
val description = s"Section Header ${header.getNumber} with name $sectionName has unusual characteristics, that shouldn't be there: ${superfluousCharac.map(_.shortName).mkString(", ")}"
anomalyList += FieldAnomaly(entry, description, UNUSUAL_SEC_CHARACTERISTICS)
}
}
}
anomalyList.toList
}
private def sectionTableInOverlay(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val overlay = new Overlay(data)
if (sectionTable.getOffset >= overlay.getOffset &&
sectionTable.getOffset < data.getFile.length) {
val description = s"Section Table (offset: ${sectionTable.getOffset}) moved to Overlay"
val locations = List(new PhysicalLocation(sectionTable.getOffset, sectionTable.getSize))
anomalyList += StructureAnomaly(PEStructureKey.SECTION_TABLE, description,
SEC_TABLE_IN_OVERLAY, locations)
}
anomalyList.toList
}
private def physicalSectionRange(section: SectionHeader): SectionRange = {
val loader = new SectionLoader(data)
val lowAlign = data.getOptionalHeader.isLowAlignmentMode
val start = section.getAlignedPointerToRaw(lowAlign)
val end = loader.getReadSize(section) + start
(start, end)
}
private def virtualSectionRange(section: SectionHeader): SectionRange = {
val lowAlign = data.getOptionalHeader.isLowAlignmentMode
val start = section.getAlignedVirtualAddress(lowAlign)
val end = section.getAlignedVirtualSize(lowAlign) + start
(start, end)
}
/**
* Checks the section headers for control symbols in the section names and
* unusual names.
*
* @return anomaly list
*/
private def checkSectionNames(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
val usualNames = List(".bss", ".cormeta", ".data", ".debug", ".drective",
".edata", ".idata", ".rsrc", ".idlsym", ".pdata", ".rdata", ".reloc",
".sbss", ".sdata", ".srdata", ".sxdata", ".text", ".tls", ".vsdata",
".xdata", ".debug$F", ".debug$P", ".debug$S", ".debug$T", ".tls$")
for (section <- sections) {
val sectionName = section.getName
if (sectionName != section.getUnfilteredName) {
val description = s"Section Header ${section.getNumber} has control symbols in name: ${filteredSymbols(section.getUnfilteredName).mkString(", ")}"
anomalyList += SectionNameAnomaly(section, description, CTRL_SYMB_IN_SEC_NAME)
}
val packer = packerNames.get(sectionName)
if(packer.isDefined) {
val description = s"Section name $sectionName is typical for ${packer.get}"
anomalyList += SectionNameAnomaly(section, description, UNUSUAL_SEC_NAME)
}
else if(sectionName == "") {
val description = s"Section name of section ${section.getNumber} is empty."
anomalyList += SectionNameAnomaly(section, description, EMPTY_SEC_NAME)
}
else if (!usualNames.contains(section.getName)) {
val description = s"Section name is unusual: $sectionName"
anomalyList += SectionNameAnomaly(section, description, UNUSUAL_SEC_NAME)
}
}
anomalyList.toList
}
/**
* Filters control code and extended code from the given string. Returns a
* list of the filtered symbols.
*
* @param str the string to filter the symbols from
* @return list of filtered symbols, each symbol represented as unicode code string
*/
private def filteredSymbols(str: String): List[String] = {
def getUnicodeValue(c: Char): String = "\\u" + Integer.toHexString(c | 0x10000).substring(1)
val controlCode: Char => Boolean = (c: Char) => c <= 32 || c == 127
val extendedCode: Char => Boolean = (c: Char) => c <= 32 || c > 127
str.map(c => if (controlCode(c) || extendedCode(c)) { getUnicodeValue(c) } else c.toString).toList
}
/**
* Checks if SizeOfRawData is larger than the file size permits.
*
* @return anomaly list
*/
//TODO tell if VA is used instead of rawdatasize.
private def checkTooLargeSizes(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) {
val sectionName = section.getName
val entry = section.getField(SectionHeaderKey.SIZE_OF_RAW_DATA)
val value = entry.getValue
val alignedPointerToRaw = section.getAlignedPointerToRaw(data.getOptionalHeader.isLowAlignmentMode)
if (value + alignedPointerToRaw > data.getFile.length()) {
val description = s"Section Header ${section.getNumber} with name $sectionName: ${entry.getKey} + aligned pointer to raw data is larger (${value + alignedPointerToRaw}) than permitted by file length (${data.getFile.length()})"
anomalyList += FieldAnomaly(entry, description, TOO_LARGE_SIZE_OF_RAW)
}
}
anomalyList.toList
}
/**
* Checks extended reloc constraints.
*
* @return anomaly list
*/
private def checkExtendedReloc(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) {
if (section.getCharacteristics.contains(IMAGE_SCN_LNK_NRELOC_OVFL)) {
val sectionName = section.getName
val entry = section.getField(SectionHeaderKey.NUMBER_OF_RELOCATIONS)
val value = entry.getValue
if (value != 0xffff) {
val description = s"Section Header ${section.getNumber} with name $sectionName: has IMAGE_SCN_LNK_NRELOC_OVFL characteristic --> ${entry.getKey} must be 0xffff, but is " + value
anomalyList += FieldAnomaly(entry, description, EXTENDED_RELOC_VIOLATIONS)
}
}
}
anomalyList.toList
}
/**
* Checks all sections whether they are physically overlapping, shuffled, or a
* duplicate of each other.
*
* @return anomaly list
*/
private def checkOverlappingOrShuffledSections(): List[Anomaly] = {
// Checks if the ranges overlap. Sections with zero size cannot overlap
def overlaps(t1: SectionRange, t2: SectionRange): Boolean =
!(((t1._1 < t2._1) && (t1._2 <= t2._1)) || ((t2._1 < t1._1) && (t2._2 <= t1._1)) || zeroSize(t1) || zeroSize(t2))
def zeroSize(range: SectionRange): Boolean = range._1 == range._2
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) {
val sectionName = section.getName
val range1 = physicalSectionRange(section)
val vrange1 = virtualSectionRange(section)
for (i <- section.getNumber + 1 to sections.length) { //correct?
val sec = sectionTable.getSectionHeader(i)
val range2 = physicalSectionRange(sec)
val vrange2 = virtualSectionRange(sec)
val locations = List(range1, range2).map(r => new PhysicalLocation(r._1, r._2 - r._1))
// ignore zero sized sections for shuffle analysis, these get their own anomaly
if (range1._1 > range2._1 && !zeroSize(range1) && !zeroSize(range2)) {
val description = s"Physically shuffled sections: section ${section.getNumber} has range 0x${range1._1.toHexString}--0x${range1._2.toHexString}, section ${sec.getNumber} has range 0x${range2._1.toHexString}--0x${range2._2.toHexString}"
anomalyList += StructureAnomaly(PEStructureKey.SECTION, description, PHYSICALLY_SHUFFLED_SEC, locations)
}
if (range1 == range2 && !zeroSize(range1)) {
val description = s"Section ${section.getNumber} with name $sectionName (range: 0x${range1._1.toHexString}--0x${range1._2.toHexString}) has same physical location as section ${sec.getNumber} with name ${sec.getName}"
anomalyList += StructureAnomaly(PEStructureKey.SECTION, description, PHYSICALLY_DUPLICATED_SEC, locations)
} else if (overlaps(range2, range1)) {
val description = s"Section ${section.getNumber} with name $sectionName (range: 0x${range1._1.toHexString}--0x${range1._2.toHexString}) physically overlaps with section ${sec.getName} with number ${sec.getNumber} (range: 0x${range2._1.toHexString}--0x${range2._2.toHexString})"
anomalyList += StructureAnomaly(PEStructureKey.SECTION, description, PHYSICALLY_OVERLAPPING_SEC, locations)
}
if (vrange1 == vrange2 && !zeroSize(range1)) {
val description = s"Section ${section.getNumber} with name $sectionName (range: 0x${vrange1._1.toHexString}--0x${vrange1._2.toHexString}) has same virtual location as section ${sec.getName} with number ${sec.getNumber} (range: 0x${vrange2._1.toHexString}--0x${vrange2._2.toHexString})"
anomalyList += StructureAnomaly(PEStructureKey.SECTION, description, VIRTUALLY_DUPLICATED_SEC, locations)
} else if (overlaps(vrange1, vrange2)) {
val description = s"Section ${section.getNumber} with name $sectionName (range: 0x${vrange1._1.toHexString}--0x${vrange1._2.toHexString}) virtually overlaps with section ${sec.getName} with number ${sec.getNumber} (range: 0x${vrange2._1.toHexString}--0x${vrange2._2.toHexString})"
anomalyList += StructureAnomaly(PEStructureKey.SECTION, description, VIRTUALLY_OVERLAPPING_SEC, locations)
}
}
}
anomalyList.toList
}
/**
* Checks all section for ascending virtual addresses
*
* @return anomaly list
*/
private def checkAscendingVA(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
val prevVA = -1
for (section <- sections) {
val sectionName = section.getName
val entry = section.getField(SectionHeaderKey.VIRTUAL_ADDRESS)
val sectionVA = entry.getValue
if (sectionVA <= prevVA) {
val range = physicalSectionRange(section)
val locations = List(new PhysicalLocation(range._1, range._2 - range._1))
val description = s"Section Header ${section.getNumber} with name $sectionName: VIRTUAL_ADDRESS ($sectionVA) should be greater than of the previous entry ($prevVA)"
anomalyList += StructureAnomaly(PEStructureKey.SECTION, description, NOT_ASCENDING_SEC_VA, locations)
}
}
anomalyList.toList
}
/**
* Checks for reserved fields in the characteristics of the sections.
*
* @return anomaly list
*/
private def checkReserved(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) {
val characteristics = section.getCharacteristics.asScala
val entry = section.getField(SectionHeaderKey.CHARACTERISTICS)
val sectionName = section.getName
characteristics.foreach(ch =>
if (ch.isReserved) {
val description = s"Section Header ${section.getNumber} with name $sectionName: Reserved characteristic used: ${ch.toString}"
anomalyList += FieldAnomaly(entry, description, RESERVED_SEC_CHARACTERISTICS)
})
}
anomalyList.toList
}
/**
* Checks for the use of deprecated fields in the section headers.
*
* @return anomaly list
*/
private def checkDeprecated(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) {
val ptrLineNrEntry = section.getField(SectionHeaderKey.POINTER_TO_LINE_NUMBERS)
val lineNrEntry = section.getField(SectionHeaderKey.NUMBER_OF_LINE_NUMBERS)
val sectionName = section.getName
val characteristics = section.getCharacteristics.asScala
for (ch <- characteristics if ch.isDeprecated) {
val entry = section.getField(SectionHeaderKey.CHARACTERISTICS)
val description = s"Section Header ${section.getNumber} with name $sectionName: Characteristic ${ch.toString} is deprecated"
anomalyList += FieldAnomaly(entry, description, DEPRECATED_SEC_CHARACTERISTICS)
}
for (entry <- List(ptrLineNrEntry, lineNrEntry) if entry.getValue != 0) {
val description = s"Section Header ${section.getNumber} with name $sectionName: ${entry.getKey} is deprecated, but has value " + entry.getValue
val subtype = if (entry.getKey == SectionHeaderKey.POINTER_TO_LINE_NUMBERS)
DEPRECATED_PTR_OF_LINE_NR
else DEPRECATED_NR_OF_LINE_NR
anomalyList += FieldAnomaly(entry, description, subtype)
}
}
anomalyList.toList
}
/**
* Checks each section for values that should be set, but are 0 nevertheless.
*
* @return anomaly list
*/
private def checkZeroValues(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) yield {
val sectionName = section.getName
checkReloc(anomalyList, section, sectionName)
checkObjectOnlyCharacteristics(anomalyList, section, sectionName)
checkUninitializedDataConstraints(anomalyList, section, sectionName)
checkZeroSizes(anomalyList, section, sectionName)
}
anomalyList.toList
}
/**
* Checks if SizeOfRawData or VirtualSize is 0 and, if true, adds the anomaly
* to the given list.
*
* @param anomalyList the list to add the anomalies to
* @param section the section to check
* @param sectionName the name to use for the anomaly description
*/
private def checkZeroSizes(anomalyList: ListBuffer[Anomaly], section: SectionHeader, sectionName: String): Unit = {
val sizeOfRaw = section.getField(SectionHeaderKey.SIZE_OF_RAW_DATA)
val virtSize = section.getField(SectionHeaderKey.VIRTUAL_SIZE)
for (entry <- List(sizeOfRaw, virtSize) if entry.getValue == 0) {
val description = s"Section Header ${section.getNumber} with name $sectionName: ${entry.getKey} is ${entry.getValue}"
val subtype = if (entry.getKey == SectionHeaderKey.VIRTUAL_SIZE)
ZERO_VIRTUAL_SIZE
else ZERO_SIZE_OF_RAW_DATA
anomalyList += FieldAnomaly(entry, description, subtype)
}
}
/**
* Checks the constraints for the uninitialized data field in the given section.
* Adds the anomaly to the given list if constraints are violated.
*
* @param anomalyList the list to add the anomalies to
* @param section the section to check
* @param sectionName the name to use for the anomaly description
*/
private def checkUninitializedDataConstraints(anomalyList: ListBuffer[Anomaly], section: SectionHeader, sectionName: String): Unit = {
def containsOnlyUnitializedData(): Boolean =
section.getCharacteristics.contains(IMAGE_SCN_CNT_UNINITIALIZED_DATA) &&
!section.getCharacteristics.contains(IMAGE_SCN_CNT_INITIALIZED_DATA)
if (containsOnlyUnitializedData()) {
val sizeEntry = section.getField(SectionHeaderKey.SIZE_OF_RAW_DATA)
val pointerEntry = section.getField(SectionHeaderKey.POINTER_TO_RAW_DATA)
for (entry <- List(sizeEntry, pointerEntry) if entry.getValue != 0) {
val value = entry.getValue
val description = s"Section Header ${section.getNumber} with name $sectionName: ${entry.getKey.toString} must be 0 for sections with only uninitialized data, but is: $value"
anomalyList += FieldAnomaly(entry, description, UNINIT_DATA_CONSTRAINTS_VIOLATION)
}
}
}
/**
* Checks SizeOfRawData and PointerOfRawData of every section for file
* alignment constraints.
*
* @return anomaly list
*/
private def checkFileAlignmentConstrains(): List[Anomaly] = {
val anomalyList = ListBuffer[Anomaly]()
val fileAlignment = data.getOptionalHeader.get(WindowsEntryKey.FILE_ALIGNMENT)
val sectionTable = data.getSectionTable
val sections = sectionTable.getSectionHeaders.asScala
for (section <- sections) {
val sizeEntry = section.getField(SectionHeaderKey.SIZE_OF_RAW_DATA)
val pointerEntry = section.getField(SectionHeaderKey.POINTER_TO_RAW_DATA)
val sectionName = section.getName
for (
entry <- List(sizeEntry, pointerEntry) if entry != null &&
fileAlignment != 0 && entry.getValue % fileAlignment != 0
) {
val description = s"Section Header ${section.getNumber} with name $sectionName: ${entry.getKey} (${entry.getValue}) must be a multiple of File Alignment ($fileAlignment)"
val subtype = if (entry.getKey == SectionHeaderKey.SIZE_OF_RAW_DATA)
NOT_FILEALIGNED_SIZE_OF_RAW
else NOT_FILEALIGNED_PTR_TO_RAW
anomalyList += FieldAnomaly(entry, description, subtype)
}
}
anomalyList.toList
}
/**
* Checks characteristics of the given section. Adds anomaly to the list if
* a section has constraints only an object file is allowed to have.
*
* @param anomalyList the list to add the anomalies to
* @param section the section to check
* @param sectionName the name to use for the anomaly description
*/
private def checkObjectOnlyCharacteristics(anomalyList: ListBuffer[Anomaly], section: SectionHeader, sectionName: String): Unit = {
val alignmentCharacteristics = SectionCharacteristic.values.filter(k => k.toString.startsWith("IMAGE_SCN_ALIGN")).toList
val objectOnly = List(IMAGE_SCN_TYPE_NO_PAD, IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE, IMAGE_SCN_LNK_COMDAT) ::: alignmentCharacteristics
for (characteristic <- section.getCharacteristics.asScala if objectOnly.contains(characteristic)) {
val description = s"Section Header ${section.getNumber} with name $sectionName: $characteristic characteristic is only valid for object files"
val chEntry = section.getField(SectionHeaderKey.CHARACTERISTICS)
anomalyList += FieldAnomaly(chEntry, description, OBJECT_ONLY_SEC_CHARACTERISTICS)
}
}
/**
* Checks PointerTo- and NumberOfRelocations for values set. Both should be zero.
*
* @param anomalyList the list to add the anomalies to
* @param section the section to check
* @param sectionName the name to use for the anomaly description
*/
private def checkReloc(anomalyList: ListBuffer[Anomaly], section: SectionHeader, sectionName: String): Unit = {
val relocEntry = section.getField(SectionHeaderKey.POINTER_TO_RELOCATIONS)
val nrRelocEntry = section.getField(SectionHeaderKey.NUMBER_OF_RELOCATIONS)
for (entry <- List(relocEntry, nrRelocEntry) if entry.getValue != 0) {
val description = s"Section Header ${section.getNumber} with name $sectionName: ${entry.getKey} should be 0 for images, but has value " + entry.getValue
val subtype = if (entry.getKey == SectionHeaderKey.NUMBER_OF_RELOCATIONS)
DEPRECATED_NR_OF_RELOC
else DEPRECATED_PTR_TO_RELOC
anomalyList += FieldAnomaly(entry, description, subtype)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy