tklengyel / drakvuf

DRAKVUF Black-box Binary Analysis
https://drakvuf.com
Other
1.04k stars 249 forks source link

Context based views #1157

Closed pwnosaur closed 3 years ago

pwnosaur commented 3 years ago

@tklengyel

I have recently been dealing with the amount of data generated through interception of system calls, when setting a breakpoint on a system call, the callback is triggered regardless of the execution context, although it can be filtered on the callback using a simple if condition to check if it is a process we are interested or not, but I have a different thought.

The idea is to create context based interception, in which we set a trap on CR3 changes, and create a list of either the processes we want the breakpoints to be triggered for or vice versa, and on every context switch on each VCPU we check if the DTB belongs to one of the processes in the previously created list, if it is, then we switch the altp2m view to the one that has the shadow pages with breakpoints activated, else if it is not, then we simply switch the view to another view that has no breakpoints in it.

Now I came across the XC API xc_altp2m_switch_to_view , this API clearly indicates that the view is switched on all VCPUs , but in order to achieve the above, it needs to be VCPU independent based switch, in order to switch the view for the VCPU that is executing instructions belonging to one of the processes we are interested in.

tklengyel commented 3 years ago

This is an interesting idea. One problem I see is that the cr3 can change during the lifetime of a process. But of course on all mov-to-cr3 you can check what's the process and make your decision then.

As for the per-vCPU altp2m switching, you won't be able to do that as a regular DRAKVUF plugin. DRAKVUF plugins are self-contained and one plugin doesn't know about another but also doesn't interfere with it. This per-vCPU optimization would obviously break this model.

So what you can do is write your application to be on top of libdrakvuf directly, not as a plugin. You subscribe to the MOV-TO-CR3 trap, and use the VMI_EVENT_RESPONSE_SLAT_ID response flag. You make your decision based on what process is scheduled to execute on the vCPU. If its a process you want to monitor? Switch to the idx view. If it's not? Switch to view 0. I think this would work no problem, we just need an API addition that you can use to query libdrakvuf for what the idx view number is.

tklengyel commented 3 years ago

You would also have to cook up some checks yourself that would establish if the page where you have traps have been modified by the "unmonitored vCPU". Effectively you would need to hash the page and check if it changed while the unmonitored vCPU executed. If it did, you would need to re-register your syscall monitor trap. Otherwise the shadow copy of the monitored page could go out-of-sync and cause issues.

In fact, it may still cause issues in case one unmonitored vCPU is modifying the page while another monitored vCPU is executing. The OS may actually have some syncronization lock in place, but when its up, the monitored vCPU would still execute with the stale shadow copy.

So I think this is going to be pretty complex with quite a lot of potential for things to go out-of-whack. But a cool idea nevertheless. I think it could be done. I probably won't be supporting it as a DRAKVUF feature but the API extension would be fine.

pwnosaur commented 3 years ago

One problem I see is that the cr3 can change during the lifetime of a process. But of course on all mov-to-cr3 you can check what's the process and make your decision then.

As you've said, we can check upon the trigger of the mov-to-cr3 event, and take the decision, there could be also more optimum solutions.

So what you can do is write your application to be on top of libdrakvuf directly, not as a plugin. You subscribe to the MOV-TO-CR3 trap, and use the VMI_EVENT_RESPONSE_SLAT_ID response flag.

Well this would work except for the VCPU part, as the API for Xen switches the view for all VCPUs all at once, I'm not sure if there is another hypercall API that would allow me to switch the view on a specific VCPU, this would require patching/modification to Xen's XC API.

we just need an API addition that you can use to query libdrakvuf for what the idx view number is.

Yeah I may add that and make a pull request with it.

You would also have to cook up some checks yourself that would establish if the page where you have traps have been modified by the "unmonitored vCPU". Effectively you would need to hash the page and check if it changed while the unmonitored vCPU executed

Another solution could be by creating a 3rd view with write guard, and this view will be activated on any VCPU that is not executing the monitored process, and then just mirror these writes to the page to keep it in-sync. sure, there would be more to it, but I assume this should work fine, correct me if I'm mistaken.

tklengyel commented 3 years ago

Well this would work except for the VCPU part, as the API for Xen switches the view for all VCPUs all at once, I'm not sure if there is another hypercall API that would allow me to switch the view on a specific VCPU, this would require patching/modification to Xen's XC API.

No, if you specify the response flag Xen switches the view only for that vCPU. There is no separate hypercall to do that, it's only possible to do right now in response to a vm_event, which is what DRAKVUF uses.

pwnosaur commented 3 years ago

No, if you specify the response flag Xen switches the view only for that vCPU. There is no separate hypercall to do that, it's only possible to do right now in response to a vm_event, which is what DRAKVUF uses.

It is working as expected, and I have added a 3rd view in which current_gfn points to its original frame but it only has R-X rights, this view is active on CPUs that are not executing processes/contexts we are interested in, and if there are write events triggered on the page it acts just like it normally would in IDX view (Switch to view 0, let it write, and then in post mem event it copies these changes ). For the API extension, I would like to take your recommendation for the optimum implementation to make sure it does not create conflicts, the current modifications I have made were in the core to allow me to use this through plugins, but as you stated that probably wouldn't support as a DRAKVUF feature, I also would like to know why you may not want to include it into DRAKVUF.

tklengyel commented 3 years ago

I like the solution with having an r-x view, different from the default view 0 rwx and the idx --x view. I would actually consider merging this support. Thing is, this wouldn't be possible to be controlled by a plugin - what if two plugins want conflicting optimizations? But having a top-level switch in drakvuf that you could use to specify which process or processes you want to have the rest of the plugins activate on would be acceptable.

pwnosaur commented 3 years ago

I have implemented the suggested idea, you can take a look at it here https://github.com/pwnosaur/drakvuf/tree/ContextSwitchInterception

-C to enable context switch based interception --context-process to add a process to monitor/wait for

The CR3 decision to switch the view to IDX depends whether the process name matches one in the list, or in strict mode both process name and pid matches, strict mode can only be used by plugins at the mean time to avoid complications in the startup, there could be further enhacements later on.

./src/drakvuf -r ~/win.json -d win -a syscalls -C --context-process explorer.exe

adding/removing processes is also accessible through plugins using

drakvuf_intercept_process_add(drakvuf_t drakvuf, char * process_name, vmi_pid_t pid, bool strict)

it takes 4 arguments, strict options is to restrict the monitoring to process and pid that matches the desired pid and name.

1182