Now that we have a working PANDA tracer with Operating System Introspection (OSI), we can use it to acquire program code coverage information using a plugin.
In the following, we will use this facility to obtain an coverage trace for a program, ch28, that has shown itself resilient against PIN, drcov and Frida.
The recording and playback memory size parameters have to match. For example, if the system for which the recording was was taken was launched using
$ ./panda-system-x86_64 --monitor stdio -m 4096 -cdrom 'ubuntu-18.04.4-desktop-amd64.iso'then the -m 4096 parameter has to be carried over to the playback command:
$ ./panda-system-x86_64 -m 4096 -replay ch28_recording -os linux-64-ubuntu -panda osi -panda osi_linux:kconf_group=ubuntu:5.3.0-28-generic:64 -panda coverage:filename=ch28_coverage.csv,mode=processPANDA[core]:os_familyno=2 bits=64 os_details=ubuntuPANDA[osi_linux]:adding argument kconf_group=ubuntu:5.3.0-28-generic:64.PANDA[coverage]:adding argument filename=ch28_coverage.csv.PANDA[coverage]:adding argument mode=process.PANDA[core]:initializing osiPANDA[core]:loading required plugin osi_linuxPANDA[core]:initializing osi_linuxPANDA[osi_linux]:W> kernelinfo bytes [20-23] not readPANDA[osi_linux]:W> kernelinfo bytes [124-127] not readPANDA[core]:/home/ubuntu/panda/panda/scripts/panda/build/x86_64-softmmu/panda/plugins/panda_osi_linux.so already loadedPANDA[core]:initializing coveragePANDA[coverage]:output file name ch28_coverage.csvPANDA[coverage]:file buffer_size 8192PANDA[coverage]:log all records DISABLEDPANDA[coverage]:mode processPANDA[core]:loading required plugin osiPANDA[core]:/home/ubuntu/panda/panda/scripts/panda/build/x86_64-softmmu/panda/plugins/panda_osi.so already loadedPANDA[coverage]:start disabled DISABLEDloading snapshot... done.opening nondet log for read : ./ch28_recording-rr-nondet.log./ch28_recording-rr-nondet.log: 991951943 instrs total.ch28_recording: 9919528 ( 1.00%) instrs. 1.22 sec. 4.04 GB ram.ch28_recording: 19839042 ( 2.00%) instrs. 2.29 sec. 4.06 GB ram.[ ... ]ch28_recording: 972112912 ( 98.00%) instrs. 70.00 sec. 4.17 GB ram.ch28_recording: 982032431 ( 99.00%) instrs. 70.88 sec. 4.17 GB ram../ch28_recording-rr-nondet.log: log is empty../ch28_recording-rr-nondet.log: log is empty.Time taken was: 72 seconds.Stats:RR_INPUT_1 number = 0, size = 0 bytesRR_INPUT_2 number = 0, size = 0 bytesRR_INPUT_4 number = 7084, size = 99176 bytesRR_INPUT_8 number = 103402, size = 1861236 bytesRR_INTERRUPT_REQUEST number = 13809, size = 193326 bytesRR_EXIT_REQUEST number = 0, size = 0 bytesRR_SKIPPED_CALL number = 11, size = 625 bytesRR_END_OF_LOG number = 1, size = 10 bytesRR_PENDING_INTERRUPTS number = 0, size = 0 bytesRR_EXCEPTION number = 0, size = 0 bytesmax_queue_len = 498Checksum of guest memory: 0x275bbf7dReplay completed successfullyExiting cpu_handle_execption loopWe now have a file, ch28_coverage.csv, which contains the code coverage information. We can import this file into IDA by using the coverage.py script that comes with the PANDA coverage plugin. Simply go to File->Script file... and select ch28_coverage.csv
A selection window will open with all the processes that were recorded. Here, we can see that ch28 has forked.
After selecting one of the two processes, IDA has the coverage information color coded and annotated. Note the sequence number and thread ID in the comments.
After selecting both processes, all of the code paths are colored. Here the fork() that separates the processes
A useful trick is to change the colors in the script file between executions to have better color coding
Now we have accurate coverage information in IDA for this pesky executable.
References: