Skip to content

Commit

Permalink
Add support for 17.0.0 (64-bit only) (#46)
Browse files Browse the repository at this point in the history
* Add nxo64 changes for 17.0.0

Co-authored-by: SciresM <[email protected]>

* Add ipcserver changes for 17.0.0

Co-authored-by: SciresM <[email protected]>

* Make debugging easier and simplify build process

Co-authored-by: astrelsky <[email protected]>

* Only consider relocations within got memory block

* Fix OOB memory access when locating s_tables

* Add nxo64 changes for finding .got section

Co-authored-by: SciresM <[email protected]>

* Fix missing .got section when .got.plt section is present

---------

Co-authored-by: SciresM <[email protected]>
Co-authored-by: astrelsky <[email protected]>
  • Loading branch information
3 people authored Oct 15, 2023
1 parent 3caff8c commit 3d849df
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 172 deletions.
107 changes: 47 additions & 60 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
// application.gradle.version property in <GHIDRA_INSTALL_DIR>/Ghidra/application.properties
// for the correction version of Gradle to use for the Ghidra installation you specify.

import org.apache.tools.ant.filters.ReplaceTokens

//----------------------START "DO NOT MODIFY" SECTION------------------------------
apply plugin: 'java'
apply plugin: 'eclipse'
def ghidraInstallDir

if (System.env.GHIDRA_INSTALL_DIR) {
Expand All @@ -34,9 +34,6 @@ else {
}
//----------------------END "DO NOT MODIFY" SECTION-------------------------------

def DISTRIBUTION_DIR = file("dist")
def PATH_IN_ZIP = "${project.name}"

def getGitHash = {
def stdout = new ByteArrayOutputStream()
exec {
Expand All @@ -58,68 +55,58 @@ configurations {
localDeps
}

def docs = file(ghidraInstallDir+'/docs/GhidraAPI_javadoc.zip')
def ghidraUserDir = System.getProperty("user.home") + "/.ghidra/.${DISTRO_PREFIX}_${RELEASE_NAME}"

dependencies {
api fileTree(dir: ghidraInstallDir + '/Ghidra/Processors', include: "**/*.jar")
implementation 'commons-primitives:commons-primitives:1.0'
localDeps group: 'org.lz4', name: 'lz4-java', version: '1.5.1'
api configurations.localDeps
}

buildExtension.enabled = false
defaultTasks 'createDistribution'

task createDistribution (type: Zip) {
archiveBaseName = "${project.name}-${project.version}-${getGitHash()}-Ghidra_${ghidra_version}".replace(' ', '_')
archiveExtension = 'zip'
destinationDirectory = DISTRIBUTION_DIR
version ''

// Make sure that we don't try to copy the same file with the same path into the
// zip (this can happen!)
duplicatesStrategy 'exclude'

// This filtered property file copy must appear before the general
// copy to ensure that it is prefered over the unmodified file
File propFile = new File(project.projectDir, "extension.properties")
from (propFile) {
String version = "${ghidra_version}"
String name = "${project.name}"
filter (ReplaceTokens, tokens: [extversion: version])
filter (ReplaceTokens, tokens: [extname: name])
into PATH_IN_ZIP
}

from (project.jar) {
into PATH_IN_ZIP + "/lib"
}
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/patch', include: "**/*.jar")
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/Configurations', include: "**/*.jar")
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/Features', include: "**/*.jar")
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/Framework', include: "**/*.jar")
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/Processors', include: "**/*.jar")
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/Debug', include: "**/*.jar")
runtimeOnly fileTree(dir: ghidraInstallDir + '/Ghidra/Extensions',
include: "**/*.jar", exclude: project.name)
runtimeOnly fileTree(dir: ghidraUserDir + "/Extensions",
include: "**/*.jar", exclude: project.name)
}

// Copy lz4 and any other dependencies that only we rely on
from (configurations.localDeps) {
into PATH_IN_ZIP + "/lib"
eclipse {
classpath {
downloadJavadoc = true
downloadSources = true
file {
whenMerged {
for (entry in entries) {
if (entry.path.contains('jar')) {
File folder = new File(entry.getPath()).getParentFile();
for (File file : folder.listFiles()) {
if (file.getName().endsWith(".zip")) {
if (file.getName().contains("-src")) {
entry.setSourcePath(it.fileReference(file));
}
entry.setJavadocPath(it.fileReference(docs));
}
}
}
}
entries.add(
new org.gradle.plugins.ide.eclipse.model.Library(
it.fileReference(new File(projectDir, '/data'))
)
)
}
}
}
}

from (project.projectDir) {
exclude 'build/**'
exclude '*.gradle'
exclude 'certification.manifest'
exclude 'dist/**'
exclude 'bin/**'
exclude 'src/**'
exclude '.gradle/**'
exclude 'gradle/**'
exclude 'gradlew'
exclude 'gradlew.bat'
exclude '.classpath'
exclude '.project'
exclude '.settings/**'
exclude 'developer_scripts'
exclude '.antProperties.xml'
exclude 'eclipse/**'

into PATH_IN_ZIP
}
buildExtension {
exclude 'gradle*'
exclude '.github/**'

doLast {
println "\nCreated " + baseName + "." + extension + " in " + destinationDir
}
archiveBaseName = "${project.name}-${project.version}-${getGitHash()}-Ghidra_${ghidra_version}".replace(' ', '_')
}
50 changes: 31 additions & 19 deletions src/main/java/adubbz/nx/analyzer/IPCAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,14 @@
*/
package adubbz.nx.analyzer;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.compress.utils.Lists;
import org.python.google.common.collect.HashBiMap;
import org.python.google.common.collect.Sets;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;

import adubbz.nx.analyzer.ipc.IPCEmulator;
import adubbz.nx.analyzer.ipc.IPCTrace;
import adubbz.nx.common.ElfCompatibilityProvider;
import adubbz.nx.common.NXRelocation;
import adubbz.nx.loader.SwitchLoader;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import generic.stl.Pair;
import ghidra.app.services.AbstractAnalyzer;
import ghidra.app.services.AnalyzerType;
Expand All @@ -47,9 +38,17 @@
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import org.apache.commons.compress.utils.Lists;
import org.python.google.common.collect.HashBiMap;
import org.python.google.common.collect.Sets;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static adubbz.nx.common.ElfCompatibilityProvider.R_FAKE_RELR;

public class IPCAnalyzer extends AbstractAnalyzer
{
Expand Down Expand Up @@ -311,8 +310,7 @@ protected List<IPCVTableEntry> createVTableEntries(Program program, ElfCompatibi
return out;
}

protected HashBiMap<Address, Address> locateSTables(Program program, ElfCompatibilityProvider elfProvider)
{
protected HashBiMap<Address, Address> locateSTables(Program program, ElfCompatibilityProvider elfProvider) throws MemoryAccessException {
HashBiMap<Address, Address> out = HashBiMap.create();
List<Pair<Long, Long>> candidates = new ArrayList<>();
AddressSpace aSpace = program.getAddressFactory().getDefaultAddressSpace();
Expand All @@ -321,8 +319,13 @@ protected HashBiMap<Address, Address> locateSTables(Program program, ElfCompatib

for (NXRelocation reloc : elfProvider.getRelocations())
{
if (reloc.addend > 0)
if (reloc.addend > 0) {
candidates.add(new Pair<>(baseAddr.getOffset() + reloc.addend, baseAddr.getOffset() + reloc.offset));
}
else if (reloc.r_type == R_FAKE_RELR) {
reloc.addend = mem.getLong(baseAddr.add(reloc.offset)) - baseAddr.getOffset();
candidates.add(new Pair<>(baseAddr.getOffset() + reloc.addend, baseAddr.getOffset() + reloc.offset));
}
}

candidates.sort(Comparator.comparing(a -> a.first));
Expand All @@ -338,7 +341,7 @@ protected HashBiMap<Address, Address> locateSTables(Program program, ElfCompatib

try
{
for (long off = text.getStart().getOffset(); off < text.getEnd().getOffset(); off += 0x4)
for (long off = text.getStart().getOffset(); off < text.getEnd().getOffset() - 0x4; off += 0x4)
{
long val1 = (elfProvider.getReader().readUnsignedInt(off) & 0xFFFFFF00L) >> 8;
long val2 = (elfProvider.getReader().readUnsignedInt(off + 0x4) & 0xFFFFFF00L) >> 8;
Expand Down Expand Up @@ -675,17 +678,26 @@ protected int getProcFuncVTableSize(Multimap<Address, IPCTrace> processFuncTrace
/**
* A map of relocated entries in the global offset table to their new values.
*/
protected Map<Address, Address> getGotDataSyms(Program program, ElfCompatibilityProvider elfProvider)
{
protected Map<Address, Address> getGotDataSyms(Program program, ElfCompatibilityProvider elfProvider) throws MemoryAccessException {
if (gotDataSyms != null)
return this.gotDataSyms;

Address baseAddr = program.getImageBase();
gotDataSyms = new HashMap<>();
MemoryBlock gotBlock = program.getMemory().getBlock(".got");

for (NXRelocation reloc : elfProvider.getRelocations())
{
if (baseAddr.add(reloc.offset).getOffset() < gotBlock.getStart().getOffset() || baseAddr.add(reloc.offset).getOffset() > gotBlock.getEnd().getOffset() + 1)
{
continue;
}

long off;

if (reloc.r_type == R_FAKE_RELR) {
reloc.addend = program.getMemory().getLong(baseAddr.add(reloc.offset)) - baseAddr.getOffset();
}

if (reloc.sym != null && reloc.sym.getSectionHeaderIndex() != ElfSectionHeaderConstants.SHN_UNDEF && reloc.sym.getValue() == 0)
{
Expand Down
59 changes: 48 additions & 11 deletions src/main/java/adubbz/nx/common/ElfCompatibilityProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
*/
package adubbz.nx.common;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import adubbz.nx.util.FullMemoryByteProvider;
import adubbz.nx.util.LegacyBinaryReader;
import ghidra.app.util.bin.BinaryReader;
Expand All @@ -28,8 +20,13 @@
import ghidra.util.Msg;
import ghidra.util.exception.NotFoundException;

import java.io.IOException;
import java.util.*;

public class ElfCompatibilityProvider
{
public static final int R_FAKE_RELR = -1;

private Program program;
private ByteProvider provider;
private BinaryReader binaryReader;
Expand Down Expand Up @@ -221,7 +218,7 @@ public List<NXRelocation> getRelocations()

try
{
if (dynamicTable.containsDynamicValue(ElfDynamicType.DT_REL.value))
if (dynamicTable.containsDynamicValue(ElfDynamicType.DT_REL))
{
Msg.info(this, "Processing DT_REL relocations...");
processRelocations(this.relocs, this.symbolTable,
Expand All @@ -236,6 +233,13 @@ public List<NXRelocation> getRelocations()
this.dynamicTable.getDynamicValue(ElfDynamicType.DT_RELA),
this.dynamicTable.getDynamicValue(ElfDynamicType.DT_RELASZ));
}

if (dynamicTable.containsDynamicValue(ElfDynamicType.DT_RELR)) {
Msg.info(this, "Processing DT_RELR relocations...");
processReadOnlyRelocations(this.relocs,
this.dynamicTable.getDynamicValue(ElfDynamicType.DT_RELR),
this.dynamicTable.getDynamicValue(ElfDynamicType.DT_RELRSZ));
}
}
catch (NotFoundException | IOException e)
{
Expand All @@ -248,12 +252,12 @@ public List<NXRelocation> getRelocations()

private Set<Long> processRelocations(List<NXRelocation> relocs, ElfSymbolTable symtab, long rel, long relsz) throws IOException
{
long base = this.program.getImageBase().getOffset();
Set<Long> locations = new HashSet<>();
int relocSize = this.isAarch32 ? 0x8 : 0x18;

for (long i = 0; i < relsz / relocSize; i++)
{
long base = this.program.getImageBase().getOffset();
long offset;
long info;
long addend;
Expand Down Expand Up @@ -297,6 +301,39 @@ private Set<Long> processRelocations(List<NXRelocation> relocs, ElfSymbolTable s
}
return locations;
}

private Set<Long> processReadOnlyRelocations(List<NXRelocation> relocs, long relr, long relrsz) throws IOException
{
long base = this.program.getImageBase().getOffset();
Set<Long> locations = new HashSet<>();
int relocSize = 0x8;

long where = 0;
for (long entryNumber = 0; entryNumber < relrsz / relocSize; entryNumber++)
{
long entry = this.binaryReader.readLong(base + relr + entryNumber * relocSize);

if ((entry & 1) != 0) {
entry >>= 1;
long i = 0;
while (i < (relocSize * 8) - 1) {
if ((entry & (1L << i)) != 0) {
locations.add(where + i * relocSize);
relocs.add(new NXRelocation(where + i * relocSize, 0, R_FAKE_RELR, null, 0));
}
i++;
}
where += relocSize * ((relocSize * 8) - 1);
}
else {
where = entry;
locations.add(where);
relocs.add(new NXRelocation(where, 0, R_FAKE_RELR, null, 0));
where += relocSize;
}
}
return locations;
}

protected MemoryBlock getDynamicBlock()
{
Expand Down
Loading

0 comments on commit 3d849df

Please sign in to comment.