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環實現的,這里只是提供了一個接口給應用程序