Last active
March 9, 2022 00:21
-
-
Save matterpreter/57897d9c49c380fd60cda4468c50ae9d to your computer and use it in GitHub Desktop.
Ghidra RPC procedure identification script
This file contains hidden or 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
//Locate RPC procecures inside of server code | |
//@author Matt Hand (@matterpreter) based on original work by Sektor7 Labs (@reenz0h) | |
//@category Functions | |
//@keybinding | |
//@menupath | |
//@toolbar | |
import ghidra.app.script.GhidraScript; | |
import ghidra.program.model.block.*; | |
import ghidra.program.model.symbol.*; | |
import ghidra.program.model.mem.*; | |
import ghidra.program.model.address.*; | |
import java.util.Arrays; | |
import java.util.HashSet; | |
public class RpcParser extends GhidraScript | |
{ | |
public void run() throws Exception | |
{ | |
println("Parsing " + this.getProgramFile().getName() + "..."); | |
// The pointer size determines the offsets | |
int pointerSize = currentProgram.getImageBase().getPointerSize(); | |
int dispatchTableOffset; | |
int interpreterInfoOffset; | |
switch(pointerSize) | |
{ | |
case 4: | |
dispatchTableOffset = 0x2C; | |
interpreterInfoOffset = 0x3C; | |
break; | |
case 8: | |
dispatchTableOffset = 0x30; | |
interpreterInfoOffset = 0x50; | |
break; | |
default: | |
println("ERROR: Invalid pointer size"); | |
return; | |
} | |
println("Pointer Size: " + pointerSize); | |
MemoryBlock textBlock = getMemoryBlock(".text"); | |
MemoryBlock rdataBlock = getMemoryBlock(".rdata"); | |
Address rdataStart = rdataBlock.getStart(); | |
Address rdataEnd = rdataBlock.getEnd(); | |
long rdataSize = rdataBlock.getSize(); | |
// Read the .rdata section | |
byte[] rdataBytes = new byte[(int)rdataSize]; | |
rdataBlock.getBytes(rdataStart, rdataBytes); | |
// Used to prevent duplicates | |
HashSet<String> procedures = new HashSet<String>(); | |
// Parse .rdata for RPC_SERVER_INTERFACE structs | |
for (int i = 0; i < rdataSize; i++) | |
{ | |
// Show the address being processed in the monitor | |
Address b = rdataStart.add(i); | |
monitor.setMessage(b.toString()); | |
monitor.checkCanceled(); | |
int rpcStructSize = bytesToInt(extractBytes(rdataBytes, i, 4)); | |
long dispatchTablePtr = bytesToLong(extractBytes(rdataBytes, i + dispatchTableOffset, pointerSize)); | |
Long interpreterInfoPtr = bytesToLong(extractBytes(rdataBytes, i + interpreterInfoOffset, pointerSize)); | |
Long offset = interpreterInfoPtr - rdataStart.getOffset() + pointerSize; | |
Long serverRoutineTablePtr = bytesToLong(extractBytes(rdataBytes, offset.intValue() , pointerSize)); | |
if (rpcStructSize <= 0 || dispatchTablePtr <= 0) | |
{ | |
continue; | |
} | |
if (rpcStructSize < 0x100 && | |
isInBlock(rdataBlock, dispatchTablePtr) && | |
isInBlock(rdataBlock, interpreterInfoPtr) && | |
isInBlock(rdataBlock, serverRoutineTablePtr)) | |
{ | |
int dispatchTableCount = bytesToInt(readData(rdataBlock, dispatchTablePtr, 4)); | |
// DispatchTableCount is usually a low value | |
if (dispatchTableCount > 0 && dispatchTableCount < 500) | |
{ | |
for (int j = 0; j < dispatchTableCount; j++) | |
{ | |
Long funcPtr = bytesToLong(readData(rdataBlock, serverRoutineTablePtr + j * pointerSize, pointerSize)); | |
if (isInBlock(textBlock, funcPtr)) | |
{ | |
String funcName; | |
try | |
{ | |
Symbol f = getSymbolAt(toAddr(funcPtr)); | |
funcName = f.getName(); | |
} | |
catch (Exception e) // When getFunctionAt() used, some functions are not recognized. Ghidra bug? | |
{ | |
funcName = "__UNRESOLVED()"; | |
} | |
procedures.add(funcName + ", " + Long.toHexString(funcPtr)); | |
} | |
} | |
} | |
} | |
} | |
println("Found " + procedures.size() + " procedures"); | |
if (procedures.size() > 0) | |
{ | |
for (String proc: procedures) | |
{ | |
println(" " + proc); | |
} | |
} | |
} | |
// Helper methods | |
public long bytesToLong(byte[] bytes) | |
{ | |
long value = 0; | |
for (int i = 0; i < bytes.length; i++) | |
{ | |
value += ((long) bytes[i] & 0xffL) << (8 * i); | |
} | |
return value; | |
} | |
public int bytesToInt(byte[] bytes) | |
{ | |
int value = 0; | |
for (int i = 0; i < bytes.length; i++) | |
{ | |
value += ((int) bytes[i] & 0xffL) << (4 * i); | |
} | |
return value; | |
} | |
public byte[] extractBytes(byte[] src, int idx, int len) | |
{ | |
byte[] a = new byte[] {0x0}; | |
try | |
{ | |
a = Arrays.copyOfRange(src, idx, idx + len); | |
} | |
catch (Exception e) | |
{ | |
// | |
} | |
return a; | |
} | |
public boolean isInBlock(MemoryBlock block, long value) | |
{ | |
if (value >= block.getStart().getOffset() && value <= block.getEnd().getOffset()) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
public byte[] readData(MemoryBlock block, Long src, int len) | |
{ | |
Address addr = toAddr(src); | |
byte[] data = new byte[(int) len]; | |
try | |
{ | |
block.getBytes(addr, data); | |
} | |
catch (MemoryAccessException e) | |
{ | |
// | |
} | |
return data; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment