在操作系统中分为用户态与内核态,而一些安全软件是通过对一些api进行挂钩进行行为监控的,而使用未挂钩函数或更底层api就成为一种免杀的方式。
api从用户态进入内核态的方式为(以CreateFile为例):
1、找到一些如Kernel32.dll,从dll导出表中查找CreateFile的地址。
2、找到ntdll.dll,找到NtCreateFile或ZwCreateFIle函数的调用号。
3、在x64下使用syscall分发,x86下为sysentry
4、若为Zw开头函数会通过SSDT表间接找到系统服务例程,而Nt开头函数是直接执行代码的。
在学习SysWhispers3前首先认识syscall,这也是学习地狱门的基础。
打上断点后,运行查看反汇编,查找addr
在知道了如何使用地狱门后,那么如何满足程序的通用性,而不是调用某个函数就改一次调用号呢?可以使用PE,通过查找ntdll.dll的导出表,通过对比需要的函数来查找服务号。
注意的是这三条链都是可以查的,并不是只能查询第一条,但对应的代码中查找的地址是要确定的
注意FullDllName是第几个出现了ntdll的,一般是第二个
6、找到ntdll后看0x30这个偏移上是ntdll的地址(注意32位与64位这个地址是不同的)
7、通过查询dll导出表,对比函数名称,就可以找到对应的服务号了
有了这些前置知识后,就可以学习SysWhispers3,一种对syscall的静态查杀绕过。相关demo,引用与https://xz.aliyun.com/t/11496
SW3_GetSyscallAddress为查找syscall基地址,并将结果保存在r15寄存器中并在最后jmp r15调用syscall,SW3_GetSyscallNumber为找到函数对应的服务号。
那么就知道这串hash对应的函数为NtAccessCheck
/
/
首先使用一段代码看系统是如何使用syscall的
int
main() {
FARPROC addr
=
GetProcAddress(LoadLibraryA(
"ntdll"
),
"NtCreateFile"
);
const char
*
a
=
"qwe"
;
}
/
/
首先使用一段代码看系统是如何使用syscall的
int
main() {
FARPROC addr
=
GetProcAddress(LoadLibraryA(
"ntdll"
),
"NtCreateFile"
);
const char
*
a
=
"qwe"
;
}
;关键汇编代码
mov r10,rcx
mov eax,
55h
;
55h
是NtCreateFile调用号
syscall
ret
;关键汇编代码
mov r10,rcx
mov eax,
55h
;
55h
是NtCreateFile调用号
syscall
ret
.data
SyscallIndex DWORD
000h
;用于保存传入的服务号,并且每次刷新用于保存下个服务号
.code
Gate proc; Gate函数用于查找函数对应服务号
mov SyscallIndex,
000h
mov SyscallIndex,ecx
ret
Gate endp
function proc
mov r10, rcx
mov eax,SyscallIndex
syscall
ret
function endp
end
.data
SyscallIndex DWORD
000h
;用于保存传入的服务号,并且每次刷新用于保存下个服务号
.code
Gate proc; Gate函数用于查找函数对应服务号
mov SyscallIndex,
000h
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)