Merge branch 'GP-2210_ryanmkurtz_PR-1892_lkempf_mz' into patch

(Closes #254, Closes #1876, Closes #1892, Closes #4318)
This commit is contained in:
Ryan Kurtz
2023-01-20 14:14:55 -05:00
6 changed files with 909 additions and 545 deletions

View File

@@ -19,8 +19,6 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.Writeable;
import ghidra.app.util.bin.format.ne.InvalidWindowsHeaderException;
import ghidra.app.util.bin.format.ne.WindowsHeader;
import ghidra.app.util.bin.format.pe.InvalidNTHeaderException;
@@ -61,26 +59,13 @@ import ghidra.util.exception.DuplicateNameException;
*
*
*/
public class DOSHeader implements StructConverter, Writeable {
public class DOSHeader extends OldDOSHeader {
/** The name to use when converting into a structure data type. */
public final static String NAME = "IMAGE_DOS_HEADER";
public final static int IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
public final static int SIZEOF_DOS_HEADER = 64;
public final static int SIZEOF_DOS_HEADER = 64;
private short e_magic; // Magic number
private short e_cblp; // Bytes on last page of file
private short e_cp; // Pages in file
private short e_crlc; // Relocations
private short e_cparhdr; // Size of header in paragraphs
private short e_minalloc; // Minimum extra paragraphs needed
private short e_maxalloc; // Maximum extra paragraphs needed
private short e_ss; // Initial (relative) SS value
private short e_sp; // Initial SP value
private short e_csum; // Checksum
private short e_ip; // Initial IP value
private short e_cs; // Initial (relative) CS value
private short e_lfarlc; // File address of relocation table
private short e_ovno; // Overlay number
private short [] e_res = new short[4]; // Reserved words
private short e_oemid; // OEM identifier (for e_oeminfo)
private short e_oeminfo; // OEM information; e_oemid specific
@@ -89,123 +74,16 @@ public class DOSHeader implements StructConverter, Writeable {
private byte [] stubBytes;
private BinaryReader reader;
/**
* Constructs a new DOS header.
* @param reader the binary reader
*/
* Constructs a new DOS header.
* @param reader the binary reader
* @throws IOException if there was an IO-related error
*/
public DOSHeader(BinaryReader reader) throws IOException {
this.reader = reader;
parse();
super(reader);
}
/**
* Returns the processor name.
* @return the processor name
*/
public String getProcessorName() {
return "x86";
}
/**
* Returns the magic number.
* @return the magic number
*/
public short e_magic() {
return e_magic;
}
/**
* Returns the number of bytes on the last page of file.
* @return the number of bytes on the last page of the file
*/
public short e_cblp() {
return e_cblp;
}
/**
* Returns the number of pages in the file.
* @return the number of pages in the file
*/
public short e_cp() {
return e_cp;
}
/**
* Returns the number of relocations.
* @return the number of relocations
*/
public short e_crlc() {
return e_crlc;
}
/**
* Returns the size of header in paragraphs.
* @return the size of header in paragraphs
*/
public short e_cparhdr() {
return e_cparhdr;
}
/**
* Returns the minimum extra paragraphs needed.
* @return the minimum extra paragraphs needed
*/
public short e_minalloc() {
return e_minalloc;
}
/**
* Returns the maximum extra paragraphs needed.
* @return the maximum extra paragraphs needed
*/
public short e_maxalloc() {
return e_maxalloc;
}
/**
* Returns the initial (relative) SS value.
* @return the initial (relative) SS value
*/
public short e_ss() {
return e_ss;
}
/**
* Returns the initial SP value.
* @return the initial SP value
*/
public short e_sp() {
return e_sp;
}
/**
* Returns the checksum.
* @return the checksum
*/
public short e_csum() {
return e_csum;
}
/**
* Returns the initial IP value.
* @return the initial IP value
*/
public short e_ip() {
return e_ip;
}
/**
* Returns the initial (relative) CS value.
* @return the initial (relative) CS value
*/
public short e_cs() {
return e_cs;
}
/**
* Returns the file address of relocation table.
* @return the file address of relocation table
*/
public short e_lfarlc() {
return e_lfarlc;
}
/**
* Returns the overlay number.
* @return the overlay number
*/
public short e_ovno() {
return e_ovno;
}
/**
/**
* Returns the reserved words.
* @return the reserved words
*/
@@ -245,9 +123,10 @@ public class DOSHeader implements StructConverter, Writeable {
* Returns true if a new EXE header exists.
* @return true if a new EXE header exists
*/
@Override
public boolean hasNewExeHeader() {
if (e_lfanew >= 0 && e_lfanew <= 0x10000) {
if (e_lfarlc == 0x40) {
if (e_lfarlc() == 0x40) {
// There are some non-NE files out there than may have e_lfarlc == 0x40, so we need
// to actually read the bytes at e_lfanew and check for the required NE signature.
try {
@@ -266,6 +145,7 @@ public class DOSHeader implements StructConverter, Writeable {
* Returns true if a PE header exists.
* @return true if a PE header exists
*/
@Override
public boolean hasPeHeader() {
if (e_lfanew >= 0 && e_lfanew <= 0x1000000) {
try {
@@ -282,24 +162,13 @@ public class DOSHeader implements StructConverter, Writeable {
return false;
}
/**
* Returns true if the DOS magic number is correct
* @return true if the DOS magic number is correct
*/
public boolean isDosSignature() {
return e_magic == IMAGE_DOS_SIGNATURE;
}
/**
* @see ghidra.app.util.bin.StructConverter#toDataType()
*/
@Override
public DataType toDataType() throws DuplicateNameException {
StructureDataType struct = new StructureDataType(NAME, 0);
struct.add(new ArrayDataType(ASCII,2,1));
for (int i=1; i <= 13; i++) {
struct.add(WORD);
}
StructureDataType struct = (StructureDataType)super.toDataType();
struct.add(new ArrayDataType(WORD,4,2));
struct.add(WORD);
struct.add(WORD);
@@ -309,20 +178,6 @@ public class DOSHeader implements StructConverter, Writeable {
struct.add(new ArrayDataType(BYTE, getProgramLen(), 1));
}
struct.getComponent( 0).setFieldName("e_magic");
struct.getComponent( 1).setFieldName("e_cblp");
struct.getComponent( 2).setFieldName("e_cp");
struct.getComponent( 3).setFieldName("e_crlc");
struct.getComponent( 4).setFieldName("e_cparhdr");
struct.getComponent( 5).setFieldName("e_minalloc");
struct.getComponent( 6).setFieldName("e_maxalloc");
struct.getComponent( 7).setFieldName("e_ss");
struct.getComponent( 8).setFieldName("e_sp");
struct.getComponent( 9).setFieldName("e_csum");
struct.getComponent(10).setFieldName("e_ip");
struct.getComponent(11).setFieldName("e_cs");
struct.getComponent(12).setFieldName("e_lfarlc");
struct.getComponent(13).setFieldName("e_ovno");
struct.getComponent(14).setFieldName("e_res[4]");
struct.getComponent(15).setFieldName("e_oemid");
struct.getComponent(16).setFieldName("e_oeminfo");
@@ -332,20 +187,6 @@ public class DOSHeader implements StructConverter, Writeable {
struct.getComponent(19).setFieldName("e_program");
}
struct.getComponent( 0).setComment("Magic number");
struct.getComponent( 1).setComment("Bytes of last page");
struct.getComponent( 2).setComment("Pages in file");
struct.getComponent( 3).setComment("Relocations");
struct.getComponent( 4).setComment("Size of header in paragraphs");
struct.getComponent( 5).setComment("Minimum extra paragraphs needed");
struct.getComponent( 6).setComment("Maximum extra paragraphs needed");
struct.getComponent( 7).setComment("Initial (relative) SS value");
struct.getComponent( 8).setComment("Initial SP value");
struct.getComponent( 9).setComment("Checksum");
struct.getComponent(10).setComment("Initial IP value");
struct.getComponent(11).setComment("Initial (relative) CS value");
struct.getComponent(12).setComment("File address of relocation table");
struct.getComponent(13).setComment("Overlay number");
struct.getComponent(14).setComment("Reserved words");
struct.getComponent(15).setComment("OEM identifier (for e_oeminfo)");
struct.getComponent(16).setComment("OEM information; e_oemid specific");
@@ -355,10 +196,17 @@ public class DOSHeader implements StructConverter, Writeable {
struct.getComponent(19).setComment("Actual DOS program");
}
struct.setCategoryPath(new CategoryPath("/DOS"));
return struct;
}
/**
* Helper to override the value of name
* @return The name of the header
*/
@Override
public String getName() {
return NAME;
}
/**
* Returns the length (in bytes) of the DOS
@@ -373,35 +221,21 @@ public class DOSHeader implements StructConverter, Writeable {
return stubBytes == null ? 0 : stubBytes.length;
}
private void parse() throws IOException {
reader.setPointerIndex(0);
e_magic = reader.readNextShort();
if (e_magic != IMAGE_DOS_SIGNATURE) {
@Override
protected void parse() throws IOException {
super.parse();
if (!isDosSignature()) {
return;
}
e_cblp = reader.readNextShort();
e_cp = reader.readNextShort();
e_crlc = reader.readNextShort();
e_cparhdr = reader.readNextShort();
e_minalloc = reader.readNextShort();
e_maxalloc = reader.readNextShort();
e_ss = reader.readNextShort();
e_sp = reader.readNextShort();
e_csum = reader.readNextShort();
e_ip = reader.readNextShort();
e_cs = reader.readNextShort();
e_lfarlc = reader.readNextShort();
e_ovno = reader.readNextShort();
e_res = reader.readNextShortArray(4);
e_oemid = reader.readNextShort();
e_oeminfo = reader.readNextShort();
e_res2 = reader.readNextShortArray(10);
e_lfanew = reader.readNextInt();
if (isDosSignature() && e_lfanew < 0x10000) {
if (e_lfanew < 0x10000) {
try {
stubBytes = e_lfanew > SIZEOF_DOS_HEADER ?
reader.readByteArray(SIZEOF_DOS_HEADER, e_lfanew - SIZEOF_DOS_HEADER) : new byte[0];
@@ -432,20 +266,7 @@ public class DOSHeader implements StructConverter, Writeable {
*/
@Override
public void write(RandomAccessFile raf, DataConverter dc) throws IOException {
raf.write(dc.getBytes(e_magic));
raf.write(dc.getBytes(e_cblp));
raf.write(dc.getBytes(e_cp));
raf.write(dc.getBytes(e_crlc));
raf.write(dc.getBytes(e_cparhdr));
raf.write(dc.getBytes(e_minalloc));
raf.write(dc.getBytes(e_maxalloc));
raf.write(dc.getBytes(e_ss));
raf.write(dc.getBytes(e_sp));
raf.write(dc.getBytes(e_csum));
raf.write(dc.getBytes(e_ip));
raf.write(dc.getBytes(e_cs));
raf.write(dc.getBytes(e_lfarlc));
raf.write(dc.getBytes(e_ovno));
super.write(raf, dc);
for (short e_re : e_res) {
raf.write(dc.getBytes(e_re));
}

View File

@@ -0,0 +1,75 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.app.util.bin.format.mz;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
/**
* A class to manage loading old-style DOS MZ executables
*/
public class MzExecutable {
private BinaryReader reader;
private OldDOSHeader header;
private List<MzRelocation> relocations = new ArrayList<>();
/**
* Constructs a new instance of an old-style MZ executable
*
* @param provider The bytes
* @throws IOException if an I/O error occurs
*/
public MzExecutable(ByteProvider provider) throws IOException {
reader = new BinaryReader(provider, true);
header = new OldDOSHeader(reader);
reader.setPointerIndex(header.e_lfarlc());
for (int i = 0; i < header.e_crlc(); i++) {
relocations.add(new MzRelocation(reader));
}
}
/**
* Returns the underlying binary reader
*
* @return the underlying binary reader
*/
public BinaryReader getBinaryReader() {
return reader;
}
/**
* Returns the DOS Header from this old-style MZ executable
*
* @return the DOS Header from this old-style MZ executable
*/
public OldDOSHeader getHeader() {
return header;
}
/**
* Returns the old-style MZ relocations
*
* @return the old-style MZ relocations
*/
public List<MzRelocation> getRelocations() {
return relocations;
}
}

View File

@@ -0,0 +1,75 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.app.util.bin.format.mz;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class MzRelocation implements StructConverter {
/** The name to use when converting into a structure data type. */
public static final String NAME = "OLD_IMAGE_DOS_RELOC";
private int segment;
private int offset;
/**
* Constructs a new old-style MZ relocation
*
* @param reader A {@link BinaryReader} positioned at the start of the relocation
* @throws IOException if there was an IO-related error
*/
public MzRelocation(BinaryReader reader) throws IOException {
offset = Short.toUnsignedInt(reader.readNextShort());
segment = Short.toUnsignedInt(reader.readNextShort());
}
/**
* Gets the segment
*
* @return The segment
*/
public int getSegment() {
return segment;
}
/**
* Gets the offset
*
* @return The offset
*/
public int getOffset() {
return offset;
}
@Override
public DataType toDataType() throws DuplicateNameException {
StructureDataType struct = new StructureDataType(NAME, 0);
struct.add(WORD, "offset", null);
struct.add(WORD, "segment", null);
struct.setCategoryPath(new CategoryPath("/DOS"));
return struct;
}
@Override
public String toString() {
return String.format("%04x:%04x", segment, offset);
}
}

View File

@@ -0,0 +1,321 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.app.util.bin.format.mz;
import java.io.IOException;
import java.io.RandomAccessFile;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.Writeable;
import ghidra.program.model.data.*;
import ghidra.util.DataConverter;
import ghidra.util.exception.DuplicateNameException;
/**
* This class represents a DOS Header
* <br>
* <pre>
* WORD e_magic; // Magic number // MANDATORY
* WORD e_cblp; // Bytes on last page of file
* WORD e_cp; // Pages in file
* WORD e_crlc; // Relocations
* WORD e_cparhdr; // Size of header in paragraphs
* WORD e_minalloc; // Minimum extra paragraphs needed
* WORD e_maxalloc; // Maximum extra paragraphs needed
* WORD e_ss; // Initial (relative) SS value
* WORD e_sp; // Initial SP value
* WORD e_csum; // Checksum
* WORD e_ip; // Initial IP value
* WORD e_cs; // Initial (relative) CS value
* WORD e_lfarlc; // File address of relocation table
* WORD e_ovno; // Overlay number
*
* </pre>
*/
public class OldDOSHeader implements StructConverter, Writeable {
/** The name to use when converting into a structure data type. */
public static final String NAME = "OLD_IMAGE_DOS_HEADER";
public static final int IMAGE_DOS_SIGNATURE = 0x5A4D;
private short e_magic;
private short e_cblp;
private short e_cp;
private short e_crlc;
private short e_cparhdr;
private short e_minalloc;
private short e_maxalloc;
private short e_ss;
private short e_sp;
private short e_csum;
private short e_ip;
private short e_cs;
private short e_lfarlc;
private short e_ovno;
protected BinaryReader reader;
/**
* Constructs a new DOS header.
* @param reader the binary reader
* @throws IOException if there was an IO-related error
*/
public OldDOSHeader(BinaryReader reader) throws IOException {
this.reader = reader;
parse();
}
/**
* Returns the processor name.
* @return the processor name
*/
public String getProcessorName() {
return "x86";
}
/**
* Returns the magic number.
* @return the magic number
*/
public short e_magic() {
return e_magic;
}
/**
* Returns the number of bytes on the last page of file.
* @return the number of bytes on the last page of the file
*/
public short e_cblp() {
return e_cblp;
}
/**
* Returns the number of pages in the file.
* @return the number of pages in the file
*/
public short e_cp() {
return e_cp;
}
/**
* Returns the number of relocations.
* @return the number of relocations
*/
public short e_crlc() {
return e_crlc;
}
/**
* Returns the size of header in paragraphs.
* @return the size of header in paragraphs
*/
public short e_cparhdr() {
return e_cparhdr;
}
/**
* Returns the minimum extra paragraphs needed.
* @return the minimum extra paragraphs needed
*/
public short e_minalloc() {
return e_minalloc;
}
/**
* Returns the maximum extra paragraphs needed.
* @return the maximum extra paragraphs needed
*/
public short e_maxalloc() {
return e_maxalloc;
}
/**
* Returns the initial (relative) SS value.
* @return the initial (relative) SS value
*/
public short e_ss() {
return e_ss;
}
/**
* Returns the initial SP value.
* @return the initial SP value
*/
public short e_sp() {
return e_sp;
}
/**
* Returns the checksum.
* @return the checksum
*/
public short e_csum() {
return e_csum;
}
/**
* Returns the initial IP value.
* @return the initial IP value
*/
public short e_ip() {
return e_ip;
}
/**
* Returns the initial (relative) CS value.
* @return the initial (relative) CS value
*/
public short e_cs() {
return e_cs;
}
/**
* Returns the file address of relocation table.
* @return the file address of relocation table
*/
public short e_lfarlc() {
return e_lfarlc;
}
/**
* Returns the overlay number.
* @return the overlay number
*/
public short e_ovno() {
return e_ovno;
}
/**
* Returns true if a new EXE header exists.
* @return true if a new EXE header exists
*/
public boolean hasNewExeHeader() {
return false;
}
/**
* Returns true if a PE header exists.
* @return true if a PE header exists
*/
public boolean hasPeHeader() {
return false;
}
/**
* Returns true if the DOS magic number is correct
* @return true if the DOS magic number is correct
*/
public boolean isDosSignature() {
return e_magic == IMAGE_DOS_SIGNATURE;
}
@Override
public DataType toDataType() throws DuplicateNameException {
StructureDataType struct = new StructureDataType(getName(), 0);
struct.add(new ArrayDataType(ASCII,2,1));
for (int i=1; i <= 13; i++) {
struct.add(WORD);
}
struct.getComponent( 0).setFieldName("e_magic");
struct.getComponent( 1).setFieldName("e_cblp");
struct.getComponent( 2).setFieldName("e_cp");
struct.getComponent( 3).setFieldName("e_crlc");
struct.getComponent( 4).setFieldName("e_cparhdr");
struct.getComponent( 5).setFieldName("e_minalloc");
struct.getComponent( 6).setFieldName("e_maxalloc");
struct.getComponent( 7).setFieldName("e_ss");
struct.getComponent( 8).setFieldName("e_sp");
struct.getComponent( 9).setFieldName("e_csum");
struct.getComponent(10).setFieldName("e_ip");
struct.getComponent(11).setFieldName("e_cs");
struct.getComponent(12).setFieldName("e_lfarlc");
struct.getComponent(13).setFieldName("e_ovno");
struct.getComponent( 0).setComment("Magic number");
struct.getComponent( 1).setComment("Bytes of last page");
struct.getComponent( 2).setComment("Pages in file");
struct.getComponent( 3).setComment("Relocations");
struct.getComponent( 4).setComment("Size of header in paragraphs");
struct.getComponent( 5).setComment("Minimum extra paragraphs needed");
struct.getComponent( 6).setComment("Maximum extra paragraphs needed");
struct.getComponent( 7).setComment("Initial (relative) SS value");
struct.getComponent( 8).setComment("Initial SP value");
struct.getComponent( 9).setComment("Checksum");
struct.getComponent(10).setComment("Initial IP value");
struct.getComponent(11).setComment("Initial (relative) CS value");
struct.getComponent(12).setComment("File address of relocation table");
struct.getComponent(13).setComment("Overlay number");
struct.setCategoryPath(new CategoryPath("/DOS"));
return struct;
}
/**
* Helper to override the value of name
* @return The name of the header
*/
protected String getName() {
return NAME;
}
protected void parse() throws IOException {
reader.setPointerIndex(0);
e_magic = reader.readNextShort();
if (!isDosSignature()) {
return;
}
e_cblp = reader.readNextShort();
e_cp = reader.readNextShort();
e_crlc = reader.readNextShort();
e_cparhdr = reader.readNextShort();
e_minalloc = reader.readNextShort();
e_maxalloc = reader.readNextShort();
e_ss = reader.readNextShort();
e_sp = reader.readNextShort();
e_csum = reader.readNextShort();
e_ip = reader.readNextShort();
e_cs = reader.readNextShort();
e_lfarlc = reader.readNextShort();
e_ovno = reader.readNextShort();
}
@Override
public void write(RandomAccessFile raf, DataConverter dc) throws IOException {
raf.write(dc.getBytes(e_magic));
raf.write(dc.getBytes(e_cblp));
raf.write(dc.getBytes(e_cp));
raf.write(dc.getBytes(e_crlc));
raf.write(dc.getBytes(e_cparhdr));
raf.write(dc.getBytes(e_minalloc));
raf.write(dc.getBytes(e_maxalloc));
raf.write(dc.getBytes(e_ss));
raf.write(dc.getBytes(e_sp));
raf.write(dc.getBytes(e_csum));
raf.write(dc.getBytes(e_ip));
raf.write(dc.getBytes(e_cs));
raf.write(dc.getBytes(e_lfarlc));
raf.write(dc.getBytes(e_ovno));
}
}

View File

@@ -1,57 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.app.util.bin.format.mz;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
/**
* A class to manage loading Old-style (MZ) Executables.
*
*
*/
public class OldStyleExecutable {
private BinaryReader reader;
private DOSHeader dosHeader;
/**
* Constructs a new instance of an old-style executable
* @param bp the byte provider
* @throws IOException if an I/O error occurs
*/
public OldStyleExecutable(ByteProvider bp) throws IOException {
reader = new BinaryReader(bp, true);
dosHeader = new DOSHeader(reader);
}
/**
* Returns the underlying binary reader.
* @return the underlying binary reader
*/
public BinaryReader getBinaryReader() {
return reader;
}
/**
* Returns the DOS Header from this old-style executable.
* @return the DOS Header from this old-style executable
*/
public DOSHeader getDOSHeader() {
return dosHeader;
}
}

View File

@@ -18,47 +18,50 @@ package ghidra.app.util.opinion;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.mz.DOSHeader;
import ghidra.app.util.bin.format.mz.OldStyleExecutable;
import ghidra.app.util.bin.format.mz.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.store.LockException;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.DataConverter;
import ghidra.util.LittleEndianDataConverter;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
/**
* A {@link Loader} for processing Microsoft DOS MZ files.
* A {@link Loader} for processing old-style DOS MZ executables
*
* @see <a href="https://wiki.osdev.org/MZ">OSDev.org MZ</a>
* @see <a href="https://www.tavi.co.uk/phobos/exeformat.html">Notes on the format of DOS .EXE files</a>
* @see <a href="https://thestarman.pcministry.com/asm/debug/Segments.html">Removing the Mystery from SEGMENT : OFFSET Addressing</a>
*/
public class MzLoader extends AbstractLibrarySupportLoader {
public final static String MZ_NAME = "Old-style DOS Executable (MZ)";
/** Option to control how memory blocks are formed */
public static final String CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_NAME =
"Create memory blocks from relocation segments";
static final boolean CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_DEFAULT = true;
private final static String ENTRY_NAME = "entry";
private final static int INITIAL_SEGMENT_VAL = 0x1000;
private final static int FAR_RETURN_OPCODE = 0xCB;
private final static byte MOVW_DS_OPCODE = (byte) 0xba;
private static final long MIN_BYTE_LENGTH = 4;
private DataConverter converter = LittleEndianDataConverter.INSTANCE;
@Override
public int getTierPriority() {
return 60; // we are less priority than PE! Important for AutoImporter
}
public MzLoader() {
}
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();
@@ -66,11 +69,11 @@ public class MzLoader extends AbstractLibrarySupportLoader {
if (provider.length() < MIN_BYTE_LENGTH) {
return loadSpecs;
}
OldStyleExecutable ose = new OldStyleExecutable(provider);
DOSHeader dos = ose.getDOSHeader();
if (dos.isDosSignature() && !dos.hasNewExeHeader() && !dos.hasPeHeader()) {
MzExecutable mz = new MzExecutable(provider);
OldDOSHeader header = mz.getHeader();
if (header.isDosSignature() && !header.hasNewExeHeader() && !header.hasPeHeader()) {
List<QueryResult> results =
QueryOpinionService.query(getName(), "" + dos.e_magic(), null);
QueryOpinionService.query(getName(), "" + header.e_magic(), null);
for (QueryResult result : results) {
loadSpecs.add(new LoadSpec(this, 0, result));
}
@@ -83,71 +86,330 @@ public class MzLoader extends AbstractLibrarySupportLoader {
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program prog,
TaskMonitor monitor, MessageLog log) throws IOException, CancelledException {
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log)
throws IOException, CancelledException {
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(prog, provider, monitor);
AddressFactory af = prog.getAddressFactory();
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
AddressFactory af = program.getAddressFactory();
if (!(af.getDefaultAddressSpace() instanceof SegmentedAddressSpace)) {
throw new IOException("Selected Language must have a segmented address space.");
}
SegmentedAddressSpace space = (SegmentedAddressSpace) af.getDefaultAddressSpace();
SymbolTable symbolTable = prog.getSymbolTable();
ProgramContext context = prog.getProgramContext();
Memory memory = prog.getMemory();
MzExecutable mz = new MzExecutable(provider);
OldStyleExecutable ose = new OldStyleExecutable(provider);
DOSHeader dos = ose.getDOSHeader();
BinaryReader reader = ose.getBinaryReader();
if (monitor.isCancelled()) {
try {
markupHeaders(program, fileBytes, mz, log, monitor);
processMemoryBlocks(program, fileBytes, space, mz, options, log, monitor);
adjustSegmentStarts(program, monitor);
processRelocations(program, space, mz, log, monitor);
processEntryPoint(program, space, mz, log, monitor);
processRegisters(program, mz, log, monitor);
}
catch (CancelledException e) {
return;
}
monitor.setMessage("Processing segments...");
processSegments(prog, fileBytes, space, reader, dos, log, monitor);
if (monitor.isCancelled()) {
return;
catch (IOException e) {
throw e;
}
monitor.setMessage("Adjusting segments...");
adjustSegmentStarts(prog);
if (monitor.isCancelled()) {
return;
catch (Exception e) {
throw new IOException(e);
}
monitor.setMessage("Processing relocations...");
doRelocations(prog, reader, dos);
if (monitor.isCancelled()) {
return;
}
monitor.setMessage("Processing symbols...");
createSymbols(space, symbolTable, dos);
if (monitor.isCancelled()) {
return;
}
monitor.setMessage("Setting registers...");
Symbol entrySymbol = SymbolUtilities.getLabelOrFunctionSymbol(prog, ENTRY_NAME,
err -> log.appendMsg("MZ", err));
setRegisters(context, entrySymbol, memory.getBlocks(), dos);
}
private void setRegisters(ProgramContext context, Symbol entry, MemoryBlock[] blocks,
DOSHeader dos) {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram) {
List<Option> list =
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
if (!loadIntoProgram) {
list.add(new Option(CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_NAME,
CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-blocksFromRelocationSegments"));
}
return list;
}
@Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program) {
if (options != null) {
for (Option option : options) {
String name = option.getName();
if (name.equals(CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_NAME)) {
if (!Boolean.class.isAssignableFrom(option.getValueClass())) {
return "Invalid type for option: " + name + " - " + option.getValueClass();
}
}
}
}
return super.validateOptions(provider, loadSpec, options, program);
}
@Override
public String getName() {
return MZ_NAME;
}
@Override
public int getTierPriority() {
return 60; // we are less priority than PE! Important for AutoImporter
}
private void markupHeaders(Program program, FileBytes fileBytes, MzExecutable mz,
MessageLog log, TaskMonitor monitor) {
monitor.setMessage("Marking up headers...");
OldDOSHeader header = mz.getHeader();
int blockSize = paragraphsToBytes(header.e_cparhdr());
try {
Address headerSpaceAddr = AddressSpace.OTHER_SPACE.getAddress(0);
MemoryBlock headerBlock = MemoryBlockUtils.createInitializedBlock(program, true,
"HEADER", headerSpaceAddr, fileBytes, 0, blockSize, "", "", false,
false, false, log);
Address addr = headerBlock.getStart();
// Header
DataUtilities.createData(program, addr, mz.getHeader().toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
// Relocation Table
List<MzRelocation> relocations = mz.getRelocations();
if (!relocations.isEmpty()) {
DataType relocationType = relocations.get(0).toDataType();
int len = relocationType.getLength();
addr = addr.add(header.e_lfarlc());
for (int i = 0; i < relocations.size(); i++) {
monitor.checkCanceled();
DataUtilities.createData(program, addr.add(i * len), relocationType, -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
}
}
catch (Exception e) {
log.appendMsg("Failed to markup headers");
}
}
private void processMemoryBlocks(Program program, FileBytes fileBytes,
SegmentedAddressSpace space, MzExecutable mz, List<Option> options, MessageLog log,
TaskMonitor monitor) throws Exception {
monitor.setMessage("Processing memory blocks...");
OldDOSHeader header = mz.getHeader();
BinaryReader reader = mz.getBinaryReader();
// If not creating memory blocks based on relocation segments, just make 1 big block for
// code, and 1 for data
if (!shouldCreateBlocksFromRelocationSegments(options)) {
int headerSize = paragraphsToBytes(header.e_cparhdr());
int loadModuleSize = pagesToBytes(header.e_cp() - 1) + header.e_cblp() - headerSize;
MemoryBlock codeBlock = MemoryBlockUtils.createInitializedBlock(program, false, "CODE",
space.getAddress(INITIAL_SEGMENT_VAL, 0), fileBytes, headerSize, loadModuleSize, "",
"mz", true, true, true, log);
int extraAllocSize = paragraphsToBytes(header.e_minalloc());
if (extraAllocSize > 0) {
MemoryBlockUtils.createUninitializedBlock(program, false, "DATA",
codeBlock.getEnd().add(1), extraAllocSize, "", "mz", true, true, false, log);
}
return;
}
// Use relocations to discover what segments are in use
Set<SegmentedAddress> knownSegments = new TreeSet<>();
Map<SegmentedAddress, Integer> relocationMap = getRelocationMap(space, mz, monitor);
for (SegmentedAddress addr : relocationMap.keySet()) {
knownSegments.add(space.getAddress(addr.getSegment(), 0));
}
// We also know about our desired load module segment, so add that
knownSegments.add(space.getAddress(INITIAL_SEGMENT_VAL, 0));
// Allocate an initialized memory block for each segment we know about
int endOffset = pagesToBytes(header.e_cp() - 1) + header.e_cblp();
MemoryBlock lastBlock = null;
List<SegmentedAddress> orderedSegments = new ArrayList<>(knownSegments);
for (int i = 0; i < orderedSegments.size(); i++) {
SegmentedAddress segmentAddr = orderedSegments.get(i);
int segmentFileOffset = addressToFileOffset(
(segmentAddr.getSegment() - INITIAL_SEGMENT_VAL) & 0xffff, 0, header);
if (segmentFileOffset < 0) {
log.appendMsg("Invalid segment start file location: " + segmentFileOffset);
continue;
}
int numBytes = 0;
if (i + 1 < orderedSegments.size()) {
SegmentedAddress end = orderedSegments.get(i + 1);
int nextSegmentFileOffset = addressToFileOffset(
(end.getSegment() - INITIAL_SEGMENT_VAL) & 0xffff, 0, header);
numBytes = nextSegmentFileOffset - segmentFileOffset;
}
else {
// last segment length
numBytes = endOffset - segmentFileOffset;
}
if (numBytes <= 0) {
log.appendMsg("No file data available for defined segment at: " + segmentAddr);
continue;
}
int numUninitBytes = 0;
if (segmentFileOffset + numBytes > endOffset) {
int calcNumBytes = numBytes;
numBytes = endOffset - segmentFileOffset;
numUninitBytes = calcNumBytes - numBytes;
}
if (numBytes > 0) {
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
"CODE_" + i, segmentAddr, fileBytes, segmentFileOffset, numBytes, "", "mz",
true, true, true, log);
if (block != null) {
lastBlock = block;
}
}
if (numUninitBytes > 0) {
MemoryBlock block =
MemoryBlockUtils.createUninitializedBlock(program, false, "CODE_" + i + "u",
segmentAddr.add(numBytes), numUninitBytes, "", "mz", true, true, false,
log);
if (block != null) {
lastBlock = block;
}
}
}
if (endOffset < reader.length()) {
int extraByteCount = (int) reader.length() - endOffset;
log.appendMsg(
String.format("File contains 0x%x extra bytes starting at file offset 0x%x",
extraByteCount, endOffset));
}
// Allocate an uninitialized memory block for extra minumum required data space
if (lastBlock != null) {
int extraAllocSize = paragraphsToBytes(header.e_minalloc());
if (extraAllocSize > 0) {
MemoryBlockUtils.createUninitializedBlock(program, false, "DATA",
lastBlock.getEnd().add(1), extraAllocSize, "", "mz", true, true, false, log);
}
}
}
private void adjustSegmentStarts(Program program, TaskMonitor monitor) throws Exception {
monitor.setMessage("Adjusting segments...");
if (!program.hasExclusiveAccess()) {
return;
}
Memory memory = program.getMemory();
MemoryBlock[] blocks = memory.getBlocks();
for (int i = 1; i < blocks.length; i++) {
monitor.checkCanceled();
MemoryBlock block = blocks[i];
if (!block.isInitialized()) {
continue;
}
//scan the first 0x10 bytes of this block
//if a FAR RETURN exists, then move that code
//to the preceding block...
int mIndex = 15;
if (block.getSize() <= 16) {
mIndex = (int) block.getSize() - 2;
}
for (; mIndex >= 0; mIndex--) {
monitor.checkCanceled();
Address offAddr = block.getStart().add(mIndex);
int val = block.getByte(offAddr);
val &= 0xff;
if (val == FAR_RETURN_OPCODE) {
// split here and join to previous
Address splitAddr = offAddr.add(1);
String oldName = block.getName();
memory.split(block, splitAddr);
memory.join(blocks[i - 1], blocks[i]);
blocks = memory.getBlocks();
blocks[i].setName(oldName);
break;
}
}
}
}
private void processRelocations(Program program, SegmentedAddressSpace space, MzExecutable mz,
MessageLog log, TaskMonitor monitor) throws Exception {
monitor.setMessage("Processing relocations...");
BinaryReader reader = mz.getBinaryReader();
Memory memory = program.getMemory();
Map<SegmentedAddress, Integer> relocationMap = getRelocationMap(space, mz, monitor);
for (Entry<SegmentedAddress, Integer> entry : relocationMap.entrySet()) {
SegmentedAddress relocationAddress = entry.getKey();
int relocationFileOffset = entry.getValue();
try {
int value = Short.toUnsignedInt(reader.readShort(relocationFileOffset));
int fixupAddrSeg = (value + INITIAL_SEGMENT_VAL) & 0xffff;
byte[] origBytes = new byte[2];
memory.getBytes(relocationAddress, origBytes);
memory.setShort(relocationAddress, (short) fixupAddrSeg);
// Add to relocation table
program.getRelocationTable()
.add(relocationAddress, 0, new long[] { relocationAddress.getSegment(),
relocationAddress.getSegmentOffset() }, origBytes, null);
}
catch (AddressOutOfBoundsException | IOException | MemoryAccessException e) {
log.appendMsg(String.format("Failed to process relocation: %s (%s)",
relocationAddress, e.getMessage()));
}
}
}
private void processEntryPoint(Program program, SegmentedAddressSpace space, MzExecutable mz,
MessageLog log, TaskMonitor monitor) {
monitor.setMessage("Processing entry point...");
OldDOSHeader header = mz.getHeader();
int ipValue = Short.toUnsignedInt(header.e_ip());
Address addr = space.getAddress(INITIAL_SEGMENT_VAL, ipValue);
SymbolTable symbolTable = program.getSymbolTable();
try {
symbolTable.createLabel(addr, ENTRY_NAME, SourceType.IMPORTED);
symbolTable.addExternalEntryPoint(addr);
}
catch (InvalidInputException e) {
log.appendMsg("Failed to process entry point");
}
}
private void processRegisters(Program program, MzExecutable mz, MessageLog log,
TaskMonitor monitor) {
monitor.setMessage("Processing registers...");
Symbol entry = SymbolUtilities.getLabelOrFunctionSymbol(program, ENTRY_NAME,
err -> log.appendMsg(err));
if (entry == null) {
return;
}
DataConverter converter = LittleEndianDataConverter.INSTANCE;
boolean shouldSetDS = false;
long dsValue = 0;
try {
for (MemoryBlock block : blocks) {
for (MemoryBlock block : program.getMemory().getBlocks()) {
if (block.contains(entry.getAddress())) {
byte instByte = block.getByte(entry.getAddress());
if (instByte == MOVW_DS_OPCODE) {//is instruction "movw %dx,$0x1234"
if (instByte == MOVW_DS_OPCODE) { //is instruction "movw %dx,$0x1234"
byte[] dsBytes = new byte[2];
block.getBytes(entry.getAddress().addWrap(1), dsBytes);
dsValue = converter.getShort(dsBytes);
@@ -161,6 +423,8 @@ public class MzLoader extends AbstractLibrarySupportLoader {
//unable to set the DS register..
}
OldDOSHeader header = mz.getHeader();
ProgramContext context = program.getProgramContext();
Register ss = context.getRegister("ss");
Register sp = context.getRegister("sp");
Register ds = context.getRegister("ds");
@@ -168,14 +432,15 @@ public class MzLoader extends AbstractLibrarySupportLoader {
try {
context.setValue(sp, entry.getAddress(), entry.getAddress(),
BigInteger.valueOf(Conv.shortToLong(dos.e_sp())));
BigInteger.valueOf(Short.toUnsignedLong(header.e_sp())));
context.setValue(ss, entry.getAddress(), entry.getAddress(),
BigInteger.valueOf(Conv.shortToLong(dos.e_ss())));
BigInteger.valueOf(
Integer.toUnsignedLong((header.e_ss() + INITIAL_SEGMENT_VAL) & 0xffff)));
BigInteger csValue = BigInteger.valueOf(
Conv.intToLong(((SegmentedAddress) entry.getAddress()).getSegment()));
Integer.toUnsignedLong(((SegmentedAddress) entry.getAddress()).getSegment()));
for (MemoryBlock block : blocks) {
for (MemoryBlock block : program.getMemory().getBlocks()) {
Address start = block.getStart();
Address end = block.getEnd();
context.setValue(cs, start, end, csValue);
@@ -189,226 +454,90 @@ public class MzLoader extends AbstractLibrarySupportLoader {
}
}
private void adjustSegmentStarts(Program prog) {
Memory mem = prog.getMemory();
/**
* Gets a {@link Map} of relocation {@link SegmentedAddress addresses} to file offsets, adjusted
* to where the image is loaded into memory
*
* @param space The address space
* @param mz The {@link MzExecutable}
* @param monitor A monitor
* @return A {@link Map} of relocation {@link SegmentedAddress addresses} to file offsets,
* adjusted to where the image is loaded into memory
* @throws CancelledException If the action was cancelled
*/
private Map<SegmentedAddress, Integer> getRelocationMap(SegmentedAddressSpace space,
MzExecutable mz,
TaskMonitor monitor) throws CancelledException {
Map<SegmentedAddress, Integer> addresses = new TreeMap<>();
if (!prog.hasExclusiveAccess()) {
return;
OldDOSHeader header = mz.getHeader();
for (MzRelocation relocation : mz.getRelocations()) {
monitor.checkCanceled();
int seg = relocation.getSegment();
int off = relocation.getOffset();
int relativeSegment = (seg - Short.toUnsignedInt(header.e_cs())) & 0xffff;
int relocationFileOffset = addressToFileOffset(relativeSegment, off, header);
SegmentedAddress relocationAddress =
space.getAddress((relativeSegment + INITIAL_SEGMENT_VAL) & 0xffff, off);
addresses.put(relocationAddress, relocationFileOffset);
}
MemoryBlock[] blocks = mem.getBlocks();
for (int i = 1; i < blocks.length; i++) {
MemoryBlock block = blocks[i];
if (!block.isInitialized()) {
continue;
}
//scan the first 0x10 bytes of this block
//if a FAR RETURN exists, then move that code
//to the preceding block...
int mIndex = 15;
if (block.getSize() <= 16) {
mIndex = (int) block.getSize() - 2;
}
for (; mIndex >= 0; mIndex--) {
try {
Address offAddr = block.getStart().add(mIndex);
int val = block.getByte(offAddr);
val &= 0xff;
if (val == FAR_RETURN_OPCODE) {
// split here and join to previous
Address splitAddr = offAddr.add(1);
String oldName = block.getName();
mem.split(block, splitAddr);
mem.join(blocks[i - 1], blocks[i]);
blocks = mem.getBlocks();
blocks[i].setName(oldName);
break;
}
}
catch (LockException e) {
// TODO: ignore?
}
catch (MemoryBlockException e) {
// TODO: ignore?
}
catch (AddressOutOfBoundsException e) {
// TODO: ignore?
}
catch (MemoryAccessException e) {
// TODO: ignore?
}
catch (NotFoundException e) {
// TODO: ignore?
}
}
}
return addresses;
}
private void processSegments(Program program, FileBytes fileBytes, SegmentedAddressSpace space,
BinaryReader reader, DOSHeader dos, MessageLog log, TaskMonitor monitor) {
try {
int relocationTableOffset = Conv.shortToInt(dos.e_lfarlc());
int csStart = INITIAL_SEGMENT_VAL;
int dataStart = dos.e_cparhdr() << 4;
HashMap<Address, Address> segMap = new HashMap<Address, Address>();
SegmentedAddress codeAddress =
space.getAddress(Conv.shortToInt(dos.e_cs()) + csStart, 0);
segMap.put(codeAddress, codeAddress);
codeAddress = space.getAddress(csStart, 0);
segMap.put(codeAddress, codeAddress); // This is there data starts loading
int numRelocationEntries = dos.e_crlc();
reader.setPointerIndex(relocationTableOffset);
for (int i = 0; i < numRelocationEntries; i++) {
int off = Conv.shortToInt(reader.readNextShort());
int seg = Conv.shortToInt(reader.readNextShort());
// compute the new segment referenced at the location
SegmentedAddress segStartAddr = space.getAddress(seg + csStart, 0);
segMap.put(segStartAddr, segStartAddr);
int location = (seg << 4) + off;
int locOffset = location + dataStart;
int value = Conv.shortToInt(reader.readShort(locOffset));
int fixupAddrSeg = (value + csStart) & Conv.SHORT_MASK;
SegmentedAddress fixupAddr = space.getAddress(fixupAddrSeg, 0);
segMap.put(fixupAddr, fixupAddr);
}
int exeBlockCount = dos.e_cp();
int exeEndOffset = exeBlockCount * 512;
int bytesUsedInLastBlock = dos.e_cblp();
if (bytesUsedInLastBlock != 0) {
exeEndOffset -= (512 - bytesUsedInLastBlock);
}
ArrayList<Address> segStartList = new ArrayList<Address>(segMap.values());
int csStartEffective = csStart << 4;
Collections.sort(segStartList);
for (int i = 0; i < segStartList.size(); i++) {
SegmentedAddress start = (SegmentedAddress) segStartList.get(i);
int readLoc = ((start.getSegment() << 4) - csStartEffective) + dataStart;
if (readLoc < 0) {
Msg.error(this, "Invalid read location " + readLoc);
continue;
}
int numBytes = 0;
if ((i + 1) < segStartList.size()) {
SegmentedAddress end = (SegmentedAddress) segStartList.get(i + 1);
int nextLoc = ((end.getSegment() << 4) - csStartEffective) + dataStart;
numBytes = nextLoc - readLoc;
}
else {
// last segment length
numBytes = exeEndOffset - readLoc;
}
if (numBytes <= 0) {
log.appendMsg("No EXE file data available for defined segment at: " + start);
continue;
}
int numUninitBytes = 0;
if ((readLoc + numBytes) > exeEndOffset) {
int calcNumBytes = numBytes;
numBytes = exeEndOffset - readLoc;
numUninitBytes = calcNumBytes - numBytes;
}
if (numBytes > 0) {
MemoryBlockUtils.createInitializedBlock(program, false, "Seg_" + i, start,
fileBytes, readLoc, numBytes, "", "mz", true, true, true, log);
}
if (numUninitBytes > 0) {
MemoryBlockUtils.createUninitializedBlock(program, false, "Seg_" + i + "u",
start.add(numBytes), numUninitBytes, "", "mz", true, true, false, log);
}
}
if (exeEndOffset < reader.length()) {
int extraByteCount = (int) reader.length() - exeEndOffset;
log.appendMsg("File contains 0x" + Integer.toHexString(extraByteCount) +
" extra bytes starting at file offset 0x" + Integer.toHexString(exeEndOffset));
}
// // create the stack segment
// SegmentedAddress stackStart = space.getAddress((dos.e_ss() + csStart), 0);
// mbu.createUninitializedBlock(false, "Stack", stackStart, dos.e_sp(), "", "", true, true, false);
}
catch (IOException e) {
throw new RuntimeException(e);
}
catch (AddressOverflowException e) {
throw new RuntimeException(e);
}
/**
* Converts a segmented address to a file offset
*
* @param segment The segment
* @param offset The offset
* @param header The header
* @return The segmented addresses converted to a file offset
*/
private int addressToFileOffset(int segment, int offset, OldDOSHeader header) {
return (segment << 4) + offset + paragraphsToBytes(header.e_cparhdr());
}
private void doRelocations(Program prog, BinaryReader reader, DOSHeader dos) {
try {
Memory mem = prog.getMemory();
SegmentedAddressSpace space =
(SegmentedAddressSpace) prog.getAddressFactory().getDefaultAddressSpace();
/**
* Converts paragraphs to bytes. There are 16 bytes in a paragraph.
*
* @param paragraphs The number of paragraphs
* @return The number of bytes in the given number of paragraphs
*/
private int paragraphsToBytes(int paragraphs) {
return paragraphs << 4;
}
int relocationTableOffset = Conv.shortToInt(dos.e_lfarlc());
int csStart = INITIAL_SEGMENT_VAL;
int dataStart = dos.e_cparhdr() << 4;
/**
* Converts pages to bytes. There are 512 bytes in a paragraph.
*
* @param pages The number of pages
* @return The number of bytes in the given number of pages
*/
private int pagesToBytes(int pages) {
return pages << 9;
}
int numRelocationEntries = dos.e_crlc();
reader.setPointerIndex(relocationTableOffset);
for (int i = 0; i < numRelocationEntries; i++) {
int off = Conv.shortToInt(reader.readNextShort());
int seg = Conv.shortToInt(reader.readNextShort());
//SegmentedAddress segStartAddr = space.getAddress(seg + csStart, 0);
int location = (seg << 4) + off;
int locOffset = location + dataStart;
// compute the new segment referenced at the location
SegmentedAddress fixupAddr = space.getAddress(seg + csStart, off);
int value = Conv.shortToInt(reader.readShort(locOffset));
int fixupAddrSeg = (value + csStart) & Conv.SHORT_MASK;
mem.setShort(fixupAddr, (short) fixupAddrSeg);
// Add to relocation table
prog.getRelocationTable()
.add(fixupAddr, 0, new long[] { off, seg }, null, null);
/**
* Checks to see if memory blocks should be created based on segments referenced in relocation
* table entries
*
* @param options A {@link List} of {@link Option}s
* @return True if memory blocks should be created based on segments reference in relocation
* table entries; otherwise, false
*/
private boolean shouldCreateBlocksFromRelocationSegments(List<Option> options) {
if (options != null) {
for (Option option : options) {
String optName = option.getName();
if (optName.equals(CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_NAME)) {
return (Boolean) option.getValue();
}
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
catch (MemoryAccessException e) {
throw new RuntimeException(e);
}
return CREATE_BLOCKS_FROM_RELOC_SEGS_OPTION_DEFAULT;
}
private void createSymbols(SegmentedAddressSpace space, SymbolTable symbolTable,
DOSHeader dos) {
int ipValue = Conv.shortToInt(dos.e_ip());
int codeSegment = Conv.shortToInt(dos.e_cs()) + INITIAL_SEGMENT_VAL;
if (codeSegment > Conv.SHORT_MASK) {
System.out.println("Invalid entry point location: " + Integer.toHexString(codeSegment) +
":" + Integer.toHexString(ipValue));
return;
}
Address addr = space.getAddress(codeSegment, ipValue);
try {
symbolTable.createLabel(addr, ENTRY_NAME, SourceType.IMPORTED);
}
catch (InvalidInputException e) {
// Just skip if we can't create
}
symbolTable.addExternalEntryPoint(addr);
}
@Override
public String getName() {
return MZ_NAME;
}
}