浏览代码

first shot of the new analyzer to find out function pointers

tags/v0.1
父节点
当前提交
f1e1460968
找不到此签名对应的密钥
共有 4 个文件被更改,包括 249 次插入194 次删除
  1. +0
    -4
      build.sh
  2. +0
    -18
      data/languages/kh2ai.cspec
  3. +97
    -16
      src/main/java/ghidra_kh2ai/ghidra_kh2aiAnalyzer.java
  4. +152
    -156
      src/main/java/ghidra_kh2ai/ghidra_kh2aiLoader.java

+ 0
- 4
build.sh 查看文件

@@ -1,4 +0,0 @@
cd data/languages
/home/govanify/Documents/projects/programming/hacking/tools/ghidra/Ghidra/Features/Decompiler/os/linux64/sleigh -c -u -a
cp -rf ../../ /home/govanify/Documents/projects/programming/hacking/tools/ghidra/Ghidra/Processors/kh2a
cd ../../

+ 0
- 18
data/languages/kh2ai.cspec 查看文件

@@ -32,26 +32,8 @@
<default_proto>
<prototype name="__stdcall" extrapop="0" stackshift="0">
<input>
<pentry minsize="1" maxsize="4" extension="inttype">
<register name="r1"/>
</pentry>
<pentry minsize="1" maxsize="4" extension="inttype">
<register name="r2"/>
</pentry>
<pentry minsize="1" maxsize="4" extension="inttype">
<register name="r3"/>
</pentry>
<pentry minsize="1" maxsize="4" extension="inttype">
<register name="r4"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="0" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="4" extension="inttype">
<register name="r0"/>
</pentry>
</output>
<unaffected>
<register name="r5"/>

+ 97
- 16
src/main/java/ghidra_kh2ai/ghidra_kh2aiAnalyzer.java 查看文件

@@ -16,12 +16,26 @@
package ghidra_kh2ai;

import ghidra.app.services.AbstractAnalyzer;
import ghidra.util.Msg;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.program.model.lang.Processor;
/**
@@ -30,29 +44,21 @@ import ghidra.program.model.lang.Processor;
public class ghidra_kh2aiAnalyzer extends AbstractAnalyzer {

public ghidra_kh2aiAnalyzer() {

// TODO: Name the analyzer and give it a description.

super("Function pointers resolver", "This analyzer scans KH2 AI files for "
+ "function pointers pushed as values and resolves them.", AnalyzerType.INSTRUCTION_ANALYZER);
}

@Override
public boolean getDefaultEnablement(Program program) {

// TODO: Return true if analyzer should be enabled by default

return true;
}

@Override
public boolean canAnalyze(Program program) {

boolean canAnalyze = program.getLanguage().getProcessor().equals(
Processor.findOrPossiblyCreateProcessor("kh2_ai"));

if (!canAnalyze) {
return false;
Processor.findOrPossiblyCreateProcessor("kh2ai"));
if (canAnalyze) {
return true;
}
return false;
}
@@ -60,18 +66,93 @@ public class ghidra_kh2aiAnalyzer extends AbstractAnalyzer {
@Override
public void registerOptions(Options options, Program program) {

// TODO: If this analyzer has custom options, register them here

options.registerOption("Option name goes here", false, null,
"Option description goes here");
//options.registerOption("Option name goes here", false, null,
// "Option description goes here");
}


// shamelessly stolen from Ghidra-Switch-Loader, creds to Adubbz
public void createOneByteFunction(Program program, String name, Address address, boolean isEntry) {
Function function = null;
try {
FunctionManager functionMgr = program.getFunctionManager();
function = functionMgr.getFunctionAt(address);
if (function == null) {
function = functionMgr.createFunction(null, address, new AddressSet(address), SourceType.IMPORTED);
}
} catch (Exception e) {
Msg.error(this, "Error while creating function at " + address + ": " + e.getMessage());
}

try {
if (name != null) {
createSymbol(program, address, name, false, null);
}
if (isEntry) {
program.getSymbolTable().addExternalEntryPoint(address);
}
} catch (Exception e) {
Msg.error(this, "Error while creating symbol " + name + " at " + address + ": " + e.getMessage());
}
}

public Symbol createSymbol(Program program, Address addr, String name, boolean pinAbsolute, Namespace namespace)
throws InvalidInputException {
// TODO: At this point, we should be marking as data or code
SymbolTable symbolTable = program.getSymbolTable();
Symbol sym = symbolTable.createLabel(addr, name, namespace, SourceType.IMPORTED);
if (pinAbsolute && !sym.isPinned()) {
sym.setPinned(true);
}
return sym;
}


public void getValueLabel(Program program, Instruction instruction) {
Address test = instruction.getAddress();
Scalar val = (Scalar)(instruction.getOpObjects(0)[0]);
if(val.getValue()!=0) {
long reloc=0x10+(val.getValue()<<1);
Address rel = program.getAddressFactory().getDefaultAddressSpace().getAddress(reloc);
createOneByteFunction(program, "test"+reloc, rel, true);
}
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {

// TODO: Perform analysis when things get added to the 'program'. Return true if the
// analysis succeeded.
Listing listing = program.getListing( );
InstructionIterator instructionIterator = listing.getInstructions( set, true );
while ( instructionIterator.hasNext( ) ) {
Instruction instruction = instructionIterator.next( );

monitor.checkCanceled( );
monitor.incrementProgress( 1 );

String mnemonicString = instruction.getMnemonicString( );
// i have NO CLUE why but some syscalls incorrectly report a 2 item array...
if (mnemonicString.contains("syscall") && instruction.getInputObjects().length==3) {
Object[] a = instruction.getInputObjects();
Scalar arg1 = (Scalar)a[1];
Scalar arg2 = (Scalar)a[2];
Scalar op1= new Scalar(32, 1);
Scalar op2= new Scalar(32, 6);
int args=8;
if (arg1.equals(op1) && arg2.equals(op2)) {
Instruction copy = instruction;
while(args!=0) {
copy=copy.getPrevious();
if (copy.getMnemonicString().contains("push.v")) {
getValueLabel(program, copy);
int b=0;
args--;
}
}
}
}
}

return false;
}

+ 152
- 156
src/main/java/ghidra_kh2ai/ghidra_kh2aiLoader.java 查看文件

@@ -22,7 +22,6 @@ import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.*;


import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
@@ -56,120 +55,111 @@ import ghidra.program.model.data.DataTypeConflictException;
import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.lang.LanguageCompilerSpecPair;
import ghidra.program.model.data.DataUtilities.ClearDataMode;

/**
* TODO: Provide class-level documentation that describes what this loader does.
*/
public class ghidra_kh2aiLoader extends AbstractLibrarySupportLoader {

@Override
public String getName() {
return "KH2 AI";
}
@Override
public String getName() {
return "KH2 AI";
}

public boolean checkUTF8(byte[] barr){
public boolean checkUTF8(byte[] barr) {

CharsetDecoder decoder = Charset.forName("ASCII").newDecoder();
ByteBuffer buf = ByteBuffer.wrap(barr);

try {
String head = decoder.decode(buf).toString();
// only lowercase, underscore and numbers. hopefully this will throw less false negatives
boolean kh = head.matches("[a-z0-9_].*");
if (kh) { return true; }
}
catch(CharacterCodingException e){
// only lowercase, underscore and numbers. hopefully this will throw less false
// negatives
boolean kh = head.matches("[a-z0-9_].*");
if (kh) {
return true;
}
} catch (CharacterCodingException e) {
return false;
}

return false;
}

public Data createData(Program program, Address address, Listing listing, DataType dt) {
try {
Data d = listing.getDataAt(address);
if (d == null || !dt.isEquivalent(d.getDataType())) {
d = DataUtilities.createData(program, address, dt, -1, false,
ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA);
}
return d;
}
catch (CodeUnitInsertionException e) {
Msg.warn(this, "KH2AI data markup conflict at " + address);
}
catch (DataTypeConflictException e) {
Msg.error(this, "KH2AI data type markup conflict:" + e.getMessage());
}
return null;
}
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();

if (checkUTF8(provider.readBytes(0,0x10)) & !checkUTF8(provider.readBytes(0x10,0x20))) {
loadSpecs.add(new LoadSpec(this, 0,
new LanguageCompilerSpecPair("kh2_ai:le:32:default", "default"), true));
}
return loadSpecs;
}

public boolean checkZero(byte[] arr) {
for (byte b : arr) {
if (b != 0) {
return false;
}
}
return true;
}

public static final long arrToLong(byte[] b)
{
long l = 0;
l |= b[3] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[0] & 0xFF;
return l;
}

// shamelessly stolen from Ghidra-Switch-Loader, creds to Adubbz
public void createOneByteFunction(Program program, String name, Address address, boolean isEntry)
{
}

public Data createData(Program program, Address address, Listing listing, DataType dt) {
try {
Data d = listing.getDataAt(address);
if (d == null || !dt.isEquivalent(d.getDataType())) {
d = DataUtilities.createData(program, address, dt, -1, false,
ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA);
}
return d;
} catch (CodeUnitInsertionException e) {
Msg.warn(this, "KH2AI data markup conflict at " + address);
} catch (DataTypeConflictException e) {
Msg.error(this, "KH2AI data type markup conflict:" + e.getMessage());
}
return null;
}

@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();

if (checkUTF8(provider.readBytes(0, 0x10)) & !checkUTF8(provider.readBytes(0x10, 0x20))) {
loadSpecs.add(new LoadSpec(this, 0, new LanguageCompilerSpecPair("kh2_ai:le:32:default", "default"), true));
}
return loadSpecs;
}

public boolean checkZero(byte[] arr) {
for (byte b : arr) {
if (b != 0) {
return false;
}
}
return true;
}

public static final long arrToLong(byte[] b) {
long l = 0;
l |= b[3] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[0] & 0xFF;
return l;
}


// shamelessly stolen from Ghidra-Switch-Loader, creds to Adubbz
public void createOneByteFunction(Program program, String name, Address address, boolean isEntry) {
Function function = null;
try
{
try {
FunctionManager functionMgr = program.getFunctionManager();
function = functionMgr.getFunctionAt(address);
if (function == null) {
function = functionMgr.createFunction(null, address, new AddressSet(address), SourceType.IMPORTED);
}
}
catch (Exception e)
{
} catch (Exception e) {
Msg.error(this, "Error while creating function at " + address + ": " + e.getMessage());
}

try
{
if (name != null)
{
try {
if (name != null) {
createSymbol(program, address, name, false, null);
}
if (isEntry) {
program.getSymbolTable().addExternalEntryPoint(address);
}
}
catch (Exception e) {
} catch (Exception e) {
Msg.error(this, "Error while creating symbol " + name + " at " + address + ": " + e.getMessage());
}
}
public Symbol createSymbol(Program program, Address addr, String name, boolean pinAbsolute, Namespace namespace) throws InvalidInputException
{
public Symbol createSymbol(Program program, Address addr, String name, boolean pinAbsolute, Namespace namespace)
throws InvalidInputException {
// TODO: At this point, we should be marking as data or code
SymbolTable symbolTable = program.getSymbolTable();
Symbol sym = symbolTable.createLabel(addr, name, namespace, SourceType.IMPORTED);
@@ -178,78 +168,84 @@ public class ghidra_kh2aiLoader extends AbstractLibrarySupportLoader {
}
return sym;
}
@Override
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log)
throws CancelledException, IOException {

// We create the header structure and add the static elements
Structure struct = new StructureDataType("header", 0);
struct.add(ghidra.app.util.bin.StructConverter.STRING, 0x10, "filename", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "unk1", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "unk2", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "unk3", null);
int off = 0;
ArrayList<Long> entrypoints = new ArrayList<Long>();
while (1==1) {
// gathers entrypoints/triggers into an arraylist and define the dynamic part of the header structure
byte[] first = provider.readBytes(0x1c+(off*8), 0x4);
byte[] second = provider.readBytes(0x1c+4+(off*8), 0x4);
if(checkZero(first) && checkZero(second)){
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "end_trigger", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "end_entry", null);
break;
}
long fa = arrToLong(first);
long sa = arrToLong(second);
entrypoints.add(fa);
entrypoints.add(sa);
log.appendMsg("kh2ai: " + fa + " " + sa);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "trigger"+(off+1), null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "entry"+(off+1), null);
off++;
}
log.appendMsg("kh2ai: " + entrypoints);

// we actually send the bytes over to the program and send off the header structure
MemoryBlockUtils mbu = new MemoryBlockUtils();
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0);
BinaryReader reader = new BinaryReader( provider, true );
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, TaskMonitor.DUMMY);
try {
MemoryBlockUtils.createInitializedBlock(program, false, "ram", start, fileBytes, 0, provider.length(), "", "KH2AI Header", true, true, true, log);
} catch (AddressOverflowException e) {
e.printStackTrace();
}
createData(program, start, program.getListing(), struct);

// we create one byte functions so that the analyzer knows that there's something there, labelling
// entrypoints in the format entry<trigger>
for (int i=0; i<entrypoints.size(); i+=2) {
long addr=0x10+(entrypoints.get(i+1)<<1);
Address entry = program.getAddressFactory().getDefaultAddressSpace().getAddress(addr);
createOneByteFunction(program, "entry"+entrypoints.get(i), entry, true);
}
}

@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean isLoadIntoProgram) {
List<Option> list =
super.getDefaultOptions(provider, loadSpec, domainObject, isLoadIntoProgram);

//list.add(new Option("Option name goes here", "Default option value goes here"));

return list;
}

@Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {

// If this loader has custom options, validate them here. Not all options require
// validation.

return super.validateOptions(provider, loadSpec, options, program);
}


@Override
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program,
TaskMonitor monitor, MessageLog log) throws CancelledException, IOException {

// We create the header structure and add the static elements
Structure struct = new StructureDataType("header", 0);
struct.add(ghidra.app.util.bin.StructConverter.STRING, 0x10, "filename", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "unk1", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "unk2", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "unk3", null);

int off = 0;
ArrayList<Long> entrypoints = new ArrayList<Long>();
while (1 == 1) {
// gathers entrypoints/triggers into an arraylist and define the dynamic part of
// the header structure
byte[] first = provider.readBytes(0x1c + (off * 8), 0x4);
byte[] second = provider.readBytes(0x1c + 4 + (off * 8), 0x4);
if (checkZero(first) && checkZero(second)) {
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "end_trigger", null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "end_entry", null);
break;
}
long fa = arrToLong(first);
long sa = arrToLong(second);
entrypoints.add(fa);
entrypoints.add(sa);
log.appendMsg("kh2ai: " + fa + " " + sa);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "trigger" + (off + 1), null);
struct.add(ghidra.app.util.bin.StructConverter.DWORD, 4, "entry" + (off + 1), null);
off++;
}
log.appendMsg("kh2ai: " + entrypoints);

// we actually send the bytes over to the program and send off the header
// structure
MemoryBlockUtils mbu = new MemoryBlockUtils();
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0);
BinaryReader reader = new BinaryReader(provider, true);
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, TaskMonitor.DUMMY);
try {
MemoryBlockUtils.createInitializedBlock(program, false, "ram", start, fileBytes, 0, provider.length(), "",
"KH2AI Header", true, true, true, log);
} catch (AddressOverflowException e) {
e.printStackTrace();
}
createData(program, start, program.getListing(), struct);

// we create one byte functions so that the analyzer knows that there's
// something there, labelling
// entrypoints in the format entry<trigger>
for (int i = 0; i < entrypoints.size(); i += 2) {
long addr = 0x10 + (entrypoints.get(i + 1) << 1);
Address entry = program.getAddressFactory().getDefaultAddressSpace().getAddress(addr);
createOneByteFunction(program, "entry" + entrypoints.get(i), entry, true);
}
}

@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec, DomainObject domainObject,
boolean isLoadIntoProgram) {
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject, isLoadIntoProgram);

// list.add(new Option("Option name goes here", "Default option value goes
// here"));

return list;
}

@Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {

// If this loader has custom options, validate them here. Not all options
// require
// validation.

return super.validateOptions(provider, loadSpec, options, program);
}
}

正在加载...
取消
保存