diff --git a/Ghidra/Features/Base/ghidra_scripts/FindAudioInProgramScript.java b/Ghidra/Features/Base/ghidra_scripts/FindAudioInProgramScript.java index bc49371a36..3b04838e24 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FindAudioInProgramScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FindAudioInProgramScript.java @@ -15,17 +15,16 @@ */ //Finds programs containing various audio resources such as WAV's //@category Resources +import java.util.ArrayList; +import java.util.List; + import ghidra.app.script.GhidraScript; import ghidra.program.model.address.Address; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.WAVEDataType; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Data; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; -import java.util.ArrayList; -import java.util.List; - public class FindAudioInProgramScript extends GhidraScript { @Override @@ -36,8 +35,10 @@ public class FindAudioInProgramScript extends GhidraScript { //look for WAV data types WAVEDataType wdt = new WAVEDataType(); + MIDIDataType mdt = new MIDIDataType(); totalFound += findAudioData("WAV", wdt, WAVEDataType.MAGIC, WAVEDataType.MAGIC_MASK); + totalFound += findAudioData("MIDI", mdt, MIDIDataType.MAGIC, MIDIDataType.MAGIC_MASK); if (totalFound == 0) { println("No Audio data found in " + currentProgram.getName()); @@ -54,12 +55,12 @@ public class FindAudioInProgramScript extends GhidraScript { int numDataFound = 0; List
foundList = scanForAudioData(pattern, mask); - //Loop over all potential found WAVs + //Loop over all potential found audio for (int i = 0; i < foundList.size(); i++) { boolean foundData = false; - //See if already applied WAV + //See if already applied data type Data data = getDataAt(foundList.get(i)); - //If not already applied, try to apply WAV data type + //If not already applied, try to apply audio data type if (data == null) { println("Trying to apply " + dataName + " datatype at " + foundList.get(i).toString()); @@ -67,7 +68,7 @@ public class FindAudioInProgramScript extends GhidraScript { try { Data newData = createData(foundList.get(i), dt); if (newData != null) { - println("Applied WAV at " + newData.getAddressString(false, true)); + printf("Applied %s at %s", dataName, newData.getAddressString(false, true)); foundData = true; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/EmbeddedMediaAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/EmbeddedMediaAnalyzer.java index 141404a300..334acc3a1f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/EmbeddedMediaAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/EmbeddedMediaAnalyzer.java @@ -81,6 +81,9 @@ public class EmbeddedMediaAnalyzer extends AbstractAnalyzer { addByteSearchPattern(searcher, program, foundMedia, new WAVEDataType(), "WAVE", WAVEDataType.MAGIC, WAVEDataType.MAGIC_MASK); + addByteSearchPattern(searcher, program, foundMedia, new MIDIDataType(), "MIDI", + MIDIDataType.MAGIC, MIDIDataType.MAGIC_MASK); + addByteSearchPattern(searcher, program, foundMedia, new AUDataType(), "AU", AUDataType.MAGIC, AUDataType.MAGIC_MASK); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ResourceDataDirectory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ResourceDataDirectory.java index b6aa8db00b..688211a313 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ResourceDataDirectory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ResourceDataDirectory.java @@ -269,6 +269,19 @@ public class ResourceDataDirectory extends DataDirectory { } PeUtils.createData(program, addr, dataType, log); } + else if (info.getName().startsWith("Rsrc_MIDI")) { + DataType dataType = null; + // Check for MIDI magic number + try { + if (program.getMemory().getInt(addr) == 0x6468544d) { + dataType = new MIDIDataType(); + } + } + catch (MemoryAccessException e) { + // ignore - let createData produce error + } + PeUtils.createData(program, addr, dataType, log); + } else if (info.getName().startsWith("Rsrc_WEVT")) { DataType dataType = null; // Check for WEVT magic number "CRIM" diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AudioPlayer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AudioPlayer.java index ab1c2a8241..7f8146753f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AudioPlayer.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AudioPlayer.java @@ -65,7 +65,7 @@ public class AudioPlayer implements Playable, LineListener { clip.start(); } catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) { - Msg.debug(this, "Unable to play audio", e); + Msg.error(this, "Unable to play audio", e); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MIDIDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MIDIDataType.java new file mode 100644 index 0000000000..4b2ab9eb6a --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MIDIDataType.java @@ -0,0 +1,145 @@ +/* ### + * 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.program.model.data; + +import java.io.DataInputStream; +import java.io.EOFException; + +import ghidra.docking.settings.Settings; +import ghidra.program.model.mem.MemBuffer; +import ghidra.util.Msg; + +public class MIDIDataType extends BuiltIn implements Dynamic { + + public static byte[] MAGIC = + new byte[] { (byte) 'M', (byte) 'T', (byte) 'h', (byte) 'd', (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 'M', (byte) 'T', (byte) 'r', (byte) 'k' }; + + public static byte[] MAGIC_MASK = + new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; + + public MIDIDataType() { + this(null); + } + + public MIDIDataType(DataTypeManager dtm) { + super(null, "MIDI-Score", dtm); + } + + @Override + public int getLength() { + return -1; + } + + @Override + public int getLength(MemBuffer buf, int maxLength) { + try (DataInputStream stream = new DataInputStream( + buf.getInputStream(0, maxLength > 0 ? maxLength : Integer.MAX_VALUE))) { + byte[] chunkType = new byte[4]; + if (stream.read(chunkType) < chunkType.length) { + throw new EOFException(); + } + if (chunkType[0] != (byte) 'M' || chunkType[1] != (byte) 'T' || + chunkType[2] != (byte) 'h' || chunkType[3] != (byte) 'd') { + return -1; + } + long chunkLength = Integer.toUnsignedLong(stream.readInt()); + if (chunkLength != 6) { + throw new InvalidDataTypeException("Unexpected header length."); + } + stream.skip(2); + int tracks = Short.toUnsignedInt(stream.readShort()); + stream.skip(2); + int computedLength = 14; + while (tracks > 0) { + if (stream.read(chunkType) < chunkType.length) { + throw new EOFException(); + } + chunkLength = Integer.toUnsignedLong(stream.readInt()); + stream.skip(chunkLength); + computedLength += 8 + chunkLength; + if (chunkType[0] != (byte) 'M' || chunkType[1] != (byte) 'T' || + chunkType[2] != (byte) 'r' || chunkType[3] != (byte) 'k') { + continue; + } + tracks--; + } + return computedLength; + } + catch (Exception e) { + Msg.debug(this, "Invalid MIDI data at " + buf.getAddress()); + return -1; + } + } + + @Override + public boolean canSpecifyLength() { + return false; + } + + @Override + public DataType clone(DataTypeManager dtm) { + if (dtm == getDataTypeManager()) { + return this; + } + return new MIDIDataType(dtm); + } + + @Override + public String getDescription() { + return "MIDI score stored within program"; + } + + @Override + public String getMnemonic(Settings settings) { + return "MIDI"; + } + + @Override + public String getRepresentation(MemBuffer buf, Settings settings, int length) { + return "