/*
* AbstractCOFFModule.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.IOException;
import java.util.Iterator;
import org.jakstab.asm.AbsoluteAddress;
import org.jakstab.disasm.Disassembler;
import org.jakstab.disasm.x86.X86Disassembler;
import org.jakstab.loader.ExecutableImage;
import org.jakstab.rtl.expressions.ExpressionFactory;
import org.jakstab.rtl.expressions.RTLMemoryLocation;
import org.jakstab.rtl.expressions.RTLNumber;
import org.jakstab.util.BinaryFileInputBuffer;
import org.jakstab.util.Logger;
/**
* An abstract class that encapsulates the common features of MS COFF and
* PE files.
*
* @author Johannes Kinder
*/
public abstract class AbstractCOFFModule implements ExecutableImage {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(AbstractCOFFModule.class);
protected BinaryFileInputBuffer inBuf;
protected COFF_Header coff_header;
protected SectionHeader[] section_headers;
protected Disassembler disassembler;
@Override
public final long getFilePointer(AbsoluteAddress va) {
long fp = getFilePointerFromRVA(va.getValue() - getBaseAddress());
if (fp >= 0) return fp;
else return -1;
}
@Override
public final boolean isCodeArea(AbsoluteAddress va) {
int section = getSectionNumber(va);
if (section < 0) return false;
else return isCodeSection(section);
}
/**
* Returns the file pointer equivalent of the given RVA.
*/
protected final long getFilePointerFromRVA(long rva){
int sct = getSectionNumberByRVA(rva);
if (sct < 0) return -1;
if (rva - getSectionHeader(sct).VirtualAddress > getSectionHeader(sct).SizeOfRawData) return -1;
return (rva - getSectionHeader(sct).VirtualAddress) + getSectionHeader(sct).PointerToRawData;
}
/**
* Returns the RVA for a given file pointer.
*/
protected final long getRVAFromFilePointer(long filePointer) {
int sct = getSectionNumber(filePointer);
if (sct < 0) return -1;
return ((filePointer - getSectionHeader(sct).PointerToRawData) + getSectionHeader(sct).VirtualAddress);
}
/**
* Returns the number of the section the given virtual address is in.
*
* @param va the virtual address
* @return the section number
*/
protected final int getSectionNumber(AbsoluteAddress va) {
return getSectionNumberByRVA(va.getValue() - getBaseAddress());
}
/**
* Returns the number of the section a given file pointer lies in.
*
* @param fp the file pointer
* @return the section number
*/
protected final int getSectionNumber(long fp) {
for (int i=0; i < getNumberOfSections(); i++)
if (getSectionHeader(i).PointerToRawData <= fp &&
(getSectionHeader(i).PointerToRawData + getSectionHeader(i).SizeOfRawData) > fp)
return i;
return -1;
}
/**
* Returns the number of the section a given RVA lies in.
*
* @param rva the Relative Virtual Address
* @return the section number
*/
protected abstract int getSectionNumberByRVA(long rva);
/**
* Returns the virtual address that corresponds to a given file pointer
*
* @param fp the file pointer
* @return the virtual address
*/
public final AbsoluteAddress getVirtualAddress(long fp) {
long rva = getRVAFromFilePointer(fp);
if (rva >= 0) return new AbsoluteAddress(rva + getBaseAddress());
else return null;
}
protected final int getNumberOfSections() {
return section_headers.length;
}
public AbsoluteAddress getMaxAddress() {
long highAddress = Long.MIN_VALUE;
for (int i=0; i < getNumberOfSections(); i++) {
highAddress = Math.max(getSectionHeader(i).VirtualAddress +
getSectionHeader(i).SizeOfRawData, highAddress);
}
highAddress += getBaseAddress();
return new AbsoluteAddress(highAddress);
}
public AbsoluteAddress getMinAddress() {
long lowAddress = Long.MAX_VALUE;
for (int i=0; i < getNumberOfSections(); i++) {
lowAddress = Math.min(getSectionHeader(i).VirtualAddress, lowAddress);
}
lowAddress += getBaseAddress();
return new AbsoluteAddress(lowAddress);
}
protected final SectionHeader getSectionHeader(int index) {
return section_headers[index];
}
protected final boolean isCodeSection(int section) {
return getSectionHeader(section).isCodeSection();
}
protected abstract long getBaseAddress();
@Override
public RTLNumber readMemoryLocation(RTLMemoryLocation m) throws IOException {
if (!(m.getAddress() instanceof RTLNumber)) return null;
AbsoluteAddress va = new AbsoluteAddress((RTLNumber)m.getAddress());
long fp = getFilePointer(va);
if (getSectionNumber(fp) >= 0) {
assert m.getBitWidth() % 8 == 0 : "Non-byte-aligned memory reference!";
long val = 0;
int bytes = m.getBitWidth()/8;
// OR together the least significant bytes
inBuf.seek(fp);
for (int i=0; i<bytes - 1; i++) {
val = val | ((long)inBuf.readBYTE()) << (i*8);
}
// do not mask the MSB with 0xFF, so we get sign extension for free
val = val | (((long)inBuf.readINT8()) << (bytes - 1) * 8);
//logger.debug("Read constant value " + val + " from address " + m + " (file offset: " + Long.toHexString(fp) + ") in image.");
return ExpressionFactory.createNumber(val, m.getBitWidth());
}
logger.debug("No value can be read from image for address " + m);
return null;
}
public byte[] getByteArray() {
return inBuf.getByteArray();
}
@Override
public Iterator<AbsoluteAddress> codeBytesIterator() {
return new Iterator<AbsoluteAddress>() {
long fp = 0;
int sec = -1;
{
moveToNextCodeSection();
}
private void moveToNextCodeSection() {
sec++;
while (sec < getNumberOfSections() && !isCodeSection(sec)) {
sec++;
}
if (sec >= getNumberOfSections()) {
fp = -1;
sec = -1;
} else {
fp = getSectionHeader(sec).PointerToRawData;
}
}
private void moveToNextCodeByte() {
fp++;
if (fp >= getSectionHeader(sec).PointerToRawData + getSectionHeader(sec).SizeOfRawData) {
moveToNextCodeSection();
if (sec < 0) {
return;
}
}
}
@Override
public boolean hasNext() {
return (fp >= 0);
}
@Override
public AbsoluteAddress next() {
if (!hasNext()) throw new IndexOutOfBoundsException();
AbsoluteAddress res = getVirtualAddress(fp);
moveToNextCodeByte();
return res;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Disassembler getDisassembler() {
if (disassembler == null) {
disassembler = new X86Disassembler(inBuf);
}
return disassembler;
}
}