patchless amsi
代码参考:https://gist.github.com/CCob/fe3b63d80890fafeca982f76c8a3efdf
解读代码可以从函数入口开始
setupAMSIBypass这个函数前面主要是获取amsiScanBuffer的地址,随即注册了一个veh异常。
然后通过调用GetThreadContext获取到了当前线程的context,这里作者用了一个小技巧:用(HANDLE)-2代替了GetCurrentThread()
然后调用enableBreakpoint下了一个硬件断点,这里我们先跟进这个函数。
这个函数最后一个参数实际上就是选择用哪个调试寄存器(DR0-3)做断点
setBits可以设置Dr7的一些位数控制Dr0-3的属性。
比如这里是将DR0-DR3的触发条件为执行,并且将DRx的触发设置为当前模式,也就是仅对当前线程有效。
enableBreakpoint(threadCtx, g_amsiScanBufferPtr, 0);
enableBreakpoint传递的参数是amsiScanBuffer的地址,当前线程和DR0,那么当执行amsiScanBuffer函数时,就会触发硬件断点。产生一个EXCEPTION_SINGLE_STEP异常。
我们注册的veh函数将会第一个进行处理
首先确定了是该异常,并且该异常产生的地址为amsiScanBuffer的地址。
returnAddress首先是拿到了amsiScanBuffer执行结束后的返回地址,也就是触发断点时刻的rsp。
scanResult是对amsiScanBuffer的第六个参数进行赋值。
Syntax
通过x64调用约定来看result此时应该在栈中的rsp+0x30的位置,他这里getArg的index参数传的5,是一样的
然后将此值清0。
然后将rip直接指回了返回地址,此时若是继续执行相当于直接跳过了amsiScanBuffer,但是这里堆栈肯定是不平衡的,由于是call进去的,所以这里相当于要pop一下,堆栈下移,rsp+8,这时adjustStackPointer函数做的事情。
堆栈也平衡了,将rax(返回值)也改为0
总结
此方式通过进程执行amsiScanBuffer的时候,动态更改amsiScanBuffer的返回结果和第六个参数来控制返回结果。