Skip to content

Commit

Permalink
Add rebuild support for VersionInfo related structures
Browse files Browse the repository at this point in the history
  • Loading branch information
swismer authored and kichik committed Jul 26, 2023
1 parent bd493ca commit 454e1de
Show file tree
Hide file tree
Showing 13 changed files with 502 additions and 179 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.kichik.pecoff4j.constant.ResourceType;
import com.kichik.pecoff4j.io.DataReader;
import com.kichik.pecoff4j.io.PEParser;
import com.kichik.pecoff4j.resources.StringFileInfo;
import com.kichik.pecoff4j.resources.StringPair;
import com.kichik.pecoff4j.resources.StringTable;
import com.kichik.pecoff4j.resources.VersionInfo;
import com.kichik.pecoff4j.util.ResourceHelper;
Expand All @@ -64,11 +65,9 @@ public class Main {
VersionInfo version = VersionInfo.read(new DataReader(data));

StringFileInfo strings = version.getStringFileInfo();
StringTable table = strings.getTable(0);
for (int j = 0; j < table.getCount(); j++) {
String key = table.getString(j).getKey();
String value = table.getString(j).getValue();
System.out.println(key + " = " + value);
StringTable table = strings.getTables().get(0);
for (List<StringPair> pair :table.getStrings()){
System.out.println(pair.getKey() + " = " + pair.getValue());
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/kichik/pecoff4j/RebuildableStructure.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kichik.pecoff4j;

import com.kichik.pecoff4j.io.IDataWriter;

import java.io.IOException;

/**
* A structure that contains values derived from its content like lengths, checksums or pointers to nested structures.
*
* Use {@link #rebuild()} to update these values prior to {@link #write(IDataWriter)}. The latter just stores the values
* as they are present.
*/
public interface RebuildableStructure {
/**
* Update derived values such as lengths, checksums, serialized layout etc.
*
* @return the updated length of this structure in bytes (might include padding)
*/
int rebuild();

/**
* Write this structure to a data writer.
*
* @param dw the writer
*/
void write(IDataWriter dw) throws IOException;
}
71 changes: 48 additions & 23 deletions src/main/java/com/kichik/pecoff4j/resources/StringFileInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,32 @@
import java.util.ArrayList;
import java.util.List;

import com.kichik.pecoff4j.RebuildableStructure;
import com.kichik.pecoff4j.io.IDataReader;
import com.kichik.pecoff4j.io.IDataWriter;
import com.kichik.pecoff4j.util.Strings;

public class StringFileInfo {
import static com.kichik.pecoff4j.util.Alignment.alignDword;

/**
* File information structure.
*
* See <a href="https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo">StringFileInfo structure</a> for details.
*/
public class StringFileInfo implements RebuildableStructure {
/** The length of this structure (in bytes) */
private int length;

/** Always 0 */
private int valueLength;

/** 1 for text data, 0 for binary data */
private int type;

/** Must be "StringFileInfo" */
private String key;
private int padding;
private List<StringTable> tables = new ArrayList<StringTable>();

private final List<StringTable> tables = new ArrayList<>();

public static StringFileInfo read(IDataReader dr) throws IOException {
int initialPos = dr.getPosition();
Expand All @@ -35,10 +50,11 @@ public static StringFileInfo read(IDataReader dr) throws IOException {
sfi.setValueLength(dr.readWord());
sfi.setType(dr.readWord());
sfi.setKey(dr.readUnicode());
sfi.setPadding(dr.align(4));
dr.align(4);

while (dr.getPosition() - initialPos < sfi.getLength())
while (dr.getPosition() - initialPos < sfi.getLength()) {
sfi.add(StringTable.read(dr));
}

return sfi;
}
Expand All @@ -50,24 +66,36 @@ public static StringFileInfo readPartial(IDataReader dr, int initialPos, int len
sfi.setValueLength(valueLength);
sfi.setType(type);
sfi.setKey(key);
sfi.setPadding(dr.align(4));
dr.align(4);
while (dr.getPosition() - initialPos < sfi.getLength()) {
sfi.add(StringTable.read(dr));
}
return sfi;
}

@Override
public int rebuild() {
valueLength = 0;

int sum = alignDword(6 + Strings.getUtf16Length(key));
for (StringTable table : tables) {
sum += table.rebuild();
}
length = sum;
return length;
}

@Override
public void write(IDataWriter dw) throws IOException {
dw.writeWord(getLength());
if (getLength() == 0) {
dw.writeWord(length);
if (length == 0) {
return;
}
dw.writeWord(getValueLength());
dw.writeWord(getType());
dw.writeUnicode(getKey());
dw.writeWord(valueLength);
dw.writeWord(type);
dw.writeUnicode(key);
dw.align(4);
for (int i = 0; i < getCount(); i++) {
StringTable table = getTable(i);
for (StringTable table : tables) {
table.write(dw);
}
}
Expand All @@ -84,6 +112,10 @@ public StringTable getTable(int index) {
return tables.get(index);
}

public List<StringTable> getTables() {
return tables;
}

public int getLength() {
return length;
}
Expand Down Expand Up @@ -116,18 +148,11 @@ public void setKey(String key) {
this.key = key;
}

public int getPadding() {
return padding;
}

public void setPadding(int padding) {
this.padding = padding;
}

public int sizeOf() {
int actualLength = 6 + padding + Strings.getUtf16Length(key);
for (StringTable t : tables)
int actualLength = alignDword(6 + Strings.getUtf16Length(key));
for (StringTable t : tables) {
actualLength += t.sizeOf();
}
return actualLength;
}
}
81 changes: 53 additions & 28 deletions src/main/java/com/kichik/pecoff4j/resources/StringPair.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,32 @@
*******************************************************************************/
package com.kichik.pecoff4j.resources;

import com.kichik.pecoff4j.RebuildableStructure;
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 {
import static com.kichik.pecoff4j.util.Alignment.*;

/**
* A string pair.
*
* See <a href="https://learn.microsoft.com/en-us/windows/win32/menurc/string-str">String structure</a> for details.
*/
public class StringPair implements RebuildableStructure {
/** The length of this structure (in bytes) */
private int length;

/** The length of this value String (in words) */
private int valueLength;

/** 1 for text data, 0 for binary data */
private int type;
private String key;
private String value;
private int padding;

public static StringPair read(IDataReader dr) throws IOException {
int initialPos = dr.getPosition();
Expand All @@ -33,40 +45,60 @@ public static StringPair read(IDataReader dr) throws IOException {
sp.setValueLength(dr.readWord());
sp.setType(dr.readWord());
sp.setKey(dr.readUnicode());
sp.setPadding(dr.align(4));
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
if (sp.getType() == 0) { // wType == 0 => binary; wLength is in bytes
valueLength /= 2;
if (valueLength > remainingWords)
}
if (valueLength > remainingWords) {
valueLength = remainingWords;
sp.setValue(dr.readUnicode(valueLength).trim());
}
sp.setValue(dr.readUnicode(valueLength - 1));

int remainingBytes = (sp.getLength() - (dr.getPosition() - initialPos));
dr.skipBytes(remainingBytes);
dr.align(4);
return sp;
}

@Override
public int rebuild() {
if (value.isEmpty()) {
// sometimes single null-byte is stored, so valueLength could also be 1 or 2 (depending on type)
valueLength = 0;
}
else if (type == 0) { // wType == 0 => binary; wLength is in bytes
valueLength = Strings.getUtf16Length(value);
} else {
valueLength = Strings.getUtf16Length(value) / 2;
}
length = sizeOf();
return length;
}

@Override
public void write(IDataWriter dw) throws IOException {
int initialPos = dw.getPosition();

dw.writeWord(getLength());
dw.writeWord(getValueLength());
dw.writeWord(getType());
dw.writeUnicode(getKey());
dw.writeWord(length);
dw.writeWord(valueLength);
dw.writeWord(type);
dw.writeUnicode(key);
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));
int remainingWords = (length - (dw.getPosition() - initialPos)) / 2;
int valueLengthInBytes = valueLength;
if (getType() == 0) {// wType == 0 => binary; wLength is in bytes
valueLengthInBytes /= 2;
}
if (valueLengthInBytes > remainingWords) {
valueLengthInBytes = remainingWords;
}
dw.writeUnicode(value, valueLengthInBytes);

int remainingBytes = (length - (dw.getPosition() - initialPos));
dw.writeByte(0, remainingBytes);
dw.align(4);
}
Expand Down Expand Up @@ -117,15 +149,8 @@ public String toString() {
}

public int sizeOf() {
return 6 + padding + Strings.getUtf16Length(key)
+ Strings.getUtf16Length(value);
return alignDword(6 + Strings.getUtf16Length(key))
+ (value.isEmpty() ? 0 : Strings.getUtf16Length(value));
}

public int getPadding() {
return padding;
}

public void setPadding(int padding) {
this.padding = padding;
}
}
Loading

0 comments on commit 454e1de

Please sign in to comment.