Package org.jakstab.loader.pe

Source Code of org.jakstab.loader.pe.ObjectFile

/*
* ObjectFile.java - This file is part of the Jakstab project.
* Copyright 2007-2012 Johannes Kinder <jk@jakstab.org>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab.loader.pe;

import java.io.*;
import java.util.*;

import org.jakstab.asm.AbsoluteAddress;
import org.jakstab.asm.DummySymbolFinder;
import org.jakstab.asm.SymbolFinder;
import org.jakstab.loader.BinaryParseException;
import org.jakstab.loader.ExportedSymbol;
import org.jakstab.loader.UnresolvedSymbol;
import org.jakstab.loader.UnresolvedSymbol.AddressingType;
import org.jakstab.rtl.expressions.RTLMemoryLocation;
import org.jakstab.rtl.expressions.RTLNumber;
import org.jakstab.ssl.Architecture;
import org.jakstab.util.BinaryFileInputBuffer;
import org.jakstab.util.FastSet;
import org.jakstab.util.Logger;

/**
* @author Johannes Kinder
*/
public class ObjectFile extends AbstractCOFFModule {

  @SuppressWarnings("unused")
  private static final Logger logger = Logger.getLogger(ObjectFile.class);
  private static final int SYMBOL_LENGTH = 18;
  private static final String MAIN_FUNCTION = "_main";
 
  private final long imageBase;
  private int codeSection;
  private final SymbolEntry[] symbolTable;
  private final RelocationEntry[] relocations;
  private long addressOfMain;
  private final AbsoluteAddress entryPoint;
  private Set<UnresolvedSymbol> unresolvedSymbols;
 
 
  public ObjectFile(File peFile, Architecture arch) throws java.io.IOException, BinaryParseException {
    this(new FileInputStream(peFile), arch);
  }

 
  public ObjectFile(InputStream inStream, Architecture arch) throws java.io.IOException, BinaryParseException {

    imageBase = 0x1000;
    inBuf = new BinaryFileInputBuffer(inStream);

    coff_header = new COFF_Header(inBuf);
    logger.debug("Reading " + coff_header.getNumberOfSections() + " sections.");

    ///// Parse Section Headers and sections /////////////////////////
    section_headers = new SectionHeader[coff_header.getNumberOfSections()];
    codeSection = -1;
   
    // Allocate section RVAs in sequence
    long currentRVA = 0;
   
    // Section indices are 1-based in the COFF specification - here we have
    // a 0-based array!
    for (int i=0;i<coff_header.getNumberOfSections();i++){
      SectionHeader sectionHeader = new SectionHeader(inBuf);
      section_headers[i] = sectionHeader;
      if (sectionHeader.getName().equals(".text") && !sectionHeader.hasComdat())
        codeSection = i;
      logger.debug("  Read section " + (i+1) + ": " + sectionHeader.getName());
     
      // Allocate virtual address for section
      if (!sectionHeader.isRemovedByLinker() && !sectionHeader.isDiscardable()) {
        sectionHeader.VirtualAddress = currentRVA;
        sectionHeader.VirtualSize = sectionHeader.SizeOfRawData;
        currentRVA += sectionHeader.VirtualSize;
        logger.debug("Allocated section " + sectionHeader.getName() + " at RVA 0x" +
            Integer.toHexString((int)sectionHeader.VirtualAddress));
      }
    }
    /////////////////////////////////////////////////////////////////
    if (codeSection < 0) {
      throw new BinaryParseException("No code section found in object file!");
    }
   
    /////////////////////////////////////////////////////////////////
    // Read string table for symbols
    Map<Integer,String> stringTable = new HashMap<Integer, String>();
    if (coff_header.getNumberOfSymbols() > 0) {
      long stringTableOffset = coff_header.getPointerToSymbolTable() + coff_header.getNumberOfSymbols() * SYMBOL_LENGTH;
      inBuf.seek(stringTableOffset);
      int stringTableSize = inBuf.readINT32();
      while (inBuf.getCurrent() < stringTableOffset + stringTableSize) {
        int stringOffset = (int)(inBuf.getCurrent() - stringTableOffset);
        String string = inBuf.readASCII();
        stringTable.put(stringOffset, string);
      }
    }
   
    /////////////////////////////////////////////////////////////////
    // Parse symbols
    logger.debug("Parsing symbol table");
    addressOfMain = -1;
    symbolTable = new SymbolEntry[coff_header.getNumberOfSymbols()];
    if (coff_header.getNumberOfSymbols() > 0) {
      inBuf.seek(coff_header.getPointerToSymbolTable());
      for (int i = 0; i < coff_header.getNumberOfSymbols(); i++) {
        symbolTable[i] = new SymbolEntry(inBuf, stringTable);
       
        if (symbolTable[i].getName().equals(MAIN_FUNCTION)) {
          // The value field contains the offset from the section start
          addressOfMain = symbolTable[i].getValue() +
          section_headers[symbolTable[i].getSectionNumber() - 1].VirtualAddress;
        }
        //logger.debug("  Symbol entry: " + symbolTable[i]);
      }
      if (addressOfMain < 0) logger.warn("No main function found in object file!");

    }
    entryPoint = new AbsoluteAddress(imageBase + addressOfMain);
    logger.debug("Main function at " + entryPoint);
   
    /////////////////////////////////////////////////////////////////
    // Parse and perform relocations
    SectionHeader csHead = getSectionHeader(codeSection);
    relocations = new RelocationEntry[csHead.NumberOfRelocations];
    unresolvedSymbols = new FastSet<UnresolvedSymbol>();
    if (csHead.NumberOfRelocations > 0) {
      logger.debug("Relocations: ");
      inBuf.seek(csHead.PointerToRelocations);
      for (int i = 0; i < csHead.NumberOfRelocations; i++) {
        relocations[i] = new RelocationEntry(inBuf);
       
        SymbolEntry symbolEntry = symbolTable[relocations[i].getTableIndex()];
       
        logger.debug("  RVA 0x" + Integer.toHexString((int)(relocations[i].getRVA() + csHead.VirtualAddress)) + ": make " +
            (relocations[i].isDirectVirtualAddress() ? "direct" : "relative") +
            " to " + symbolEntry);
       
        // Mark relocation entries to external symbols as unresolved, so
        // the resolution mechanism in the Program class takes care of them
        UnresolvedSymbol.AddressingType mode;
        if (relocations[i].isDirectVirtualAddress()) mode = AddressingType.ABSOLUTE;
        else if (relocations[i].isRelativeDisplacement()) mode = AddressingType.PC_RELATIVE;
        else throw new RuntimeException("Unknown addressing type for unresolved symbol " + relocations[i].toString());

        String name = symbolEntry.getName();
        name = stripSymbolName(name);

        UnresolvedSymbol unresolvedSymbol = new UnresolvedSymbol(
            this,
            name,
            (int)(getFilePointerFromRVA(relocations[i].getRVA() + csHead.VirtualAddress)),
            mode
        );
        // If it's an external symbol, rely on the Program class to resolve it
        if (symbolEntry.isExternal()) {
          logger.debug("  -- Marking as external reference");
          unresolvedSymbols.add(unresolvedSymbol);
        } else {
          // Otherwise, perform relocation now
          logger.debug("  -- Relocating " + symbolEntry.getName());
          AbsoluteAddress relocatedAddress = new AbsoluteAddress(
              symbolEntry.getValue() + imageBase +
              section_headers[symbolEntry.getSectionNumber() - 1].VirtualAddress
              );
          logger.debug("  New address: " + relocatedAddress);
          unresolvedSymbol.resolve(relocatedAddress);
        }
      }
    }

    inBuf = null;

  }
 
  /*
   * @see org.jakstab.loader.ExecutableImage#getEntryPoint()
   */
  @Override
  public AbsoluteAddress getEntryPoint() {
    return entryPoint;
  }

  /*
   * @see org.jakstab.loader.ExecutableImage#isReadOnly(org.jakstab.asm.AbsoluteAddress)
   */
  @Override
  public boolean isReadOnly(AbsoluteAddress a) {
    return false;
    //throw new UnsupportedOperationException();
  }

  /*
   * @see org.jakstab.loader.ExecutableImage#readMemoryLocation(org.jakstab.rtl.expressions.RTLMemoryLocation)
   */
  @Override
  public RTLNumber readMemoryLocation(RTLMemoryLocation m) throws IOException {
    if (!(m.getAddress() instanceof RTLNumber)) return null;
   
    // AbsoluteAddress va = new AbsoluteAddress((RTLNumber)m.getAddress());

    // Do some symbol magic and possibly return something

    return super.readMemoryLocation(m);
  }


  @Override
  public SymbolFinder getSymbolFinder() {
    return new DummySymbolFinder();
  }


  @Override
  protected final long getBaseAddress() {
    return imageBase;
  }
 
  @Override
  protected final int getSectionNumberByRVA(long rva) {
    if (rva < 0) return -1;
    for (int i=0; i < getNumberOfSections(); i++) {
      SectionHeader sh = getSectionHeader(i);
      // Only look for sections that are actually loaded
      if (sh.isDiscardable() || sh.isRemovedByLinker())
        continue;
      if (sh.VirtualAddress <= rva &&
          (sh.VirtualAddress + sh.VirtualSize) > rva) {
        return i;
      }
    }
    return -1;
  }


  @Override
  public Set<UnresolvedSymbol> getUnresolvedSymbols() {
    return unresolvedSymbols;
  }
 
  @Override
  public Set<ExportedSymbol> getExportedSymbols() {
    Set<ExportedSymbol> exportedSymbols = new FastSet<ExportedSymbol>();
   
    for (int i = 0; i < coff_header.getNumberOfSymbols(); i++) {
      if (symbolTable[i].isExternal()  && symbolTable[i].getSectionNumber() > 0) {
        String name = symbolTable[i].getName();
        // section number is 1-based in symbol table
        int section = symbolTable[i].getSectionNumber() - 1;

        //logger.debug("Locating symbol " + name);
       
        name = stripSymbolName(name);
        long fp = getSectionHeader(section).PointerToRawData + symbolTable[i].getValue();
       
        AbsoluteAddress address = getVirtualAddress(fp);
        exportedSymbols.add(new ExportedSymbol(this, name, address));
        logger.debug("Exporting " + name + " at file offset " + fp + ", section offset " +
            symbolTable[i].getValue() + " in " + section_headers[section].getName() + ", which evaluates to VA " + address);

      }
      i += symbolTable[i].getNumberOfAuxSymbols();
    }   
   
    return exportedSymbols;
  }


  private String stripSymbolName(String symbolName) {
    String name = symbolName;
   
    // Strip calling convention prefix
    if (name.startsWith("@")) name = name.substring(1);
    // Strip a leading underscore generated by some compilers
    if (name.startsWith("_")) name = name.substring(1);
    // Strip trailing stack increment
    int lastAt = name.lastIndexOf('@');
    if (name.substring(lastAt + 1).matches("\\d+")) {
      name = name.substring(0, lastAt);
    }
    return name;
  }

}
TOP

Related Classes of org.jakstab.loader.pe.ObjectFile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.