diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomException.java new file mode 100644 index 0000000000..97b4eb19ea --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomException.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.som; + +/** + * An exception class to handle encountering invalid SOM headers + */ +public class SomException extends Exception { + + /** + * Constructs a new exception with the specified detail message + * + * @param message the detail message + */ + public SomException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified cause and a detail message + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} + * method (a {@code null} value is permitted, and indicates that the cause is nonexistent + * or unknown). + */ + public SomException(Exception cause) { + super(cause); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomHeader.java index 8eab52fc09..5b2a08be4f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/som/SomHeader.java @@ -84,12 +84,18 @@ public class SomHeader implements StructConverter { * Creates a new {@link SomHeader} * * @param reader A {@link BinaryReader} positioned at the start of the header + * @throws SomException if the header is invalid * @throws IOException if there was an IO-related error */ - public SomHeader(BinaryReader reader) throws IOException { + public SomHeader(BinaryReader reader) throws SomException, IOException { systemId = reader.readNextUnsignedShort(); magic = reader.readNextUnsignedShort(); versionId = reader.readNextUnsignedInt(); + + if (!hasValidMagic() || !hasValidVersionId()) { + throw new SomException("Invalid SOM header"); + } + fileTime = new SomSysClock(reader); entrySpace = reader.readNextUnsignedInt(); entrySubspace = reader.readNextUnsignedInt(); @@ -173,25 +179,6 @@ public class SomHeader implements StructConverter { return magic; } - /** - * {@return true if this {@link SomHeader} has a valid magic number; otherwise false} - */ - public boolean hasValidMagic() { - return switch (magic) { - case MAGIC_LIBRARY: - case MAGIC_RELOCATABLE: - case MAGIC_NON_SHAREABLE_EXE: - case MAGIC_SHAREABLE_EXE: - case MAGIC_SHARABLE_DEMAND_LOADABLE_EXE: - case MAGIC_DYNAMIC_LOAD_LIBRARY: - case MAGIC_SHARED_LIBRARY: - case MAGIC_RELOCATABLE_LIBRARY: - yield true; - default: - yield false; - }; - } - /** * {@return the version ID} */ @@ -199,13 +186,6 @@ public class SomHeader implements StructConverter { return versionId; } - /** - * {@return true if this {@link SomHeader} has a valid version ID; otherwise false} - */ - public boolean hasValidVersionId() { - return versionId == 85082112 || versionId == 87102412; - } - /** * {@return the file time} */ @@ -558,6 +538,32 @@ public class SomHeader implements StructConverter { } } + /** + * {@return true if this {@link SomHeader} has a valid magic number; otherwise false} + */ + private boolean hasValidMagic() { + return switch (magic) { + case MAGIC_LIBRARY: + case MAGIC_RELOCATABLE: + case MAGIC_NON_SHAREABLE_EXE: + case MAGIC_SHAREABLE_EXE: + case MAGIC_SHARABLE_DEMAND_LOADABLE_EXE: + case MAGIC_DYNAMIC_LOAD_LIBRARY: + case MAGIC_SHARED_LIBRARY: + case MAGIC_RELOCATABLE_LIBRARY: + yield true; + default: + yield false; + }; + } + + /** + * {@return true if this {@link SomHeader} has a valid version ID; otherwise false} + */ + private boolean hasValidVersionId() { + return versionId == 85082112 || versionId == 87102412; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { StructureDataType struct = new StructureDataType("header", SIZE); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MotorolaHexLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MotorolaHexLoader.java index 58c6557c12..6bc60a94f3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MotorolaHexLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MotorolaHexLoader.java @@ -77,10 +77,12 @@ public class MotorolaHexLoader extends AbstractProgramLoader { } static boolean isPossibleHexFile(ByteProvider provider) { + final int MAX_BLANK_LINES = 100; try (BoundedBufferedReader reader = new BoundedBufferedReader(new InputStreamReader(provider.getInputStream(0)))) { + int i = 0; String line = reader.readLine(); - while (line.matches("^\\s*$")) { + while (i++ < MAX_BLANK_LINES && line.isBlank()) { line = reader.readLine(); } return line.matches("^[S:][0-9a-fA-F]+$"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/SomLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/SomLoader.java index 362d73c2eb..9351483351 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/SomLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/SomLoader.java @@ -50,18 +50,16 @@ public class SomLoader extends AbstractProgramWrapperLoader { try { SomHeader header = new SomHeader(new BinaryReader(provider, false)); - if (header.hasValidMagic() && header.hasValidVersionId()) { - List results = QueryOpinionService.query(getName(), - Integer.toString(header.getSystemId()), null); - for (QueryResult result : results) { - loadSpecs.add(new LoadSpec(this, 0, result)); - } - if (loadSpecs.isEmpty()) { - loadSpecs.add(new LoadSpec(this, 0, true)); - } + List results = QueryOpinionService.query(getName(), + Integer.toString(header.getSystemId()), null); + for (QueryResult result : results) { + loadSpecs.add(new LoadSpec(this, 0, result)); + } + if (loadSpecs.isEmpty()) { + loadSpecs.add(new LoadSpec(this, 0, true)); } } - catch (IOException e) { + catch (SomException | IOException e) { // that's ok, not a System Object Model } return loadSpecs;