Skip to content

Commit 3872904

Browse files
committed
Merge remote-tracking branch
'origin/GT-3451_dev747368_PR-1403_agatti_iso9660fixes' Fixes NationalSecurityAgency#1403
2 parents 84fda70 + 42b4e66 commit 3872904

File tree

2 files changed

+139
-49
lines changed

2 files changed

+139
-49
lines changed

Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Directory.java

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import ghidra.util.exception.DuplicateNameException;
2121

2222
import java.io.*;
23+
import java.time.DateTimeException;
24+
import java.time.LocalDateTime;
2325

2426
public class ISO9660Directory implements StructConverter {
2527

@@ -128,7 +130,7 @@ public DataType toDataType() throws DuplicateNameException, IOException {
128130
*/
129131
@Override
130132
public String toString() {
131-
StringBuffer buff = new StringBuffer();
133+
StringBuilder buff = new StringBuilder();
132134

133135
buff.append("Directory Record Length: 0x" + Integer.toHexString(directoryRecordLength) +
134136
"\n");
@@ -222,32 +224,62 @@ private byte getFlagBit(byte flagByte, int flagIndex) {
222224
return (byte) ((flagByte >>> flagIndex) & 1);
223225
}
224226

227+
/**
228+
* Parses the given buffer as an ISO9660 timestamp and returns it as a
229+
* human readable string representation.
230+
*
231+
* Invalid buffers that are still big enough to hold a timestamp are
232+
* still parsed and converted, albeit they are marked as invalid when
233+
* presented to the user.
234+
*
235+
* @param byteArray the buffer to parse (both standard and extended
236+
* formats are handled).
237+
* @return a string with the human readable timestamp.
238+
*/
225239
private String createDateTimeString(byte[] byteArray) {
240+
if (byteArray == null || byteArray.length < 7) {
241+
return "INVALID (truncated or missing)";
242+
}
226243

227-
if (byteArray != null) {
228-
if (byteArray.length > 6) {
229-
int timeOffset = byteArray[byteArray.length - 1];
230-
int i1, i2, i3, i4, i5, i6;
231-
i1 = 1900 + byteArray[0]; //Years since 1900
232-
i2 = byteArray[1]; //Month of year
233-
i3 = byteArray[2]; //Day of month
234-
i4 = byteArray[3]; //Hour of day
235-
i5 = byteArray[4]; //Minute of hour
236-
i6 = byteArray[5]; //Second of minute
237-
238-
//Time zone offset from GMT in 15 minute intervals,
239-
//starting at interval -48 (west) and running up to
240-
//interval 52 (east)
241-
int timeZone = (-48 + timeOffset) / 4; //GMT offset
242-
String dateTime =
243-
String.format("%d-%d-%d %d:ds:%d GMT%d", i1, i2, i3, i4, i5, i6, timeZone);
244-
245-
return dateTime;
246-
}
244+
// Time zone offset from GMT in 15 minute intervals,
245+
// starting at interval -48 (west) and running up to
246+
// interval 52 (east)
247+
int timeOffset = byteArray[byteArray.length - 1];
248+
249+
int i1, i2, i3, i4, i5, i6;
250+
i1 = 1900 + byteArray[0]; // Years since 1900
251+
i2 = byteArray[1]; // Month of year
252+
i3 = byteArray[2]; // Day of month
253+
i4 = byteArray[3]; // Hour of day
254+
i5 = byteArray[4]; // Minute of hour
255+
i6 = byteArray[5]; // Second of minute
256+
257+
// The buffer contains an invalid timezone offset.
258+
boolean validBuffer = true;
259+
if (timeOffset < -48 || timeOffset > 52) {
260+
validBuffer = false;
261+
}
262+
263+
// The buffer contains an invalid date/time.
264+
try {
265+
LocalDateTime.of(i1, i2, i3, i4, i5, i6);
266+
} catch (DateTimeException exception) {
267+
validBuffer = false;
268+
}
247269

270+
StringBuilder builder = new StringBuilder();
271+
if (!validBuffer) {
272+
builder.append("INVALID (");
273+
}
274+
int timezoneIntegral = timeOffset / 4;
275+
int timezoneFractional = (Math.abs(timeOffset) % 4) * 15;
276+
builder.append(String.format("%04d-%02d-%02d %02d:%02d:%02d GMT%c%02d%02d", i1, i2, i3,
277+
i4, i5, i6, timezoneIntegral < 0 ? '-' : '+', timezoneIntegral, timezoneFractional));
278+
if (!validBuffer) {
279+
builder.append(")");
248280
}
249-
return "";
250281

282+
return builder.toString();
251283
}
252284

253285
private void setReaderToBigEndian(BinaryReader reader) {

Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660VolumeDescriptor.java

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import ghidra.util.exception.DuplicateNameException;
2121

2222
import java.io.IOException;
23+
import java.time.DateTimeException;
24+
import java.time.LocalDateTime;
2325

2426
public class ISO9660VolumeDescriptor extends ISO9660BaseVolume {
2527
private byte unused; // Always 0x00
@@ -192,7 +194,7 @@ else if (super.getTypeCode() == ISO9660Constants.VOLUME_DESC_SUPPL_VOLUME_DESC)
192194

193195
@Override
194196
public String toString() {
195-
StringBuffer buff = new StringBuffer();
197+
StringBuilder buff = new StringBuilder();
196198

197199
buff.append("Type Code: 0x" + Integer.toHexString(super.getTypeCode()) + " => " +
198200
getTypeCodeString() + "\n");
@@ -246,38 +248,94 @@ public String toString() {
246248
}
247249

248250
/**
249-
* Creates a formatted date/time string based on the byteArray passed in.
250-
* @param byteArray the array containing the bytes to format into date/time
251-
* @return The formatted date/time string
251+
* Checks whether the given string is entirely made up of ASCII digits.
252+
*
253+
* @param string the string to check.
254+
* @return true if all characters in the string are ASCII digits, false
255+
* otherwise.
252256
*/
257+
private boolean isDigitsStringValid(String string) {
258+
for (int i = 0; i < string.length(); i++) {
259+
char c = string.charAt(i);
260+
if (c < '0' || c > '9') {
261+
return false;
262+
}
263+
}
264+
265+
return true;
266+
}
253267

268+
/**
269+
* Parses the given buffer as an ISO9660 timestamp and returns it as a
270+
* human readable string representation.
271+
*
272+
* Invalid buffers that are still big enough to hold a timestamp are
273+
* still parsed and converted, albeit they are marked as invalid when
274+
* presented to the user.
275+
*
276+
* @param byteArray the buffer to parse (only extended timestamp format
277+
* is handled).
278+
* @return a string with the human readable timestamp.
279+
*/
254280
protected String createDateTimeString(byte[] byteArray) {
281+
if (byteArray == null || byteArray.length < 17) {
282+
return "INVALID (truncated or missing)";
283+
}
284+
285+
String s1, s2, s3, s4, s5, s6, s7;
286+
287+
// Time zone offset from GMT in 15 minute intervals,
288+
// starting at interval -48 (west) and running up to
289+
// interval 52 (east)
290+
int timeOffset = byteArray[byteArray.length - 1];
291+
292+
String bString = new String(byteArray);
293+
s1 = bString.substring(0, 4); //year 1 to 9999
294+
s2 = bString.substring(4, 6); //month 1 to 12
295+
s3 = bString.substring(6, 8); //day 1 to 31
296+
s4 = bString.substring(8, 10); //hour 0 to 23
297+
s5 = bString.substring(10, 12); //minute 0 to 59
298+
s6 = bString.substring(12, 14); //second 0 to 59
299+
s7 = bString.substring(14, 16); //ms 0 to 99
300+
301+
// Validate strings first.
302+
boolean validBuffer = isDigitsStringValid(s1) && isDigitsStringValid(s2) && isDigitsStringValid(s3) &&
303+
isDigitsStringValid(s4) && isDigitsStringValid(s5) && isDigitsStringValid(s6) && isDigitsStringValid(s7);
304+
305+
try {
306+
// The buffer contains an invalid date/time.
307+
LocalDateTime.of(Integer.parseInt(s1), Integer.parseInt(s2), Integer.parseInt(s3),
308+
Integer.parseInt(s4), Integer.parseInt(s5), Integer.parseInt(s6));
309+
} catch (NumberFormatException | DateTimeException e) {
310+
validBuffer = false;
311+
}
312+
313+
// The buffer contains an invalid timezone offset.
314+
if (timeOffset < -48 || timeOffset > 52) {
315+
validBuffer = false;
316+
}
317+
318+
/*
319+
* Time zone offset from GMT in 15 minute intervals,
320+
* starting at interval -48 (west) and running up to
321+
* interval 52 (east).
322+
*/
323+
int timezoneIntegral = timeOffset / 4;
324+
int timezoneFractional = (Math.abs(timeOffset) % 4) * 15;
325+
326+
StringBuilder builder = new StringBuilder();
327+
if (!validBuffer) {
328+
builder.append("INVALID(");
329+
}
330+
331+
builder.append(String.format("%s-%s-%s %s:%s:%s.%s GMT%c%02d%02d", s1, s2, s3, s4, s5, s6, s7,
332+
timezoneIntegral < 0 ? '-' : '+', timezoneIntegral, timezoneFractional));
255333

256-
if (byteArray != null) {
257-
String s1, s2, s3, s4, s5, s6, s7;
258-
int timeOffset = byteArray[byteArray.length - 1];
259-
String bString = new String(byteArray);
260-
s1 = bString.substring(0, 4); //year 1 to 9999
261-
s2 = bString.substring(4, 6); //month 1 to 12
262-
s3 = bString.substring(6, 8); //day 1 to 31
263-
s4 = bString.substring(8, 10); //hour 0 to 23
264-
s5 = bString.substring(10, 12); //minute 0 to 59
265-
s6 = bString.substring(12, 14); //second 0 to 59
266-
s7 = bString.substring(14, 16); //ms 0 to 99
267-
268-
/*
269-
* Time zone offset from GMT in 15 minute intervals,
270-
* starting at interval -48 (west) and running up to
271-
* interval 52 (east).
272-
*/
273-
int timeZone = (-48 + timeOffset) / 4; //GMT offset
274-
String dateTime =
275-
String.format("%s-%s-%s %s:%s:%s.%s GMT%d", s1, s2, s3, s4, s5, s6, s7, timeZone);
276-
277-
return dateTime;
334+
if (!validBuffer) {
335+
builder.append(")");
278336
}
279-
return "";
280337

338+
return builder.toString();
281339
}
282340

283341
public byte getUnused() {

0 commit comments

Comments
 (0)