Or how to escape PIN in 5 instructions, using the self-modification technique seen in the previous post. Ready ? Go:
#include <stdio.h> main() { asm("call foo\n\t" "foo: pop %rax\n\t" "movl $0x4004e7, 10(%eax)\n\t" // put @nottraced() in the next mov "movl $0x4004fb, %eax\n\t" // @traced(), will be overwritten // by @nottraced() if not instrumented "call *%rax\n\t"); }
// we don't want PIN to analyse this nottraced() { printf("trace me if you can!\n"); }
// we want PIN to analyse this, a dummy function traced() { printf("you're not supposed to get here\n"); }
As usual: compile, make the .text section and the program header writable, and run.
reynaudd@lhs-2:~/test/packed$ ./escape2 trace me if you can!
reynaudd@lhs-2:~/test/packed$ pin -t ../pin-2.5-24110-gcc.4.0.0-ia32_intel64-linux/source/tools/ManualExamples/obj-intel64/inscount0.so -- ./escape2 you're not supposed to get here
‘Nuff said.
UPDATE: as the authors of PIN pointed out, this situation in handled correctly by PIN with the option -smc_strict. That’s because for performance reasons (and standards compliance), PIN makes the assumption that there is at least a taken branch between a modification of the code and its execution (i.e. no basic block modifies itself). My example violates this assumption.