FunctionMonitor

The FunctionMonitor plugin notifies other plugins of function calls and returns.

Suppose that you are writing a plugin that needs to monitor calls to a function that starts at address 0x400120 in a process called my.exe. Your plugin also wants to monitor all returns from that function.

Proceed as follows:

  1. Create a new plugin. You can use the Example plugin as a template.

  2. Obtain the instance of the FunctionMonitor plugin in the initialize() method of the plugin.

  3. Register a handler for the onCall signal provided by FunctionMonitor.

  4. In the call handler, check the current program counter to determine if this is a function that you want to process further or not. You can also use information about callers and callees provided by the signal to make this decision.

  5. In the call handler, register a return handler, if necessary.

  6. Do not forget to add my.exe to the configuration of ProcessExecutionDetector in s2e-config.lua.

The following part shows the code that implements the steps explained above.

// 1. Write a new analysis plugin (e.g., based on the Example plugin)
void Example::initialize() {
    // 2. Get an instance of the FunctionMonitor plugin
    FunctionMonitor *monitor = s2e()->getPlugin<FunctionMonitor>();

    // 3. Get a notification when a function is called
    monitor->onCall.connect(sigc::mem_fun(*this, &Example::onCall));
}

To monitor the function located at 0x400120, write the following handlers:

void Example::onCall(S2EExecutionState *state, const ModuleDescriptorConstPtr &source,
                     const ModuleDescriptorConstPtr &dest, uint64_t callerPc, uint64_t calleePc,
                     const FunctionMonitor::ReturnSignalPtr &returnSignal) {
    // Filter out functions we don't care about
    if (state->regs()->getPc() != 0x400120) {
        return;
    }

    // If you do not want to track returns, do not connect a return signal.
    // Here, we pass the program counter to the return handler to identify the function
    // from which execution returns.
    returnSignal->connect(
        sigc::bind(sigc::mem_fun(*this, &Example::onRet), 0x400120));
}

void Example::onRet(S2EExecutionState *state, const ModuleDescriptorConstPtr &source,
                    const ModuleDescriptorConstPtr &dest, uint64_t returnSite,
                    uint64_t functionPc) {
    getDebugStream(state) << "Execution returned from function " << hexval(functionPc) << "\n";
}

Call/return handlers are paired: whenever the return instruction is executed and the stack pointer corresponds to the one at the call instruction, the return handler tied to that call is executed.

You can pass as many parameters as you want to your call or return handlers. For this, you can use the fsigc++ bind feature.

Note

You can also instrument functions from Lua code using the LuaFunctionInstrumentation plugin.