All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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