Tracing and Coverage - measures and countermeasures

One of the most useful tools for the reverse engineer (and forward engineer :P) is the program execution tracer. This yields either program coverage (which lines have been executed) or a full trace (list of executed instructions in order). Qira is a very good example, leveraging either qemu or PIN to take an execution trace and display it in a nice GUI. But many other tracers exist, including DynamoRIO drcov, Frida and Intel PIN based tools. Finally, there are whole-system tracers, such as PANDA. I want to compare some of these, point out some shortcomings and illustrate some countermeasures.

In a previous post on ptrace, I showed some simple programs that will detect a debugger using ptrace. Let's start with those and then explore tougher cookies....

Super simple ptrace check

#include <stdio.h>
#include <sys/ptrace.h>

int main() {
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) {
        printf("Debugger\n");
    } else {
        printf("Normal\n");
    }
    return 0;
}

qira

$ qira ./superSimplePtrace
Debugger

qira using PIN

$ qira --pin ./superSimplePtrace
Normal

drcov

$ bin64/drrun -t drcov -- ./superSimplePtrace

frida

  • no need yet, test in more difficult case below

PANDA

  • no need yet, test in more difficult case below

Less simple ptrace check - multiple ptrace calls in single thread

#include <stdio.h>
#include <sys/ptrace.h>

int main() 
{
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) // first call
        {
        printf("Debugger (first check)\n");
        } 
    else 
        {
        if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) // second call
            {
            printf("Normal\n");
            } 
        else 
            {
            printf("Debugger (second check)\n");
            }
        }
return 0;
}

qira

$ qira ./lessSimplePtrace
Debugger (first check)

qira using PIN

$ qira --pin ./lessSimplePtrace 
Normal

drcov

$ bin64/drrun -t drcov -- ./lessSimplePtrace
Normal

frida

  • no need yet, test in more difficult case below

PANDA

  • no need yet, test in more difficult case below

Self-debugging ptrace

This code is adapted from [5]

#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <stdio.h>

int main()
{
 pid_t child;
 int status;
 switch((child = fork()))
 {
 case 0:
  ptrace(PTRACE_TRACEME);
 // this is the actual 'useful' program code should run for a while - simulate with delay
 usleep(1000000);
 printf("no debugger detected!");
 return 2;
 case -1:
        perror("fork");
        return 1;
 default:
        if (ptrace(PTRACE_ATTACH, child))
 {
  kill(child, SIGKILL);
  printf("ptrace_attach failed - debugger?");
  return 3;
 }
 while (waitpid(child, &status,0) != -1)
  ptrace(PTRACE_CONT,child,0,0);
 return 0;
 }
  return 0;
}

qira

$ qira ./selfdebug
ptrace_attach failed - debugger?

qira using PIN

$ qira --pin ./selfdebug
no debugger detected!

drcov

$ bin64/drrun -t drcov -- ./selfdebug
no debugger detected!

frida

  • no need yet, test in more difficult case below

PANDA

  • no need yet, test in more difficult case below

Nanomites

Next, I am running a binary challenge from root-me.org that contains nanomites [2],[3].

qira

This fails, even when the correct flag is passed.

$ qira ./ch28.bin 
So you want to trace me?!
Wrong! try hard! :)

qira using PIN

This fails, even when the correct flag is passed. But in a different place than before.

$ qira --pin ./ch28.bin 
Please input the flag:
Hummmmmmm NO WAY.

drcov

This fails, even when the correct flag is passed, same place as qira with PIN

$ bin64/drrun -t drcov -- ./ch28.bin  
Please input the flag:
Hummmmmmm NO WAY.

frida

I am using a script by lighthouse [5] to collect coverage information using frida.

$ sudo python frida-drcov.py ch28.bin
[*] Attaching to pid '6171' on device 'local'...
[+] Attached. Loading script...
[+] Got module info.
Starting to stalk threads...
Stalking thread 6171.
Done stalking threads.
[*] Now collecting info, control-D to terminate....

and in a separate window

$ ./ch28.bin 
Please input the flag:
Hummmmmmm NO WAY.

PANDA

details here: PANDA for code coverage with IDA pro

$ ./ch28.bin 
Please input the flag:
POOOOOOOOOOOOOOOOOOOOOOOOO God damn!! You won!

Whole system tracing is the only method that can yield code coverage and execution tracing in this and many other cases...

References: