自动查找对结构偏移量的调用

逆向工程 吉德拉
2021-07-02 05:50:42

我想在运行时通过函数参数通过指向结构的指针自动搜索对不存在于二进制文件中的函数的函数调用以及传递函数地址的函数。

例如,JNI 类的第一个参数JNIEnv*是一个结构,其中包含指向 JNI 函数的指针。如果您反编译 JNI 函数,您可能会看到如下内容:

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

使用 Ghidra 的 pcode API,是否可以自动定位对该FindClass函数的所有引用作为参考,FindClass在 x86 程序集和 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个回答

如果要使用 pcode API 执行此操作,则必须检查PTRSUB操作的参数,这->与 C 级别上的等效

PTRSUB 执行简单的指针计算 input0 + input1,但也明确指示 input0 是对结构化数据类型的引用,并且正在访问其子组件之一。Input0 是指向结构开头的指针,input1 是到子组件的字节偏移量。作为操作,PTRSUB 生成一个指向子组件的指针并将其存储在输出中。

请注意,该PTRSUB操作不是问题中发布的原始 pcode 的一部分,而是在分析过程中由反编译器插入的。再次引用文档:

以下操作码不是作为机器指令原始转换为 p 代码操作的一部分生成的,因此它们都不能用于处理器规范。但是,它们可能会在稍后阶段通过各种分析算法引入。

以下代码将查找给定函数中对结构偏移量的所有调用:

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());
                }
            }
        }
    }
}

或者,您可以在程序中搜索对 FindClass 字段的所有引用,但请注意,这将列出所有类型的引用,而不仅仅是调用:

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) {
    System.out.println(location.getLocationOfUse());
}

(这基本上相当于 UI 中的“Find Uses of JNIEnv.FindClass ...”)