Skip to content

Instantly share code, notes, and snippets.

@matterpreter
Last active March 9, 2022 00:21
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save matterpreter/57897d9c49c380fd60cda4468c50ae9d to your computer and use it in GitHub Desktop.
Save matterpreter/57897d9c49c380fd60cda4468c50ae9d to your computer and use it in GitHub Desktop.
Ghidra RPC procedure identification script
//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