I want to automate the search for function calls to a function that does not live in the binary and where the function address is passed, via a pointer to a struct, at runtime through a function parameter.

As an example, the first parameter of a JNI class is JNIEnv* which is a struct that contains, among other things, pointers to JNI functions. If you decompile a JNI function, you might see something like this:

Java_com_example_ExampleClass_exampleMethod(JNIEnv*, ...) {
   clazz = (*(*env)->FindClass)(env,jniClass);

Using Ghidra's pcode API, is it possible to automate locating all references to the FindClass function? For reference, the call to FindClass will look something like the following in x86 assembly and pcode.

  000fd2be 8b 0f           MOV        ECX,dword ptr [EDI]
                                              $U16f0:4 = LOAD ram(EDI)
                                              ECX = COPY $U16f0

  000fd2c7 ff 51 18        CALL       dword ptr [ECX + 0x18]
                                              $U3a0:4 = INT_ADD ECX, 24:4
                                              ESP = INT_SUB ESP, 4:4
                                              STORE ram(ESP), 0xfd2ca:4
                                              $U16f0:4 = LOAD ram($U3a0)
                                              CALLIND $U16f0

1 Answer 1


If you want to do this using the pcode API, you will have to examine the arguments to the PTRSUB operation, which is the equivalent of -> on the C level.

A PTRSUB performs the simple pointer calculation, input0 + input1, but also indicates explicitly that input0 is a reference to a structured data-type and one of its subcomponents is being accessed. Input0 is a pointer to the beginning of the structure, and input1 is a byte offset to the subcomponent. As an operation, PTRSUB produces a pointer to the subcomponent and stores it in output.

Note that the PTRSUB operation is not a part of the raw pcode posted in the question, but it is inserted by the decompiler during the analysis process. Quoting the documentation again:

The following opcodes are not generated as part of the raw translation of a machine instruction into p-code operations, so none of them can be used in a processor specification. But, they may be introduced at a later stage by various analysis algorithms.

The following code will find all calls to a structure offset in a given function:

DecompileResults results = decompiler.decompileFunction(function,
    decompiler.getOptions().getDefaultTimeout(), monitor);
HighFunction hfunc = results.getHighFunction();

for(PCodeOpAST op : hfunc.getPcodeOps()) {
    if (op.getOpcode() == PcodeOp.CALLIND) {
        Varnode funcAddress = op.getInput(0);

        while (funcAddress.getDef().getOpcode() == PcodeOp.LOAD)
            funcAddress = funcAddress.getDef().getInput(1);

        if (funcAddress.getDef().getOpcode() == PcodeOp.PTRSUB) {
            Varnode struct = funcAddress.getDef().getInput(0);
            Varnode field = funcAddress.getDef().getInput(1);

            DataType structDataType = struct.getHigh().getDataType();
            while(structDataType instanceof Pointer || structDataType instanceof TypeDef) {
                if (structDataType instanceof Pointer)
                    structDataType = ((Pointer)structDataType).getDataType();
                else if (structDataType instanceof TypeDef)
                    structDataType = ((TypeDef)structDataType).getDataType();

            if (structDataType instanceof Structure && field.isConstant()) {
                Structure structure = (Structure) structDataType;
                int offset = (int)field.getOffset();

                DataTypeComponent component = structure.getComponentAt(offset);
                System.out.println("Call to " + component.getFieldName() +
                    " in " + structure.getName());
                for (int i = 1; i < op.getNumInputs(); ++i) {  // 0 is the function address, 1+ are arguments
                    System.out.println(" with argument " + i + " = " + op.getInput(i).getAddress());

Alternatively, you can search for all references to the FindClass field in the program instead, but note that this will list all types of references, not just calls:

DataType jniEnvDataType = program.getDataTypeManager().getDataType("/jni_all.h/JNIEnv");
Accumulator<LocationReference> accumulator = new ListAccumulator<>();
ReferenceUtils.findDataTypeReferences(accumulator, jniEnvDataType, "FindClass",
    program, TaskMonitor.DUMMY);
for(LocationReference location : accumulator) {

(this is basically the equivalent of "Find Uses of JNIEnv.FindClass ..." in the UI)

  • nice solution for the problem!
    – karsten
    Aug 30, 2023 at 8:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.