64位下 3环64位进程进行系统调用分析
以下将以ReadProcessMemory为例进行分析
kernel32.dll
sub rsp, 38h;
mov rax, [rsp+38h+lpNumberOfBytesRead];
mov [rsp+38h+var_18], rax ; 这是将第五个参数(存放以读数据字节数的地址)保存到一个局部变量中
call <JMP.&ReadProcessMemory>;调用kernelbase.dll中的ReadProcessMemory
add rsp, 38h;
retn;
kernelbase.dll
sub rsp, 48h
lea rax, [rsp+48h+var_18]
mov [rsp+48h+var_28], rax
call cs:__imp_NtReadVirtualMemory 调用ntdll中的NtReadVirtualMemory
mov rdx, [rsp+48h+lpNumberOfBytesRead]
test rdx, rdx
jnz lpNumberOfBytesReadNotNull
ReadProcessMemoryEnd:
test eax, eax
js loc_7FF3889EE99
mov eax, 1
add rsp, 48h
retn
ntdll.dll
ZwReadVirtualMemory:
mov r10, rcx ; 此时r10 代表的是第一个参数的值 在这里即进程句柄
mov eax, 3Ch ;3ch 代表的是在内核中对应的SSDT表中对应的索引号,这里不详细展开对于SSDT的介绍
syscall ; 通过syscall便可以提权进入内核
retn
可能有些读者会很奇怪 明明调用的是NtReadVirtualMemory 这里确实ZwReadVirtualMemory,其实在3环下Zw和Nt代表的是同一个函数,而在0环下Zw会通过系统调用界面调用到Nt函数。
下面介绍syscall具体做了哪些工作
以上是Inter手册的介绍,我们可以看出在执行syscall会有以下几项操作
1:将rip的值赋予rcx
2:将MSR_LSTAR(0xC0000082)寄存器里的值 赋予rip(KiSystemCall64)
3:将eflag赋给r11
4:将MSR_FMASK(0xC0000082)寄存器的值赋给eflag
5:MSR_STAR(0xC0000081)寄存器值的[32:47]的值赋给cs 加8赋给ss
接下来就是进入0环后的代码