PANDA is a platform that allows the complete tracing of execution of a virtual machine. This is incredibly useful. The system trace can be operated on after the fact using a rich API. With this API a so-called plugin is written, and this plugin then operates on the trace. There are many sample plugins available already ranging from capturing the GUI output to a video to extracting binary coverage information for IDA pro. Setting it up can be tricky, especially with newer kernels, and so I am documenting my process here. I like to use live DVDs as the client VMs, and so this example covers setup of PANDA for the usage of that case.
I am drawing from the install process is described at : https://github.com/panda-re/panda/blob/master/panda/docs/build_ubuntu.md
On a clean Ubuntu 18.04.03
$ sudo apt install git# clone and build PANDA. This does download PANDA twice, but it's easy this way.
$ git clone https://github.com/panda-re/panda$ cd panda/panda/scripts/$ ./install_ubuntu.sh$ cd panda/build/x86_64-softmmu$ ./panda-system-x86_64 --monitor stdio -m 4096 -cdrom '/home/ubuntu/Downloads/ubuntu-18.04.4-desktop-amd64.iso'This will launch a virtual machine which can be traced, in this case a Ubuntu 18.04.04 live DVD
The --monitor stdio option kept the qemu command prompt open to receive commands and this is where we issue the command to start recording
(qemu) begin_record therecording(qemu) writing snapshot: ./therecording-rr-snpopening nondet log for write: ./therecording-rr-nondet.logend_record(qemu) Time taken was: 7 seconds.Checksum of guest memory: 0x4e5c394aAnd at some point we end the recording
(qemu) end_recordThis record can now be operated on. One possibility is to generate a movie showing what the GUI displayed during the recording session.
$ sudo apt install ffmpeg$ ./panda-system-x86_64 -m 4096 -replay therecording -panda replaymovie$ ../../../../plugins/replaymovie/movie.sh$ ffplay replay.mp4And this can then be turned into an animated GIF, if you have need for that.
$ ffmpeg -y -i replay.mp4 -vf palettegen palette.png$ ffmpeg -i replay.mp4 -i palette.png -lavfi paletteuse out.gifThe animation below is the outcome of this recording.
Next, in order for PANDA to be able to dissect the client system, we have to collect some information inside it. So from within the virtual machine,
$ sudo apt install git build-essential linux-headers-`uname -r`$ git clone https://github.com/panda-re/panda$ cd panda/panda/plugins/osi_linux/utils/kernelinfo$ make$ sudo insmod kernelinfo.koThe above command will fail with an error message, but it will provide the required information through dmesg
$ dmesgHowever: Newer Linux kernels randomize the address space so these numbers change every boot.
This can be turned off at boot time by passing the kernel boot parameter nokaslr
I have collected this information for the boot DVDs for Ubuntu 18.04.3 and 18.04.4 with KASLR disabled:
Update: These kernel parameters have been merged to PANDA: https://github.com/panda-re/panda/pull/559
So you don't have to collect it if you are using those two kernels.
for 18.04.3:
name = 5.0.0-23-generic|#24~18.04.1-Ubuntu SMP Mon Jul 29 16:12:28 UTC 2019|x86_64version.a = 5version.b = 0version.c = 15task.per_cpu_offsets_addr = 18446744071599519776task.per_cpu_offset_0_addr = 18446612687365341184task.current_task_addr = 89088task.init_addr = 18446744071602009920#task.per_cpu_offsets_addr = FFFFFFFF823B7820#task.per_cpu_offset_0_addr = 0xFFFF88813BA00000#task.current_task_addr = 0x00015C00#task.init_addr = 0xFFFFFFFF82617740task.size = 9152task.tasks_offset = 1960task.pid_offset = 2216task.tgid_offset = 2220task.group_leader_offset = 2280task.thread_group_offset = 2392task.real_parent_offset = 2232task.parent_offset = 2240task.mm_offset = 2040task.stack_offset = 24task.real_cred_offset = 2624task.cred_offset = 2632task.comm_offset = 2640task.comm_size = 16task.files_offset = 2712cred.uid_offset = 4cred.gid_offset = 8cred.euid_offset = 20cred.egid_offset = 24mm.size = 1032mm.mmap_offset = 0mm.pgd_offset = 80mm.arg_start_offset = 304mm.start_brk_offset = 280mm.brk_offset = 288mm.start_stack_offset = 296vma.size = 208vma.vm_mm_offset = 64vma.vm_start_offset = 0vma.vm_end_offset = 8vma.vm_next_offset = 16vma.vm_flags_offset = 80vma.vm_file_offset = 160fs.f_path_dentry_offset = 24fs.f_path_mnt_offset = 16fs.f_pos_offset = 104fs.fdt_offset = 32fs.fdtab_offset = 40fs.fd_offset = 8qstr.size = 16qstr.name_offset = 8path.d_name_offset = 32path.d_iname_offset = 56path.d_parent_offset = 24path.d_op_offset = 96path.d_dname_offset = 72path.mnt_root_offset = 0path.mnt_parent_offset = -16path.mnt_mountpoint_offset = -8and for 18.04.4:
name = 5.3.0-28-generic|#30~18.04.1-Ubuntu SMP Fri Jan 17 06:14:09 UTC 2020|x86_64version.a = 5version.b = 3version.c = 13task.per_cpu_offsets_addr = 18446744071599864096task.per_cpu_offset_0_addr = 18446612687365341184task.current_task_addr = 93120task.init_addr = 18446744071601993600#task.per_cpu_offsets_addr = FFFFFFFF8240B920#task.per_cpu_offset_0_addr = 0xFFFF88813BA00000#task.current_task_addr = 0x00016BC0#task.init_addr = 0xFFFFFFFF82613780task.size = 9152task.tasks_offset = 1984task.pid_offset = 2240task.tgid_offset = 2244task.group_leader_offset = 2304task.thread_group_offset = 2416task.real_parent_offset = 2256task.parent_offset = 2264task.mm_offset = 2064task.stack_offset = 24task.real_cred_offset = 2648task.cred_offset = 2656task.comm_offset = 2672task.comm_size = 16task.files_offset = 2744cred.uid_offset = 4cred.gid_offset = 8cred.euid_offset = 20cred.egid_offset = 24mm.size = 1032mm.mmap_offset = 0mm.pgd_offset = 80mm.arg_start_offset = 304mm.start_brk_offset = 280mm.brk_offset = 288mm.start_stack_offset = 296vma.size = 208vma.vm_mm_offset = 64vma.vm_start_offset = 0vma.vm_end_offset = 8vma.vm_next_offset = 16vma.vm_flags_offset = 80vma.vm_file_offset = 160fs.f_path_dentry_offset = 24fs.f_path_mnt_offset = 16fs.f_pos_offset = 104fs.fdt_offset = 32fs.fdtab_offset = 40fs.fd_offset = 8qstr.size = 16qstr.name_offset = 8path.d_name_offset = 32path.d_iname_offset = 56path.d_parent_offset = 24path.d_op_offset = 96path.d_dname_offset = 72path.mnt_root_offset = 0path.mnt_parent_offset = -16path.mnt_mountpoint_offset = -8This information should be in panda/plugins/kernelinfo.conf
Now the client VM system is basically set up for tracing. However, user mode binaries also use ASLR by default, and it is helpful in many cases to disable that too. This can be done by running the following command in the VM:
more /proc/sys/kernel/randomize_va_spaceecho 0 | sudo tee /proc/sys/kernel/randomize_va_spaceWe can now test the PANDA installation with some of its' other built-in plugins. There is a long list of existing plugins here:
First, lets test the OSI data we collected above:
ubuntu@ubuntu:~/panda/panda/scripts/panda/build/x86_64-softmmu$ ./panda-system-x86_64 -m 4096 -replay therecording -panda osi -panda osi_linux:kconf_group=ubuntu:5.3.0-28-generic:64 -os linux-64-ubuntu -panda osi_test > ositest.txtPANDA[osi_linux]:adding argument kconf_group=ubuntu:5.3.0-28-generic:64.PANDA[core]:os_familyno=2 bits=64 os_details=ubuntuPANDA[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 osi_testPANDA[core]:loading required plugin osiPANDA[core]:/home/ubuntu/panda/panda/scripts/panda/build/x86_64-softmmu/panda/plugins/panda_osi.so already loadedubuntu@ubuntu:~/panda/panda/scripts/panda/build/x86_64-softmmu$ tail ositest.txt RR_INTERRUPT_REQUEST number = 2620, size = 36680 bytesRR_EXIT_REQUEST number = 0, size = 0 bytesRR_SKIPPED_CALL number = 0, size = 0 bytesRR_END_OF_LOG number = 1, size = 10 bytesRR_PENDING_INTERRUPTS number = 0, size = 0 bytesRR_EXCEPTION number = 0, size = 0 bytesmax_queue_len = 397Checksum of guest memory: 0x4e5c394aReplay completed successfullyExiting cpu_handle_execption loopubuntu@ubuntu:~/panda/panda/scripts/panda/build/x86_64-softmmu$ Exellent! Lets try to collect process coverage data:
ubuntu@ubuntu:~/panda/panda/scripts/panda/build/x86_64-softmmu$ ./panda-system-x86_64 -m 4096 -replay therecording -panda osi -panda osi_linux:kconf_group=ubuntu:5.3.0-28-generic:64 -os linux-64-ubuntu -panda coverage:filename=test_coverage.csv,mode=processPANDA[osi_linux]:adding argument kconf_group=ubuntu:5.3.0-28-generic:64.PANDA[core]:os_familyno=2 bits=64 os_details=ubuntuPANDA[coverage]:adding argument filename=test_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 test_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 : ./therecording-rr-nondet.log./therecording-rr-nondet.log: 139046873 instrs total.therecording: 1390473 ( 1.00%) instrs. 0.59 sec. 4.04 GB ram.therecording: 2780941 ( 2.00%) instrs. 0.81 sec. 4.04 GB ram.therecording: 4171411 ( 3.00%) instrs. 1.06 sec. 4.05 GB ram.therecording: 5561875 ( 4.00%) instrs. 1.30 sec. 4.05 GB ram.therecording: 6952345 ( 5.00%) instrs. 1.52 sec. 4.06 GB ram.[ ... ]therecording: 134875470 ( 97.00%) instrs. 14.07 sec. 4.13 GB ram.therecording: 136265941 ( 98.00%) instrs. 14.23 sec. 4.13 GB ram.therecording: 137656406 ( 99.00%) instrs. 14.37 sec. 4.13 GB ram../therecording-rr-nondet.log: log is empty../therecording-rr-nondet.log: log is empty.Time taken was: 14 seconds.Stats:RR_INPUT_1 number = 0, size = 0 bytesRR_INPUT_2 number = 0, size = 0 bytesRR_INPUT_4 number = 1413, size = 19782 bytesRR_INPUT_8 number = 18855, size = 339390 bytesRR_INTERRUPT_REQUEST number = 2620, size = 36680 bytesRR_EXIT_REQUEST number = 0, size = 0 bytesRR_SKIPPED_CALL number = 0, size = 0 bytesRR_END_OF_LOG number = 1, size = 10 bytesRR_PENDING_INTERRUPTS number = 0, size = 0 bytesRR_EXCEPTION number = 0, size = 0 bytesmax_queue_len = 397Checksum of guest memory: 0x4e5c394aReplay completed successfullyExiting cpu_handle_execption loopubuntu@ubuntu:~/panda/panda/scripts/panda/build/x86_64-softmmu$ Great! We now have PANDA set up to record coverage data for processes launched straight from a live DVD. This coverage data can then be imported into IDA pro. But that is a topic for another day...
Bridged networking is a pain. I prefer to make a drive image
$ dd if=/dev/zero of=diskimg.ext3 bs=100M count=1$ mkfs.ext3 diskimg.ext3$ sudo ./panda-system-x86_64 --enable-kvm --monitor stdio -m 4096 -cdrom 'ubuntu-18.04.4-desktop-amd64.iso' -hda diskimg.ext3 To mount the file in the host after exiting PANDA
$ mkdir /home/ubuntu/vmshare$ sudo mount -o loop diskimg.ext3 /home/ubuntu/vmshare/and to unmount the file
$ umount /home/ubuntu/vmshareThe instructions above are the simplest way to get going. However, the script - install_ubuntu.sh - downloads another copy of PANDA using git. And sometimes one wants to recompile after making some code changes. So, if PANDA is already compiled on the computer, and compiled and the prerequisites are installed, one can compile manually like this:
Make sure to adjust the prefix directoy to the location on your computer..
$ ./configure --target-list=x86_64-softmmu,i386-softmmu,arm-softmmu,ppc-softmmu --prefix=/home/jan/Downloads/panda/panda/scripts/panda/build/install --python=/usr/bin/python2 --disable-vhost-net --extra-cflags=-DXC_WANT_COMPAT_DEVICEMODEL_API --extra-cflags=-DOSI_PROC_EVENTS --extra-cflags=-DOSI_MAX_PROC=256 --extra-cflags=-DOSI_LINUX_PSDEBUG --extra-cflags=-Wformat-truncation=0 --disable-werror --cc=gcc-9 --cxx=g++-9 --host-cc=gcc-9$ makeReferences: