Instantly share code, notes, and snippets.
Created
January 4, 2021 22:20
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save samunders-core/278ff7f7298811d962dfed57cfd44393 to your computer and use it in GitHub Desktop.
Ghidra script - add .so files to memory of main executable based on /proc/self/maps offsets. Assumes .so files are already in Ghidra project
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
//setup memory map from /proc/self/maps | |
//@author sam_ | |
//@category | |
//@keybinding | |
//@menupath | |
//@toolbar | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.List; | |
import ghidra.app.script.GhidraScript; | |
import ghidra.app.util.HexLong; | |
import ghidra.app.util.Option; | |
import ghidra.app.util.bin.ByteProvider; | |
import ghidra.app.util.importer.MessageLog; | |
import ghidra.app.util.opinion.BinaryLoader; | |
import ghidra.app.util.opinion.LoadSpec; | |
import ghidra.app.util.opinion.Loader; | |
import ghidra.formats.gfilesystem.FileSystemService; | |
import ghidra.formats.gfilesystem.GFile; | |
import ghidra.framework.model.DomainFile; | |
import ghidra.framework.model.DomainFolder; | |
import ghidra.framework.model.Project; | |
import ghidra.framework.model.ProjectData; | |
import ghidra.program.model.address.Address; | |
import ghidra.program.model.address.AddressFactory; | |
import ghidra.program.model.mem.MemoryBlock; | |
import ghidra.util.Msg; | |
import ghidra.util.exception.CancelledException; | |
public class SharedLibrariesMemoryMap extends GhidraScript { | |
@Override | |
protected void run() throws Exception { | |
File f = askFile("Import contents of /proc/$(pgrep " + currentProgram.getName() + ")/maps", "Select map file"); | |
if (f != null) { | |
try (InputStream in = new FileInputStream(f); InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr)) { | |
Loader l = new BinaryLoader(); | |
MessageLog log = new MessageLog(); | |
Project project = state.getProject(); | |
ProjectData projectData = project.getProjectData(); | |
DomainFolder rootFolder = projectData.getRootFolder(); | |
for (String line = br.readLine(); line != null && !monitor.isCancelled(); line = br.readLine()) { | |
parseLine(line, rootFolder, currentProgram.getAddressFactory(), FileSystemService.getInstance(), l, log); | |
} | |
} | |
} | |
} | |
private void parseLine(String line, DomainFolder rootFolder, AddressFactory af, FileSystemService fss, Loader l, MessageLog log) { | |
String[] parts = line.split("\\s+", 4); // 93c7c000-93c88000 r-xp 00000000 fd:600 5909 /lib/libstdc++.so.6.0.20 | |
int lastSlash = parts[parts.length - 1].lastIndexOf('/'); | |
DomainFile df = lastSlash >= 0 ? rootFolder.getFile(parts[parts.length - 1].substring(lastSlash + 1)) : null; | |
GFile gf = df != null ? fss.getLocalGFile(new File(df.getMetadata().getOrDefault("Executable Location", df.getPathname()))) : null; | |
if (gf != null && gf.getName().matches("^.+[.]so\\b.*$") && parts.length >= 4) { | |
try (ByteProvider bp = fss.getByteProvider(gf.getFSRL(), monitor)) { | |
// 47740000-477dd000 r-xp 00000000 fd:00 179 /lib/libstdc++.so.6.0.20 | |
// 477dd000-477ed000 ---p 0009d000 fd:00 179 /lib/libstdc++.so.6.0.20 | |
// 477ed000-477f0000 r--p 0009d000 fd:00 179 /lib/libstdc++.so.6.0.20 | |
// 477f0000-477f2000 rw-p 000a0000 fd:00 179 /lib/libstdc++.so.6.0.20 | |
Collection<LoadSpec> specs = l.findSupportedLoadSpecs(bp); | |
Address start = af.getAddress("0x" + parts[0].replaceFirst("-.*$", "")), end = af.getAddress("0x" + parts[0].replaceFirst("^.*-", "")); | |
long offset = Long.decode("0x" + parts[2]); | |
boolean r = parts[1].matches("^[-r].*$"), w = parts[1].length() > 1 && 'w' == parts[1].charAt(1), x = parts[1].length() > 2 && 'x' == parts[1].charAt(2); | |
if (!addToProgram(bp, specs, start, end, offset, r, w, x, log)) { | |
Msg.warn(getClass(), "Failed to add to program: " + line); | |
} | |
} catch (CancelledException abort) { | |
} catch (Exception e) { | |
Msg.error(getClass(), line, e); | |
} | |
} else if (line.matches("^.+[.]so\\b.*$")) { | |
println("gf=" + gf + ", #parts=" + parts.length + ", path=" + (df != null ? df.getMetadata().get("Executable Location") : null) + " => skipping " + line); | |
} | |
} | |
private String f(boolean value, String f) { | |
return value ? f : "-"; | |
} | |
private boolean addToProgram(ByteProvider bp, Collection<LoadSpec> specs, Address start, Address end, long offset, boolean r, boolean w, boolean x, MessageLog log) throws CancelledException, IOException { | |
String off = String.format("0x%08x", offset), prefix = bp.getName() + "@" + off + "."; | |
int instance = 0; | |
for (MemoryBlock block = currentProgram.getMemory().getBlock(prefix + instance); block != null; block = currentProgram.getMemory().getBlock(prefix + instance)) { | |
++instance; | |
} | |
Collection<Option> opts = Arrays.asList( | |
new Option(BinaryLoader.OPTION_NAME_BLOCK_NAME, prefix + instance, String.class, Loader.COMMAND_LINE_ARG_PREFIX + "-blockName"), | |
new Option(BinaryLoader.OPTION_NAME_BASE_ADDR, start, Address.class, Loader.COMMAND_LINE_ARG_PREFIX + "-baseAddr"), | |
new Option(BinaryLoader.OPTION_NAME_LEN, new HexLong(end.subtract(start)), HexLong.class, Loader.COMMAND_LINE_ARG_PREFIX + "-length"), | |
new Option(BinaryLoader.OPTION_NAME_FILE_OFFSET, new HexLong(offset), HexLong.class, Loader.COMMAND_LINE_ARG_PREFIX + "-fileOffset") | |
); | |
Msg.debug(getClass(), "Mapping " + bp.getName() + " offset " + off + " at " + start + ", flags " + f(r, "r") + f(w, "w") + f(x, "x")); | |
for (LoadSpec loadSpec : specs) { | |
List<Option> options = loadSpec.getLoader().getDefaultOptions(bp, loadSpec, null, true); | |
options.removeIf((opt) -> opts.stream().anyMatch((o) -> o.getName().equals(opt.getName()))); | |
options.addAll(opts); // ghidra.plugin.importer.ImporterUtilities.addContentToProgram(PluginTool, Program, FSRL, LoadSpec, List<Option>, TaskMonitor) but without summary dialog | |
if (loadSpec.getLoader().loadInto(bp, loadSpec, options, log, currentProgram, monitor)) { | |
MemoryBlock block = currentProgram.getMemory().getBlock(prefix + instance); | |
block.setRead(r); | |
block.setWrite(w); | |
block.setExecute(x); | |
return true; | |
} | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment