From a95ee6eb678b4fb8740d4d1404ba11c6b06bc820 Mon Sep 17 00:00:00 2001 From: swismer <47538304+swismer@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:54:38 +0200 Subject: [PATCH] Parser and Assembler refactoring (#31) * 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 --- README.md | 4 +- pom.xml | 7 +- .../pecoff4j/AttributeCertificateTable.java | 16 + .../java/com/kichik/pecoff4j/BoundImport.java | 17 + .../pecoff4j/BoundImportDirectoryTable.java | 81 ++ .../com/kichik/pecoff4j/CLRRuntimeHeader.java | 29 + .../java/com/kichik/pecoff4j/COFFHeader.java | 27 + .../java/com/kichik/pecoff4j/DOSHeader.java | 74 ++ .../java/com/kichik/pecoff4j/DOSStub.java | 20 + .../com/kichik/pecoff4j/DebugDirectory.java | 18 + .../com/kichik/pecoff4j/ExportDirectory.java | 21 + .../java/com/kichik/pecoff4j/ImageData.java | 154 ++++ .../kichik/pecoff4j/ImageDataDirectory.java | 19 + .../com/kichik/pecoff4j/ImportDirectory.java | 25 + .../kichik/pecoff4j/ImportDirectoryEntry.java | 20 + .../kichik/pecoff4j/ImportDirectoryTable.java | 23 + .../java/com/kichik/pecoff4j/ImportEntry.java | 14 + .../kichik/pecoff4j/LoadConfigDirectory.java | 34 + .../com/kichik/pecoff4j/OptionalHeader.java | 111 +++ src/main/java/com/kichik/pecoff4j/PE.java | 266 ++++++ .../java/com/kichik/pecoff4j/PESignature.java | 16 + .../kichik/pecoff4j/ResourceDirectory.java | 71 ++ .../pecoff4j/ResourceDirectoryTable.java | 26 + .../com/kichik/pecoff4j/ResourceEntry.java | 60 ++ .../java/com/kichik/pecoff4j/SectionData.java | 68 ++ .../com/kichik/pecoff4j/SectionHeader.java | 33 + .../com/kichik/pecoff4j/SectionTable.java | 29 + .../com/kichik/pecoff4j/io/DataWriter.java | 32 + .../com/kichik/pecoff4j/io/IDataReader.java | 37 + .../com/kichik/pecoff4j/io/IDataWriter.java | 14 +- .../com/kichik/pecoff4j/io/PEAssembler.java | 329 +------- .../java/com/kichik/pecoff4j/io/PEParser.java | 772 +++--------------- .../kichik/pecoff4j/io/ResourceAssembler.java | 87 +- .../kichik/pecoff4j/io/ResourceParser.java | 330 +++----- .../kichik/pecoff4j/io/SectionAssembler.java | 14 - .../com/kichik/pecoff4j/resources/Bitmap.java | 18 + .../pecoff4j/resources/BitmapFileHeader.java | 13 +- .../pecoff4j/resources/BitmapInfoHeader.java | 36 + .../pecoff4j/resources/FixedFileInfo.java | 39 + .../pecoff4j/resources/IconDirectory.java | 25 + .../resources/IconDirectoryEntry.java | 30 + .../kichik/pecoff4j/resources/IconImage.java | 67 ++ .../kichik/pecoff4j/resources/Manifest.java | 16 + .../kichik/pecoff4j/resources/RGBQuad.java | 21 + .../pecoff4j/resources/StringFileInfo.java | 49 ++ .../kichik/pecoff4j/resources/StringPair.java | 50 ++ .../pecoff4j/resources/StringTable.java | 39 + .../com/kichik/pecoff4j/resources/Var.java | 29 + .../pecoff4j/resources/VarFileInfo.java | 29 + .../pecoff4j/resources/VersionInfo.java | 59 ++ .../kichik/pecoff4j/util/IconExtractor.java | 5 +- .../com/kichik/pecoff4j/util/IconFile.java | 10 +- .../com/kichik/pecoff4j/ReadWriteTest.java | 100 +++ .../kichik/pecoff4j/VersionStringsTest.java | 4 +- .../kichik/pecoff4j/io/ValidatingWriter.java | 107 +++ 55 files changed, 2328 insertions(+), 1316 deletions(-) delete mode 100644 src/main/java/com/kichik/pecoff4j/io/SectionAssembler.java create mode 100644 src/test/java/com/kichik/pecoff4j/ReadWriteTest.java create mode 100644 src/test/java/com/kichik/pecoff4j/io/ValidatingWriter.java diff --git a/README.md b/README.md index a9dac13..4d4f208 100644 --- a/README.md +++ b/README.md @@ -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; @@ -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); diff --git a/pom.xml b/pom.xml index c0cc492..2be7e02 100644 --- a/pom.xml +++ b/pom.xml @@ -29,8 +29,7 @@ UTF-8 - - 7 + 8 @@ -114,9 +113,9 @@ 9 - + - java-7-compile + java-8-compile compile diff --git a/src/main/java/com/kichik/pecoff4j/AttributeCertificateTable.java b/src/main/java/com/kichik/pecoff4j/AttributeCertificateTable.java index 9947614..3178880 100644 --- a/src/main/java/com/kichik/pecoff4j/AttributeCertificateTable.java +++ b/src/main/java/com/kichik/pecoff4j/AttributeCertificateTable.java @@ -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. @@ -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; } diff --git a/src/main/java/com/kichik/pecoff4j/BoundImport.java b/src/main/java/com/kichik/pecoff4j/BoundImport.java index eda258c..4644695 100644 --- a/src/main/java/com/kichik/pecoff4j/BoundImport.java +++ b/src/main/java/com/kichik/pecoff4j/BoundImport.java @@ -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; } diff --git a/src/main/java/com/kichik/pecoff4j/BoundImportDirectoryTable.java b/src/main/java/com/kichik/pecoff4j/BoundImportDirectoryTable.java index 89af24b..32d7da0 100644 --- a/src/main/java/com/kichik/pecoff4j/BoundImportDirectoryTable.java +++ b/src/main/java/com/kichik/pecoff4j/BoundImportDirectoryTable.java @@ -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 imports = new ArrayList(); + public static BoundImportDirectoryTable read(DataReader dr) throws IOException { + BoundImportDirectoryTable bidt = new BoundImportDirectoryTable(); + List imports = new ArrayList(); + BoundImport bi = null; + while ((bi = BoundImport.read(dr)) != null) { + bidt.add(bi); + imports.add(bi); + } + Collections.sort(imports, new Comparator() { + @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 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() { + @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); } diff --git a/src/main/java/com/kichik/pecoff4j/CLRRuntimeHeader.java b/src/main/java/com/kichik/pecoff4j/CLRRuntimeHeader.java index 7bc4e5a..575b38c 100644 --- a/src/main/java/com/kichik/pecoff4j/CLRRuntimeHeader.java +++ b/src/main/java/com/kichik/pecoff4j/CLRRuntimeHeader.java @@ -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; @@ -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; } diff --git a/src/main/java/com/kichik/pecoff4j/COFFHeader.java b/src/main/java/com/kichik/pecoff4j/COFFHeader.java index d25e489..53f7d63 100644 --- a/src/main/java/com/kichik/pecoff4j/COFFHeader.java +++ b/src/main/java/com/kichik/pecoff4j/COFFHeader.java @@ -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; @@ -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; } diff --git a/src/main/java/com/kichik/pecoff4j/DOSHeader.java b/src/main/java/com/kichik/pecoff4j/DOSHeader.java index 69cd241..392658e 100644 --- a/src/main/java/com/kichik/pecoff4j/DOSHeader.java +++ b/src/main/java/com/kichik/pecoff4j/DOSHeader.java @@ -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; @@ -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; } diff --git a/src/main/java/com/kichik/pecoff4j/DOSStub.java b/src/main/java/com/kichik/pecoff4j/DOSStub.java index 6d9e84a..87c0632 100644 --- a/src/main/java/com/kichik/pecoff4j/DOSStub.java +++ b/src/main/java/com/kichik/pecoff4j/DOSStub.java @@ -9,12 +9,32 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + /** * Used to store the stub program. */ public class DOSStub { private byte[] stub; + public static DOSStub read(DOSHeader header, IDataReader dr) + throws IOException { + DOSStub ds = new DOSStub(); + int pos = dr.getPosition(); + int add = header.getAddressOfNewExeHeader(); + byte[] stub = new byte[add - pos]; + dr.read(stub); + ds.setStub(stub); + return ds; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeBytes(getStub()); + } + public byte[] getStub() { return stub; } diff --git a/src/main/java/com/kichik/pecoff4j/DebugDirectory.java b/src/main/java/com/kichik/pecoff4j/DebugDirectory.java index 2ebfaba..c4c9ca5 100644 --- a/src/main/java/com/kichik/pecoff4j/DebugDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/DebugDirectory.java @@ -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 Debug Directory (Image Only). Section 6.1.1 of the PE/COFF * spec v8. @@ -24,6 +27,21 @@ public class DebugDirectory extends DataObject { private int addressOfRawData; private int pointerToRawData; + public static DebugDirectory read(byte[] b) throws IOException { + DebugDirectory dd = new DebugDirectory(); + dd.set(b); + DataReader dr = new DataReader(b); + dd.setCharacteristics(dr.readDoubleWord()); + dd.setTimeDateStamp(dr.readDoubleWord()); + dd.setMajorVersion(dr.readWord()); + dd.setMajorVersion(dr.readWord()); + dd.setType(dr.readDoubleWord()); + dd.setSizeOfData(dr.readDoubleWord()); + dd.setAddressOfRawData(dr.readDoubleWord()); + dd.setPointerToRawData(dr.readDoubleWord()); + return dd; + } + public int getCharacteristics() { return characteristics; } diff --git a/src/main/java/com/kichik/pecoff4j/ExportDirectory.java b/src/main/java/com/kichik/pecoff4j/ExportDirectory.java index 74224df..868844d 100644 --- a/src/main/java/com/kichik/pecoff4j/ExportDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/ExportDirectory.java @@ -9,9 +9,12 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.DataReader; import com.kichik.pecoff4j.util.DataObject; import com.kichik.pecoff4j.util.Reflection; +import java.io.IOException; + /** * The export directory table. See section 6.3.1 of the PE/COFF specification * v8. @@ -29,6 +32,24 @@ public class ExportDirectory extends DataObject { private long namePointerRVA; private long ordinalTableRVA; + public static ExportDirectory read(byte[] b) throws IOException { + DataReader dr = new DataReader(b); + ExportDirectory edt = new ExportDirectory(); + edt.set(b); + edt.setExportFlags(dr.readDoubleWord()); + edt.setTimeDateStamp(dr.readDoubleWord()); + edt.setMajorVersion(dr.readWord()); + edt.setMinorVersion(dr.readWord()); + edt.setNameRVA(dr.readDoubleWord()); + edt.setOrdinalBase(dr.readDoubleWord()); + edt.setAddressTableEntries(dr.readDoubleWord()); + edt.setNumberOfNamePointers(dr.readDoubleWord()); + edt.setExportAddressTableRVA(dr.readDoubleWord()); + edt.setNamePointerRVA(dr.readDoubleWord()); + edt.setOrdinalTableRVA(dr.readDoubleWord()); + return edt; + } + public long getExportFlags() { return exportFlags; } diff --git a/src/main/java/com/kichik/pecoff4j/ImageData.java b/src/main/java/com/kichik/pecoff4j/ImageData.java index 514c763..7a5b963 100644 --- a/src/main/java/com/kichik/pecoff4j/ImageData.java +++ b/src/main/java/com/kichik/pecoff4j/ImageData.java @@ -9,8 +9,16 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.constant.ImageDataDirectoryType; +import com.kichik.pecoff4j.io.ByteArrayDataReader; +import com.kichik.pecoff4j.io.DataEntry; +import com.kichik.pecoff4j.io.DataReader; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; import com.kichik.pecoff4j.util.IntMap; +import java.io.IOException; + public class ImageData { private byte[] headerPadding; // TODO find out what this is @@ -42,6 +50,152 @@ public class ImageData { // Any trailing data private byte[] trailingData; + public void read(PE pe, DataEntry entry, IDataReader dr) + throws IOException { + + // Read any preamble data + byte[] pa = dr.readNonZeroOrNull(entry.pointer); + if (pa != null) + put(entry.index, pa); + + // Read the image data + ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory( + entry.index); + byte[] b = new byte[idd.getSize()]; + dr.read(b); + + switch (entry.index) { + case ImageDataDirectoryType.EXPORT_TABLE: + setExportTable(ExportDirectory.read(b)); + break; + case ImageDataDirectoryType.IMPORT_TABLE: + setImportTable(ImportDirectory.read(b, entry.baseAddress)); + break; + case ImageDataDirectoryType.RESOURCE_TABLE: + setResourceTable(ResourceDirectory.read(new ByteArrayDataReader(b), entry.baseAddress)); + break; + case ImageDataDirectoryType.EXCEPTION_TABLE: + setExceptionTable(b); + break; + case ImageDataDirectoryType.CERTIFICATE_TABLE: + setCertificateTable(AttributeCertificateTable.read(b)); + break; + case ImageDataDirectoryType.BASE_RELOCATION_TABLE: + setBaseRelocationTable(b); + break; + case ImageDataDirectoryType.DEBUG: + setDebug(DebugDirectory.read(b)); + break; + case ImageDataDirectoryType.ARCHITECTURE: + setArchitecture(b); + break; + case ImageDataDirectoryType.GLOBAL_PTR: + setGlobalPtr(b); + break; + case ImageDataDirectoryType.TLS_TABLE: + setTlsTable(b); + break; + case ImageDataDirectoryType.LOAD_CONFIG_TABLE: + setLoadConfigTable(LoadConfigDirectory.read(pe, b)); + break; + case ImageDataDirectoryType.BOUND_IMPORT: + setBoundImports(BoundImportDirectoryTable.read(new DataReader(b))); + break; + case ImageDataDirectoryType.IAT: + setIat(b); + break; + case ImageDataDirectoryType.DELAY_IMPORT_DESCRIPTOR: + setDelayImportDescriptor(b); + break; + case ImageDataDirectoryType.CLR_RUNTIME_HEADER: + setClrRuntimeHeader(CLRRuntimeHeader.read(b)); + break; + case ImageDataDirectoryType.RESERVED: + setReserved(b); + break; + } + } + + public void write(PE pe, DataEntry entry, IDataWriter dw) + throws IOException { + ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory( + entry.index); + RVAConverter rvc = pe.getSectionTable().getRVAConverter(); + int prd = idd.getVirtualAddress(); + if (entry.index != ImageDataDirectoryType.CERTIFICATE_TABLE) + prd = rvc.convertVirtualAddressToRawDataPointer(idd + .getVirtualAddress()); + if (prd > dw.getPosition()) { + byte[] pa = pe.getImageData().getPreamble(entry.index); + if (pa != null) + dw.writeBytes(pa); + else + dw.writeByte(0, prd - dw.getPosition()); + } + + switch (entry.index) { + case ImageDataDirectoryType.EXPORT_TABLE: + dw.writeBytes(getExportTable().get()); + break; + case ImageDataDirectoryType.IMPORT_TABLE: + dw.writeBytes(getImportTable().get()); + break; + case ImageDataDirectoryType.RESOURCE_TABLE: + dw.writeBytes(getResourceTable().get()); + break; + case ImageDataDirectoryType.EXCEPTION_TABLE: + dw.writeBytes(getExceptionTable()); + break; + case ImageDataDirectoryType.CERTIFICATE_TABLE: + dw.writeBytes(getCertificateTable().get()); + break; + case ImageDataDirectoryType.BASE_RELOCATION_TABLE: + dw.writeBytes(getBaseRelocationTable()); + break; + case ImageDataDirectoryType.DEBUG: + dw.writeBytes(getDebug().get()); + break; + case ImageDataDirectoryType.ARCHITECTURE: + dw.writeBytes(getArchitecture()); + break; + case ImageDataDirectoryType.GLOBAL_PTR: + dw.writeBytes(getGlobalPtr()); + break; + case ImageDataDirectoryType.TLS_TABLE: + dw.writeBytes(getTlsTable()); + break; + case ImageDataDirectoryType.LOAD_CONFIG_TABLE: + break; + case ImageDataDirectoryType.BOUND_IMPORT: + getBoundImports().write(pe, dw); + break; + case ImageDataDirectoryType.IAT: + dw.writeBytes(getIat()); + break; + case ImageDataDirectoryType.DELAY_IMPORT_DESCRIPTOR: + dw.writeBytes(getDelayImportDescriptor()); + break; + case ImageDataDirectoryType.CLR_RUNTIME_HEADER: + dw.writeBytes(getClrRuntimeHeader().get()); + break; + case ImageDataDirectoryType.RESERVED: + dw.writeBytes(getReserved()); + break; + } + } + + public void writeDebugRawData(DataEntry entry, IDataWriter dw) + throws IOException { + if (entry.pointer > dw.getPosition()) { + byte[] pa = getDebugRawDataPreamble(); + if (pa != null) + dw.writeBytes(pa); + else + dw.writeByte(0, entry.pointer - dw.getPosition()); + } + dw.writeBytes(getDebugRawData()); + } + public byte[] getHeaderPadding() { return headerPadding; } diff --git a/src/main/java/com/kichik/pecoff4j/ImageDataDirectory.java b/src/main/java/com/kichik/pecoff4j/ImageDataDirectory.java index d97e1ad..0131962 100644 --- a/src/main/java/com/kichik/pecoff4j/ImageDataDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/ImageDataDirectory.java @@ -9,10 +9,29 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class ImageDataDirectory { private int virtualAddress; private int size; + public static ImageDataDirectory read(IDataReader dr) + throws IOException { + ImageDataDirectory idd = new ImageDataDirectory(); + idd.setVirtualAddress(dr.readDoubleWord()); + idd.setSize(dr.readDoubleWord()); + return idd; + } + + public void write(IDataWriter dw) + throws IOException { + dw.writeDoubleWord(getVirtualAddress()); + dw.writeDoubleWord(getSize()); + } + public int getVirtualAddress() { return virtualAddress; } diff --git a/src/main/java/com/kichik/pecoff4j/ImportDirectory.java b/src/main/java/com/kichik/pecoff4j/ImportDirectory.java index d7db740..6343915 100644 --- a/src/main/java/com/kichik/pecoff4j/ImportDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/ImportDirectory.java @@ -9,9 +9,11 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import com.kichik.pecoff4j.io.DataReader; import com.kichik.pecoff4j.util.DataObject; public class ImportDirectory extends DataObject { @@ -20,6 +22,29 @@ public class ImportDirectory extends DataObject { private List nameTables = new ArrayList(); private List addressTables = new ArrayList(); + public static ImportDirectory read(byte[] b, int baseAddress) + throws IOException { + DataReader dr = new DataReader(b); + ImportDirectory id = new ImportDirectory(); + ImportDirectoryEntry ide = null; + while ((ide = ImportDirectoryEntry.read(dr)) != null) { + id.add(ide); + } + + /* + * FIXME - name table refer to data outside image directory for (int i = + * 0; i < id.size(); i++) { ImportDirectoryEntry e = id.getEntry(i); + * dr.jumpTo(e.getNameRVA() - baseAddress); String name = dr.readUtf(); + * dr.jumpTo(e.getImportLookupTableRVA() - baseAddress); + * ImportDirectoryTable nt = readImportDirectoryTable(dr, baseAddress); + * dr.jumpTo(e.getImportAddressTableRVA() - baseAddress); + * ImportDirectoryTable at = null; // readImportDirectoryTable(dr, // + * baseAddress); id.add(name, nt, at); } + */ + + return id; + } + public void add(ImportDirectoryEntry entry) { entries.add(entry); } diff --git a/src/main/java/com/kichik/pecoff4j/ImportDirectoryEntry.java b/src/main/java/com/kichik/pecoff4j/ImportDirectoryEntry.java index ec2e4f7..4f31bee 100644 --- a/src/main/java/com/kichik/pecoff4j/ImportDirectoryEntry.java +++ b/src/main/java/com/kichik/pecoff4j/ImportDirectoryEntry.java @@ -9,6 +9,10 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.IDataReader; + +import java.io.IOException; + public class ImportDirectoryEntry { private int importLookupTableRVA; private int timeDateStamp; @@ -16,6 +20,22 @@ public class ImportDirectoryEntry { private int nameRVA; private int importAddressTableRVA; + public static ImportDirectoryEntry read(IDataReader dr) throws IOException { + ImportDirectoryEntry id = new ImportDirectoryEntry(); + id.setImportLookupTableRVA(dr.readDoubleWord()); + id.setTimeDateStamp(dr.readDoubleWord()); + id.setForwarderChain(dr.readDoubleWord()); + id.setNameRVA(dr.readDoubleWord()); + id.setImportAddressTableRVA(dr.readDoubleWord()); + + // The last entry is null + if (id.getImportLookupTableRVA() == 0) { + return null; + } + + return id; + } + public int getImportLookupTableRVA() { return importLookupTableRVA; } diff --git a/src/main/java/com/kichik/pecoff4j/ImportDirectoryTable.java b/src/main/java/com/kichik/pecoff4j/ImportDirectoryTable.java index eb5062f..3cabbcf 100644 --- a/src/main/java/com/kichik/pecoff4j/ImportDirectoryTable.java +++ b/src/main/java/com/kichik/pecoff4j/ImportDirectoryTable.java @@ -9,11 +9,34 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.IDataReader; + +import java.io.IOException; import java.util.ArrayList; public class ImportDirectoryTable { private ArrayList imports = new ArrayList(); + public static ImportDirectoryTable read(IDataReader dr, int baseAddress) throws IOException { + ImportDirectoryTable idt = new ImportDirectoryTable(); + ImportEntry ie = null; + while ((ie = ImportEntry.read(dr)) != null) { + idt.add(ie); + } + + for (int i = 0; i < idt.size(); i++) { + ImportEntry iee = idt.getEntry(i); + if ((iee.getVal() & 0x80000000) != 0) { + iee.setOrdinal(iee.getVal() & 0x7fffffff); + } else { + dr.jumpTo(iee.getVal() - baseAddress); + dr.readWord(); // FIXME this is an index into the export table + iee.setName(dr.readUtf()); + } + } + return idt; + } + public void add(ImportEntry entry) { imports.add(entry); } diff --git a/src/main/java/com/kichik/pecoff4j/ImportEntry.java b/src/main/java/com/kichik/pecoff4j/ImportEntry.java index aa8646f..74de62e 100644 --- a/src/main/java/com/kichik/pecoff4j/ImportEntry.java +++ b/src/main/java/com/kichik/pecoff4j/ImportEntry.java @@ -9,11 +9,25 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.IDataReader; + +import java.io.IOException; + public class ImportEntry { private int val; private int ordinal; private String name; + public static ImportEntry read(IDataReader dr) throws IOException { + ImportEntry ie = new ImportEntry(); + ie.setVal(dr.readDoubleWord()); + if (ie.getVal() == 0) { + return null; + } + + return ie; + } + public int getOrdinal() { return ordinal; } diff --git a/src/main/java/com/kichik/pecoff4j/LoadConfigDirectory.java b/src/main/java/com/kichik/pecoff4j/LoadConfigDirectory.java index a3c07d9..e778f9d 100644 --- a/src/main/java/com/kichik/pecoff4j/LoadConfigDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/LoadConfigDirectory.java @@ -9,8 +9,11 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.DataReader; import com.kichik.pecoff4j.util.DataObject; +import java.io.IOException; + public class LoadConfigDirectory extends DataObject { private int size; private int timeDateStamp; @@ -33,6 +36,37 @@ public class LoadConfigDirectory extends DataObject { private long seHandlerTable; private long seHandlerCount; + public static LoadConfigDirectory read(PE pe, byte[] b) throws IOException { + DataReader dr = new DataReader(b); + LoadConfigDirectory lcd = new LoadConfigDirectory(); + lcd.set(b); + lcd.setSize(dr.readDoubleWord()); + lcd.setTimeDateStamp(dr.readDoubleWord()); + lcd.setMajorVersion(dr.readWord()); + lcd.setMinorVersion(dr.readWord()); + lcd.setGlobalFlagsClear(dr.readDoubleWord()); + lcd.setGlobalFlagsSet(dr.readDoubleWord()); + lcd.setCriticalSectionDefaultTimeout(dr.readDoubleWord()); + lcd.setDeCommitFreeBlockThreshold(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + lcd.setDeCommitTotalFreeThreshold(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + lcd.setLockPrefixTable(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + lcd.setMaximumAllocationSize(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + lcd.setVirtualMemoryThreshold(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + lcd.setProcessAffinityMask(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + lcd.setProcessHeapFlags(dr.readDoubleWord()); + lcd.setCsdVersion(dr.readWord()); + lcd.setReserved(dr.readWord()); + lcd.setEditList(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + if (dr.hasMore()) // optional + lcd.setSecurityCookie(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + if (dr.hasMore()) // optional + lcd.setSeHandlerTable(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + if (dr.hasMore()) // optional + lcd.setSeHandlerCount(pe.is64() ? dr.readLong() : dr.readDoubleWord()); + + return lcd; + } + public int getSize() { return size; } diff --git a/src/main/java/com/kichik/pecoff4j/OptionalHeader.java b/src/main/java/com/kichik/pecoff4j/OptionalHeader.java index 6916c00..b68d442 100644 --- a/src/main/java/com/kichik/pecoff4j/OptionalHeader.java +++ b/src/main/java/com/kichik/pecoff4j/OptionalHeader.java @@ -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 OptionalHeader { public static final int MAGIC_PE32 = 0x10b; public static final int MAGIC_PE32plus = 0x20b; @@ -48,6 +53,112 @@ public class OptionalHeader { // The data directories private ImageDataDirectory[] dataDirectories; + public static OptionalHeader read(IDataReader dr) + throws IOException { + OptionalHeader oh = new OptionalHeader(); + oh.setMagic(dr.readWord()); + boolean is64 = oh.isPE32plus(); + oh.setMajorLinkerVersion(dr.readByte()); + oh.setMinorLinkerVersion(dr.readByte()); + oh.setSizeOfCode(dr.readDoubleWord()); + oh.setSizeOfInitializedData(dr.readDoubleWord()); + oh.setSizeOfUninitializedData(dr.readDoubleWord()); + oh.setAddressOfEntryPoint(dr.readDoubleWord()); + oh.setBaseOfCode(dr.readDoubleWord()); + + if (!is64) + oh.setBaseOfData(dr.readDoubleWord()); + + // NT additional fields. + oh.setImageBase(is64 ? dr.readLong() : dr.readDoubleWord()); + oh.setSectionAlignment(dr.readDoubleWord()); + oh.setFileAlignment(dr.readDoubleWord()); + oh.setMajorOperatingSystemVersion(dr.readWord()); + oh.setMinorOperatingSystemVersion(dr.readWord()); + oh.setMajorImageVersion(dr.readWord()); + oh.setMinorImageVersion(dr.readWord()); + oh.setMajorSubsystemVersion(dr.readWord()); + oh.setMinorSubsystemVersion(dr.readWord()); + oh.setWin32VersionValue(dr.readDoubleWord()); + oh.setSizeOfImage(dr.readDoubleWord()); + oh.setSizeOfHeaders(dr.readDoubleWord()); + oh.setCheckSum(dr.readDoubleWord()); + oh.setSubsystem(dr.readWord()); + oh.setDllCharacteristics(dr.readWord()); + oh.setSizeOfStackReserve(is64 ? dr.readLong() : dr.readDoubleWord()); + oh.setSizeOfStackCommit(is64 ? dr.readLong() : dr.readDoubleWord()); + oh.setSizeOfHeapReserve(is64 ? dr.readLong() : dr.readDoubleWord()); + oh.setSizeOfHeapCommit(is64 ? dr.readLong() : dr.readDoubleWord()); + oh.setLoaderFlags(dr.readDoubleWord()); + oh.setNumberOfRvaAndSizes(dr.readDoubleWord()); + + // Data directories + ImageDataDirectory[] dds = new ImageDataDirectory[16]; + for (int i = 0; i < dds.length; i++) { + dds[i] = ImageDataDirectory.read(dr); + } + oh.setDataDirectories(dds); + + return oh; + } + + public void write(IDataWriter dw) + throws IOException { + boolean is64 = isPE32plus(); + + dw.writeWord(getMagic()); + dw.writeByte(getMajorLinkerVersion()); + dw.writeByte(getMinorLinkerVersion()); + dw.writeDoubleWord(getSizeOfCode()); + dw.writeDoubleWord(getSizeOfInitializedData()); + dw.writeDoubleWord(getSizeOfUninitializedData()); + dw.writeDoubleWord(getAddressOfEntryPoint()); + dw.writeDoubleWord(getBaseOfCode()); + if (!is64) + dw.writeDoubleWord(getBaseOfData()); + + // NT additional fields. + if (is64) + dw.writeLong(getImageBase()); + else + dw.writeDoubleWord((int) getImageBase()); + + dw.writeDoubleWord(getSectionAlignment()); + dw.writeDoubleWord(getFileAlignment()); + dw.writeWord(getMajorOperatingSystemVersion()); + dw.writeWord(getMinorOperatingSystemVersion()); + dw.writeWord(getMajorImageVersion()); + dw.writeWord(getMinorImageVersion()); + dw.writeWord(getMajorSubsystemVersion()); + dw.writeWord(getMinorSubsystemVersion()); + dw.writeDoubleWord(getWin32VersionValue()); + dw.writeDoubleWord(getSizeOfImage()); + dw.writeDoubleWord(getSizeOfHeaders()); + dw.writeDoubleWord(getCheckSum()); + dw.writeWord(getSubsystem()); + dw.writeWord(getDllCharacteristics()); + if (is64) { + dw.writeLong(getSizeOfStackReserve()); + dw.writeLong(getSizeOfStackCommit()); + dw.writeLong(getSizeOfHeapReserve()); + dw.writeLong(getSizeOfHeapCommit()); + } else { + dw.writeDoubleWord((int) getSizeOfStackReserve()); + dw.writeDoubleWord((int) getSizeOfStackCommit()); + dw.writeDoubleWord((int) getSizeOfHeapReserve()); + dw.writeDoubleWord((int) getSizeOfHeapCommit()); + } + + dw.writeDoubleWord(getLoaderFlags()); + dw.writeDoubleWord(getNumberOfRvaAndSizes()); + + // Data directories + int ddc = getDataDirectoryCount(); + for (int i = 0; i < ddc; i++) { + getDataDirectory(i).write(dw); + } + } + public int getMagic() { return magic; } diff --git a/src/main/java/com/kichik/pecoff4j/PE.java b/src/main/java/com/kichik/pecoff4j/PE.java index 92108ae..333f59c 100644 --- a/src/main/java/com/kichik/pecoff4j/PE.java +++ b/src/main/java/com/kichik/pecoff4j/PE.java @@ -9,6 +9,13 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.constant.ImageDataDirectoryType; +import com.kichik.pecoff4j.io.DataEntry; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class PE { private DOSHeader dosHeader; private DOSStub stub; @@ -19,6 +26,265 @@ public class PE { private SectionTable sectionTable; private boolean is64bit; + public static PE read(IDataReader dr) throws IOException { + PE pe = new PE(); + pe.setDosHeader(DOSHeader.read(dr)); + + // Check if we have an old file type + if (pe.getDosHeader().getAddressOfNewExeHeader() == 0 + || pe.getDosHeader().getAddressOfNewExeHeader() > 8192) { + return pe; + } + + pe.setStub(DOSStub.read(pe.getDosHeader(), dr)); + pe.setSignature(PESignature.read(dr)); + + // Check signature to ensure we have a pe/coff file + if (!pe.getSignature().isValid()) { + return pe; + } + + pe.setCoffHeader(COFFHeader.read(dr)); + pe.setOptionalHeader(OptionalHeader.read(dr)); + pe.setSectionTable(SectionTable.read(pe, dr)); + + pe.set64(pe.getOptionalHeader().isPE32plus()); + + // Now read the rest of the file + DataEntry entry = null; + while ((entry = pe.findNextEntry(dr.getPosition())) != null) { + if (entry.isSection) { + SectionData.read(pe, entry, dr); + } else if (entry.isDebugRawData) { + readDebugRawData(pe, entry, dr); + } else { + pe.getImageData().read(pe, entry, dr); + } + } + + // Read any trailing data + byte[] tb = dr.readAll(); + if (tb.length > 0) { + pe.getImageData().setTrailingData(tb); + } + + return pe; + } + + public DataEntry findNextEntry(int pos) { + DataEntry de = new DataEntry(); + + // Check sections first + int ns = getCoffHeader().getNumberOfSections(); + for (int i = 0; i < ns; i++) { + SectionHeader sh = getSectionTable().getHeader(i); + if (sh.getSizeOfRawData() > 0 + && sh.getPointerToRawData() >= pos + && (de.pointer == 0 || sh.getPointerToRawData() < de.pointer)) { + de.pointer = sh.getPointerToRawData(); + de.index = i; + de.isSection = true; + } + } + + // Now check image data directories + RVAConverter rvc = getSectionTable().getRVAConverter(); + int dc = getOptionalHeader().getDataDirectoryCount(); + for (int i = 0; i < dc; i++) { + ImageDataDirectory idd = getOptionalHeader().getDataDirectory(i); + if (idd.getSize() > 0) { + int prd = idd.getVirtualAddress(); + // Assume certificate live outside section ? + if (i != ImageDataDirectoryType.CERTIFICATE_TABLE + && isInsideSection(idd)) { + prd = rvc.convertVirtualAddressToRawDataPointer(idd + .getVirtualAddress()); + } + if (prd >= pos && (de.pointer == 0 || prd < de.pointer)) { + de.pointer = prd; + de.index = i; + de.isSection = false; + } + } + } + + // Check debug + ImageData id = getImageData(); + DebugDirectory dd = null; + if (id != null) + dd = id.getDebug(); + if (dd != null) { + int prd = dd.getPointerToRawData(); + if (prd >= pos && (de.pointer == 0 || prd < de.pointer)) { + de.pointer = prd; + de.index = -1; + de.isDebugRawData = true; + de.isSection = false; + de.baseAddress = prd; + } + } + + if (de.pointer == 0) + return null; + + return de; + } + + private boolean isInsideSection(ImageDataDirectory idd) { + int prd = idd.getVirtualAddress(); + int pex = prd + idd.getSize(); + SectionTable st = getSectionTable(); + int ns = st.getNumberOfSections(); + for (int i = 0; i < ns; i++) { + SectionHeader sh = st.getHeader(i); + int vad = sh.getVirtualAddress(); + int vex = vad + sh.getVirtualSize(); + if (prd >= vad && prd < vex && pex <= vex) + return true; + } + return false; + } + + private static void readDebugRawData(PE pe, DataEntry entry, IDataReader dr) + throws IOException { + // Read any preamble data + ImageData id = pe.getImageData(); + byte[] pa = dr.readNonZeroOrNull(entry.pointer); + if (pa != null) + id.setDebugRawDataPreamble(pa); + DebugDirectory dd = id.getDebug(); + byte[] b = new byte[dd.getSizeOfData()]; + dr.read(b); + id.setDebugRawData(b); + } + + public void write(IDataWriter dw) throws IOException { + getDosHeader().write(dw); + getStub().write(dw); + getSignature().write(dw); + getCoffHeader().write(dw); + getOptionalHeader().write(dw); + getSectionTable().write(dw); + + // Now write out the rest + DataEntry entry = null; + while ((entry = findNextEntry(dw.getPosition())) != null) { + if (entry.isSection) { + writeSection(entry, dw); + } else if (entry.isDebugRawData) { + writeDebugRawData(entry, dw); + } else { + writeImageData(entry, dw); + } + } + + // Dump out any trailing data - TODO find out what this is + byte[] tb = getImageData().getTrailingData(); + if (tb != null) + dw.writeBytes(tb); + } + + private void writeImageData(DataEntry entry, IDataWriter dw) + throws IOException { + ImageDataDirectory idd = getOptionalHeader().getDataDirectory( + entry.index); + RVAConverter rvc = getSectionTable().getRVAConverter(); + int prd = idd.getVirtualAddress(); + if (entry.index != ImageDataDirectoryType.CERTIFICATE_TABLE) + prd = rvc.convertVirtualAddressToRawDataPointer(idd + .getVirtualAddress()); + if (prd > dw.getPosition()) { + byte[] pa = getImageData().getPreamble(entry.index); + if (pa != null) + dw.writeBytes(pa); + else + dw.writeByte(0, prd - dw.getPosition()); + } + + ImageData id = getImageData(); + + switch (entry.index) { + case ImageDataDirectoryType.EXPORT_TABLE: + dw.writeBytes(id.getExportTable().get()); + break; + case ImageDataDirectoryType.IMPORT_TABLE: + dw.writeBytes(id.getImportTable().get()); + break; + case ImageDataDirectoryType.RESOURCE_TABLE: + dw.writeBytes(id.getResourceTable().get()); + break; + case ImageDataDirectoryType.EXCEPTION_TABLE: + dw.writeBytes(id.getExceptionTable()); + break; + case ImageDataDirectoryType.CERTIFICATE_TABLE: + dw.writeBytes(id.getCertificateTable().get()); + break; + case ImageDataDirectoryType.BASE_RELOCATION_TABLE: + dw.writeBytes(id.getBaseRelocationTable()); + break; + case ImageDataDirectoryType.DEBUG: + dw.writeBytes(id.getDebug().get()); + break; + case ImageDataDirectoryType.ARCHITECTURE: + dw.writeBytes(id.getArchitecture()); + break; + case ImageDataDirectoryType.GLOBAL_PTR: + dw.writeBytes(id.getGlobalPtr()); + break; + case ImageDataDirectoryType.TLS_TABLE: + dw.writeBytes(id.getTlsTable()); + break; + case ImageDataDirectoryType.LOAD_CONFIG_TABLE: + break; + case ImageDataDirectoryType.BOUND_IMPORT: + id.getBoundImports().write(this, dw); + break; + case ImageDataDirectoryType.IAT: + dw.writeBytes(id.getIat()); + break; + case ImageDataDirectoryType.DELAY_IMPORT_DESCRIPTOR: + dw.writeBytes(id.getDelayImportDescriptor()); + break; + case ImageDataDirectoryType.CLR_RUNTIME_HEADER: + dw.writeBytes(id.getClrRuntimeHeader().get()); + break; + case ImageDataDirectoryType.RESERVED: + dw.writeBytes(id.getReserved()); + break; + } + } + + private void writeDebugRawData(DataEntry entry, IDataWriter dw) + throws IOException { + if (entry.pointer > dw.getPosition()) { + byte[] pa = getImageData().getDebugRawDataPreamble(); + if (pa != null) + dw.writeBytes(pa); + else + dw.writeByte(0, entry.pointer - dw.getPosition()); + } + dw.writeBytes(getImageData().getDebugRawData()); + } + + private void writeSection(DataEntry entry, IDataWriter dw) + throws IOException { + SectionTable st = getSectionTable(); + SectionHeader sh = st.getHeader(entry.index); + SectionData sd = st.getSection(entry.index); + int prd = sh.getPointerToRawData(); + if (prd > dw.getPosition()) { + byte[] pa = sd.getPreamble(); + if (pa != null) { + dw.writeBytes(pa); + } else { + dw.writeByte(0, prd - dw.getPosition()); + } + } + + byte[] b = sd.getData(); + dw.writeBytes(b); + } + public DOSHeader getDosHeader() { return dosHeader; } diff --git a/src/main/java/com/kichik/pecoff4j/PESignature.java b/src/main/java/com/kichik/pecoff4j/PESignature.java index ab86851..3dfede8 100644 --- a/src/main/java/com/kichik/pecoff4j/PESignature.java +++ b/src/main/java/com/kichik/pecoff4j/PESignature.java @@ -9,6 +9,10 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; import java.util.Arrays; public class PESignature { @@ -16,6 +20,18 @@ public class PESignature { private static byte[] expected2 = new byte[] { 0x50, 0x69, 0x00, 0x00 }; private byte[] signature; + public static PESignature read(IDataReader dr) throws IOException { + PESignature ps = new PESignature(); + byte[] signature = new byte[4]; + dr.read(signature); + ps.setSignature(signature); + return ps; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeBytes(getSignature()); + } + public byte[] getSignature() { return signature; } diff --git a/src/main/java/com/kichik/pecoff4j/ResourceDirectory.java b/src/main/java/com/kichik/pecoff4j/ResourceDirectory.java index a2acb6c..662a1c2 100644 --- a/src/main/java/com/kichik/pecoff4j/ResourceDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/ResourceDirectory.java @@ -9,15 +9,86 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import java.io.IOException; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Queue; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; import com.kichik.pecoff4j.util.DataObject; public class ResourceDirectory extends DataObject { private ResourceDirectoryTable table; private List entries = new ArrayList(); + public static ResourceDirectory read(IDataReader dr, int baseAddress) throws IOException { + ResourceDirectory d = new ResourceDirectory(); + d.setTable(ResourceDirectoryTable.read(dr)); + int ne = d.getTable().getNumNameEntries() + + d.getTable().getNumIdEntries(); + for (int i = 0; i < ne; i++) { + d.add(ResourceEntry.read(dr, baseAddress)); + } + + return d; + } + + public void write(IDataWriter dw) throws IOException { + List dirs = new ArrayList<>(); + List entries = new ArrayList<>(); + flattenInto(dirs, entries); + + for (ResourceDirectory dir : dirs) { + dir.getTable().write(dw); + for (int i = 0; i < dir.size(); i++) { + dir.get(i).writeNonLeaf(dw); + } + } + for (ResourceEntry re : entries) { + re.writeLeaf(dw); + } + Collections.sort(entries, Comparator.comparing(ResourceEntry::getDataRVA)); + for (ResourceDirectory dir : dirs) { + for (int i = 0; i < dir.size(); i++) { + dir.get(i).writeName(dw); + } + } + for (ResourceEntry re : entries) { + re.writeName(dw); + } + dw.align(4); + for (ResourceEntry re : entries) { + re.writeData(dw); + dw.align(4); + } + } + + /** + * Traverse tree in breath-first order and fill directories and entries into given lists. + */ + private void flattenInto(List dirs, List entries) { + Queue toProcess = new ArrayDeque<>(); + toProcess.add(this); + dirs.add(this); + + while (!toProcess.isEmpty()) { + ResourceDirectory dir = toProcess.poll(); + for (int i = 0; i < dir.size(); i++) { + ResourceEntry entry = dir.get(i); + if (entry.getDirectory() != null) { + toProcess.add(entry.getDirectory()); + dirs.add(entry.getDirectory()); + } else { + entries.add(entry); + } + } + } + } + public ResourceDirectoryTable getTable() { return table; } diff --git a/src/main/java/com/kichik/pecoff4j/ResourceDirectoryTable.java b/src/main/java/com/kichik/pecoff4j/ResourceDirectoryTable.java index 366d9b2..12b3419 100644 --- a/src/main/java/com/kichik/pecoff4j/ResourceDirectoryTable.java +++ b/src/main/java/com/kichik/pecoff4j/ResourceDirectoryTable.java @@ -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 ResourceDirectoryTable { private int characteristics; private int timeDateStamp; @@ -17,6 +22,27 @@ public class ResourceDirectoryTable { private int numNameEntries; private int numIdEntries; + public static ResourceDirectoryTable read(IDataReader dr) throws IOException { + ResourceDirectoryTable t = new ResourceDirectoryTable(); + t.setCharacteristics(dr.readDoubleWord()); + t.setTimeDateStamp(dr.readDoubleWord()); + t.setMajorVersion(dr.readWord()); + t.setMinVersion(dr.readWord()); + t.setNumNameEntries(dr.readWord()); + t.setNumIdEntries(dr.readWord()); + + return t; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeDoubleWord(getCharacteristics()); + dw.writeDoubleWord(getTimeDateStamp()); + dw.writeWord(getMajorVersion()); + dw.writeWord(getMinVersion()); + dw.writeWord(getNumNameEntries()); + dw.writeWord(getNumIdEntries()); + } + public int getCharacteristics() { return characteristics; } diff --git a/src/main/java/com/kichik/pecoff4j/ResourceEntry.java b/src/main/java/com/kichik/pecoff4j/ResourceEntry.java index 2021cb6..8c5dd7e 100644 --- a/src/main/java/com/kichik/pecoff4j/ResourceEntry.java +++ b/src/main/java/com/kichik/pecoff4j/ResourceEntry.java @@ -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 ResourceEntry { private int id; private String name; @@ -19,6 +24,61 @@ public class ResourceEntry { private int codePage; private int reserved; + public static ResourceEntry read(IDataReader dr, int baseAddress) throws IOException { + ResourceEntry re = new ResourceEntry(); + int id = dr.readDoubleWord(); + re.setId(id); + int offset = dr.readDoubleWord(); + re.setOffset(offset); + int pos = dr.getPosition(); + if ((id & 0x80000000) != 0) { + dr.jumpTo(id & 0x7fffffff); + re.setName(dr.readUnicode(dr.readWord())); + } + if ((offset & 0x80000000) != 0) { + dr.jumpTo(offset & 0x7fffffff); + re.setDirectory(ResourceDirectory.read(dr, baseAddress)); + } else { + dr.jumpTo(offset); + int rva = dr.readDoubleWord(); + int size = dr.readDoubleWord(); + int cp = dr.readDoubleWord(); + int res = dr.readDoubleWord(); + re.setDataRVA(rva); + re.setCodePage(cp); + re.setReserved(res); + dr.jumpTo(rva - baseAddress); + byte[] b = new byte[size]; + dr.read(b); + re.setData(b); + } + dr.jumpTo(pos); + return re; + } + + public void writeNonLeaf(IDataWriter dw) throws IOException { + dw.writeDoubleWord(getId()); + dw.writeDoubleWord(getOffset()); + } + + public void writeLeaf(IDataWriter dw) throws IOException { + dw.writeDoubleWord(getDataRVA()); + dw.writeDoubleWord(getData().length); + dw.writeDoubleWord(getCodePage()); + dw.writeDoubleWord(getReserved()); + } + + public void writeName(IDataWriter dw) throws IOException { + if (name != null) { + dw.writeWord(name.length()); + dw.writeUnicode(name, name.length()); + } + } + + public void writeData(IDataWriter dw) throws IOException { + dw.writeBytes(getData()); + } + public int getId() { return id; } diff --git a/src/main/java/com/kichik/pecoff4j/SectionData.java b/src/main/java/com/kichik/pecoff4j/SectionData.java index b109141..3f9b78e 100644 --- a/src/main/java/com/kichik/pecoff4j/SectionData.java +++ b/src/main/java/com/kichik/pecoff4j/SectionData.java @@ -9,10 +9,78 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import com.kichik.pecoff4j.constant.ImageDataDirectoryType; +import com.kichik.pecoff4j.io.ByteArrayDataReader; +import com.kichik.pecoff4j.io.DataEntry; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class SectionData { private byte[] data; private byte[] preamble; + public static SectionData read(PE pe, DataEntry entry, IDataReader dr) + throws IOException { + SectionTable st = pe.getSectionTable(); + SectionHeader sh = st.getHeader(entry.index); + SectionData sd = new SectionData(); + + // Read any preamble - store if non-zero + byte[] pa = dr.readNonZeroOrNull(sh.getPointerToRawData()); + if (pa != null) + sd.setPreamble(pa); + + // Read in the raw data block + dr.jumpTo(sh.getPointerToRawData()); + byte[] b = new byte[sh.getSizeOfRawData()]; + dr.read(b); + sd.setData(b); + st.put(entry.index, sd); + + // Check for an image directory within this section + int ddc = pe.getOptionalHeader().getDataDirectoryCount(); + for (int i = 0; i < ddc; i++) { + if (i == ImageDataDirectoryType.CERTIFICATE_TABLE) + continue; + ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory(i); + if (idd.getSize() > 0) { + int vad = sh.getVirtualAddress(); + int vex = vad + sh.getVirtualSize(); + int dad = idd.getVirtualAddress(); + if (dad >= vad && dad < vex) { + int off = dad - vad; + IDataReader idr = new ByteArrayDataReader(b, off, + idd.getSize()); + DataEntry de = new DataEntry(i, 0); + de.baseAddress = sh.getVirtualAddress(); + pe.getImageData().read(pe, de, idr); + } + } + } + return sd; + } + + public void write(PE pe, DataEntry entry, IDataWriter dw) + throws IOException { + SectionTable st = pe.getSectionTable(); + SectionHeader sh = st.getHeader(entry.index); + SectionData sd = st.getSection(entry.index); + int prd = sh.getPointerToRawData(); + if (prd > dw.getPosition()) { + byte[] pa = sd.getPreamble(); + if (pa != null) { + dw.writeBytes(pa); + } else { + dw.writeByte(0, prd - dw.getPosition()); + } + } + + byte[] b = sd.getData(); + dw.writeBytes(b); + } + public byte[] getPreamble() { return preamble; } diff --git a/src/main/java/com/kichik/pecoff4j/SectionHeader.java b/src/main/java/com/kichik/pecoff4j/SectionHeader.java index 908598a..f677b10 100644 --- a/src/main/java/com/kichik/pecoff4j/SectionHeader.java +++ b/src/main/java/com/kichik/pecoff4j/SectionHeader.java @@ -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 SectionHeader { private String name; private int virtualSize; @@ -21,6 +26,34 @@ public class SectionHeader { private int numberOfLineNumbers; private int characteristics; + public static SectionHeader read(IDataReader dr) throws IOException { + SectionHeader sh = new SectionHeader(); + sh.setName(dr.readUtf(8)); + sh.setVirtualSize(dr.readDoubleWord()); + sh.setVirtualAddress(dr.readDoubleWord()); + sh.setSizeOfRawData(dr.readDoubleWord()); + sh.setPointerToRawData(dr.readDoubleWord()); + sh.setPointerToRelocations(dr.readDoubleWord()); + sh.setPointerToLineNumbers(dr.readDoubleWord()); + sh.setNumberOfRelocations(dr.readWord()); + sh.setNumberOfLineNumbers(dr.readWord()); + sh.setCharacteristics(dr.readDoubleWord()); + return sh; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeUtf(getName(), 8); + dw.writeDoubleWord(getVirtualSize()); + dw.writeDoubleWord(getVirtualAddress()); + dw.writeDoubleWord(getSizeOfRawData()); + dw.writeDoubleWord(getPointerToRawData()); + dw.writeDoubleWord(getPointerToRelocations()); + dw.writeDoubleWord(getPointerToLineNumbers()); + dw.writeWord(getNumberOfRelocations()); + dw.writeWord(getNumberOfLineNumbers()); + dw.writeDoubleWord(getCharacteristics()); + } + public String getName() { return name; } diff --git a/src/main/java/com/kichik/pecoff4j/SectionTable.java b/src/main/java/com/kichik/pecoff4j/SectionTable.java index 6cc0631..8336f29 100644 --- a/src/main/java/com/kichik/pecoff4j/SectionTable.java +++ b/src/main/java/com/kichik/pecoff4j/SectionTable.java @@ -9,11 +9,14 @@ *******************************************************************************/ package com.kichik.pecoff4j; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; import com.kichik.pecoff4j.util.IntMap; public class SectionTable { @@ -28,6 +31,32 @@ public class SectionTable { private IntMap sections = new IntMap(); private RVAConverter rvaConverter; + public static SectionTable read(PE pe, IDataReader dr) + throws IOException { + SectionTable st = new SectionTable(); + int ns = pe.getCoffHeader().getNumberOfSections(); + for (int i = 0; i < ns; i++) { + st.add(SectionHeader.read(dr)); + } + + SectionHeader[] sorted = st.getHeadersPointerSorted(); + int[] virtualAddress = new int[sorted.length]; + int[] pointerToRawData = new int[sorted.length]; + for (int i = 0; i < sorted.length; i++) { + virtualAddress[i] = sorted[i].getVirtualAddress(); + pointerToRawData[i] = sorted[i].getPointerToRawData(); + } + + st.setRvaConverter(new RVAConverter(virtualAddress, pointerToRawData)); + return st; + } + + public void write(IDataWriter dw) throws IOException { + for (SectionHeader header : headers) { + header.write(dw); + } + } + public void add(SectionHeader header) { headers.add(header); } diff --git a/src/main/java/com/kichik/pecoff4j/io/DataWriter.java b/src/main/java/com/kichik/pecoff4j/io/DataWriter.java index 0f99621..de8ba6e 100644 --- a/src/main/java/com/kichik/pecoff4j/io/DataWriter.java +++ b/src/main/java/com/kichik/pecoff4j/io/DataWriter.java @@ -10,6 +10,7 @@ package com.kichik.pecoff4j.io; import java.io.BufferedOutputStream; +import java.io.EOFException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -103,4 +104,35 @@ public void writeUtf(String s) throws IOException { out.write(0); position += b.length + 1; } + + @Override + public void writeUnicode(String s) throws IOException { + for (char c : s.toCharArray()) { + writeWord(c); + } + writeWord(0); + } + + @Override + public void writeUnicode(String s, int len) throws IOException { + char[] c = s.toCharArray(); + int i = 0; + for (; i < c.length && i < len; i++) { + writeWord(c[i]); + } + for (; i < len; i++) { + writeWord(0); + } + } + + @Override + public int align(int alignment) throws IOException { + int off = (alignment - (getPosition() % alignment)) % alignment; + try { + writeByte(0, off); + } catch (EOFException ignored) { + // no need to align when it's at the end of its data + } + return off; + } } diff --git a/src/main/java/com/kichik/pecoff4j/io/IDataReader.java b/src/main/java/com/kichik/pecoff4j/io/IDataReader.java index 03326cb..29c104f 100644 --- a/src/main/java/com/kichik/pecoff4j/io/IDataReader.java +++ b/src/main/java/com/kichik/pecoff4j/io/IDataReader.java @@ -9,6 +9,7 @@ *******************************************************************************/ package com.kichik.pecoff4j.io; +import java.io.EOFException; import java.io.IOException; public interface IDataReader extends AutoCloseable { @@ -41,4 +42,40 @@ public interface IDataReader extends AutoCloseable { public abstract String readUnicode(int size) throws IOException; public abstract byte[] readAll() throws IOException; + + /** + * Align the reader's position + * @param alignment the alignment in bytes + * @return the number of bytes that have been skipped + */ + default int align(int alignment) throws IOException { + int off = (alignment - (getPosition() % alignment)) % alignment; + try { + skipBytes(off); + } catch (EOFException ignored) { + // no need to align when it's at the end of its data + } + return off; + } + + /** + * Read all bytes until the given position is reached. If all these bytes are zero, null is returned instead. + */ + default byte[] readNonZeroOrNull(int pointer) throws IOException { + if (pointer > getPosition()) { + byte[] pa = new byte[pointer - getPosition()]; + read(pa); + boolean zeroes = true; + for (int i = 0; i < pa.length; i++) { + if (pa[i] != 0) { + zeroes = false; + break; + } + } + if (!zeroes) + return pa; + } + + return null; + } } diff --git a/src/main/java/com/kichik/pecoff4j/io/IDataWriter.java b/src/main/java/com/kichik/pecoff4j/io/IDataWriter.java index b043e06..78dc5e2 100644 --- a/src/main/java/com/kichik/pecoff4j/io/IDataWriter.java +++ b/src/main/java/com/kichik/pecoff4j/io/IDataWriter.java @@ -9,6 +9,7 @@ *******************************************************************************/ package com.kichik.pecoff4j.io; +import java.io.EOFException; import java.io.IOException; public interface IDataWriter { @@ -28,5 +29,16 @@ public interface IDataWriter { void writeUtf(String s, int len) throws IOException; - public abstract int getPosition(); + void writeUnicode(String s) throws IOException; + + void writeUnicode(String s, int len) throws IOException; + + int getPosition(); + + /** + * Align the writer's position by writing zeros + * @param alignment the alignment in bytes + * @return the number of bytes that have been written + */ + int align(int alignment) throws IOException; } diff --git a/src/main/java/com/kichik/pecoff4j/io/PEAssembler.java b/src/main/java/com/kichik/pecoff4j/io/PEAssembler.java index 73e9997..69fb442 100644 --- a/src/main/java/com/kichik/pecoff4j/io/PEAssembler.java +++ b/src/main/java/com/kichik/pecoff4j/io/PEAssembler.java @@ -9,33 +9,13 @@ *******************************************************************************/ package com.kichik.pecoff4j.io; +import com.kichik.pecoff4j.PE; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.kichik.pecoff4j.BoundImport; -import com.kichik.pecoff4j.BoundImportDirectoryTable; -import com.kichik.pecoff4j.COFFHeader; -import com.kichik.pecoff4j.DOSHeader; -import com.kichik.pecoff4j.DOSStub; -import com.kichik.pecoff4j.ImageData; -import com.kichik.pecoff4j.ImageDataDirectory; -import com.kichik.pecoff4j.OptionalHeader; -import com.kichik.pecoff4j.PE; -import com.kichik.pecoff4j.PESignature; -import com.kichik.pecoff4j.RVAConverter; -import com.kichik.pecoff4j.SectionData; -import com.kichik.pecoff4j.SectionHeader; -import com.kichik.pecoff4j.SectionTable; -import com.kichik.pecoff4j.constant.ImageDataDirectoryType; public class PEAssembler { public static byte[] toBytes(PE pe) throws IOException { @@ -58,306 +38,11 @@ public static void write(PE pe, OutputStream os) throws IOException { dw.flush(); } + /** + * @deprecated use {@link PE#write(IDataWriter)} instead + */ + @Deprecated public static void write(PE pe, IDataWriter dw) throws IOException { - write(pe.getDosHeader(), dw); - write(pe.getStub(), dw); - write(pe.getSignature(), dw); - write(pe.getCoffHeader(), dw); - write(pe.getOptionalHeader(), dw); - writeSectionHeaders(pe, dw); - - // Now write out the rest - DataEntry entry = null; - while ((entry = PEParser.findNextEntry(pe, dw.getPosition())) != null) { - if (entry.isSection) { - writeSection(pe, entry, dw); - } else if (entry.isDebugRawData) { - writeDebugRawData(pe, entry, dw); - } else { - writeImageData(pe, entry, dw); - } - } - - // Dump out any trailing data - TODO find out what this is - byte[] tb = pe.getImageData().getTrailingData(); - if (tb != null) - dw.writeBytes(tb); - } - - private static void write(DOSHeader dh, IDataWriter dw) throws IOException { - dw.writeWord(dh.getMagic()); - dw.writeWord(dh.getUsedBytesInLastPage()); - dw.writeWord(dh.getFileSizeInPages()); - dw.writeWord(dh.getNumRelocationItems()); - dw.writeWord(dh.getHeaderSizeInParagraphs()); - dw.writeWord(dh.getMinExtraParagraphs()); - dw.writeWord(dh.getMaxExtraParagraphs()); - dw.writeWord(dh.getInitialSS()); - dw.writeWord(dh.getInitialSP()); - dw.writeWord(dh.getChecksum()); - dw.writeWord(dh.getInitialIP()); - dw.writeWord(dh.getInitialRelativeCS()); - dw.writeWord(dh.getAddressOfRelocationTable()); - dw.writeWord(dh.getOverlayNumber()); - int[] res = dh.getReserved(); - for (int i = 0; i < res.length; i++) { - dw.writeWord(res[i]); - } - dw.writeWord(dh.getOemId()); - dw.writeWord(dh.getOemInfo()); - int[] res2 = dh.getReserved2(); - for (int i = 0; i < res2.length; i++) { - dw.writeWord(res2[i]); - } - dw.writeDoubleWord(dh.getAddressOfNewExeHeader()); - } - - private static void write(DOSStub stub, IDataWriter dw) throws IOException { - dw.writeBytes(stub.getStub()); - } - - private static void write(PESignature s, IDataWriter dw) throws IOException { - dw.writeBytes(s.getSignature()); - } - - private static void write(COFFHeader ch, IDataWriter dw) throws IOException { - dw.writeWord(ch.getMachine()); - dw.writeWord(ch.getNumberOfSections()); - dw.writeDoubleWord(ch.getTimeDateStamp()); - dw.writeDoubleWord(ch.getPointerToSymbolTable()); - dw.writeDoubleWord(ch.getNumberOfSymbols()); - dw.writeWord(ch.getSizeOfOptionalHeader()); - dw.writeWord(ch.getCharacteristics()); - } - - private static void write(OptionalHeader oh, IDataWriter dw) - throws IOException { - boolean is64 = oh.isPE32plus(); - - dw.writeWord(oh.getMagic()); - dw.writeByte(oh.getMajorLinkerVersion()); - dw.writeByte(oh.getMinorLinkerVersion()); - dw.writeDoubleWord(oh.getSizeOfCode()); - dw.writeDoubleWord(oh.getSizeOfInitializedData()); - dw.writeDoubleWord(oh.getSizeOfUninitializedData()); - dw.writeDoubleWord(oh.getAddressOfEntryPoint()); - dw.writeDoubleWord(oh.getBaseOfCode()); - if (!is64) - dw.writeDoubleWord(oh.getBaseOfData()); - - // NT additional fields. - if (is64) - dw.writeLong(oh.getImageBase()); - else - dw.writeDoubleWord((int) oh.getImageBase()); - - dw.writeDoubleWord(oh.getSectionAlignment()); - dw.writeDoubleWord(oh.getFileAlignment()); - dw.writeWord(oh.getMajorOperatingSystemVersion()); - dw.writeWord(oh.getMinorOperatingSystemVersion()); - dw.writeWord(oh.getMajorImageVersion()); - dw.writeWord(oh.getMinorImageVersion()); - dw.writeWord(oh.getMajorSubsystemVersion()); - dw.writeWord(oh.getMinorSubsystemVersion()); - dw.writeDoubleWord(oh.getWin32VersionValue()); - dw.writeDoubleWord(oh.getSizeOfImage()); - dw.writeDoubleWord(oh.getSizeOfHeaders()); - dw.writeDoubleWord(oh.getCheckSum()); - dw.writeWord(oh.getSubsystem()); - dw.writeWord(oh.getDllCharacteristics()); - if (is64) { - dw.writeLong(oh.getSizeOfStackReserve()); - dw.writeLong(oh.getSizeOfStackCommit()); - dw.writeLong(oh.getSizeOfHeapReserve()); - dw.writeLong(oh.getSizeOfHeapCommit()); - } else { - dw.writeDoubleWord((int) oh.getSizeOfStackReserve()); - dw.writeDoubleWord((int) oh.getSizeOfStackCommit()); - dw.writeDoubleWord((int) oh.getSizeOfHeapReserve()); - dw.writeDoubleWord((int) oh.getSizeOfHeapCommit()); - } - - dw.writeDoubleWord(oh.getLoaderFlags()); - dw.writeDoubleWord(oh.getNumberOfRvaAndSizes()); - - // Data directories - int ddc = oh.getDataDirectoryCount(); - for (int i = 0; i < ddc; i++) { - write(oh.getDataDirectory(i), dw); - } - } - - private static void write(ImageDataDirectory idd, IDataWriter dw) - throws IOException { - dw.writeDoubleWord(idd.getVirtualAddress()); - dw.writeDoubleWord(idd.getSize()); - } - - private static void writeSectionHeaders(PE pe, IDataWriter dw) - throws IOException { - SectionTable st = pe.getSectionTable(); - int ns = st.getNumberOfSections(); - for (int i = 0; i < ns; i++) { - SectionHeader sh = st.getHeader(i); - writeSectionHeader(sh, dw); - } - } - - private static void writeSectionHeader(SectionHeader sh, IDataWriter dw) - throws IOException { - dw.writeUtf(sh.getName(), 8); - dw.writeDoubleWord(sh.getVirtualSize()); - dw.writeDoubleWord(sh.getVirtualAddress()); - dw.writeDoubleWord(sh.getSizeOfRawData()); - dw.writeDoubleWord(sh.getPointerToRawData()); - dw.writeDoubleWord(sh.getPointerToRelocations()); - dw.writeDoubleWord(sh.getPointerToLineNumbers()); - dw.writeWord(sh.getNumberOfRelocations()); - dw.writeWord(sh.getNumberOfLineNumbers()); - dw.writeDoubleWord(sh.getCharacteristics()); - } - - private static void writeImageData(PE pe, DataEntry entry, IDataWriter dw) - throws IOException { - ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory( - entry.index); - RVAConverter rvc = pe.getSectionTable().getRVAConverter(); - int prd = idd.getVirtualAddress(); - if (entry.index != ImageDataDirectoryType.CERTIFICATE_TABLE) - prd = rvc.convertVirtualAddressToRawDataPointer(idd - .getVirtualAddress()); - if (prd > dw.getPosition()) { - byte[] pa = pe.getImageData().getPreamble(entry.index); - if (pa != null) - dw.writeBytes(pa); - else - dw.writeByte(0, prd - dw.getPosition()); - } - - ImageData id = pe.getImageData(); - - switch (entry.index) { - case ImageDataDirectoryType.EXPORT_TABLE: - dw.writeBytes(id.getExportTable().get()); - break; - case ImageDataDirectoryType.IMPORT_TABLE: - dw.writeBytes(id.getImportTable().get()); - break; - case ImageDataDirectoryType.RESOURCE_TABLE: - dw.writeBytes(id.getResourceTable().get()); - break; - case ImageDataDirectoryType.EXCEPTION_TABLE: - dw.writeBytes(id.getExceptionTable()); - break; - case ImageDataDirectoryType.CERTIFICATE_TABLE: - dw.writeBytes(id.getCertificateTable().get()); - break; - case ImageDataDirectoryType.BASE_RELOCATION_TABLE: - dw.writeBytes(id.getBaseRelocationTable()); - break; - case ImageDataDirectoryType.DEBUG: - dw.writeBytes(id.getDebug().get()); - break; - case ImageDataDirectoryType.ARCHITECTURE: - dw.writeBytes(id.getArchitecture()); - break; - case ImageDataDirectoryType.GLOBAL_PTR: - dw.writeBytes(id.getGlobalPtr()); - break; - case ImageDataDirectoryType.TLS_TABLE: - dw.writeBytes(id.getTlsTable()); - break; - case ImageDataDirectoryType.LOAD_CONFIG_TABLE: - break; - case ImageDataDirectoryType.BOUND_IMPORT: - write(pe, id.getBoundImports(), dw); - break; - case ImageDataDirectoryType.IAT: - dw.writeBytes(id.getIat()); - break; - case ImageDataDirectoryType.DELAY_IMPORT_DESCRIPTOR: - dw.writeBytes(id.getDelayImportDescriptor()); - break; - case ImageDataDirectoryType.CLR_RUNTIME_HEADER: - dw.writeBytes(id.getClrRuntimeHeader().get()); - break; - case ImageDataDirectoryType.RESERVED: - dw.writeBytes(id.getReserved()); - break; - } - } - - private static void writeDebugRawData(PE pe, DataEntry entry, IDataWriter dw) - throws IOException { - if (entry.pointer > dw.getPosition()) { - byte[] pa = pe.getImageData().getDebugRawDataPreamble(); - if (pa != null) - dw.writeBytes(pa); - else - dw.writeByte(0, entry.pointer - dw.getPosition()); - } - dw.writeBytes(pe.getImageData().getDebugRawData()); - } - - private static void writeSection(PE pe, DataEntry entry, IDataWriter dw) - throws IOException { - SectionTable st = pe.getSectionTable(); - SectionHeader sh = st.getHeader(entry.index); - SectionData sd = st.getSection(entry.index); - int prd = sh.getPointerToRawData(); - if (prd > dw.getPosition()) { - byte[] pa = sd.getPreamble(); - if (pa != null) { - dw.writeBytes(pa); - } else { - dw.writeByte(0, prd - dw.getPosition()); - } - } - - byte[] b = sd.getData(); - dw.writeBytes(b); - } - - private static void write(PE pe, BoundImportDirectoryTable bidt, - IDataWriter dw) throws IOException { - int pos = dw.getPosition(); - List bil = new ArrayList(); - - for (int i = 0; i < bidt.size(); i++) { - BoundImport bi = bidt.get(i); - bil.add(bi); - dw.writeDoubleWord((int) bi.getTimestamp()); - dw.writeWord(bi.getOffsetToModuleName()); - dw.writeWord(bi.getNumberOfModuleForwarderRefs()); - } - - Collections.sort(bil, new Comparator() { - @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); - } + pe.write(dw); } } diff --git a/src/main/java/com/kichik/pecoff4j/io/PEParser.java b/src/main/java/com/kichik/pecoff4j/io/PEParser.java index d9317ea..d0665b9 100644 --- a/src/main/java/com/kichik/pecoff4j/io/PEParser.java +++ b/src/main/java/com/kichik/pecoff4j/io/PEParser.java @@ -9,27 +9,12 @@ *******************************************************************************/ package com.kichik.pecoff4j.io; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - import com.kichik.pecoff4j.AttributeCertificateTable; -import com.kichik.pecoff4j.BoundImport; -import com.kichik.pecoff4j.BoundImportDirectoryTable; -import com.kichik.pecoff4j.CLRRuntimeHeader; import com.kichik.pecoff4j.COFFHeader; import com.kichik.pecoff4j.DOSHeader; import com.kichik.pecoff4j.DOSStub; import com.kichik.pecoff4j.DebugDirectory; import com.kichik.pecoff4j.ExportDirectory; -import com.kichik.pecoff4j.ImageData; import com.kichik.pecoff4j.ImageDataDirectory; import com.kichik.pecoff4j.ImportDirectory; import com.kichik.pecoff4j.ImportDirectoryEntry; @@ -39,15 +24,15 @@ import com.kichik.pecoff4j.OptionalHeader; import com.kichik.pecoff4j.PE; import com.kichik.pecoff4j.PESignature; -import com.kichik.pecoff4j.RVAConverter; -import com.kichik.pecoff4j.ResourceDirectory; -import com.kichik.pecoff4j.ResourceDirectoryTable; -import com.kichik.pecoff4j.ResourceEntry; -import com.kichik.pecoff4j.SectionData; import com.kichik.pecoff4j.SectionHeader; import com.kichik.pecoff4j.SectionTable; -import com.kichik.pecoff4j.constant.ImageDataDirectoryType; -import com.kichik.pecoff4j.util.IntMap; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; public class PEParser { public static PE parse(InputStream is) throws IOException { @@ -74,726 +59,179 @@ public static PE parse(Path path) throws IOException { } } + /** + * @deprecated use {@link PE#read(IDataReader)} instead + */ + @Deprecated public static PE read(IDataReader dr) throws IOException { - PE pe = new PE(); - pe.setDosHeader(readDos(dr)); - - // Check if we have an old file type - if (pe.getDosHeader().getAddressOfNewExeHeader() == 0 - || pe.getDosHeader().getAddressOfNewExeHeader() > 8192) { - return pe; - } - - pe.setStub(readStub(pe.getDosHeader(), dr)); - pe.setSignature(readSignature(dr)); - - // Check signature to ensure we have a pe/coff file - if (!pe.getSignature().isValid()) { - return pe; - } - - pe.setCoffHeader(readCOFF(dr)); - pe.setOptionalHeader(readOptional(dr)); - pe.setSectionTable(readSectionHeaders(pe, dr)); - - pe.set64(pe.getOptionalHeader().isPE32plus()); - - // Now read the rest of the file - DataEntry entry = null; - while ((entry = findNextEntry(pe, dr.getPosition())) != null) { - if (entry.isSection) { - readSection(pe, entry, dr); - } else if (entry.isDebugRawData) { - readDebugRawData(pe, entry, dr); - } else { - readImageData(pe, entry, dr); - } - } - - // Read any trailing data - byte[] tb = dr.readAll(); - if (tb.length > 0) { - pe.getImageData().setTrailingData(tb); - } - - return pe; + return PE.read(dr); } + /** + * @deprecated use {@link DOSHeader#read(IDataReader)} instead + */ + @Deprecated public static DOSHeader readDos(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; + return DOSHeader.read(dr); } + /** + * @deprecated use {@link DOSStub#read(DOSHeader, IDataReader)} instead + */ + @Deprecated public static DOSStub readStub(DOSHeader header, IDataReader dr) throws IOException { - DOSStub ds = new DOSStub(); - int pos = dr.getPosition(); - int add = header.getAddressOfNewExeHeader(); - byte[] stub = new byte[add - pos]; - dr.read(stub); - ds.setStub(stub); - return ds; + return DOSStub.read(header, dr); } + /** + * @deprecated use {@link PESignature#read(IDataReader)} instead + */ + @Deprecated public static PESignature readSignature(IDataReader dr) throws IOException { - PESignature ps = new PESignature(); - byte[] signature = new byte[4]; - dr.read(signature); - ps.setSignature(signature); - return ps; + return PESignature.read(dr); } + /** + * @deprecated use {@link COFFHeader#read(IDataReader)} instead + */ + @Deprecated public static COFFHeader readCOFF(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; + return COFFHeader.read(dr); } + /** + * @deprecated use {@link OptionalHeader#read(IDataReader)} instead + */ + @Deprecated public static OptionalHeader readOptional(IDataReader dr) throws IOException { - OptionalHeader oh = new OptionalHeader(); - oh.setMagic(dr.readWord()); - boolean is64 = oh.isPE32plus(); - oh.setMajorLinkerVersion(dr.readByte()); - oh.setMinorLinkerVersion(dr.readByte()); - oh.setSizeOfCode(dr.readDoubleWord()); - oh.setSizeOfInitializedData(dr.readDoubleWord()); - oh.setSizeOfUninitializedData(dr.readDoubleWord()); - oh.setAddressOfEntryPoint(dr.readDoubleWord()); - oh.setBaseOfCode(dr.readDoubleWord()); - - if (!is64) - oh.setBaseOfData(dr.readDoubleWord()); - - // NT additional fields. - oh.setImageBase(is64 ? dr.readLong() : dr.readDoubleWord()); - oh.setSectionAlignment(dr.readDoubleWord()); - oh.setFileAlignment(dr.readDoubleWord()); - oh.setMajorOperatingSystemVersion(dr.readWord()); - oh.setMinorOperatingSystemVersion(dr.readWord()); - oh.setMajorImageVersion(dr.readWord()); - oh.setMinorImageVersion(dr.readWord()); - oh.setMajorSubsystemVersion(dr.readWord()); - oh.setMinorSubsystemVersion(dr.readWord()); - oh.setWin32VersionValue(dr.readDoubleWord()); - oh.setSizeOfImage(dr.readDoubleWord()); - oh.setSizeOfHeaders(dr.readDoubleWord()); - oh.setCheckSum(dr.readDoubleWord()); - oh.setSubsystem(dr.readWord()); - oh.setDllCharacteristics(dr.readWord()); - oh.setSizeOfStackReserve(is64 ? dr.readLong() : dr.readDoubleWord()); - oh.setSizeOfStackCommit(is64 ? dr.readLong() : dr.readDoubleWord()); - oh.setSizeOfHeapReserve(is64 ? dr.readLong() : dr.readDoubleWord()); - oh.setSizeOfHeapCommit(is64 ? dr.readLong() : dr.readDoubleWord()); - oh.setLoaderFlags(dr.readDoubleWord()); - oh.setNumberOfRvaAndSizes(dr.readDoubleWord()); - - // Data directories - ImageDataDirectory[] dds = new ImageDataDirectory[16]; - for (int i = 0; i < dds.length; i++) { - dds[i] = readImageDD(dr); - } - oh.setDataDirectories(dds); - - return oh; + return OptionalHeader.read(dr); } + /** + * @deprecated use {@link ImageDataDirectory#read(IDataReader)} instead + */ + @Deprecated public static ImageDataDirectory readImageDD(IDataReader dr) throws IOException { - ImageDataDirectory idd = new ImageDataDirectory(); - idd.setVirtualAddress(dr.readDoubleWord()); - idd.setSize(dr.readDoubleWord()); - return idd; + return ImageDataDirectory.read(dr); } + /** + * @deprecated use {@link SectionTable#read(PE, IDataReader)} instead + */ + @Deprecated public static SectionTable readSectionHeaders(PE pe, IDataReader dr) throws IOException { - SectionTable st = new SectionTable(); - int ns = pe.getCoffHeader().getNumberOfSections(); - for (int i = 0; i < ns; i++) { - st.add(readSectionHeader(dr)); - } - - SectionHeader[] sorted = st.getHeadersPointerSorted(); - int[] virtualAddress = new int[sorted.length]; - int[] pointerToRawData = new int[sorted.length]; - for (int i = 0; i < sorted.length; i++) { - virtualAddress[i] = sorted[i].getVirtualAddress(); - pointerToRawData[i] = sorted[i].getPointerToRawData(); - } - - st.setRvaConverter(new RVAConverter(virtualAddress, pointerToRawData)); - return st; + return SectionTable.read(pe, dr); } + /** + * @deprecated use {@link SectionHeader#read(IDataReader)} instead + */ + @Deprecated public static SectionHeader readSectionHeader(IDataReader dr) throws IOException { - SectionHeader sh = new SectionHeader(); - sh.setName(dr.readUtf(8)); - sh.setVirtualSize(dr.readDoubleWord()); - sh.setVirtualAddress(dr.readDoubleWord()); - sh.setSizeOfRawData(dr.readDoubleWord()); - sh.setPointerToRawData(dr.readDoubleWord()); - sh.setPointerToRelocations(dr.readDoubleWord()); - sh.setPointerToLineNumbers(dr.readDoubleWord()); - sh.setNumberOfRelocations(dr.readWord()); - sh.setNumberOfLineNumbers(dr.readWord()); - sh.setCharacteristics(dr.readDoubleWord()); - return sh; + return SectionHeader.read(dr); } + /** + * @deprecated use {@link PE#findNextEntry(int)} instead + */ + @Deprecated public static DataEntry findNextEntry(PE pe, int pos) { - DataEntry de = new DataEntry(); - - // Check sections first - int ns = pe.getCoffHeader().getNumberOfSections(); - for (int i = 0; i < ns; i++) { - SectionHeader sh = pe.getSectionTable().getHeader(i); - if (sh.getSizeOfRawData() > 0 - && sh.getPointerToRawData() >= pos - && (de.pointer == 0 || sh.getPointerToRawData() < de.pointer)) { - de.pointer = sh.getPointerToRawData(); - de.index = i; - de.isSection = true; - } - } - - // Now check image data directories - RVAConverter rvc = pe.getSectionTable().getRVAConverter(); - int dc = pe.getOptionalHeader().getDataDirectoryCount(); - for (int i = 0; i < dc; i++) { - ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory(i); - if (idd.getSize() > 0) { - int prd = idd.getVirtualAddress(); - // Assume certificate live outside section ? - if (i != ImageDataDirectoryType.CERTIFICATE_TABLE - && isInsideSection(pe, idd)) { - prd = rvc.convertVirtualAddressToRawDataPointer(idd - .getVirtualAddress()); - } - if (prd >= pos && (de.pointer == 0 || prd < de.pointer)) { - de.pointer = prd; - de.index = i; - de.isSection = false; - } - } - } - - // Check debug - ImageData id = pe.getImageData(); - DebugDirectory dd = null; - if (id != null) - dd = id.getDebug(); - if (dd != null) { - int prd = dd.getPointerToRawData(); - if (prd >= pos && (de.pointer == 0 || prd < de.pointer)) { - de.pointer = prd; - de.index = -1; - de.isDebugRawData = true; - de.isSection = false; - de.baseAddress = prd; - } - } - - if (de.pointer == 0) - return null; - - return de; - } - - private static boolean isInsideSection(PE pe, ImageDataDirectory idd) { - int prd = idd.getVirtualAddress(); - int pex = prd + idd.getSize(); - SectionTable st = pe.getSectionTable(); - int ns = st.getNumberOfSections(); - for (int i = 0; i < ns; i++) { - SectionHeader sh = st.getHeader(i); - int vad = sh.getVirtualAddress(); - int vex = vad + sh.getVirtualSize(); - if (prd >= vad && prd < vex && pex <= vex) - return true; - } - return false; - } - - private static void readImageData(PE pe, DataEntry entry, IDataReader dr) - throws IOException { - - // Read any preamble data - ImageData id = pe.getImageData(); - byte[] pa = readPreambleData(entry.pointer, dr); - if (pa != null) - id.put(entry.index, pa); - - // Read the image data - ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory( - entry.index); - byte[] b = new byte[idd.getSize()]; - dr.read(b); - - switch (entry.index) { - case ImageDataDirectoryType.EXPORT_TABLE: - id.setExportTable(readExportDirectory(b)); - break; - case ImageDataDirectoryType.IMPORT_TABLE: - id.setImportTable(readImportDirectory(b, entry.baseAddress)); - break; - case ImageDataDirectoryType.RESOURCE_TABLE: - id.setResourceTable(readResourceDirectory(b, entry.baseAddress)); - break; - case ImageDataDirectoryType.EXCEPTION_TABLE: - id.setExceptionTable(b); - break; - case ImageDataDirectoryType.CERTIFICATE_TABLE: - id.setCertificateTable(readAttributeCertificateTable(b)); - break; - case ImageDataDirectoryType.BASE_RELOCATION_TABLE: - id.setBaseRelocationTable(b); - break; - case ImageDataDirectoryType.DEBUG: - id.setDebug(readDebugDirectory(b)); - break; - case ImageDataDirectoryType.ARCHITECTURE: - id.setArchitecture(b); - break; - case ImageDataDirectoryType.GLOBAL_PTR: - id.setGlobalPtr(b); - break; - case ImageDataDirectoryType.TLS_TABLE: - id.setTlsTable(b); - break; - case ImageDataDirectoryType.LOAD_CONFIG_TABLE: - id.setLoadConfigTable(readLoadConfigDirectory(pe, b)); - break; - case ImageDataDirectoryType.BOUND_IMPORT: - id.setBoundImports(readBoundImportDirectoryTable(b)); - break; - case ImageDataDirectoryType.IAT: - id.setIat(b); - break; - case ImageDataDirectoryType.DELAY_IMPORT_DESCRIPTOR: - id.setDelayImportDescriptor(b); - break; - case ImageDataDirectoryType.CLR_RUNTIME_HEADER: - id.setClrRuntimeHeader(readClrRuntimeHeader(b)); - break; - case ImageDataDirectoryType.RESERVED: - id.setReserved(b); - break; - } - } - - private static byte[] readPreambleData(int pointer, IDataReader dr) - throws IOException { - if (pointer > dr.getPosition()) { - byte[] pa = new byte[pointer - dr.getPosition()]; - dr.read(pa); - boolean zeroes = true; - for (int i = 0; i < pa.length; i++) { - if (pa[i] != 0) { - zeroes = false; - break; - } - } - if (!zeroes) - return pa; - } - - return null; - } - - private static void readDebugRawData(PE pe, DataEntry entry, IDataReader dr) - throws IOException { - // Read any preamble data - ImageData id = pe.getImageData(); - byte[] pa = readPreambleData(entry.pointer, dr); - if (pa != null) - id.setDebugRawDataPreamble(pa); - DebugDirectory dd = id.getDebug(); - byte[] b = new byte[dd.getSizeOfData()]; - dr.read(b); - id.setDebugRawData(b); - } - - private static void readSection(PE pe, DataEntry entry, IDataReader dr) - throws IOException { - SectionTable st = pe.getSectionTable(); - SectionHeader sh = st.getHeader(entry.index); - SectionData sd = new SectionData(); - - // Read any preamble - store if non-zero - byte[] pa = readPreambleData(sh.getPointerToRawData(), dr); - if (pa != null) - sd.setPreamble(pa); - - // Read in the raw data block - dr.jumpTo(sh.getPointerToRawData()); - byte[] b = new byte[sh.getSizeOfRawData()]; - dr.read(b); - sd.setData(b); - st.put(entry.index, sd); - - // Check for an image directory within this section - int ddc = pe.getOptionalHeader().getDataDirectoryCount(); - for (int i = 0; i < ddc; i++) { - if (i == ImageDataDirectoryType.CERTIFICATE_TABLE) - continue; - ImageDataDirectory idd = pe.getOptionalHeader().getDataDirectory(i); - if (idd.getSize() > 0) { - int vad = sh.getVirtualAddress(); - int vex = vad + sh.getVirtualSize(); - int dad = idd.getVirtualAddress(); - if (dad >= vad && dad < vex) { - int off = dad - vad; - IDataReader idr = new ByteArrayDataReader(b, off, - idd.getSize()); - DataEntry de = new DataEntry(i, 0); - de.baseAddress = sh.getVirtualAddress(); - readImageData(pe, de, idr); - } - } - } - } - - private static CLRRuntimeHeader readClrRuntimeHeader( - 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; - } - - private static BoundImportDirectoryTable readBoundImportDirectoryTable( - byte[] b) throws IOException { - DataReader dr = new DataReader(b); - BoundImportDirectoryTable bidt = new BoundImportDirectoryTable(); - List imports = new ArrayList(); - BoundImport bi = null; - while ((bi = readBoundImport(dr)) != null) { - bidt.add(bi); - imports.add(bi); - } - Collections.sort(imports, new Comparator() { - @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; - } - - private static BoundImport readBoundImport(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; + return pe.findNextEntry(pos); } + /** + * @deprecated use {@link ImportDirectory#read(byte[], int)} instead + */ + @Deprecated public static ImportDirectory readImportDirectory(byte[] b, int baseAddress) throws IOException { - DataReader dr = new DataReader(b); - ImportDirectory id = new ImportDirectory(); - ImportDirectoryEntry ide = null; - while ((ide = readImportDirectoryEntry(dr)) != null) { - id.add(ide); - } - - /* - * FIXME - name table refer to data outside image directory for (int i = - * 0; i < id.size(); i++) { ImportDirectoryEntry e = id.getEntry(i); - * dr.jumpTo(e.getNameRVA() - baseAddress); String name = dr.readUtf(); - * dr.jumpTo(e.getImportLookupTableRVA() - baseAddress); - * ImportDirectoryTable nt = readImportDirectoryTable(dr, baseAddress); - * dr.jumpTo(e.getImportAddressTableRVA() - baseAddress); - * ImportDirectoryTable at = null; // readImportDirectoryTable(dr, // - * baseAddress); id.add(name, nt, at); } - */ - - return id; + return ImportDirectory.read(b, baseAddress); } + /** + * @deprecated use {@link ImportDirectoryEntry#read(IDataReader)} instead + */ + @Deprecated public static ImportDirectoryEntry readImportDirectoryEntry(IDataReader dr) throws IOException { - ImportDirectoryEntry id = new ImportDirectoryEntry(); - id.setImportLookupTableRVA(dr.readDoubleWord()); - id.setTimeDateStamp(dr.readDoubleWord()); - id.setForwarderChain(dr.readDoubleWord()); - id.setNameRVA(dr.readDoubleWord()); - id.setImportAddressTableRVA(dr.readDoubleWord()); - - // The last entry is null - if (id.getImportLookupTableRVA() == 0) { - return null; - } - - return id; + return ImportDirectoryEntry.read(dr); } + /** + * @deprecated use {@link ImportDirectoryTable#read(IDataReader, int)} instead + */ + @Deprecated public static ImportDirectoryTable readImportDirectoryTable(IDataReader dr, int baseAddress) throws IOException { - ImportDirectoryTable idt = new ImportDirectoryTable(); - ImportEntry ie = null; - while ((ie = readImportEntry(dr)) != null) { - idt.add(ie); - } - - for (int i = 0; i < idt.size(); i++) { - ImportEntry iee = idt.getEntry(i); - if ((iee.getVal() & 0x80000000) != 0) { - iee.setOrdinal(iee.getVal() & 0x7fffffff); - } else { - dr.jumpTo(iee.getVal() - baseAddress); - dr.readWord(); // FIXME this is an index into the export table - iee.setName(dr.readUtf()); - } - } - return idt; + return ImportDirectoryTable.read(dr, baseAddress); } + /** + * @deprecated use {@link ImportEntry#read(IDataReader)} instead + */ + @Deprecated public static ImportEntry readImportEntry(IDataReader dr) throws IOException { - ImportEntry ie = new ImportEntry(); - ie.setVal(dr.readDoubleWord()); - if (ie.getVal() == 0) { - return null; - } - - return ie; + return ImportEntry.read(dr); } + /** + * @deprecated use {@link ExportDirectory#read(byte[])} instead + */ + @Deprecated public static ExportDirectory readExportDirectory(byte[] b) throws IOException { - DataReader dr = new DataReader(b); - ExportDirectory edt = new ExportDirectory(); - edt.set(b); - edt.setExportFlags(dr.readDoubleWord()); - edt.setTimeDateStamp(dr.readDoubleWord()); - edt.setMajorVersion(dr.readWord()); - edt.setMinorVersion(dr.readWord()); - edt.setNameRVA(dr.readDoubleWord()); - edt.setOrdinalBase(dr.readDoubleWord()); - edt.setAddressTableEntries(dr.readDoubleWord()); - edt.setNumberOfNamePointers(dr.readDoubleWord()); - edt.setExportAddressTableRVA(dr.readDoubleWord()); - edt.setNamePointerRVA(dr.readDoubleWord()); - edt.setOrdinalTableRVA(dr.readDoubleWord()); - return edt; + return ExportDirectory.read(b); } + /** + * @deprecated use {@link LoadConfigDirectory#read(PE, byte[])} instead + */ + @Deprecated public static LoadConfigDirectory readLoadConfigDirectory(PE pe, byte[] b) throws IOException { - DataReader dr = new DataReader(b); - LoadConfigDirectory lcd = new LoadConfigDirectory(); - lcd.set(b); - lcd.setSize(dr.readDoubleWord()); - lcd.setTimeDateStamp(dr.readDoubleWord()); - lcd.setMajorVersion(dr.readWord()); - lcd.setMinorVersion(dr.readWord()); - lcd.setGlobalFlagsClear(dr.readDoubleWord()); - lcd.setGlobalFlagsSet(dr.readDoubleWord()); - lcd.setCriticalSectionDefaultTimeout(dr.readDoubleWord()); - lcd.setDeCommitFreeBlockThreshold(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - lcd.setDeCommitTotalFreeThreshold(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - lcd.setLockPrefixTable(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - lcd.setMaximumAllocationSize(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - lcd.setVirtualMemoryThreshold(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - lcd.setProcessAffinityMask(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - lcd.setProcessHeapFlags(dr.readDoubleWord()); - lcd.setCsdVersion(dr.readWord()); - lcd.setReserved(dr.readWord()); - lcd.setEditList(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - if (dr.hasMore()) // optional - lcd.setSecurityCookie(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - if (dr.hasMore()) // optional - lcd.setSeHandlerTable(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - if (dr.hasMore()) // optional - lcd.setSeHandlerCount(pe.is64() ? dr.readLong() : dr.readDoubleWord()); - - return lcd; + return LoadConfigDirectory.read(pe, b); } + /** + * @deprecated use {@link DebugDirectory#read(byte[])} instead + */ + @Deprecated public static DebugDirectory readDebugDirectory(byte[] b) throws IOException { return readDebugDirectory(b, new DataReader(b)); } + /** + * @deprecated use {@link DebugDirectory#read(byte[])} instead + */ + @Deprecated public static DebugDirectory readDebugDirectory(byte[] b, IDataReader dr) throws IOException { - DebugDirectory dd = new DebugDirectory(); - dd.set(b); - dd.setCharacteristics(dr.readDoubleWord()); - dd.setTimeDateStamp(dr.readDoubleWord()); - dd.setMajorVersion(dr.readWord()); - dd.setMajorVersion(dr.readWord()); - dd.setType(dr.readDoubleWord()); - dd.setSizeOfData(dr.readDoubleWord()); - dd.setAddressOfRawData(dr.readDoubleWord()); - dd.setPointerToRawData(dr.readDoubleWord()); - return dd; - } - - private static ResourceDirectory readResourceDirectory(byte[] b, - int baseAddress) throws IOException { - IDataReader dr = new ByteArrayDataReader(b); - return readResourceDirectory(dr, baseAddress); - } - - private static ResourceDirectory readResourceDirectory(IDataReader dr, - int baseAddress) throws IOException { - ResourceDirectory d = new ResourceDirectory(); - d.setTable(readResourceDirectoryTable(dr)); - int ne = d.getTable().getNumNameEntries() - + d.getTable().getNumIdEntries(); - for (int i = 0; i < ne; i++) { - d.add(readResourceEntry(dr, baseAddress)); - } - - return d; - } - - private static ResourceEntry readResourceEntry(IDataReader dr, - int baseAddress) throws IOException { - ResourceEntry re = new ResourceEntry(); - int id = dr.readDoubleWord(); - int offset = dr.readDoubleWord(); - re.setOffset(offset); - int pos = dr.getPosition(); - if ((id & 0x80000000) != 0) { - dr.jumpTo(id & 0x7fffffff); - re.setName(dr.readUnicode(dr.readWord())); - } else { - re.setId(id); - } - if ((offset & 0x80000000) != 0) { - dr.jumpTo(offset & 0x7fffffff); - re.setDirectory(readResourceDirectory(dr, baseAddress)); - } else { - dr.jumpTo(offset); - int rva = dr.readDoubleWord(); - int size = dr.readDoubleWord(); - int cp = dr.readDoubleWord(); - int res = dr.readDoubleWord(); - re.setDataRVA(rva); - re.setCodePage(cp); - re.setReserved(res); - dr.jumpTo(rva - baseAddress); - byte[] b = new byte[size]; - dr.read(b); - re.setData(b); - } - dr.jumpTo(pos); - return re; - } - - private static ResourceDirectoryTable readResourceDirectoryTable( - IDataReader dr) throws IOException { - ResourceDirectoryTable t = new ResourceDirectoryTable(); - t.setCharacteristics(dr.readDoubleWord()); - t.setTimeDateStamp(dr.readDoubleWord()); - t.setMajorVersion(dr.readWord()); - t.setMinVersion(dr.readWord()); - t.setNumNameEntries(dr.readWord()); - t.setNumIdEntries(dr.readWord()); - - return t; + return DebugDirectory.read(b); } + /** + * @deprecated use {@link AttributeCertificateTable#read(byte[])} instead + */ + @Deprecated public static AttributeCertificateTable readAttributeCertificateTable(byte[] b) throws IOException { return readAttributeCertificateTable(b, new DataReader(b)); } + /** + * @deprecated use {@link AttributeCertificateTable#read(byte[])} instead + */ + @Deprecated public static AttributeCertificateTable readAttributeCertificateTable(byte[] b, IDataReader dr) throws IOException { - AttributeCertificateTable dd = new AttributeCertificateTable(); - dd.set(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; + return AttributeCertificateTable.read(b); } } diff --git a/src/main/java/com/kichik/pecoff4j/io/ResourceAssembler.java b/src/main/java/com/kichik/pecoff4j/io/ResourceAssembler.java index f1426e2..c943e4c 100644 --- a/src/main/java/com/kichik/pecoff4j/io/ResourceAssembler.java +++ b/src/main/java/com/kichik/pecoff4j/io/ResourceAssembler.java @@ -11,6 +11,7 @@ import java.io.IOException; +import com.kichik.pecoff4j.resources.Bitmap; import com.kichik.pecoff4j.resources.BitmapInfoHeader; import com.kichik.pecoff4j.resources.FixedFileInfo; import com.kichik.pecoff4j.resources.IconDirectory; @@ -18,81 +19,57 @@ import com.kichik.pecoff4j.resources.IconImage; import com.kichik.pecoff4j.resources.RGBQuad; +@Deprecated public class ResourceAssembler { + /** + * @deprecated use {@link FixedFileInfo#write(IDataWriter)} instead + */ + @Deprecated public static void write(FixedFileInfo info, IDataWriter dw) throws IOException { - dw.writeDoubleWord(info.getSignature()); - dw.writeDoubleWord(info.getStrucVersion()); - dw.writeDoubleWord(info.getFileVersionMS()); - dw.writeDoubleWord(info.getFileVersionLS()); - dw.writeDoubleWord(info.getProductVersionMS()); - dw.writeDoubleWord(info.getProductVersionLS()); - dw.writeDoubleWord(info.getFileFlagMask()); - dw.writeDoubleWord(info.getFileFlags()); - dw.writeDoubleWord(info.getFileOS()); - dw.writeDoubleWord(info.getFileType()); - dw.writeDoubleWord(info.getFileSubtype()); - dw.writeDoubleWord(info.getFileDateMS()); - dw.writeDoubleWord(info.getFileDateLS()); + info.write(dw); } + /** + * @deprecated use {@link RGBQuad#write(IDataWriter)} instead + */ + @Deprecated public static void write(RGBQuad rgb, IDataWriter dw) throws IOException { - dw.writeByte(rgb.getBlue()); - dw.writeByte(rgb.getGreen()); - dw.writeByte(rgb.getRed()); - dw.writeByte(rgb.getReserved()); + rgb.write(dw); } + /** + * @deprecated use {@link IconImage#write(IDataWriter)} instead + */ + @Deprecated public static void write(IconImage ii, IDataWriter dw) throws IOException { - if (ii.getHeader() != null) { - write(ii.getHeader(), dw); - RGBQuad[] colors = ii.getColors(); - if (colors != null) { - for (int i = 0; i < colors.length; i++) { - write(colors[i], dw); - } - } - dw.writeBytes(ii.getXorMask()); - dw.writeBytes(ii.getAndMask()); - } else { - dw.writeBytes(ii.getPNG()); - } + ii.write(dw); } + /** + * @deprecated use {@link BitmapInfoHeader#write(IDataWriter)} instead + */ + @Deprecated public static void write(BitmapInfoHeader bih, IDataWriter dw) throws IOException { - dw.writeDoubleWord(bih.getSize()); - dw.writeDoubleWord(bih.getWidth()); - dw.writeDoubleWord(bih.getHeight()); - dw.writeWord(bih.getPlanes()); - dw.writeWord(bih.getBitCount()); - dw.writeDoubleWord(bih.getCompression()); - dw.writeDoubleWord(bih.getSizeImage()); - dw.writeDoubleWord(bih.getXpelsPerMeter()); - dw.writeDoubleWord(bih.getYpelsPerMeter()); - dw.writeDoubleWord(bih.getClrUsed()); - dw.writeDoubleWord(bih.getClrImportant()); + bih.write(dw); } + /** + * @deprecated use {@link IconDirectoryEntry#write(IDataWriter)} instead + */ + @Deprecated public static void write(IconDirectoryEntry ide, IDataWriter dw) throws IOException { - dw.writeByte(ide.getWidth()); - dw.writeByte(ide.getHeight()); - dw.writeByte(ide.getColorCount()); - dw.writeByte(ide.getReserved()); - dw.writeWord(ide.getPlanes()); - dw.writeWord(ide.getBitCount()); - dw.writeDoubleWord(ide.getBytesInRes()); - dw.writeDoubleWord(ide.getOffset()); + ide.write(dw); } + /** + * @deprecated use {@link IconDirectory#write(IDataWriter)} instead + */ + @Deprecated public static void write(IconDirectory id, IDataWriter dw) throws IOException { - dw.writeWord(id.getReserved()); - dw.writeWord(id.getType()); - dw.writeWord(id.getCount()); - for (int i = 0; i < id.getCount(); i++) { - write(id.getEntry(i), dw); - } + id.write(dw); } } diff --git a/src/main/java/com/kichik/pecoff4j/io/ResourceParser.java b/src/main/java/com/kichik/pecoff4j/io/ResourceParser.java index 9016499..6a60a0f 100644 --- a/src/main/java/com/kichik/pecoff4j/io/ResourceParser.java +++ b/src/main/java/com/kichik/pecoff4j/io/ResourceParser.java @@ -10,7 +10,6 @@ *******************************************************************************/ package com.kichik.pecoff4j.io; -import java.io.EOFException; import java.io.IOException; import com.kichik.pecoff4j.resources.Bitmap; @@ -29,304 +28,159 @@ import com.kichik.pecoff4j.resources.VarFileInfo; import com.kichik.pecoff4j.resources.VersionInfo; +@Deprecated public class ResourceParser { + /** + * @deprecated use {@link Bitmap#read(IDataReader)} instead + */ + @Deprecated public static Bitmap readBitmap(IDataReader dr) throws IOException { - Bitmap bm = new Bitmap(); - bm.setFileHeader(readBitmapFileHeader(dr)); - bm.setInfoHeader(readBitmapInfoHeader(dr)); - - return bm; + return Bitmap.read(dr); } + /** + * @deprecated use {@link BitmapFileHeader#read(IDataReader)} instead + */ + @Deprecated public static BitmapFileHeader readBitmapFileHeader(IDataReader dr) throws IOException { - BitmapFileHeader bfh = new BitmapFileHeader(); - bfh.setType(dr.readWord()); - bfh.setSize(dr.readDoubleWord()); - bfh.setReserved1(dr.readWord()); - bfh.setReserved2(dr.readWord()); - bfh.setOffBits(dr.readDoubleWord()); - - return bfh; + return BitmapFileHeader.read(dr); } + /** + * @deprecated use {@link BitmapInfoHeader#read(IDataReader)} instead + */ + @Deprecated public static BitmapInfoHeader readBitmapInfoHeader(IDataReader dr) throws IOException { - BitmapInfoHeader bh = new BitmapInfoHeader(); - bh.setSize(dr.readDoubleWord()); - bh.setWidth(dr.readDoubleWord()); - bh.setHeight(dr.readDoubleWord()); - bh.setPlanes(dr.readWord()); - bh.setBitCount(dr.readWord()); - bh.setCompression(dr.readDoubleWord()); - bh.setSizeImage(dr.readDoubleWord()); - bh.setXpelsPerMeter(dr.readDoubleWord()); - bh.setYpelsPerMeter(dr.readDoubleWord()); - bh.setClrUsed(dr.readDoubleWord()); - bh.setClrImportant(dr.readDoubleWord()); - - return bh; + return BitmapInfoHeader.read(dr); } + /** + * @deprecated use {@link FixedFileInfo#read(IDataReader)} instead + */ + @Deprecated public static FixedFileInfo readFixedFileInfo(IDataReader dr) throws IOException { - FixedFileInfo ffi = new FixedFileInfo(); - ffi.setSignature(dr.readDoubleWord()); - ffi.setStrucVersion(dr.readDoubleWord()); - ffi.setFileVersionMS(dr.readDoubleWord()); - ffi.setFileVersionLS(dr.readDoubleWord()); - ffi.setProductVersionMS(dr.readDoubleWord()); - ffi.setProductVersionLS(dr.readDoubleWord()); - ffi.setFileFlagMask(dr.readDoubleWord()); - ffi.setFileFlags(dr.readDoubleWord()); - ffi.setFileOS(dr.readDoubleWord()); - ffi.setFileType(dr.readDoubleWord()); - ffi.setFileSubtype(dr.readDoubleWord()); - ffi.setFileDateMS(dr.readDoubleWord()); - ffi.setFileDateLS(dr.readDoubleWord()); - return ffi; + return FixedFileInfo.read(dr); } + /** + * @deprecated use {@link IconImage#readIcon(IDataReader, int)} instead + */ + @Deprecated public static IconImage readIconImage(IDataReader dr, int bytesInRes) throws IOException { - IconImage ii = new IconImage(); - int quadSize = 0; - ii.setHeader(readBitmapInfoHeader(dr)); - if (ii.getHeader().getClrUsed() != 0) { - quadSize = ii.getHeader().getClrUsed(); - } else { - if (ii.getHeader().getBitCount() <= 8) { - quadSize = 1 << ii.getHeader().getBitCount(); - } else { - quadSize = 0; - } - } - - int numBytesPerLine = ((((ii.getHeader().getWidth() - * ii.getHeader().getPlanes() * ii.getHeader().getBitCount()) + 31) >> 5) << 2); - int xorSize = numBytesPerLine * ii.getHeader().getHeight() / 2; - int andSize = bytesInRes - (quadSize * 4) - ii.getHeader().getSize() - - xorSize; - - if (quadSize > 0) { - RGBQuad[] colors = new RGBQuad[quadSize]; - for (int i = 0; i < quadSize; i++) { - colors[i] = readRGB(dr); - } - ii.setColors(colors); - } - - byte[] xorMask = new byte[xorSize]; - dr.read(xorMask); - ii.setXorMask(xorMask); - - byte[] andMask = new byte[andSize]; - dr.read(andMask); - ii.setAndMask(andMask); - - return ii; + return IconImage.readIcon(dr, bytesInRes); } + /** + * @deprecated use {@link IconImage#readPNG(byte[])} instead + */ + @Deprecated public static IconImage readPNG(byte[] data) { - IconImage ii = new IconImage(); - ii.setPngData(data); - return ii; + return IconImage.readPNG(data); } + /** + * @deprecated use {@link VersionInfo#read(IDataReader)} instead + */ + @Deprecated public static VersionInfo readVersionInfo(byte[] data) throws IOException { return readVersionInfo(new DataReader(data)); } + /** + * @deprecated use {@link VersionInfo#read(IDataReader)} instead + */ + @Deprecated public static VersionInfo readVersionInfo(IDataReader dr) throws IOException { - int versionInfoPos = dr.getPosition(); - VersionInfo vi = new VersionInfo(); - vi.setLength(dr.readWord()); - vi.setValueLength(dr.readWord()); - vi.setType(dr.readWord()); - vi.setKey(dr.readUnicode()); - alignDataReader(dr); - vi.setFixedFileInfo(ResourceParser.readFixedFileInfo(dr)); - alignDataReader(dr); - - while (dr.getPosition() < versionInfoPos + vi.getLength()) { - int initialPos = dr.getPosition(); - - int length = dr.readWord(); - if (length == 0) { - break; - } - int valueLength = dr.readWord(); - int type = dr.readWord(); - String key = dr.readUnicode(); - if ("VarFileInfo".equals(key)) { - vi.setVarFileInfo(readVarFileInfo(dr, initialPos, length, valueLength, type, key)); - } else if ("StringFileInfo".equals(key)) { - vi.setStringFileInfo(readStringFileInfo(dr, initialPos, length, valueLength, type, key)); - } else { - dr.jumpTo(initialPos + length); - break; - } - } - - return vi; + return VersionInfo.read(dr); } - - public static VarFileInfo readVarFileInfo(IDataReader dr, int initialPos, int length, int valueLength, int type, String key) throws IOException { - VarFileInfo vfi = new VarFileInfo(); - vfi.setLength(length); - vfi.setValueLength(valueLength); - vfi.setType(type); - vfi.setKey(key); - alignDataReader(dr); - while (dr.getPosition() < initialPos + length) { - vfi.addVar(readVar(dr)); - } - return vfi; + /** + * @deprecated use {@link VarFileInfo#readPartial(IDataReader, int, int, int, int, String)} instead + */ + @Deprecated + public static VarFileInfo readVarFileInfo(IDataReader dr, int initialPos, int length, int valueLength, int type, String key) throws IOException { + return VarFileInfo.readPartial(dr, initialPos, length, valueLength, type, key); } + /** + * @deprecated use {@link Var#read(IDataReader)} instead + */ + @Deprecated public static Var readVar(IDataReader dr) throws IOException { - Var v = new Var(); - int initialPos = dr.getPosition(); - v.setLength(dr.readWord()); - v.setValueLength(dr.readWord()); - v.setType(dr.readWord()); - v.setKey(dr.readUnicode()); - alignDataReader(dr); - while (dr.getPosition() < initialPos + v.getLength()) { - v.addValue(dr.readDoubleWord()); - } - return v; + return Var.read(dr); } + /** + * @deprecated use {@link StringTable#read(IDataReader)} instead + */ + @Deprecated public static StringTable readStringTable(IDataReader dr) throws IOException { - int initialPos = dr.getPosition(); - - StringTable vfi = new StringTable(); - vfi.setLength(dr.readWord()); - if (vfi.getLength() == 0) { - return null; - } - vfi.setValueLength(dr.readWord()); - vfi.setType(dr.readWord()); - vfi.setKey(dr.readUnicode()); - vfi.setPadding(alignDataReader(dr)); - - while (dr.getPosition() - initialPos < vfi.getLength()) - vfi.add(readStringPair(dr)); - - return vfi; + return StringTable.read(dr); } + /** + * @deprecated use {@link StringPair#read(IDataReader)} instead + */ + @Deprecated public static StringPair readStringPair(IDataReader dr) throws IOException { - int initialPos = dr.getPosition(); - - StringPair sp = new StringPair(); - sp.setLength(dr.readWord()); - sp.setValueLength(dr.readWord()); - sp.setType(dr.readWord()); - sp.setKey(dr.readUnicode()); - sp.setPadding(alignDataReader(dr)); - - int remainingWords = (sp.getLength() - (dr.getPosition() - initialPos)) / 2; - int valueLength = sp.getValueLength(); - if (sp.getType() == 0) // wType == 0 => binary; wLength is in bytes - valueLength /= 2; - if (valueLength > remainingWords) - valueLength = remainingWords; - sp.setValue(dr.readUnicode(valueLength).trim()); - - int remainingBytes = (sp.getLength() - (dr.getPosition() - initialPos)); - dr.skipBytes(remainingBytes); - alignDataReader(dr); - return sp; + return StringPair.read(dr); } + /** + * @deprecated use {@link Manifest#read(IDataReader, int)} instead + */ + @Deprecated public static Manifest readManifest(IDataReader dr, int length) throws IOException { - Manifest mf = new Manifest(); - mf.set(dr.readUtf(length)); - return mf; + return Manifest.read(dr, length); } + /** + * @deprecated use {@link RGBQuad#read(IDataReader)} instead + */ + @Deprecated public static RGBQuad readRGB(IDataReader dr) throws IOException { - RGBQuad r = new RGBQuad(); - r.setBlue(dr.readByte()); - r.setGreen(dr.readByte()); - r.setRed(dr.readByte()); - r.setReserved(dr.readByte()); - return r; + return RGBQuad.read(dr); } - - public static StringFileInfo readStringFileInfo(IDataReader dr, int initialPos, int length, int valueLength, int type, String key) throws IOException { - StringFileInfo sfi = new StringFileInfo(); - sfi.setLength(length); - sfi.setValueLength(valueLength); - sfi.setType(type); - sfi.setKey(key); - sfi.setPadding(alignDataReader(dr)); - while (dr.getPosition() - initialPos < sfi.getLength()) { - sfi.add(readStringTable(dr)); - } - return sfi; + /** + * @deprecated use {@link StringFileInfo#readPartial(IDataReader, int, int, int, int, String)} instead + */ + @Deprecated + public static StringFileInfo readStringFileInfo(IDataReader dr, int initialPos, int length, int valueLength, int type, String key) throws IOException { + return StringFileInfo.readPartial(dr, initialPos, length, valueLength, type, key); } + /** + * @deprecated use {@link StringFileInfo#read(IDataReader)} instead + */ + @Deprecated public static StringFileInfo readStringFileInfo(IDataReader dr) throws IOException { - int initialPos = dr.getPosition(); - - StringFileInfo sfi = new StringFileInfo(); - - sfi.setLength(dr.readWord()); - sfi.setValueLength(dr.readWord()); - sfi.setType(dr.readWord()); - sfi.setKey(dr.readUnicode()); - sfi.setPadding(alignDataReader(dr)); - - while (dr.getPosition() - initialPos < sfi.getLength()) - sfi.add(readStringTable(dr)); - - return sfi; + return StringFileInfo.read(dr); } + /** + * @deprecated use {@link IconDirectoryEntry#read(IDataReader)} instead + */ + @Deprecated public static IconDirectoryEntry readIconDirectoryEntry(IDataReader dr) throws IOException { - IconDirectoryEntry ge = new IconDirectoryEntry(); - ge.setWidth(dr.readByte()); - ge.setHeight(dr.readByte()); - ge.setColorCount(dr.readByte()); - ge.setReserved(dr.readByte()); - ge.setPlanes(dr.readWord()); - ge.setBitCount(dr.readWord()); - ge.setBytesInRes(dr.readDoubleWord()); - ge.setOffset(dr.readDoubleWord()); - - return ge; + return IconDirectoryEntry.read(dr); } + /** + * @deprecated use {@link IconDirectory#read(IDataReader)} instead + */ + @Deprecated public static IconDirectory readIconDirectory(IDataReader dr) throws IOException { - IconDirectory gi = new IconDirectory(); - gi.setReserved(dr.readWord()); - gi.setType(dr.readWord()); - int count = dr.readWord(); - for (int i = 0; i < count; i++) { - gi.add(readIconDirectoryEntry(dr)); - } - - return gi; - } - - private static int alignDataReader(IDataReader dr) throws IOException { - int off = (4 - (dr.getPosition() % 4)) % 4; - try { - dr.skipBytes(off); - } catch (EOFException ignored) { - // no need to align when it's at the end of its data - } - return off; + return IconDirectory.read(dr); } } diff --git a/src/main/java/com/kichik/pecoff4j/io/SectionAssembler.java b/src/main/java/com/kichik/pecoff4j/io/SectionAssembler.java deleted file mode 100644 index 086ffa9..0000000 --- a/src/main/java/com/kichik/pecoff4j/io/SectionAssembler.java +++ /dev/null @@ -1,14 +0,0 @@ -/******************************************************************************* - * This program and the accompanying materials - * are made available under the terms of the Common Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v10.html - * - * Contributors: - * Peter Smith - *******************************************************************************/ -package com.kichik.pecoff4j.io; - -public class SectionAssembler { - -} diff --git a/src/main/java/com/kichik/pecoff4j/resources/Bitmap.java b/src/main/java/com/kichik/pecoff4j/resources/Bitmap.java index abc3c71..3725579 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/Bitmap.java +++ b/src/main/java/com/kichik/pecoff4j/resources/Bitmap.java @@ -9,12 +9,30 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class Bitmap { private BitmapFileHeader fileHeader; private BitmapInfoHeader infoHeader; private byte[] colors; private byte[] bitmapBits; + public static Bitmap read(IDataReader dr) throws IOException { + Bitmap bm = new Bitmap(); + bm.setFileHeader(BitmapFileHeader.read(dr)); + bm.setInfoHeader(BitmapInfoHeader.read(dr)); + + return bm; + } + + public void write(IDataWriter dw) throws IOException { + getFileHeader().write(dw); + getInfoHeader().write(dw); + } + public BitmapFileHeader getFileHeader() { return fileHeader; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/BitmapFileHeader.java b/src/main/java/com/kichik/pecoff4j/resources/BitmapFileHeader.java index 0c28363..960d20a 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/BitmapFileHeader.java +++ b/src/main/java/com/kichik/pecoff4j/resources/BitmapFileHeader.java @@ -11,7 +11,8 @@ import java.io.IOException; -import com.kichik.pecoff4j.io.DataReader; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; public class BitmapFileHeader { private int type; @@ -20,7 +21,7 @@ public class BitmapFileHeader { private int reserved2; private int offBits; - public static BitmapFileHeader read(DataReader dr) throws IOException { + public static BitmapFileHeader read(IDataReader dr) throws IOException { BitmapFileHeader bfh = new BitmapFileHeader(); bfh.type = dr.readWord(); bfh.size = dr.readDoubleWord(); @@ -31,6 +32,14 @@ public static BitmapFileHeader read(DataReader dr) throws IOException { return bfh; } + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getType()); + dw.writeDoubleWord(getSize()); + dw.writeWord(getReserved1()); + dw.writeWord(getReserved2()); + dw.writeDoubleWord(getOffBits()); + } + public int getType() { return type; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/BitmapInfoHeader.java b/src/main/java/com/kichik/pecoff4j/resources/BitmapInfoHeader.java index 0c8243a..dd36330 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/BitmapInfoHeader.java +++ b/src/main/java/com/kichik/pecoff4j/resources/BitmapInfoHeader.java @@ -9,6 +9,11 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class BitmapInfoHeader { private int size; private int width; @@ -22,6 +27,37 @@ public class BitmapInfoHeader { private int clrUsed; private int clrImportant; + public static BitmapInfoHeader read(IDataReader dr) throws IOException { + BitmapInfoHeader bh = new BitmapInfoHeader(); + bh.setSize(dr.readDoubleWord()); + bh.setWidth(dr.readDoubleWord()); + bh.setHeight(dr.readDoubleWord()); + bh.setPlanes(dr.readWord()); + bh.setBitCount(dr.readWord()); + bh.setCompression(dr.readDoubleWord()); + bh.setSizeImage(dr.readDoubleWord()); + bh.setXpelsPerMeter(dr.readDoubleWord()); + bh.setYpelsPerMeter(dr.readDoubleWord()); + bh.setClrUsed(dr.readDoubleWord()); + bh.setClrImportant(dr.readDoubleWord()); + + return bh; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeDoubleWord(getSize()); + dw.writeDoubleWord(getWidth()); + dw.writeDoubleWord(getHeight()); + dw.writeWord(getPlanes()); + dw.writeWord(getBitCount()); + dw.writeDoubleWord(getCompression()); + dw.writeDoubleWord(getSizeImage()); + dw.writeDoubleWord(getXpelsPerMeter()); + dw.writeDoubleWord(getYpelsPerMeter()); + dw.writeDoubleWord(getClrUsed()); + dw.writeDoubleWord(getClrImportant()); + } + public int getSize() { return size; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/FixedFileInfo.java b/src/main/java/com/kichik/pecoff4j/resources/FixedFileInfo.java index b6c2049..2aa43cc 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/FixedFileInfo.java +++ b/src/main/java/com/kichik/pecoff4j/resources/FixedFileInfo.java @@ -9,6 +9,11 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class FixedFileInfo { private int signature; private int strucVersion; @@ -24,6 +29,40 @@ public class FixedFileInfo { private int fileDateMS; private int fileDateLS; + public static FixedFileInfo read(IDataReader dr) throws IOException { + FixedFileInfo ffi = new FixedFileInfo(); + ffi.setSignature(dr.readDoubleWord()); + ffi.setStrucVersion(dr.readDoubleWord()); + ffi.setFileVersionMS(dr.readDoubleWord()); + ffi.setFileVersionLS(dr.readDoubleWord()); + ffi.setProductVersionMS(dr.readDoubleWord()); + ffi.setProductVersionLS(dr.readDoubleWord()); + ffi.setFileFlagMask(dr.readDoubleWord()); + ffi.setFileFlags(dr.readDoubleWord()); + ffi.setFileOS(dr.readDoubleWord()); + ffi.setFileType(dr.readDoubleWord()); + ffi.setFileSubtype(dr.readDoubleWord()); + ffi.setFileDateMS(dr.readDoubleWord()); + ffi.setFileDateLS(dr.readDoubleWord()); + return ffi; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeDoubleWord(getSignature()); + dw.writeDoubleWord(getStrucVersion()); + dw.writeDoubleWord(getFileVersionMS()); + dw.writeDoubleWord(getFileVersionLS()); + dw.writeDoubleWord(getProductVersionMS()); + dw.writeDoubleWord(getProductVersionLS()); + dw.writeDoubleWord(getFileFlagMask()); + dw.writeDoubleWord(getFileFlags()); + dw.writeDoubleWord(getFileOS()); + dw.writeDoubleWord(getFileType()); + dw.writeDoubleWord(getFileSubtype()); + dw.writeDoubleWord(getFileDateMS()); + dw.writeDoubleWord(getFileDateLS()); + } + public int getSignature() { return signature; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/IconDirectory.java b/src/main/java/com/kichik/pecoff4j/resources/IconDirectory.java index c892136..f4c4375 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/IconDirectory.java +++ b/src/main/java/com/kichik/pecoff4j/resources/IconDirectory.java @@ -9,6 +9,10 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; import java.util.ArrayList; public class IconDirectory { @@ -16,6 +20,27 @@ public class IconDirectory { private int type; private ArrayList entries = new ArrayList(); + public static IconDirectory read(IDataReader dr) throws IOException { + IconDirectory gi = new IconDirectory(); + gi.setReserved(dr.readWord()); + gi.setType(dr.readWord()); + int count = dr.readWord(); + for (int i = 0; i < count; i++) { + gi.add(IconDirectoryEntry.read(dr)); + } + + return gi; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getReserved()); + dw.writeWord(getType()); + dw.writeWord(getCount()); + for (int i = 0; i < getCount(); i++) { + getEntry(i).write(dw); + } + } + public void add(IconDirectoryEntry entry) { entries.add(entry); } diff --git a/src/main/java/com/kichik/pecoff4j/resources/IconDirectoryEntry.java b/src/main/java/com/kichik/pecoff4j/resources/IconDirectoryEntry.java index 9ee859c..deb1164 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/IconDirectoryEntry.java +++ b/src/main/java/com/kichik/pecoff4j/resources/IconDirectoryEntry.java @@ -9,6 +9,11 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class IconDirectoryEntry { private int width; private int height; @@ -19,6 +24,31 @@ public class IconDirectoryEntry { private int bytesInRes; private int offset; + public static IconDirectoryEntry read(IDataReader dr) throws IOException { + IconDirectoryEntry ge = new IconDirectoryEntry(); + ge.setWidth(dr.readByte()); + ge.setHeight(dr.readByte()); + ge.setColorCount(dr.readByte()); + ge.setReserved(dr.readByte()); + ge.setPlanes(dr.readWord()); + ge.setBitCount(dr.readWord()); + ge.setBytesInRes(dr.readDoubleWord()); + ge.setOffset(dr.readDoubleWord()); + + return ge; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeByte(getWidth()); + dw.writeByte(getHeight()); + dw.writeByte(getColorCount()); + dw.writeByte(getReserved()); + dw.writeWord(getPlanes()); + dw.writeWord(getBitCount()); + dw.writeDoubleWord(getBytesInRes()); + dw.writeDoubleWord(getOffset()); + } + public int getWidth() { return width; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/IconImage.java b/src/main/java/com/kichik/pecoff4j/resources/IconImage.java index 2e834a5..8ddcfa7 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/IconImage.java +++ b/src/main/java/com/kichik/pecoff4j/resources/IconImage.java @@ -9,6 +9,11 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class IconImage { private BitmapInfoHeader header; private RGBQuad[] colors; @@ -16,6 +21,68 @@ public class IconImage { private byte[] andMask; private byte[] pngData; + public static IconImage readIcon(IDataReader dr, int bytesInRes) + throws IOException { + IconImage ii = new IconImage(); + int quadSize = 0; + ii.setHeader(BitmapInfoHeader.read(dr)); + if (ii.getHeader().getClrUsed() != 0) { + quadSize = ii.getHeader().getClrUsed(); + } else { + if (ii.getHeader().getBitCount() <= 8) { + quadSize = 1 << ii.getHeader().getBitCount(); + } else { + quadSize = 0; + } + } + + int numBytesPerLine = ((((ii.getHeader().getWidth() + * ii.getHeader().getPlanes() * ii.getHeader().getBitCount()) + 31) >> 5) << 2); + int xorSize = numBytesPerLine * ii.getHeader().getHeight() / 2; + int andSize = bytesInRes - (quadSize * 4) - ii.getHeader().getSize() + - xorSize; + + if (quadSize > 0) { + RGBQuad[] colors = new RGBQuad[quadSize]; + for (int i = 0; i < quadSize; i++) { + colors[i] = RGBQuad.read(dr); + } + ii.setColors(colors); + } + + byte[] xorMask = new byte[xorSize]; + dr.read(xorMask); + ii.setXorMask(xorMask); + + byte[] andMask = new byte[andSize]; + dr.read(andMask); + ii.setAndMask(andMask); + + return ii; + } + + public static IconImage readPNG(byte[] data) { + IconImage ii = new IconImage(); + ii.setPngData(data); + return ii; + } + + public void write(IDataWriter dw) throws IOException { + if (getHeader() != null) { + getHeader().write(dw); + RGBQuad[] colors = getColors(); + if (colors != null) { + for (int i = 0; i < colors.length; i++) { + colors[i].write(dw); + } + } + dw.writeBytes(getXorMask()); + dw.writeBytes(getAndMask()); + } else { + dw.writeBytes(getPNG()); + } + } + public BitmapInfoHeader getHeader() { return header; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/Manifest.java b/src/main/java/com/kichik/pecoff4j/resources/Manifest.java index 6399b8a..f85e8df 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/Manifest.java +++ b/src/main/java/com/kichik/pecoff4j/resources/Manifest.java @@ -9,9 +9,25 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class Manifest { private String str; + public static Manifest read(IDataReader dr, int length) + throws IOException { + Manifest mf = new Manifest(); + mf.set(dr.readUtf(length)); + return mf; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeUtf(get(), get().length()); + } + public String get() { return str; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/RGBQuad.java b/src/main/java/com/kichik/pecoff4j/resources/RGBQuad.java index 68bcd60..1420741 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/RGBQuad.java +++ b/src/main/java/com/kichik/pecoff4j/resources/RGBQuad.java @@ -9,12 +9,33 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; + public class RGBQuad { private int blue; private int green; private int red; private int reserved; + public static RGBQuad read(IDataReader dr) throws IOException { + RGBQuad r = new RGBQuad(); + r.setBlue(dr.readByte()); + r.setGreen(dr.readByte()); + r.setRed(dr.readByte()); + r.setReserved(dr.readByte()); + return r; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeByte(getBlue()); + dw.writeByte(getGreen()); + dw.writeByte(getRed()); + dw.writeByte(getReserved()); + } + public int getBlue() { return blue; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/StringFileInfo.java b/src/main/java/com/kichik/pecoff4j/resources/StringFileInfo.java index 8d9c3a1..5578186 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/StringFileInfo.java +++ b/src/main/java/com/kichik/pecoff4j/resources/StringFileInfo.java @@ -10,9 +10,12 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; import com.kichik.pecoff4j.util.Strings; public class StringFileInfo { @@ -23,6 +26,52 @@ public class StringFileInfo { private int padding; private List tables = new ArrayList(); + public static StringFileInfo read(IDataReader dr) throws IOException { + int initialPos = dr.getPosition(); + + StringFileInfo sfi = new StringFileInfo(); + + sfi.setLength(dr.readWord()); + sfi.setValueLength(dr.readWord()); + sfi.setType(dr.readWord()); + sfi.setKey(dr.readUnicode()); + sfi.setPadding(dr.align(4)); + + while (dr.getPosition() - initialPos < sfi.getLength()) + sfi.add(StringTable.read(dr)); + + return sfi; + } + + public static StringFileInfo readPartial(IDataReader dr, int initialPos, int length, int valueLength, int type, String key) throws IOException { + StringFileInfo sfi = new StringFileInfo(); + + sfi.setLength(length); + sfi.setValueLength(valueLength); + sfi.setType(type); + sfi.setKey(key); + sfi.setPadding(dr.align(4)); + while (dr.getPosition() - initialPos < sfi.getLength()) { + sfi.add(StringTable.read(dr)); + } + return sfi; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getLength()); + if (getLength() == 0) { + return; + } + dw.writeWord(getValueLength()); + dw.writeWord(getType()); + dw.writeUnicode(getKey()); + dw.align(4); + for (int i = 0; i < getCount(); i++) { + StringTable table = getTable(i); + table.write(dw); + } + } + public void add(StringTable table) { tables.add(table); } diff --git a/src/main/java/com/kichik/pecoff4j/resources/StringPair.java b/src/main/java/com/kichik/pecoff4j/resources/StringPair.java index dcba31f..30886ac 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/StringPair.java +++ b/src/main/java/com/kichik/pecoff4j/resources/StringPair.java @@ -10,9 +10,13 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; import com.kichik.pecoff4j.util.Reflection; import com.kichik.pecoff4j.util.Strings; +import java.io.IOException; + public class StringPair { private int length; private int valueLength; @@ -21,6 +25,52 @@ public class StringPair { private String value; private int padding; + public static StringPair read(IDataReader dr) throws IOException { + int initialPos = dr.getPosition(); + + StringPair sp = new StringPair(); + sp.setLength(dr.readWord()); + sp.setValueLength(dr.readWord()); + sp.setType(dr.readWord()); + sp.setKey(dr.readUnicode()); + sp.setPadding(dr.align(4)); + + int remainingWords = (sp.getLength() - (dr.getPosition() - initialPos)) / 2; + int valueLength = sp.getValueLength(); + if (sp.getType() == 0) // wType == 0 => binary; wLength is in bytes + valueLength /= 2; + if (valueLength > remainingWords) + valueLength = remainingWords; + sp.setValue(dr.readUnicode(valueLength).trim()); + + int remainingBytes = (sp.getLength() - (dr.getPosition() - initialPos)); + dr.skipBytes(remainingBytes); + dr.align(4); + return sp; + } + + public void write(IDataWriter dw) throws IOException { + int initialPos = dw.getPosition(); + + dw.writeWord(getLength()); + dw.writeWord(getValueLength()); + dw.writeWord(getType()); + dw.writeUnicode(getKey()); + dw.align(4); + + int remainingWords = (getLength() - (dw.getPosition() - initialPos)) / 2; + int valueLength = getValueLength(); + if (getType() == 0) // wType == 0 => binary; wLength is in bytes + valueLength /= 2; + if (valueLength > remainingWords) + valueLength = remainingWords; + dw.writeUnicode(getValue(), valueLength); + + int remainingBytes = (getLength() - (dw.getPosition() - initialPos)); + dw.writeByte(0, remainingBytes); + dw.align(4); + } + public int getLength() { return length; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/StringTable.java b/src/main/java/com/kichik/pecoff4j/resources/StringTable.java index 2a645b2..af12a67 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/StringTable.java +++ b/src/main/java/com/kichik/pecoff4j/resources/StringTable.java @@ -10,9 +10,12 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; import com.kichik.pecoff4j.util.Strings; public class StringTable { @@ -23,6 +26,42 @@ public class StringTable { private int padding; private List strings = new ArrayList(); + public static StringTable read(IDataReader dr) throws IOException { + int initialPos = dr.getPosition(); + + StringTable vfi = new StringTable(); + vfi.setLength(dr.readWord()); + if (vfi.getLength() == 0) { + return null; + } + vfi.setValueLength(dr.readWord()); + vfi.setType(dr.readWord()); + vfi.setKey(dr.readUnicode()); + vfi.setPadding(dr.align(4)); + + while (dr.getPosition() - initialPos < vfi.getLength()) + vfi.add(StringPair.read(dr)); + + return vfi; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getLength()); + if (getLength() == 0) { + return; + } + + dw.writeWord(getValueLength()); + dw.writeWord(getType()); + dw.writeUnicode(getKey()); + dw.align(4); + + for (int i = 0; i < getCount(); i++) { + StringPair pair = getString(i); + pair.write(dw); + } + } + public void add(StringPair string) { strings.add(string); } diff --git a/src/main/java/com/kichik/pecoff4j/resources/Var.java b/src/main/java/com/kichik/pecoff4j/resources/Var.java index 95e950c..e2b2c2e 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/Var.java +++ b/src/main/java/com/kichik/pecoff4j/resources/Var.java @@ -1,5 +1,9 @@ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -15,6 +19,31 @@ public class Var { private String key; private final List values = new ArrayList<>(); + public static Var read(IDataReader dr) throws IOException { + Var v = new Var(); + int initialPos = dr.getPosition(); + v.setLength(dr.readWord()); + v.setValueLength(dr.readWord()); + v.setType(dr.readWord()); + v.setKey(dr.readUnicode()); + dr.align(4); + while (dr.getPosition() < initialPos + v.getLength()) { + v.addValue(dr.readDoubleWord()); + } + return v; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getLength()); + dw.writeWord(getValueLength()); + dw.writeWord(getType()); + dw.writeUnicode(getKey()); + dw.align(4); + for (Integer value : getValues()) { + dw.writeDoubleWord(value); + } + } + public int getLength() { return length; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/VarFileInfo.java b/src/main/java/com/kichik/pecoff4j/resources/VarFileInfo.java index 7679848..5f2b210 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/VarFileInfo.java +++ b/src/main/java/com/kichik/pecoff4j/resources/VarFileInfo.java @@ -9,6 +9,10 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; + +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -19,6 +23,31 @@ public class VarFileInfo { private String key; private final List vars = new ArrayList<>(); + public static VarFileInfo readPartial(IDataReader dr, int initialPos, int length, int valueLength, int type, String key) throws IOException { + VarFileInfo vfi = new VarFileInfo(); + vfi.setLength(length); + vfi.setValueLength(valueLength); + vfi.setType(type); + vfi.setKey(key); + dr.align(4); + + while (dr.getPosition() < initialPos + length) { + vfi.addVar(Var.read(dr)); + } + return vfi; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getLength()); + dw.writeWord(getValueLength()); + dw.writeWord(getType()); + dw.writeUnicode(getKey()); + dw.align(4); + for (Var v : getVars()) { + v.write(dw); + } + } + public void setLength(int length) { this.length = length; } diff --git a/src/main/java/com/kichik/pecoff4j/resources/VersionInfo.java b/src/main/java/com/kichik/pecoff4j/resources/VersionInfo.java index f853c73..c641502 100644 --- a/src/main/java/com/kichik/pecoff4j/resources/VersionInfo.java +++ b/src/main/java/com/kichik/pecoff4j/resources/VersionInfo.java @@ -9,6 +9,12 @@ *******************************************************************************/ package com.kichik.pecoff4j.resources; +import com.kichik.pecoff4j.io.IDataReader; +import com.kichik.pecoff4j.io.IDataWriter; +import com.kichik.pecoff4j.io.ResourceAssembler; + +import java.io.IOException; + public class VersionInfo { private int length; private int valueLength; @@ -18,6 +24,59 @@ public class VersionInfo { private StringFileInfo stringFileInfo; private VarFileInfo varFileInfo; + public static VersionInfo read(IDataReader dr) + throws IOException { + int versionInfoPos = dr.getPosition(); + VersionInfo vi = new VersionInfo(); + vi.setLength(dr.readWord()); + vi.setValueLength(dr.readWord()); + vi.setType(dr.readWord()); + vi.setKey(dr.readUnicode()); + dr.align(4); + vi.setFixedFileInfo(FixedFileInfo.read(dr)); + dr.align(4); + + while (dr.getPosition() < versionInfoPos + vi.getLength()) { + int initialPos = dr.getPosition(); + + int length = dr.readWord(); + if (length == 0) { + break; + } + int valueLength = dr.readWord(); + int type = dr.readWord(); + String key = dr.readUnicode(); + if ("VarFileInfo".equals(key)) { + vi.setVarFileInfo(VarFileInfo.readPartial(dr, initialPos, length, valueLength, type, key)); + } else if ("StringFileInfo".equals(key)) { + vi.setStringFileInfo(StringFileInfo.readPartial(dr, initialPos, length, valueLength, type, key)); + } else { + dr.jumpTo(initialPos + length); + break; + } + } + + return vi; + } + + public void write(IDataWriter dw) throws IOException { + dw.writeWord(getLength()); + dw.writeWord(getValueLength()); + dw.writeWord(getType()); + dw.writeUnicode(getKey()); + dw.align(4); + ResourceAssembler.write(getFixedFileInfo(), dw); + + StringFileInfo stringFileInfo = getStringFileInfo(); + if (stringFileInfo != null) { + stringFileInfo.write(dw); + } + VarFileInfo varFileInfo = getVarFileInfo(); + if (varFileInfo != null) { + varFileInfo.write(dw); + } + } + public int getLength() { return length; } diff --git a/src/main/java/com/kichik/pecoff4j/util/IconExtractor.java b/src/main/java/com/kichik/pecoff4j/util/IconExtractor.java index febc2b8..1833e17 100644 --- a/src/main/java/com/kichik/pecoff4j/util/IconExtractor.java +++ b/src/main/java/com/kichik/pecoff4j/util/IconExtractor.java @@ -20,7 +20,6 @@ import com.kichik.pecoff4j.io.DataReader; import com.kichik.pecoff4j.io.DataWriter; import com.kichik.pecoff4j.io.PEParser; -import com.kichik.pecoff4j.io.ResourceParser; import com.kichik.pecoff4j.resources.GroupIconDirectory; import com.kichik.pecoff4j.resources.GroupIconDirectoryEntry; import com.kichik.pecoff4j.resources.IconDirectory; @@ -60,10 +59,10 @@ public static void extract(File pecoff, File outputDir) throws IOException { ide.setBytesInRes(d.length); // Check for PNG data if (gide.getWidth() == 0 && gide.getHeight() == 0) { - IconImage ii = ResourceParser.readPNG(d); + IconImage ii = IconImage.readPNG(d); images[j] = ii; } else { - IconImage ii = ResourceParser.readIconImage(new DataReader( + IconImage ii = IconImage.readIcon(new DataReader( d), gide.getBytesInRes()); images[j] = ii; } diff --git a/src/main/java/com/kichik/pecoff4j/util/IconFile.java b/src/main/java/com/kichik/pecoff4j/util/IconFile.java index bc91dae..69cc0c4 100644 --- a/src/main/java/com/kichik/pecoff4j/util/IconFile.java +++ b/src/main/java/com/kichik/pecoff4j/util/IconFile.java @@ -16,8 +16,6 @@ import com.kichik.pecoff4j.io.DataReader; import com.kichik.pecoff4j.io.IDataReader; import com.kichik.pecoff4j.io.IDataWriter; -import com.kichik.pecoff4j.io.ResourceAssembler; -import com.kichik.pecoff4j.io.ResourceParser; import com.kichik.pecoff4j.resources.IconDirectory; import com.kichik.pecoff4j.resources.IconImage; @@ -35,11 +33,11 @@ public static IconFile parse(File file) throws IOException { public static IconFile read(IDataReader dr) throws IOException { IconFile ic = new IconFile(); - ic.directory = ResourceParser.readIconDirectory(dr); + ic.directory = IconDirectory.read(dr); ic.images = new IconImage[ic.directory.getCount()]; for (int i = 0; i < ic.directory.getCount(); i++) { dr.jumpTo(ic.directory.getEntry(i).getOffset()); - ic.images[i] = ResourceParser.readIconImage(dr, ic.directory + ic.images[i] = IconImage.readIcon(dr, ic.directory .getEntry(i).getBytesInRes()); } return ic; @@ -51,9 +49,9 @@ public void write(IDataWriter dw) throws IOException { directory.getEntry(i).setOffset(offset); offset += images[i].sizeOf(); } - ResourceAssembler.write(directory, dw); + directory.write(dw); for (int i = 0; i < images.length; i++) { - ResourceAssembler.write(images[i], dw); + images[i].write(dw); } } diff --git a/src/test/java/com/kichik/pecoff4j/ReadWriteTest.java b/src/test/java/com/kichik/pecoff4j/ReadWriteTest.java new file mode 100644 index 0000000..06b258c --- /dev/null +++ b/src/test/java/com/kichik/pecoff4j/ReadWriteTest.java @@ -0,0 +1,100 @@ +package com.kichik.pecoff4j; + +import com.kichik.pecoff4j.constant.ResourceType; +import com.kichik.pecoff4j.io.DataReader; +import com.kichik.pecoff4j.io.PEParser; +import com.kichik.pecoff4j.io.ValidatingWriter; +import com.kichik.pecoff4j.resources.GroupIconDirectory; +import com.kichik.pecoff4j.resources.IconImage; +import com.kichik.pecoff4j.resources.Manifest; +import com.kichik.pecoff4j.resources.VersionInfo; +import com.kichik.pecoff4j.util.ResourceHelper; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +public class ReadWriteTest { + @Test + public void testReadWriteExe() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/WinRun4J.exe")); + ValidatingWriter writer = new ValidatingWriter(new DataReader(getClass().getResourceAsStream("/WinRun4J.exe"))); + + pe.write(writer); + writer.assertEndOfStream(); + } + + @Test + public void testReadWriteDll() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/clr/ClassLibrary.dll")); + ValidatingWriter writer = new ValidatingWriter(new DataReader(getClass().getResourceAsStream("/clr/ClassLibrary.dll"))); + + pe.write(writer); + writer.assertEndOfStream(); + } + + @Test + public void testReadWriteResources() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/WinRun4J.exe")); + ValidatingWriter writer = new ValidatingWriter(new DataReader(pe.getSectionTable().findSection(".rsrc").getData())); + + pe.getImageData().getResourceTable().write(writer); + writer.align(pe.getOptionalHeader().getFileAlignment()); + writer.assertEndOfStream(); + } + + @Test + public void testReadWriteVersionInfo() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/WinRun4J.exe")); + + ResourceDirectory rd = pe.getImageData().getResourceTable(); + ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO); + for (ResourceEntry entry : entries) { + VersionInfo versionInfo = VersionInfo.read(new DataReader(entry.getData())); + ValidatingWriter writer = new ValidatingWriter(new DataReader(entry.getData())); + versionInfo.write(writer); + writer.assertEndOfStream(); + } + } + + @Test + public void testReadWriteManifest() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/WinRun4J.exe")); + + ResourceDirectory rd = pe.getImageData().getResourceTable(); + ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.MANIFEST); + for (ResourceEntry entry : entries) { + Manifest manifest = Manifest.read(new DataReader(entry.getData()), entry.getData().length); + ValidatingWriter writer = new ValidatingWriter(new DataReader(entry.getData())); + manifest.write(writer); + writer.assertEndOfStream(); + } + } + + @Test + public void testReadWriteIconGroups() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/WinRun4J.exe")); + + ResourceDirectory rd = pe.getImageData().getResourceTable(); + ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.GROUP_ICON); + for (ResourceEntry entry : entries) { + GroupIconDirectory dir = GroupIconDirectory.read(new DataReader(entry.getData())); + ValidatingWriter writer = new ValidatingWriter(new DataReader(entry.getData())); + dir.write(writer); + writer.assertEndOfStream(); + } + } + + @Test + public void testReadWriteIcons() throws IOException { + PE pe = PEParser.parse(getClass().getResourceAsStream("/WinRun4J.exe")); + + ResourceDirectory rd = pe.getImageData().getResourceTable(); + ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.ICON); + for (ResourceEntry entry : entries) { + IconImage image = IconImage.readIcon(new DataReader(entry.getData()), entry.getData().length); + ValidatingWriter writer = new ValidatingWriter(new DataReader(entry.getData())); + image.write(writer); + writer.assertEndOfStream(); + } + } +} diff --git a/src/test/java/com/kichik/pecoff4j/VersionStringsTest.java b/src/test/java/com/kichik/pecoff4j/VersionStringsTest.java index 84c4b97..7e3ed28 100644 --- a/src/test/java/com/kichik/pecoff4j/VersionStringsTest.java +++ b/src/test/java/com/kichik/pecoff4j/VersionStringsTest.java @@ -12,8 +12,8 @@ import java.io.IOException; 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.StringPair; import com.kichik.pecoff4j.resources.StringTable; @@ -36,7 +36,7 @@ public void testVersionStrings() throws IOException { ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO); assertEquals(1, entries.length); - VersionInfo version = ResourceParser.readVersionInfo(entries[0].getData()); + VersionInfo version = VersionInfo.read(new DataReader(entries[0].getData())); // check StringFileInfo structure StringFileInfo strings = version.getStringFileInfo(); diff --git a/src/test/java/com/kichik/pecoff4j/io/ValidatingWriter.java b/src/test/java/com/kichik/pecoff4j/io/ValidatingWriter.java new file mode 100644 index 0000000..2c3f247 --- /dev/null +++ b/src/test/java/com/kichik/pecoff4j/io/ValidatingWriter.java @@ -0,0 +1,107 @@ +package com.kichik.pecoff4j.io; + +import org.junit.jupiter.api.Assertions; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * A {@link IDataWriter} that validates the data to write by comparing them + * to the data provided by a {@link IDataReader}. Compared to a diff of + * expected and actual byte array, this approach provides immediate feedback + * when a value is written that is not expected. + */ +public class ValidatingWriter implements IDataWriter { + + private static final byte[] PADDING = "PADDINGXXPADDING".getBytes(StandardCharsets.US_ASCII); + + private final IDataReader expected; + + public ValidatingWriter(IDataReader expected) { + this.expected = expected; + } + + @Override + public void writeByte(int b) throws IOException { + Assertions.assertEquals(expected.readByte(), b); + } + + @Override + public void writeByte(int b, int count) throws IOException { + for (int i = 0; i < count; i++) { + Assertions.assertEquals(expected.readByte(), b); + } + } + + @Override + public void writeBytes(byte[] b) throws IOException { + byte[] expectedBytes = new byte[b.length]; + expected.read(expectedBytes); + Assertions.assertArrayEquals(expectedBytes, b); + } + + @Override + public void writeDoubleWord(int dw) throws IOException { + Assertions.assertEquals(expected.readDoubleWord(), dw); + } + + @Override + public void writeWord(int w) throws IOException { + Assertions.assertEquals(expected.readWord(), w); + } + + @Override + public void writeLong(long l) throws IOException { + Assertions.assertEquals(expected.readLong(), l); + } + + @Override + public int getPosition() { + return expected.getPosition(); + } + + @Override + public void writeUtf(String s, int len) throws IOException { + int position = expected.getPosition(); + Assertions.assertEquals(expected.readUtf(len), s); + Assertions.assertEquals(position + len, expected.getPosition()); + } + + @Override + public void writeUtf(String s) throws IOException { + Assertions.assertEquals(expected.readUtf(s.length()), s); + } + + @Override + public void writeUnicode(String s) throws IOException { + Assertions.assertEquals(expected.readUnicode(), s); + } + + @Override + public void writeUnicode(String s, int len) throws IOException { + Assertions.assertEquals(expected.readUnicode(len).trim(), s); + } + + @Override + public int align(int alignment) throws IOException { + int off = (alignment - (getPosition() % alignment)) % alignment; + if (off != 0) { + for (int i = 0; i < off; i++) { + int value = expected.readByte(); + if (value != 0 && value != PADDING[i % 16]) { + Assertions.fail("Padding unexpected"); + } + } + } + return off; + } + + public void assertEndOfStream() throws IOException { + Assertions.assertFalse(expected.hasMore()); + } + + @Override + public String toString() { + return "ValidatingWriter @ " + getPosition(); + } +}