Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c81a34e
commit a458659
Showing
9 changed files
with
569 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* ### | ||
* 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 firmware.uefi_te; | ||
|
||
import ghidra.app.util.bin.BinaryReader; | ||
import ghidra.app.util.bin.StructConverter; | ||
import ghidra.program.model.data.DataType; | ||
import ghidra.program.model.data.Structure; | ||
import ghidra.program.model.data.StructureDataType; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* Parser for PE32/TE image data directories, which have the following fields: | ||
* | ||
* PE32/TE Image Data Directory | ||
* +------+------+-----------------+ | ||
* | Type | Size | Description | | ||
* +------+------+-----------------+ | ||
* | u32 | 4 | Virtual Address | | ||
* | u32 | 4 | Size | | ||
* +------+------+-----------------+ | ||
* | ||
* Ghidra has existing classes to parse this structure, but they are dependent on PE32 NT headers. | ||
*/ | ||
public class EFIImageDataDirectory implements StructConverter { | ||
private int virtualAddress; | ||
private int size; | ||
|
||
/** | ||
* Constructs an EFIImageDataDirectory from a specified BinaryReader. | ||
* | ||
* @param reader the specified BinaryReader | ||
*/ | ||
public EFIImageDataDirectory(BinaryReader reader) throws IOException { | ||
virtualAddress = reader.readNextInt(); | ||
size = reader.readNextInt(); | ||
} | ||
|
||
/** | ||
* Returns the virtual address of the current data directory. | ||
* | ||
* @return the virtual address of the current data directory | ||
*/ | ||
public int getVirtualAddress() { | ||
return virtualAddress; | ||
} | ||
|
||
/** | ||
* Returns the size of the current data directory. | ||
* | ||
* @return the size of the current data directory | ||
*/ | ||
public int getSize() { | ||
return size; | ||
} | ||
|
||
@Override | ||
public DataType toDataType() { | ||
Structure structure = new StructureDataType("efi_image_data_dir_t", 0); | ||
structure.add(DWORD, 4, "virtual_address", null); | ||
structure.add(DWORD, 4, "size", null); | ||
return structure; | ||
} | ||
} |
150 changes: 150 additions & 0 deletions
150
src/main/java/firmware/uefi_te/EFIImageSectionHeader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/* ### | ||
* 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 firmware.uefi_te; | ||
|
||
import ghidra.app.util.bin.BinaryReader; | ||
import ghidra.app.util.bin.StructConverter; | ||
import ghidra.program.model.data.ArrayDataType; | ||
import ghidra.program.model.data.DataType; | ||
import ghidra.program.model.data.Structure; | ||
import ghidra.program.model.data.StructureDataType; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* Parser for PE32/TE section headers, which have the following fields: | ||
* | ||
* PE32/TE Image Section Header | ||
* +---------+------+------------------------+ | ||
* | Type | Size | Description | | ||
* +---------+------+------------------------+ | ||
* | char[8] | 8 | Name | | ||
* | u32 | 4 | Virtual Size | | ||
* | u32 | 4 | Virtual Address | | ||
* | u32 | 4 | Raw Data Size | | ||
* | u32 | 4 | Raw Data Pointer | | ||
* | u32 | 4 | Relocations Pointer | | ||
* | u32 | 4 | Line Numbers Pointer | | ||
* | u16 | 2 | Number of Relocations | | ||
* | u16 | 2 | Number of Line Numbers | | ||
* | u32 | 4 | Characteristics | | ||
* +---------+------+------------------------+ | ||
* | ||
* Ghidra has existing classes to parse this structure, but they are dependent on PE32 NT headers. | ||
*/ | ||
public class EFIImageSectionHeader implements StructConverter { | ||
// Original header fields | ||
private String name; | ||
private int virtualSize; | ||
private int virtualAddress; | ||
private int rawDataSize; | ||
private int rawDataPointer; | ||
private int relocationsPointer; | ||
private int lineNumbersPointer; | ||
private short numRelocations; | ||
private short numLineNumbers; | ||
private int characteristics; | ||
|
||
/** | ||
* Constructs an EFIImageSectionHeader from a specified BinaryReader. | ||
* | ||
* @param reader the specified BinaryReader | ||
*/ | ||
public EFIImageSectionHeader(BinaryReader reader) throws IOException { | ||
name = reader.readNextAsciiString(8); | ||
virtualSize = reader.readNextInt(); | ||
virtualAddress = reader.readNextInt(); | ||
rawDataSize = reader.readNextInt(); | ||
rawDataPointer = reader.readNextInt(); | ||
relocationsPointer = reader.readNextInt(); | ||
lineNumbersPointer = reader.readNextInt(); | ||
numRelocations = reader.readNextShort(); | ||
numLineNumbers = reader.readNextShort(); | ||
characteristics = reader.readNextInt(); | ||
} | ||
|
||
/** | ||
* Returns the name of the current section. | ||
* | ||
* @return the name of the current section | ||
*/ | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
/** | ||
* Returns the virtual address for the current section. | ||
* | ||
* @return the virtual address for the current section | ||
*/ | ||
public int getVirtualAddress() { | ||
return virtualAddress; | ||
} | ||
|
||
/** | ||
* Returns the virtual size for the current section. | ||
* | ||
* @return the virtual size for the current section | ||
*/ | ||
public int getVirtualSize() { | ||
return virtualSize; | ||
} | ||
|
||
/** | ||
* Returns whether the current section is executable. | ||
* | ||
* @return whether the current section is executable | ||
*/ | ||
public boolean isExecutable() { | ||
return (characteristics & TerseExecutableConstants.SectionCharacteristics.MEM_EXECUTE) | ||
!= 0; | ||
} | ||
|
||
/** | ||
* Returns whether the current section is readable. | ||
* | ||
* @return whether the current section is readable | ||
*/ | ||
public boolean isReadable() { | ||
return (characteristics & TerseExecutableConstants.SectionCharacteristics.MEM_READ) != 0; | ||
} | ||
|
||
/** | ||
* Returns whether the current section is writable. | ||
* | ||
* @return whether the current section is writable | ||
*/ | ||
public boolean isWritable() { | ||
return (characteristics & TerseExecutableConstants.SectionCharacteristics.MEM_WRITE) != 0; | ||
} | ||
|
||
@Override | ||
public DataType toDataType() { | ||
Structure structure = new StructureDataType("efi_image_section_hdr_t", 0); | ||
structure.add(new ArrayDataType(ASCII, 8, 1), "name", null); | ||
structure.add(DWORD, 4, "virtual_size", null); | ||
structure.add(DWORD, 4, "virtual_addr", null); | ||
structure.add(DWORD, 4, "raw_data_size", null); | ||
structure.add(DWORD, 4, "raw_data_ptr", null); | ||
structure.add(DWORD, 4, "relocations_ptr", null); | ||
structure.add(DWORD, 4, "line_numbers_ptr", null); | ||
structure.add(WORD, 2, "num_relocations", null); | ||
structure.add(WORD, 2, "num_line_numbers", null); | ||
structure.add(DWORD, 4, "characteristics", null); | ||
return structure; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* ### | ||
* 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 firmware.uefi_te; | ||
|
||
import ghidra.app.util.Option; | ||
import ghidra.app.util.bin.BinaryReader; | ||
import ghidra.app.util.bin.ByteProvider; | ||
import ghidra.app.util.bin.format.pe.MachineConstants; | ||
import ghidra.app.util.importer.MemoryConflictHandler; | ||
import ghidra.app.util.importer.MessageLog; | ||
import ghidra.app.util.opinion.AbstractLibrarySupportLoader; | ||
import ghidra.app.util.opinion.LoadSpec; | ||
import ghidra.program.flatapi.FlatProgramAPI; | ||
import ghidra.program.model.address.Address; | ||
import ghidra.program.model.lang.LanguageCompilerSpecPair; | ||
import ghidra.program.model.listing.Program; | ||
import ghidra.program.model.mem.MemoryBlock; | ||
import ghidra.util.Msg; | ||
import ghidra.util.task.TaskMonitor; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
/** | ||
* Loader for Terse Executable (TE) binaries. Terse Executables are simplified PE/COFF executables, | ||
* used in the UEFI PI stage. | ||
*/ | ||
public class TELoader extends AbstractLibrarySupportLoader { | ||
@Override | ||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) { | ||
ArrayList<LoadSpec> loadSpecs = new ArrayList<>(); | ||
BinaryReader reader = new BinaryReader(provider, true); | ||
TerseExecutableHeader header = null; | ||
try { | ||
header = new TerseExecutableHeader(reader); | ||
} catch (IOException e) { | ||
return loadSpecs; | ||
} | ||
|
||
switch (header.getMachineType()) { | ||
case MachineConstants.IMAGE_FILE_MACHINE_I386: | ||
loadSpecs.add(new LoadSpec(this, header.getImageBase(), | ||
new LanguageCompilerSpecPair("x86:LE:32:default", "windows"), true)); | ||
break; | ||
case MachineConstants.IMAGE_FILE_MACHINE_AMD64: | ||
loadSpecs.add(new LoadSpec(this, header.getImageBase(), | ||
new LanguageCompilerSpecPair("x86:LE:64:default", "windows"), true)); | ||
break; | ||
} | ||
|
||
return loadSpecs; | ||
} | ||
|
||
@Override | ||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, | ||
Program program, MemoryConflictHandler handler, TaskMonitor monitor, | ||
MessageLog log) throws IOException { | ||
BinaryReader reader = new BinaryReader(provider, true); | ||
TerseExecutableHeader teHeader = new TerseExecutableHeader(reader); | ||
EFIImageSectionHeader[] sectionHeaders = teHeader.getSections(); | ||
FlatProgramAPI api = new FlatProgramAPI(program, monitor); | ||
InputStream inputStream = provider.getInputStream(0); | ||
|
||
try { | ||
// Create a segment for the TE header and section headers. | ||
api.createMemoryBlock("Headers", api.toAddr( | ||
teHeader.getImageBase() + teHeader.getHeaderOffset()), inputStream, | ||
TerseExecutableConstants.TE_HEADER_SIZE + teHeader.getNumSections() * | ||
TerseExecutableConstants.SECTION_HEADER_SIZE, false); | ||
|
||
// Mark the TE header and section headers as data. | ||
api.createData(api.toAddr(teHeader.getImageBase() + teHeader.getHeaderOffset()), | ||
teHeader.toDataType()); | ||
long sectionHeaderOffset = teHeader.getImageBase() + teHeader.getHeaderOffset() + | ||
TerseExecutableConstants.TE_HEADER_SIZE; | ||
for (EFIImageSectionHeader sectionHeader : sectionHeaders) { | ||
api.createData(api.toAddr(sectionHeaderOffset), sectionHeader.toDataType()); | ||
sectionHeaderOffset += TerseExecutableConstants.SECTION_HEADER_SIZE; | ||
} | ||
|
||
// Create a segment for each section. | ||
for (EFIImageSectionHeader sectionHeader : sectionHeaders) { | ||
inputStream = provider.getInputStream(sectionHeader.getVirtualAddress() - | ||
teHeader.getHeaderOffset()); | ||
long startAddress = teHeader.getImageBase() + sectionHeader.getVirtualAddress(); | ||
MemoryBlock section = api.createMemoryBlock(sectionHeader.getName(), | ||
api.toAddr(startAddress), inputStream, sectionHeader.getVirtualSize(), | ||
false); | ||
|
||
// Set the appropriate permissions for this segment. | ||
section.setRead(sectionHeader.isReadable()); | ||
section.setWrite(sectionHeader.isWritable()); | ||
section.setExecute(sectionHeader.isExecutable()); | ||
|
||
Msg.debug(this, String.format("Added %s section: 0x%X-0x%X", | ||
sectionHeader.getName(), startAddress, startAddress + | ||
sectionHeader.getVirtualSize())); | ||
} | ||
|
||
// Define the entry point function. | ||
Address entryPoint = api.toAddr(teHeader.getImageBase() + teHeader.getEntryPointAddress()); | ||
api.addEntryPoint(entryPoint); | ||
api.createFunction(entryPoint, "_ModuleEntryPoint"); | ||
} catch (Exception e) { | ||
Msg.showError(this, null, getName() + " Loader", e.getMessage(), e); | ||
} | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "Terse Executable (TE)"; | ||
} | ||
} |
Oops, something went wrong.