After some discussion on the Pin mailing list, I came to the conclusion that the best approach to my previous issue with Pin is simply to check the integrity of all values that get put in the EIP register from a potentially tainted source. This is arguably what I should have been doing anyway to detect the potential for an exploit. This means instrumenting ret, call, loop, jmp and its conditional variants, and checking the integrity of the value being used. In this context, this means checking that the value isn’t tainted by user input, and it is a fairly trivial task. For example, this code is called from my instruction instrumentation routine, registered with Pin:
VOID processRET(INS ins) { INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(checkRETIntegrity), IARG_REG_VALUE, LEVEL_BASE::REG_ESP, IARG_CONTEXT, IARG_INST_PTR, IARG_END); }
Which results in the following function being called before any ret instructions are executed, and passed the value currently stored in the ESP register:
(tmgr is a custom object that maintains taint propagation information)
VOID checkRETIntegrity(ADDRINT espVal, CONTEXT *ctx, ADDRINT pc) { if (tmgr.isMemLocTainted(espVal, DWORD_SIZE)) { postRunAnalysis(true, ctx, TAINTED_RET); exit(0); } }
Using this same technique I can also check for corruption of the stored frame pointer. I should also extend these checks to read/writes from and to memory. The problem gets a little more complex here as it is quite common for user input to taint the offset of a read/write. The only practical approach that springs to mind is to check the location being read/written is mapped; as attempting to detect if the tainted index is within a certain bound involves solving a whole other array of associated problems.