windows内核研究(系统调用 1)
WindowsAPI函数的调用过程
什么是WindowsApi?
Windows API(Application Programming Interface,应用程序编程接口)是微软为Windows操作系统提供的一套系统级编程接口,允许开发者与操作系统内核、硬件、系统服务等进行交互。它是开发Windows应用程序的基础,几乎所有Windows软件(包括系统工具、驱动程序、桌面应用等)都直接或间接依赖于Windows API
WindowsApi主要存放在C:\Windows\system32下的所有.dll文件
几个重要的DLl:
- Kernel32.dll:最核心的功能模块,比如管理内存,进程和线程相关的函数等
- User32.dll:是Windows用户界面相关应用程序接口,如创建窗口和发送消息等。
- GDI32.dll:图形设备接口,包含用于画图和显示
- NTdll.dll:大多数的Api都会通过这个dll进入内核(0环)
分析ReadProcessMemory函数
使用IDA Pro打开Kernel32.dll
,这个DLL中就有ReadProcessMemory
在windows7系统之后 ,微软把很多内部函数的实现细节放到了KernelBase.dll这个DLL当中,Kernel32.dll在后面的系统为了兼容任然存在,这里我们直接分析
KernelBase.dll
x86
这里就是它32位的ReadProcessMemory的代码
.text:10152910 ; BOOL __stdcall ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead)
.text:10152910 public _ReadProcessMemory@20
.text:10152910 _ReadProcessMemory@20 proc near ; CODE XREF: EnumProcessModulesInternal(x,x,x,x,x)+55↑p
.text:10152910 ; EnumProcessModulesInternal(x,x,x,x,x)+74↑p ...
.text:10152910
.text:10152910 NumberOfBytesRead= dword ptr -4
.text:10152910 hProcess = dword ptr 8
.text:10152910 lpBaseAddress = dword ptr 0Ch
.text:10152910 lpBuffer = dword ptr 10h
.text:10152910 nSize = dword ptr 14h
.text:10152910 lpNumberOfBytesRead= dword ptr 18h
.text:10152910
.text:10152910 ; FUNCTION CHUNK AT .text:101E1E90 SIZE 0000000E BYTES
.text:10152910
.text:10152910 mov edi, edi ; 热补丁占用
.text:10152912 push ebp ; ebp入栈
.text:10152913 mov ebp, esp ; 提升堆栈
.text:10152915 push ecx ; 提升栈,相当于sub esp,4
.text:10152916 lea eax, [ebp+NumberOfBytesRead] ; 获取ebp-4位置的地址给eax
.text:10152919 push eax ; NumberOfBytesRead 参数5: 实际读取字节数的指针
.text:1015291A push [ebp+nSize] ; NumberOfBytesToRead 参数4: 要读取的字节数
.text:1015291D push [ebp+lpBuffer] ; Buffer 参数3: 目标缓冲区
.text:10152920 push [ebp+lpBaseAddress] ; BaseAddress 参数2: 源内存地址
.text:10152923 push [ebp+hProcess] ; ProcessHandle 参数1: 进程句柄
.text:10152926 call ds:__imp__NtReadVirtualMemory@20 ; 调用内核函数
.text:1015292C mov edx, [ebp+lpNumberOfBytesRead] ; 取lpNumberOfBytesRead指针
.text:1015292F test edx, edx ; 判断参数lpNumberOfBytesRead是否为空
.text:10152931 jnz short loc_10152946 ; 不等于0则跳转
.text:10152933
.text:10152933 loc_10152933: ; CODE XREF: ReadProcessMemory(x,x,x,x,x)+3B↓j
.text:10152933 test eax, eax ; 判断是否成功执行函数
.text:10152935 js loc_101E1E90 ; 为负数(失败)则跳转
.text:1015293B mov eax, 1 ; 成功返回1
.text:10152940
.text:10152940 loc_10152940: ; CODE XREF: ReadProcessMemory(x,x,x,x,x)+8F589↓j
.text:10152940 mov esp, ebp ; 还原栈底
.text:10152942 pop ebp ; 恢复ebp
.text:10152943 retn 14h ; 栈内平衡
.text:10152946 ; ---------------------------------------------------------------------------
.text:10152946
.text:10152946 loc_10152946: ; CODE XREF: ReadProcessMemory(x,x,x,x,x)+21↑j
.text:10152946 mov ecx, [ebp+NumberOfBytesRead] ; 从局部变量加载实际读取的字节数
.text:10152949 mov [edx], ecx ; 写入lpNumberOfBytesRead
.text:1015294B jmp short loc_10152933 ; 返回
.text:1015294B _ReadProcessMemory@20 endp
x64
这里就是它64位的ReadProcessMemory的代码
.text:00000001800BBF70 ; 由于x64的调用约定,前4个参数是由rcx,rdx,r8,r9来传递的
.text:00000001800BBF70
.text:00000001800BBF70 ; BOOL __stdcall ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead)
.text:00000001800BBF70 public ReadProcessMemory
.text:00000001800BBF70 ReadProcessMemory proc near ; CODE XREF: WerpReadPeb32FromProcess+EA↓p
.text:00000001800BBF70 ; tip2::details::recover_vector<tip2::details::TestData *>(void * const,void const *,tip2::vector_nothrow<tip2::details::TestData *> &)+3D↓p ...
.text:00000001800BBF70
.text:00000001800BBF70 NumberOfBytesRead= qword ptr -28h
.text:00000001800BBF70 var_18 = qword ptr -18h
.text:00000001800BBF70 lpNumberOfBytesRead= qword ptr 28h
.text:00000001800BBF70
.text:00000001800BBF70 sub rsp, 48h ; 分配48h(72)字节的栈空间
.text:00000001800BBF74 lea rax, [rsp+48h+var_18] ; 获取局部变量var_18的地址
.text:00000001800BBF79 mov [rsp+48h+var_18], 0 ; 初始化var_18为0
.text:00000001800BBF82 mov [rsp+48h+NumberOfBytesRead], rax ; NumberOfBytesRead
.text:00000001800BBF87 call cs:__imp_NtReadVirtualMemory ; 调用内核函数
.text:00000001800BBF8E nop dword ptr [rax+rax+00h] ; 什么都不做(字节对齐)
.text:00000001800BBF93 mov rcx, [rsp+48h+lpNumberOfBytesRead] ; 获取lpNumberOfBytesRead地址值给rcx
.text:00000001800BBF98 test rcx, rcx ; 是否为null
.text:00000001800BBF9B jz short loc_1800BBFA5 ; 为null则跳过
.text:00000001800BBF9D mov rdx, [rsp+48h+var_18] ; 获取实际读取的字节数
.text:00000001800BBFA2 mov [rcx], rdx ; 保存到lpNumberOfBytesRead指针
.text:00000001800BBFA5
.text:00000001800BBFA5 loc_1800BBFA5: ; CODE XREF: ReadProcessMemory+2B↑j
.text:00000001800BBFA5 test eax, eax ; 校验值
.text:00000001800BBFA7 js short loc_1800BBFB4 ; 为负数则跳转
.text:00000001800BBFA9 mov eax, 1 ; 设置eax为1
.text:00000001800BBFAE add rsp, 48h ; 堆栈平衡
.text:00000001800BBFB2 retn ; 返回
.text:00000001800BBFB2 ; ---------------------------------------------------------------------------
.text:00000001800BBFB3 align 4
.text:00000001800BBFB4
.text:00000001800BBFB4 loc_1800BBFB4: ; CODE XREF: ReadProcessMemory+37↑j
.text:00000001800BBFB4 mov ecx, eax
.text:00000001800BBFB6 call BaseSetLastNTError ; 调用方法
.text:00000001800BBFBB xor eax, eax ; eax清0
.text:00000001800BBFBD add rsp, 48h ; 堆栈平衡
.text:00000001800BBFC1 retn ; 返回
函数真正的实现是在NtReadVirtualMemory
中
分析NtReadVirtualMemory
查看导出表,可以看到是由ntdll来提供的
打开ntdll,打到这个方法
真正的实现是在0环实现的,这里只是提供了一个接口给应用程序