系统调用syscall绕过方式

350 阅读6分钟

声明:本内容只作为个人学习研究使用,请勿用作其他用途。

PEB

进程环境信息块,是一个从内核中分配给每个进程的用户模式结构,每一个进程都会有从ring0分配给该进程的进程环境块。

GS段寄存器指向当前的TEB结构,可以看到PEB在TEB的0x30偏移处

微软并没有定义PEB结构,因此需要我们自定义

typedef struct _PEB {
    BOOLEAN InheritedAddressSpace;
    BOOLEAN ReadImageFileExecOptions;
    BOOLEAN BeingDebugged;
    BOOLEAN Spare;
    HANDLE Mutant;
    PVOID ImageBase;
    PPEB_LDR_DATA LoaderData;
    PVOID ProcessParameters;
    PVOID SubSystemData;
    PVOID ProcessHeap;
    PVOID FastPebLock;
    PVOID FastPebLockRoutine;
    PVOID FastPebUnlockRoutine;
    ULONG EnvironmentUpdateCount;
    PVOID* KernelCallbackTable;
    PVOID EventLogSection;
    PVOID EventLog;
    PVOID FreeList;
    ULONG TlsExpansionCounter;
    PVOID TlsBitmap;
    ULONG TlsBitmapBits[0x2];
    PVOID ReadOnlySharedMemoryBase;
    PVOID ReadOnlySharedMemoryHeap;
    PVOID* ReadOnlyStaticServerData;
    PVOID AnsiCodePageData;
    PVOID OemCodePageData;
    PVOID UnicodeCaseTableData;
    ULONG NumberOfProcessors;
    ULONG NtGlobalFlag;
    BYTE Spare2[0x4];
    LARGE_INTEGER CriticalSectionTimeout;
    ULONG HeapSegmentReserve;
    ULONG HeapSegmentCommit;
    ULONG HeapDeCommitTotalFreeThreshold;
    ULONG HeapDeCommitFreeBlockThreshold;
    ULONG NumberOfHeaps;
    ULONG MaximumNumberOfHeaps;
    PVOID** ProcessHeaps;
    PVOID GdiSharedHandleTable;
    PVOID ProcessStarterHelper;
    PVOID GdiDCAttributeList;
    PVOID LoaderLock;
    ULONG OSMajorVersion;
    ULONG OSMinorVersion;
    ULONG OSBuildNumber;
    ULONG OSPlatformId;
    ULONG ImageSubSystem;
    ULONG ImageSubSystemMajorVersion;
    ULONG ImageSubSystemMinorVersion;
    ULONG GdiHandleBuffer[0x22];
    ULONG PostProcessInitRoutine;
    ULONG TlsExpansionBitmap;
    BYTE TlsExpansionBitmapBits[0x80];
    ULONG SessionId;
} PEB, * PPEB;

在PEB中的0x0c处为一指针,指向PEB_LDR_DATA结构,该结构体包含有关为进程加载的模块的信息(存储着该进程所有模块数据的链表)。

PEB_LDR_DATA的0x0c,0x14,0x1c中为三个双向链表LIST_ENTRY,在struct _LDR_MODULE的0x00,0x08和0x10处是三个对应的同名称的LIST_ENTRY, PEB_LDR_DATAstruct _LDR_MODULE就是通过这三个LIST_ENTRY对应连接起来的。

typedef struct _LDR_MODULE {
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID BaseAddress;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    SHORT LoadCount;
    SHORT TlsIndex;
    LIST_ENTRY HashTableEntry;
    ULONG TimeDateStamp;
} LDR_MODULE, * PLDR_MODULE;

三个双向链表分别代表模块加载顺序,模块在内存中的加载顺序以及模块初始化装载的顺序

每个双向链表都是指向进程装载的模块,结构中的每个指针,指向了一个LDR_DATA_TABLE_ENTRY的结构(如图)

这个结构很重要,提供了内存模块的基址和dll名称

image-20230512150236742.png

syscall

基础知识

x86 windows 使用 sysenter 实现系统调用

x64 windows 使用 syscall 实现系统调用

目前syscall已经成为了绕过AV/EDR所使用的主流方式,可以用它绕过一些敏感函数的调用监控(R3)。主流的AV/EDR都会对敏感函数进行HOOK,而syscall则可以用来绕过该类检测。

  • 使用syscall的步骤

    • 使用GetModuleHandle找到ntdll的基址
    • 解析DLL的导出表
    • 查找syscall number
    • 执行syscall

ntdll

在win10中,除了minimal和pico进程外,所有用户态的进程默认情况下都隐式链接到ntdll.dll

一般情况下,ntdll.dll在内存中的第二个模块,kernel32.dll是第三个模块。然而有些杀软会改变内存中的模块顺序列表,因此我们需要先确定指向的内存模块是ntdll.dll。如果访问了错误的模块,很显然不会加载任何功能。

  • ntCreateThread

image-20230512151418873.png

  • ntCreateProcess

image-20230512151438019.png

  • ntAllocateVirtualMemory

image-20230512151452026.png 差别就在传入 eax 寄存器的值不同,这里存储的是系统调用号,基于 eax 所存储的值的不同,syscall 进入内核调用的内核函数也不同。

EDR检测与绕过原理

在创建R3进程的时候,EDR会hook用户层的相关windows API调用,从而完成对进程动态行为的监控。比如,hook VirtualAlloc,监控内存分配。hook CreateProcess,监控进程创建。可以在用户层完成hook,也可以在内核层hook。用户层hook的好处是对性能的影响较小,相对于内核层hook更稳定,不容易导致系统蓝屏,所以很多EDR会选择在用户层hook,同时在内核层使用回调监控重要的内核api调用。一个进程分配了RWX属性的内存,或者修改了内存属性,将RW的内存修改为了RWX,由于RWX内存属性是shellcode或反射型DLL加载所用的内存属性,因此EDR会对申请的内存进行扫描,匹配到恶意软件的yara规则后,将会杀死恶意进程,并向控制中心发送告警。

为了避免在用户层被EDR hook的敏感函数检测到敏感行为,利用从ntdll中读取到的系统调用号进行系统直接调用来绕过敏感API函数的hook。主要来应对 EDR 对 Ring3 API 的 HOOK,不同版本的 Windows Ntxxx 函数的系统调用号不同,且调用时需要逆向各 API 的结构方便调用。

SysWhispers2

https://github.com/jthuraisamy/SysWhispers2

代码原理是从内存当中的ntdll中根据导出函数的地址顺序,确定系统调用号。

SW2_PopulateSyscallList函数其具体含义是先解析 Ntdll.dll 的 导出地址表 EAT,定位所有以 “Zw” 开头的函数,最后按地址从小到大进行排序。代码太长这里就不贴了,项目里有 另一个函数是 SW2_GetSyscallNumber,这个函数循环遍历 SW2_PopulateSyscallList 的数组,如果 Hash 相等就返回 循环的值 作为 SyscallNumber。

(遇到hook ntdll系统调用的EDR就不行了)

用python脚本生成需要的文件,通过包含头文件就可以syscall。使用时只需要把.h, .c, .std.asm三个文件拷贝过来,然后根据github上的教程做就行。

Hell's Gate地狱之门

https://github.com/am0nsec/HellsGate/

是另一种syscall动态调用方案,对内存中已经加载的ntdll.dll模块遍历导出表,根据函数哈希找到函数地址,将这个函数读取出来后动态获取对应的系统调用号。

这种方式的好处是可以准确的获取系统调用号,缺点是需要一个干净的内存ntdll模块,否则将无法完成syscall调用号的获取(遇到hook ntdll系统调用的EDR就不行了)。

Halo's Gate光环之门

不能直接用

是地狱之门的加强。光环之门可以应对native api被hook的情况

EDR不可能HOOK全部的Nt*函数,总有一些不敏感的函数没有被HOOK。根据syscall的特征字节码4C 8B D1 B8,在内存中原本native api在的位置向上向下每32个字节进行搜索。找到没有被HOOK的存根后获取其系统调用号再减去移动的步数,就是所要搜索的系统调用号。

https://github.com/boku7/AsmHalosGate

Tartarus’ Gate

这个项目的作者声称是对光环之门的加强,只是检测第一个字节和第四个字节是否是0xe9(jmp),来判断函数是否被hook。 新增的代码判断没有意义。

https://github.com/trickster0/TartarusGate

DripLoader

https://github.com/xuanxuan0/DripLoader

搜索内存中,找到内存块属性为free的内存

寻找合适的内存基址,cVmResv即shellode长度/内存块大小+1,即一共需要多少块内存。当确定的基址连续cVmResv块的内存都free,返回这个基址

ParallelSyscalls(unhook)

该项目的亮点是使用syscall从磁盘读取ntdll.dll,最后一步利用 LdrpThunkSignature 恢复系统调用。

https://github.com/mdsecactivebreach/ParallelSyscalls

unhook的几种技术

https://xz.aliyun.com/t/11532