Ghidraでその場で中間コード表現を変更する

Ghidraツール用のghidranodejsモジュールを開発していたとき、SLEIGHアセンブリ言語でV8オペコード(Node.jsで使用されるJavaScriptエンジン)を正しく実装できるとは限らないことに気付きました。V8、JVMなどのランタイム環境では、単一のオペコードで非常に複雑なアクションを実行できます。この問題を解決するために、Ghidraは、構成概念Pコード(Ghidraの中間表現言語)を動的に注入するためのメカニズムを提供します。このメカニズムを使用して、逆コンパイラーの出力を次のように変換することができました。





これで:





CallRuntime. . . Runtime- V8 (kRuntimeId). (range — -, rangedst — ). SLEIGH, Ghidra , :





, , .





  1. Runtime- kRuntimeId.





  2. , .





  3. .





  4. .





  5. .





, SLEIGH, , . , ( -) ( ) , p-code, Ghidra. ?





(slaspec) , CallRuntimeCallOther. , (  — ), , Ghidra Java , Java , p-code , Java.





, .





SLEIGH

CallRuntime . SLEIGH Ghidra v8.





:





define pcodeop CallRuntimeCallOther;
      
      



:





:CallRuntime [kRuntimeId], range^rangedst is op = 0x53; kRuntimeId; range;       rangedst {
	CallRuntimeCallOther(2, 0);
}
      
      



, , 0x53, CallRuntime



CallRuntimeCallOther



2 0. (CallRuntime



) (CallWithSpread



, CallUndefinedReceiver



. .).





, : V8_PcodeInjectLibrary. ghidra.program.model.lang.PcodeInjectLibrary



p-code .





V8_PcodeInjectLibrary



:





package v8_bytecode;

import

public class V8_PcodeInjectLibrary extends PcodeInjectLibrary {

	public V8_PcodeInjectLibrary(SleighLanguage l) {

	}

}
      
      



V8_PcodeInjectLibrary



, Ghidra, pcodeInjectLibraryClass



pspec, Ghidra , p-code.





<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
  <programcounter register="pc"/>
  <properties>
  	<property key="pcodeInjectLibraryClass" value="v8_bytecode.V8_PcodeInjectLibrary"/>
  </properties>
</processor_spec>
      
      



CallRuntimeCallOther



cspec. Ghidra V8_PcodeInjectLibrary



, cspec-.





	<callotherfixup targetop="CallRuntimeCallOther">
		<pcode dynamic="true">			
			<input name="outsize"/> 
		</pcode>
	</callotherfixup>
      
      



(, , ) .





HashSet, .  — language. CallRuntimeCallOther



, , .





public class V8_PcodeInjectLibrary extends PcodeInjectLibrary {
	private Set<String> implementedOps;
	private SleighLanguage language;

	public V8_PcodeInjectLibrary(SleighLanguage l) {
		super(l);
		language = l;
		String translateSpec = language.buildTranslatorTag(language.getAddressFactory(),
				getUniqueBase(), language.getSymbolTable());
		PcodeParser parser = null;
		try {
			parser = new PcodeParser(translateSpec);
		}
		catch (JDOMException e1) {
			e1.printStackTrace();
		}
		implementedOps = new HashSet<>();
		implementedOps.add("CallRuntimeCallOther");
	}
}
      
      



Ghidra getPayload



V8_PcodeInjectLibrary



CallRuntimeCallOther



, V8_InjectCallVariadic



( ) .





	@Override
	/**
	* This method is called by DecompileCallback.getPcodeInject.
	*/
	public InjectPayload getPayload(int type, String name, Program program, String context) {
		if (type == InjectPayload.CALLMECHANISM_TYPE) {
			return null;
		}

		if (!implementedOps.contains(name)) {
			return super.getPayload(type, name, program, context);
		}

		V8_InjectPayload payload = null; 
		switch (name) {
		case ("CallRuntimeCallOther"):
			payload = new V8_InjectCallVariadic("", language, 0);
			break;
		default:
			return super.getPayload(type, name, program, context);
		}

		return payload;
	}
      
      



p-code

p-code V8_InjectCallVariadic. .





package v8_bytecode;

import

public class V8_InjectCallVariadic extends V8_InjectPayload {

public V8_InjectCallVariadic(String sourceName, SleighLanguage language, long uniqBase) {
		super(sourceName, language, uniqBase);
	}
//  .      RUNTIMETYPE
	int INTRINSICTYPE = 1;
	int RUNTIMETYPE = 2;
	int PROPERTYTYPE = 3;

	@Override
	public PcodeOp[] getPcode(Program program, InjectContext context) {
			}

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

}
      
      



, getPcode



pCode V8_PcodeOpEmitter



pCode ( ).





V8_PcodeOpEmitter pCode = new V8_PcodeOpEmitter(language, context.baseAddr, uniqueBase); 
      
      



context ( ) , .





Address opAddr = context.baseAddr;
      
      



:





Instruction instruction = program.getListing().getInstructionAt(opAddr);
      
      



context



, SLEIGH.





Integer funcType = (int) context.inputlist.get(0).getOffset();
Integer receiver = (int) context.inputlist.get(1).getOffset();
      
      



Pcode.





//   
if (funcType != PROPERTYTYPE) {
//  kRuntimeId —   
			Integer index = (int) instruction.getScalar(0).getValue();
//  Pcode    cpool    pCode  V8_PcodeOpEmitter.     .
			pCode.emitAssignVarnodeFromPcodeOpCall("call_target", 4, "cpool", "0", "0x" + opAddr.toString(), index.toString(), 
					funcType.toString());
		}


//   « »
Object[] tOpObjects = instruction.getOpObjects(2);
// get caller args count to save only necessary ones
Object[] opObjects;
Register recvOp = null;
if (receiver == 1) {
}
else {
opObjects = new Object[tOpObjects.length];
System.arraycopy(tOpObjects, 0, opObjects, 0, tOpObjects.length);
}


//     
try {
	callerParamsCount = program.getListing().getFunctionContaining(opAddr).getParameterCount();
}
catch(Exception e) {
	callerParamsCount = 0;
}

//      aN  .    ,  Ghidra      
Integer callerArgIndex = 0;
for (; callerArgIndex < callerParamsCount; callerArgIndex++) {
	pCode.emitPushCat1Value("a" + callerArgIndex);
}

//        aN
Integer argIndex = opObjects.length;
for (Object o: opObjects) {
	argIndex--;
	Register currentOp = (Register)o;
	pCode.emitAssignVarnodeFromVarnode("a" + argIndex, currentOp.toString(), 4);
}

//  
pCode.emitVarnodeCall("call_target", 4);

//      
while (callerArgIndex > 0) {
	callerArgIndex--;
	pCode.emitPopCat1Value("a" + callerArgIndex);
}

//   P-Code 
return pCode.getPcodeOps();
      
      



V8_PcodeOpEmitter (https://github.com/PositiveTechnologies/ghidra_nodejs/blob/main/src/main/java/v8_bytecode/V8_PcodeOpEmitter.java), JVM. p-code . .





emitAssignVarnodeFromPcodeOpCall(String varnodeName, int size, String pcodeop, String... args)





Varnode — p-code, , p-code. ,  — Varnode.





. p-code pcodeop



args



varnodeName



:





varnodeName = pcodeop(args[0], args[1], …);
      
      



emitPushCat1Value(String valueName) emitPopCat1Value (String valueName)





p-code push pop Varnode valueName



.





emitAssignVarnodeFromVarnode (String varnodeOutName, String varnodeInName, int size)





p-code varnodeOutName = varnodeInName







emitVarnodeCall (String target, int size)





P-Code target.





Ghidra. p-code  — bytenode Node.JS. github.com. , -!





- , - .





Node.js : , , .








All Articles