Skip to content

Commit

Permalink
Parser and Assembler refactoring (#31)
Browse files Browse the repository at this point in the history
* Update minimal Java version to 8

* Move read and write functionality to corresponding data structures

* Add read-write test for whole PE file

* Move read and write functionality for resources to corresponding data structures

* Add write support for more resource structures
  • Loading branch information
swismer committed Jun 26, 2023
1 parent c0f33e8 commit a95ee6e
Show file tree
Hide file tree
Showing 55 changed files with 2,328 additions and 1,316 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ import com.kichik.pecoff4j.PE;
import com.kichik.pecoff4j.ResourceDirectory;
import com.kichik.pecoff4j.ResourceEntry;
import com.kichik.pecoff4j.constant.ResourceType;
import com.kichik.pecoff4j.io.DataReader;
import com.kichik.pecoff4j.io.PEParser;
import com.kichik.pecoff4j.io.ResourceParser;
import com.kichik.pecoff4j.resources.StringFileInfo;
import com.kichik.pecoff4j.resources.StringTable;
import com.kichik.pecoff4j.resources.VersionInfo;
Expand All @@ -61,7 +61,7 @@ public class Main {
ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO);
for (int i = 0; i < entries.length; i++) {
byte[] data = entries[i].getData();
VersionInfo version = ResourceParser.readVersionInfo(data);
VersionInfo version = VersionInfo.read(new DataReader(data));

StringFileInfo strings = version.getStringFileInfo();
StringTable table = strings.getTable(0);
Expand Down
7 changes: 3 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- must be marked as 7 instead of 1.7; otherwise: "Fatal error compiling: release version 1.7 not supported" -->
<maven.compiler.release>7</maven.compiler.release>
<maven.compiler.release>8</maven.compiler.release>
</properties>

<scm>
Expand Down Expand Up @@ -114,9 +113,9 @@
<release>9</release>
</configuration>
</execution>
<!-- compile sources for Java 7 -->
<!-- compile sources for Java 8 -->
<execution>
<id>java-7-compile</id>
<id>java-8-compile</id>
<goals>
<goal>compile</goal>
</goals>
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/kichik/pecoff4j/AttributeCertificateTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
*******************************************************************************/
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.io.DataReader;
import com.kichik.pecoff4j.util.DataObject;

import java.io.IOException;

/**
* Encapsulates the Attribute Certificate Table (Image Only). Section 5.7 of the PE/COFF
* spec Rev10.
Expand All @@ -21,6 +24,19 @@ public class AttributeCertificateTable extends DataObject {
private int certificateType;
private byte[] certificate;

public static AttributeCertificateTable read(byte[] b) throws IOException {
AttributeCertificateTable dd = new AttributeCertificateTable();
dd.set(b);
DataReader dr = new DataReader(b);
dd.setLength(dr.readDoubleWord());
dd.setRevision(dr.readWord());
dd.setCertificateType(dr.readWord());
byte[] certificate = new byte[dd.getLength() - 8];
dr.read(certificate);
dd.setCertificate(certificate);
return dd;
}

public int getLength() {
return length;
}
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/kichik/pecoff4j/BoundImport.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,29 @@
*******************************************************************************/
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.io.IDataReader;

import java.io.IOException;

public class BoundImport {
private long timestamp;
private int offsetToModuleName;
private String moduleName;
private int numModuleForwarderRefs;

public static BoundImport read(IDataReader dr) throws IOException {
BoundImport bi = new BoundImport();
bi.setTimestamp(dr.readDoubleWord());
bi.setOffsetToModuleName(dr.readWord());
bi.setNumberOfModuleForwarderRefs(dr.readWord());

if (bi.getTimestamp() == 0 && bi.getOffsetToModuleName() == 0
&& bi.getNumberOfModuleForwarderRefs() == 0)
return null;

return bi;
}

public long getTimestamp() {
return timestamp;
}
Expand Down
81 changes: 81 additions & 0 deletions src/main/java/com/kichik/pecoff4j/BoundImportDirectoryTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,93 @@
*******************************************************************************/
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.constant.ImageDataDirectoryType;
import com.kichik.pecoff4j.io.DataReader;
import com.kichik.pecoff4j.io.IDataWriter;
import com.kichik.pecoff4j.util.IntMap;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class BoundImportDirectoryTable {
private List<BoundImport> imports = new ArrayList();

public static BoundImportDirectoryTable read(DataReader dr) throws IOException {
BoundImportDirectoryTable bidt = new BoundImportDirectoryTable();
List<BoundImport> imports = new ArrayList<BoundImport>();
BoundImport bi = null;
while ((bi = BoundImport.read(dr)) != null) {
bidt.add(bi);
imports.add(bi);
}
Collections.sort(imports, new Comparator<BoundImport>() {
@Override
public int compare(BoundImport o1, BoundImport o2) {
return o1.getOffsetToModuleName() - o2.getOffsetToModuleName();
}
});
IntMap names = new IntMap();
for (int i = 0; i < imports.size(); i++) {
bi = imports.get(i);
int offset = bi.getOffsetToModuleName();
String n = (String) names.get(offset);
if (n == null) {
dr.jumpTo(offset);
n = dr.readUtf();
names.put(offset, n);
}
bi.setModuleName(n);
}
return bidt;
}

public void write(PE pe, IDataWriter dw) throws IOException {
int pos = dw.getPosition();
List<BoundImport> bil = new ArrayList();

for (int i = 0; i < size(); i++) {
BoundImport bi = get(i);
bil.add(bi);
dw.writeDoubleWord((int) bi.getTimestamp());
dw.writeWord(bi.getOffsetToModuleName());
dw.writeWord(bi.getNumberOfModuleForwarderRefs());
}

Collections.sort(bil, new Comparator<BoundImport>() {
@Override
public int compare(BoundImport o1, BoundImport o2) {
return o1.getOffsetToModuleName() - o2.getOffsetToModuleName();
}
});

// Now write out empty block
dw.writeDoubleWord(0);
dw.writeDoubleWord(0);

// Now write out module names
Set names = new HashSet();
for (int i = 0; i < bil.size(); i++) {
String s = bil.get(i).getModuleName();
if (!names.contains(s))
dw.writeUtf(s);
names.add(s);
}

// Check for empty block at end - padding for alignment
int dpos = dw.getPosition() - pos;
int bis = pe.getOptionalHeader()
.getDataDirectory(ImageDataDirectoryType.BOUND_IMPORT)
.getSize();
if (bis > dpos) {
dw.writeByte(0, bis - dpos);
}
}

public void add(BoundImport bi) {
imports.add(bi);
}
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/kichik/pecoff4j/CLRRuntimeHeader.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.io.DataReader;
import com.kichik.pecoff4j.util.DataObject;

import java.io.IOException;

public class CLRRuntimeHeader extends DataObject {
private int headerSize;
private int majorRuntimeVersion;
Expand All @@ -23,6 +26,32 @@ public class CLRRuntimeHeader extends DataObject {
private int managedNativeHeaderAddress;
private int managedNativeHeaderSize;

public static CLRRuntimeHeader read(byte[] b) throws IOException {
DataReader dr = new DataReader(b);
CLRRuntimeHeader clrrh = new CLRRuntimeHeader();
clrrh.set(b);
clrrh.setHeaderSize(dr.readDoubleWord());
clrrh.setMajorRuntimeVersion(dr.readWord());
clrrh.setMinorRuntimeVersion(dr.readWord());
clrrh.setMetaDataDirectoryAddress(dr.readDoubleWord());
clrrh.setMetaDataDirectorySize(dr.readDoubleWord());
clrrh.setFlags(dr.readDoubleWord());
clrrh.setEntryPointToken(dr.readDoubleWord());
clrrh.setResourcesDirectoryAddress(dr.readDoubleWord());
clrrh.setResourcesDirectorySize(dr.readDoubleWord());
clrrh.setStrongNameSignatureAddress(dr.readDoubleWord());
clrrh.setStrongNameSignatureSize(dr.readDoubleWord());
clrrh.setCodeManagerTableAddress(dr.readDoubleWord());
clrrh.setCodeManagerTableSize(dr.readDoubleWord());
clrrh.setvTableFixupsAddress(dr.readDoubleWord());
clrrh.setvTableFixupsSize(dr.readDoubleWord());
clrrh.setExportAddressTableJumpsAddress(dr.readDoubleWord());
clrrh.setExportAddressTableJumpsSize(dr.readDoubleWord());
clrrh.setManagedNativeHeaderAddress(dr.readDoubleWord());
clrrh.setManagedNativeHeaderSize(dr.readDoubleWord());
return clrrh;
}

public int getHeaderSize() {
return headerSize;
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/kichik/pecoff4j/COFFHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
*******************************************************************************/
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.io.IDataReader;
import com.kichik.pecoff4j.io.IDataWriter;

import java.io.IOException;

public class COFFHeader {
private int machine;
private int numberOfSections;
Expand All @@ -18,6 +23,28 @@ public class COFFHeader {
private int sizeOfOptionalHeader;
private int characteristics;

public static COFFHeader read(IDataReader dr) throws IOException {
COFFHeader h = new COFFHeader();
h.setMachine(dr.readWord());
h.setNumberOfSections(dr.readWord());
h.setTimeDateStamp(dr.readDoubleWord());
h.setPointerToSymbolTable(dr.readDoubleWord());
h.setNumberOfSymbols(dr.readDoubleWord());
h.setSizeOfOptionalHeader(dr.readWord());
h.setCharacteristics(dr.readWord());
return h;
}

public void write(IDataWriter dw) throws IOException {
dw.writeWord(getMachine());
dw.writeWord(getNumberOfSections());
dw.writeDoubleWord(getTimeDateStamp());
dw.writeDoubleWord(getPointerToSymbolTable());
dw.writeDoubleWord(getNumberOfSymbols());
dw.writeWord(getSizeOfOptionalHeader());
dw.writeWord(getCharacteristics());
}

public int getMachine() {
return machine;
}
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/com/kichik/pecoff4j/DOSHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
*******************************************************************************/
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.io.IDataReader;
import com.kichik.pecoff4j.io.IDataWriter;

import java.io.IOException;

public class DOSHeader {
public static final int DOS_MAGIC = 0;

Expand All @@ -33,6 +38,75 @@ public class DOSHeader {
private int addressOfNewExeHeader;
private int stubSize;

public static DOSHeader read(IDataReader dr) throws IOException {
DOSHeader dh = new DOSHeader();
dh.setMagic(dr.readWord());
dh.setUsedBytesInLastPage(dr.readWord());
dh.setFileSizeInPages(dr.readWord());
dh.setNumRelocationItems(dr.readWord());
dh.setHeaderSizeInParagraphs(dr.readWord());
dh.setMinExtraParagraphs(dr.readWord());
dh.setMaxExtraParagraphs(dr.readWord());
dh.setInitialSS(dr.readWord());
dh.setInitialSP(dr.readWord());
dh.setChecksum(dr.readWord());
dh.setInitialIP(dr.readWord());
dh.setInitialRelativeCS(dr.readWord());
dh.setAddressOfRelocationTable(dr.readWord());
dh.setOverlayNumber(dr.readWord());
int[] reserved = new int[4];
for (int i = 0; i < reserved.length; i++) {
reserved[i] = dr.readWord();
}
dh.setReserved(reserved);
dh.setOemId(dr.readWord());
dh.setOemInfo(dr.readWord());
int[] reserved2 = new int[10];
for (int i = 0; i < reserved2.length; i++) {
reserved2[i] = dr.readWord();
}
dh.setReserved2(reserved2);
dh.setAddressOfNewExeHeader(dr.readDoubleWord());

// calc stub size
int stubSize = dh.getFileSizeInPages() * 512
- (512 - dh.getUsedBytesInLastPage());
if (stubSize > dh.getAddressOfNewExeHeader())
stubSize = dh.getAddressOfNewExeHeader();
stubSize -= dh.getHeaderSizeInParagraphs() * 16;
dh.setStubSize(stubSize);

return dh;
}

public void write(IDataWriter dw) throws IOException {
dw.writeWord(getMagic());
dw.writeWord(getUsedBytesInLastPage());
dw.writeWord(getFileSizeInPages());
dw.writeWord(getNumRelocationItems());
dw.writeWord(getHeaderSizeInParagraphs());
dw.writeWord(getMinExtraParagraphs());
dw.writeWord(getMaxExtraParagraphs());
dw.writeWord(getInitialSS());
dw.writeWord(getInitialSP());
dw.writeWord(getChecksum());
dw.writeWord(getInitialIP());
dw.writeWord(getInitialRelativeCS());
dw.writeWord(getAddressOfRelocationTable());
dw.writeWord(getOverlayNumber());
int[] res = getReserved();
for (int i = 0; i < res.length; i++) {
dw.writeWord(res[i]);
}
dw.writeWord(getOemId());
dw.writeWord(getOemInfo());
int[] res2 = getReserved2();
for (int i = 0; i < res2.length; i++) {
dw.writeWord(res2[i]);
}
dw.writeDoubleWord(getAddressOfNewExeHeader());
}

public int getMagic() {
return magic;
}
Expand Down

0 comments on commit a95ee6e

Please sign in to comment.