The FunctionMonitor plugin catches the call/return machine instructions and invokes the corresponding handlers.
Suppose that you want to monitor the call to a function that starts at address 0xC00F0120 in kernel space. You also wish to monitor all the returns from that function.
Proceed as follows:
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 *m_monitor = static_cast<FunctionMonitor*>(s2e()->getPlugin("FunctionMonitor"));
//3. Monitor the translation of each translation block
s2e()->getCorePlugin()->onTransl ateBlockStart.connect(
sigc::mem_fun(*this, &Example::slotTranslateBlockStart));
}
For example, to monitor the kernel-mode function located at 0xC00F012, specify, issue a call as follows:
void Example::slotTranslateBlockStart(ExecutionSignal *signal,
S2EExecutionState *state,
TranslationBlock *tb,
uint64_t pc)
{
FunctionMonitor::CallSignal *callSignal;
//4. Obtain the address of the function to be monitored
//The hard-coded value can be specified in the configuration file your plugin
uint64_t functionAddress = 0xC00F0120;
//5. Register a function call monitor at program counter 0xC00F0120.
//This is done in two steps:
// a. Register a call signal for the specified address
// b. Connect as many signal handlers as needed
if (m_registered) {
//You must make sure that you do not register the same handler more than
//once, unless you want it to be called multiple times.
return;
}
//a. Register a call signal for address 0xC00F0120
callSignal = m_monitor->getCallSignal(state, functionAddress, -1);
//b. Register one signal handler for the function call.
//Whenever a call instruction whose target is 0xC00F0120 is detected, FunctionMonitor
//will invoke myFunctionCallMonitor
callSignal->connect(sigc::mem_fun(*this, &Example::myFunctionCallMonitor));
}
The FunctionMonitor plugin has one important methods that returns a call descriptor tied to the specified program counter/process id:
FunctionMonitor::CallSignal*
FunctionMonitor::getCallSignal(
S2EExecutionState *state,
uint64_t eip,
uint64_t cr3
);
The call handler looks as follows:
//This handler is called after the call instruction is executed, and before the first instruction
//of the called function is run.
void Example::myFunctionCallMonitor(S2EExecutionState* state, FunctionMonitorState *fns)
{
s2e()->getMessagesStream() << "My function handler is called" << std::end;
//...
//Perform here any analysis or state manipulation you wish
//...
//6. Register the return handler
//The FunctionMonitor plugin invokes this method whenever the return instruction corresponding
//to this call is executed.
FUNCMON_REGISTER_RETURN(state, fns, Example::myFunctionRetMonitor)
}
Finally, the return handler looks as follows:
//FunctionMonitor invokes this handler right after the return instruction is executed, and
//before the next instruction is run.
void Example::myFunctionRetMonitor(S2EExecutionState *state)
{
//...
//Perform here any analysis or state manipulation you wish
//...
}
Call/return handlers are paired: FunctionMonitor tracks stack pointers. 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 wish to your call handlers. You are not limited to the default S2EExecutionState and FunctionMonitorState. For this, you can use the sigc++ bind feature.