Skip to content

Instantly share code, notes, and snippets.

@roblabla
Last active March 13, 2019 22:09
Show Gist options
  • Save roblabla/595804337223f1ff843a29bbeb07896b to your computer and use it in GitHub Desktop.
Save roblabla/595804337223f1ff843a29bbeb07896b to your computer and use it in GitHub Desktop.
/* ###
* 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 kernel70loader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import org.apache.commons.collections4.MapUtils;
import org.python.google.common.io.ByteStreams;
import generic.continues.RethrowContinuesFactory;
import ghidra.app.script.ScriptMessage;
import ghidra.app.util.MemoryBlockUtil;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.elf.ElfDynamicType;
import ghidra.app.util.bin.format.elf.ElfHeader;
import ghidra.app.util.bin.format.elf.ElfRelocationTable;
import ghidra.app.util.bin.format.elf.ElfSectionHeader;
import ghidra.app.util.bin.format.elf.ElfStringTable;
import ghidra.app.util.bin.format.elf.ElfSymbol;
import ghidra.app.util.bin.format.elf.ElfSymbolTable;
import ghidra.app.util.bin.format.elf.relocation.AARCH64_ElfRelocationConstants;
import ghidra.app.util.bin.format.elf.relocation.ElfRelocationContext;
import ghidra.app.util.importer.MemoryConflictHandler;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.AbstractProgramLoader;
import ghidra.app.util.opinion.LoadSpec;
import ghidra.app.util.opinion.LoaderTier;
import ghidra.app.util.opinion.QueryOpinionService;
import ghidra.app.util.opinion.QueryResult;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.store.LockException;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.LanguageCompilerSpecPair;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* TODO: Provide class-level documentation that describes what this loader does.
*/
public class Kernel70LoaderLoader extends AbstractProgramLoader {
/**
* Prints the message to the console followed by a line feed.
*
* @param message the message to print
* @see #printf(String, Object...)
*/
public void println(String message) {
String decoratedMessage = "Kernel70Loader > " + message;
// note: use a Message object to facilite script message log filtering
Msg.info(Kernel70LoaderLoader.class, new ScriptMessage(decoratedMessage));
}
@Override
public String getName() {
return "NX Kernel";
}
// Examine the bytes in 'provider' to determine if this loader can load it. If it
// can load it, return the appropriate load specifications
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
byte[] header = { (byte)0xDF, 'O', (byte)0x03, (byte)0xD5, 'A', 'B', '8', (byte)0xD5 };
List<LoadSpec> loadSpecs = new ArrayList<>();
if (Arrays.equals(provider.readBytes(0, 8), header)) {
this.println("Looking for version");
if (this.find_version(provider) != 0) {
List<QueryResult> results = QueryOpinionService.query(getName(), "0", null);
for (QueryResult result : results) {
loadSpecs.add(new LoadSpec(this, 0, result));
}
if (loadSpecs.isEmpty()) {
loadSpecs.add(new LoadSpec(this, 0, true));
}
return loadSpecs;
}
}
return null;
}
@Override
public LoaderTier getTier() {
return LoaderTier.SPECIALIZED_TARGET_LOADER;
}
@Override
public int getTierPriority() {
return 0;
}
@Override
protected List<Program> loadProgram(ByteProvider provider, String programName, DomainFolder programFolder,
LoadSpec loadSpec, List<Option> options, MessageLog log, Object consumer, TaskMonitor monitor)
throws IOException, CancelledException {
ArrayList<Program> programs = new ArrayList<Program>();
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
Language language = getLanguageService().getLanguage(pair.languageID);
CompilerSpec compilerSpec = language.getCompilerSpecByID(pair.compilerSpecID);
Address imageBaseAddr = language.getAddressFactory().getDefaultAddressSpace().getAddress(
loadSpec.getDesiredImageBase());
Program program = createProgram(provider, programName, imageBaseAddr, getName(),
language, compilerSpec, consumer);
if (this.loadProgramInto(provider, loadSpec, options, log, program, monitor, MemoryConflictHandler.ALWAYS_OVERWRITE)) {
programs.add(program);
}
return programs;
}
@Override
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
MessageLog log, Program program, TaskMonitor monitor, MemoryConflictHandler memoryConflictHandler)
throws IOException, CancelledException {
MemoryBlockUtil mbu = new MemoryBlockUtil(program, memoryConflictHandler);
int id = program.startTransaction("Loading");
boolean success = false;
try {
AddressSpace aspace = program.getAddressFactory().getDefaultAddressSpace();
Address base = aspace.getAddress(0x80060000L);
program.setImageBase(base, true);
log.appendMsg("Doing stuff");
processSections(provider, program, aspace, mbu, log, monitor);
success = true;
} catch (AddressOverflowException | IllegalStateException | LockException | AddressOutOfBoundsException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | MemoryAccessException e) {
log.appendException(e);
StringWriter s = new StringWriter();
e.printStackTrace(new PrintWriter(s));
println(s.toString());
return false;
} finally {
mbu.dispose();
program.endTransaction(id, success);
}
return true;
}
int find_version(ByteProvider provider) throws IOException {
byte[] disambiguator_5 = { (byte)0x62, (byte)0xBF, (byte)0x21, (byte)0x10 };
//byte[] disambiguator_6 = { (byte)0xDF, 'O', (byte)0x03, (byte)0xD5, 'A', 'B', '8', (byte)0xD5 };
byte[] disambiguator_7 = { (byte)0x21, (byte)0x00, (byte)0x40, (byte)0xF9 };
byte[] disambiguator = provider.readBytes(0x814, 4);
if (Arrays.equals(disambiguator, disambiguator_5)) {
return 5;
} else if (Arrays.equals(disambiguator, disambiguator_7)) {
return 7;
} else {
return 0;
}
}
void processSections(ByteProvider provider, Program program, AddressSpace aspace, MemoryBlockUtil mbu, MessageLog log, TaskMonitor monitor) throws IOException, AddressOverflowException, AddressOutOfBoundsException, CancelledException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, MemoryAccessException {
monitor.setMessage("Process sections...");
int version = this.find_version(provider);
// TODO: Get base addr
long base_addr = 0x80060000L;
long text_end;
if (version == 5) {
text_end = 0x800BD000L;
} else if (version == 6) {
text_end = 0x800BF000L;
} else if (version == 7) {
text_end = 0x800C0000L;
} else {
return;
}
long textoff = 0;
long textsize = text_end - base_addr;
long rodataoff = text_end - base_addr;
long rodatasize = 0x800D0000L - text_end;
long dataoff = 0x800D0000L - base_addr;
long dataend_off;
if (version == 5) {
dataend_off = 0x800D1A40L;
} else if (version == 6) {
dataend_off = 0x800D1B18L;
} else if (version == 7) {
dataend_off = 0x800D1B28L;
} else {
return;
}
long datasize = dataend_off - 0x800D0000L; // 0x800E4000 - 0x800D0000
long flatsize = dataoff + datasize;
long dynamicoff_addr;
if (version == 5) {
dynamicoff_addr = 0x800D1378L;
} else if (version == 6) {
dynamicoff_addr = 0x800D1440L;
} else if (version == 7) {
dynamicoff_addr = 0x800D1440L;
} else {
return;
}
long dynamicoff = dynamicoff_addr - base_addr;
long bssoff = dataend_off - base_addr;
long bssend;
if (version == 5 || version == 6) {
bssend = 0x800E4000L - base_addr;
} else if (version == 7) {
bssend = 0x800E5000L - base_addr;
} else {
return;
}
datasize = bssoff - dataoff;
long bsssize = bssend - bssoff;
println(String.format(".text: %x or %x", textoff, base_addr + textoff));
mbu.createInitializedBlock(".text", aspace.getAddress(base_addr + textoff), provider.getInputStream(textoff), textsize, "", null, true, false, true, monitor);
mbu.createInitializedBlock(".rodata", aspace.getAddress(base_addr + rodataoff), provider.getInputStream(rodataoff), rodatasize, "", null, true, false, false, monitor);
mbu.createInitializedBlock(".data", aspace.getAddress(base_addr + dataoff), provider.getInputStream(dataoff), datasize, "", null, true, true, false, monitor);
mbu.createUninitializedBlock(false, ".bss", aspace.getAddress(base_addr + bssoff), bsssize, "", null, true, true, false);
// Read dynamic
BinaryReader reader = new BinaryReader(provider, true);
HashMap<Integer, Object> dynamic = new HashMap<>();
dynamic.put(ElfDynamicType.DT_NEEDED.value, new ArrayList<Long>());
HashMap<Integer, ElfDynamicType> dyntypes = new HashMap<>();
ElfDynamicType.addDefaultTypes(dyntypes);
MapUtils.debugPrint(System.out, "mymap", dyntypes);
long i;
for (i = 0; i < (flatsize - dynamicoff) / 0x10; i++) {
monitor.checkCanceled();
int tag = (int)reader.readLong(dynamicoff + i * 0x10);
long val = reader.readLong(dynamicoff + i * 0x10 + 8);
String name = "UNKNOWN";
if (dyntypes.containsKey(tag)) {
name = dyntypes.get(tag).name;
}
println(String.format("Got .dynamic entry tag %s (%x) val %x", name, tag, val));
if (tag == ElfDynamicType.DT_NULL.value) {
break;
}
if (tag == ElfDynamicType.DT_NEEDED.value) {
ArrayList<Long> v = (ArrayList<Long>)dynamic.get(tag);
v.add(val);
} else {
dynamic.put(tag, val);
}
}
long dynamicend = dynamicoff_addr + i * 0x10;
long dynamicsize = dynamicend - dynamicoff_addr;
println(String.format("Adding .dynamic at %x", dynamicoff));
// TODO: Create comment to point out start of dynamic
byte[] dynstr;
if (dynamic.containsKey(ElfDynamicType.DT_STRTAB.value) && dynamic.containsKey(ElfDynamicType.DT_STRSZ.value)) {
dynstr = provider.readBytes((long)dynamic.get(ElfDynamicType.DT_STRTAB.value), (long)dynamic.get(ElfDynamicType.DT_STRSZ.value));
} else {
println("Warning: no dynstr");
dynstr = new byte[0];
}
// TODO: Add section for dynstr, init_array, fini_array, .rela.dyn, .rel.dyn, .rel.plt
// TODO: Get needed
// TODO: Load dynsym
FactoryBundledWithBinaryReader factreader = new FactoryBundledWithBinaryReader(RethrowContinuesFactory.INSTANCE, provider, true);
ElfStringTable strtable = ElfStringTable.createElfStringTable(factreader, null, null,
(Long)dynamic.get(ElfDynamicType.DT_STRTAB.value),
(Long)dynamic.get(ElfDynamicType.DT_STRTAB.value),
(Long)dynamic.get(ElfDynamicType.DT_STRSZ.value));
// Get symbol table length through DT_HASH. DT_HASH.nchain is equal to the length of the
// symbol table
long hash_off = (long)dynamic.get(ElfDynamicType.DT_HASH.value);
long nchain = reader.readUnsignedInt(hash_off + 4);
Method m = ElfSymbolTable.class.getDeclaredMethod("createElfSymbolTable", FactoryBundledWithBinaryReader.class,
ElfHeader.class, ElfSectionHeader.class, long.class, long.class,
long.class, long.class, ElfStringTable.class, boolean.class);
m.setAccessible(true);
ElfSymbolTable symtable = (ElfSymbolTable)m.invoke(null, factreader, new ElfHeader(), null,
dynamic.get(ElfDynamicType.DT_SYMTAB.value),
dynamic.get(ElfDynamicType.DT_SYMTAB.value),
nchain * (long)dynamic.get(ElfDynamicType.DT_SYMENT.value),
dynamic.get(ElfDynamicType.DT_SYMENT.value),
strtable, true);
// Relocations
ArrayList<Relocation> relocs = new ArrayList<>();
if (dynamic.containsKey(ElfDynamicType.DT_REL.value)) {
processRelocations(program, reader, relocs, symtable,
(long)dynamic.get(ElfDynamicType.DT_REL.value),
(long)dynamic.get(ElfDynamicType.DT_RELSZ.value));
}
if (dynamic.containsKey(ElfDynamicType.DT_RELA.value)) {
processRelocations(program, reader, relocs, symtable,
(long)dynamic.get(ElfDynamicType.DT_RELA.value),
(long)dynamic.get(ElfDynamicType.DT_RELASZ.value));
}
if (dynamic.containsKey(ElfDynamicType.DT_JMPREL.value)) {
println("Got DT_JMPREL. This probably needs to be handled!");
}
// TODO: Handle .got
// TODO: Handle imports
// Relocations again
for (Relocation reloc : relocs) {
Address target = aspace.getAddress(reloc.offset + base_addr);
if (reloc.r_type == AARCH64_ElfRelocationConstants.R_AARCH64_GLOB_DAT ||
reloc.r_type == AARCH64_ElfRelocationConstants.R_AARCH64_JUMP_SLOT ||
reloc.r_type == AARCH64_ElfRelocationConstants.R_AARCH64_ABS32) {
if (reloc.sym == null) {
println(String.format("Error: Relocation at %x failed", target));
} else {
program.getMemory().setLong(target, reloc.sym.getValue() + base_addr + reloc.addend);
}
} else if (reloc.r_type == AARCH64_ElfRelocationConstants.R_AARCH64_RELATIVE) {
long target_val = program.getMemory().getLong(target);
program.getMemory().setLong(target, target_val + base_addr);
} else {
println(String.format("TODO: r_type %x", reloc.r_type));
}
}
// TODO: Find functions.
}
class Relocation {
public Relocation(long offset, long r_type, ElfSymbol sym, long addend) {
this.offset = offset;
this.r_type = r_type;
this.sym = sym;
this.addend = addend;
}
long offset;
long r_type;
ElfSymbol sym;
long addend;
}
private Set<Long> processRelocations(Program program, BinaryReader provider, List<Relocation> relocs, ElfSymbolTable symtab, long rel, long relsz) throws IOException {
Set<Long> locations = new HashSet<Long>();
for (long i = 0; i < relsz / 0x18; i++) {
long offset = provider.readLong(rel + i * 0x18);
long info = provider.readLong(rel + i * 0x18 + 8);
long addend = provider.readLong(rel + i * 0x18 + 0x10);
long r_type = info & 0xffffffff;
long r_sym = info >> 32;
ElfSymbol sym;
if (r_sym != 0) {
sym = symtab.getSymbolAt(r_sym);
} else {
sym = null;
}
if (r_type != AARCH64_ElfRelocationConstants.R_AARCH64_TLSDESC) {
locations.add(offset);
}
relocs.add(new Relocation(offset, r_type, sym, addend));
}
return locations;
}
}
<opinions>
<!-- The primary and secondary constraint values must be specified as a decimal string -->
<constraint loader="NX Kernel" compilerSpecID="default">
<constraint primary="0" processor="AARCH64" endian="little" size="64" variant="v8A" />
</constraint>
</opinions>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment