dorkbox.peParser.PE Maven / Gradle / Ivy
/*
* Copyright 2012 dorkbox, llc
*
* 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 dorkbox.peParser;
import dorkbox.peParser.headers.resources.ResourceDataEntry;
import dorkbox.peParser.headers.resources.ResourceDirectoryEntry;
import dorkbox.peParser.headers.resources.ResourceDirectoryHeader;
import dorkbox.peParser.types.ByteDefinition;
import dorkbox.peParser.types.ImageDataDir;
import dorkbox.util.OS;
import dorkbox.peParser.headers.COFFFileHeader;
import dorkbox.peParser.headers.Header;
import dorkbox.peParser.headers.OptionalHeader;
import dorkbox.peParser.headers.SectionTable;
import dorkbox.peParser.headers.SectionTableEntry;
import dorkbox.peParser.misc.DirEntry;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedList;
public class PE {
// info from:
// http://evilzone.org/tutorials/(paper)-portable-executable-format-and-its-rsrc-section/
// http://www.skynet.ie/~caolan/pub/winresdump/winresdump/doc/pefile.html (older version of the doc...)
// http://www.csn.ul.ie/~caolan/pub/winresdump/winresdump/doc/pefile2.html
// http://msdn.microsoft.com/en-us/library/ms809762.aspx
/**
* Gets the version number.
*/
public static
String getVersion() {
return "2.6";
}
private static final int PE_OFFSET_LOCATION = 0x3c;
private static final byte[] PE_SIG = "PE\0\0".getBytes();
// TODO: should use an input stream to load header info, instead of the entire thing!
public ByteArray fileBytes = null;
private COFFFileHeader coffHeader;
public OptionalHeader optionalHeader;
private SectionTable sectionTable;
public PE(String fileName) {
File file = new File(fileName);
try {
fromInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void fromInputStream(File file) throws FileNotFoundException, IOException {
FileInputStream fileInputStream = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
byte[] buffer = new byte[4096];
int read = 0;
while ((read = fileInputStream.read(buffer)) > 0) {
baos.write(buffer, 0, read);
}
baos.flush();
fileInputStream.close();
this.fileBytes = new ByteArray(baos.toByteArray());
// initialize header info
if (isPE()) {
int offset = getPEOffset() + PE_SIG.length;
this.fileBytes.seek(offset);
this.coffHeader = new COFFFileHeader(this.fileBytes);
this.optionalHeader = new OptionalHeader(this.fileBytes);
int numberOfEntries = this.coffHeader.NumberOfSections.get().intValue();
this.sectionTable = new SectionTable(this.fileBytes, numberOfEntries);
// now the bytes are positioned at the start of the section table. ALl other info MUST be done relative to byte offsets/locations!
// fixup directory names -> table names (from spec!)
for (SectionTableEntry section : this.sectionTable.sections) {
long sectionAddress = section.VIRTUAL_ADDRESS.get().longValue();
long sectionSize = section.SIZE_OF_RAW_DATA.get().longValue();
for (ImageDataDir entry : this.optionalHeader.tables) {
long optionAddress = entry.get().longValue();
if (sectionAddress <= optionAddress &&
sectionAddress + sectionSize > optionAddress) {
entry.setSection(section);
break;
}
}
}
// fixup directories
for (ImageDataDir entry : this.optionalHeader.tables) {
if (entry.getType() == DirEntry.RESOURCE) {
// fixup resources
SectionTableEntry section = entry.getSection();
long delta = section.VIRTUAL_ADDRESS.get().longValue() - section.POINTER_TO_RAW_DATA.get().longValue();
long offsetInFile = entry.get().longValue() - delta;
if (offsetInFile > Integer.MAX_VALUE) {
throw new RuntimeException("Unable to set offset to more than 2gb!");
}
this.fileBytes.seek((int) offsetInFile);
this.fileBytes.mark(); // resource data is offset from the beginning of the header!
Header root = new ResourceDirectoryHeader(this.fileBytes, section, 0);
entry.data = root;
}
}
}
}
public String getInfo() {
if (isPE()) {
StringBuilder b = new StringBuilder();
b.append("PE signature offset: ").append(getPEOffset()).append(OS.LINE_SEPARATOR)
.append("PE signature correct: ").append("yes").append( OS.LINE_SEPARATOR)
.append(OS.LINE_SEPARATOR)
.append("----------------").append(OS.LINE_SEPARATOR)
.append("COFF header info").append(OS.LINE_SEPARATOR)
.append("----------------").append(OS.LINE_SEPARATOR);
for (ByteDefinition> bd : this.coffHeader.headers) {
bd.format(b);
}
b.append(OS.LINE_SEPARATOR);
b.append("--------------------").append(OS.LINE_SEPARATOR)
.append("Optional header info").append(OS.LINE_SEPARATOR)
.append("--------------------").append(OS.LINE_SEPARATOR);
for (ByteDefinition> bd : this.optionalHeader.headers) {
bd.format(b);
}
b.append(OS.LINE_SEPARATOR);
b.append(OS.LINE_SEPARATOR)
.append("-------------").append(OS.LINE_SEPARATOR)
.append("Section Table").append(OS.LINE_SEPARATOR)
.append("-------------").append(OS.LINE_SEPARATOR)
.append(OS.LINE_SEPARATOR);
for (SectionTableEntry section : this.sectionTable.sections) {
for (ByteDefinition> bd : section.headers) {
bd.format(b);
}
}
b.append(OS.LINE_SEPARATOR);
return b.toString();
} else {
return "PE signature not found. The given file is not a PE file." + OS.LINE_SEPARATOR;
}
}
private int getPEOffset() {
this.fileBytes.mark();
this.fileBytes.seek(PE_OFFSET_LOCATION);
int read = this.fileBytes.readUShort(2).intValue();
this.fileBytes.reset();
return read;
}
public boolean isPE() {
// always have to start from zero if we ask this.
int offset = getPEOffset();
int saved = this.fileBytes.position();
this.fileBytes.seek(0);
try {
for (int i = 0; i < PE_SIG.length; i++) {
if (this.fileBytes.readRaw(offset + i) != PE_SIG[i]) {
return false;
}
}
return true;
} finally {
this.fileBytes.seek(saved);
}
}
public ByteArrayInputStream getLargestResourceAsStream() {
for (ImageDataDir entry : this.optionalHeader.tables) {
if (entry.getType() == DirEntry.RESOURCE) {
ResourceDataEntry check = null;
LinkedList LIST = new LinkedList();
ResourceDirectoryHeader root = (ResourceDirectoryHeader) entry.data;
for (ResourceDirectoryEntry rootEntry : root.entries) {
LIST.add(rootEntry);
}
while(LIST.peek() != null) {
ResourceDataEntry valid = check(check, LIST, LIST.poll());
if (valid != null) {
check = valid;
}
}
// now return our resource, but it has to be wrapped in a new stream!
return new ByteArrayInputStream(check.getData(this.fileBytes));
}
}
return null;
}
private ResourceDataEntry check(ResourceDataEntry check, LinkedList LIST, ResourceDirectoryEntry entry) {
if (entry.isDirectory) {
for (ResourceDirectoryEntry rootEntry : entry.directory.entries) {
LIST.add(rootEntry);
}
} else {
// this is what we are looking for!
ResourceDataEntry dataEntry = entry.resourceDataEntry;
if (check == null || check.SIZE.get().longValue() < dataEntry.SIZE.get().longValue()) {
return dataEntry;
}
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy