分析WriteProcessMemory调用过程
首先咱们先查看kernel32.dll这个库,并找到WriteProcessMemory
.text:7C802213 WriteProcessMemory proc near ; DATA XREF: .text:off_7C802654↓o .text:7C802213 .text:7C802213 BaseAddress = dword ptr -8 .text:7C802213 NumberOfBytesToProtect= dword ptr -4 .text:7C802213 ProcessHandle = dword ptr 8 .text:7C802213 lpBaseAddress = dword ptr 0Ch .text:7C802213 Buffer = dword ptr 10h .text:7C802213 OldAccessProtection= dword ptr 14h .text:7C802213 lpNumberOfBytesWritten= dword ptr 18h .text:7C802213 .text:7C802213 mov edi, edi .text:7C802215 push ebp .text:7C802216 mov ebp, esp .text:7C802218 push ecx .text:7C802219 push ecx .text:7C80221A mov eax, [ebp+lpBaseAddress] .text:7C80221D push ebx .text:7C80221E mov ebx, [ebp+OldAccessProtection] .text:7C802221 push esi .text:7C802222 mov esi, ds:NtProtectVirtualMemory .text:7C802228 push edi .text:7C802229 mov edi, [ebp+ProcessHandle] .text:7C80222C mov [ebp+BaseAddress], eax .text:7C80222F lea eax, [ebp+OldAccessProtection] .text:7C802232 push eax ; OldAccessProtection .text:7C802233 push 40h ; NewAccessProtection .text:7C802235 lea eax, [ebp+NumberOfBytesToProtect] .text:7C802238 push eax ; NumberOfBytesToProtect .text:7C802239 lea eax, [ebp+BaseAddress] .text:7C80223C push eax ; BaseAddress .text:7C80223D push edi ; ProcessHandle .text:7C80223E mov [ebp+NumberOfBytesToProtect], ebx .text:7C802241 call esi ; NtProtectVirtualMemory .text:7C802243 cmp eax, 0C000004Eh .text:7C802248 jz short loc_7C8022A6 .text:7C80224A .text:7C80224A loc_7C80224A: ; CODE XREF: WriteProcessMemory+A4↓j .text:7C80224A test eax, eax .text:7C80224C jl short loc_7C80229B .text:7C80224E mov eax, [ebp+OldAccessProtection] .text:7C802251 test al, 0CCh .text:7C802253 jz short loc_7C8022B9 .text:7C802255 lea ecx, [ebp+OldAccessProtection] .text:7C802258 push ecx ; OldAccessProtection .text:7C802259 push eax ; NewAccessProtection .text:7C80225A lea eax, [ebp+NumberOfBytesToProtect] .text:7C80225D push eax ; NumberOfBytesToProtect .text:7C80225E lea eax, [ebp+BaseAddress] .text:7C802261 push eax ; BaseAddress .text:7C802262 push edi ; ProcessHandle .text:7C802263 call esi ; NtProtectVirtualMemory .text:7C802265 lea eax, [ebp+ProcessHandle] .text:7C802268 push eax ; NumberOfBytesWritten .text:7C802269 push ebx ; NumberOfBytesToWrite .text:7C80226A push [ebp+Buffer] ; Buffer .text:7C80226D push [ebp+lpBaseAddress] ; BaseAddress .text:7C802270 push edi ; ProcessHandle .text:7C802271 call ds:NtWriteVirtualMemory .text:7C802277 mov ecx, [ebp+lpNumberOfBytesWritten] .text:7C80227A test ecx, ecx .text:7C80227C jnz loc_7C802320 .text:7C802282 .text:7C802282 loc_7C802282: ; CODE XREF: WriteProcessMemory+112↓j .text:7C802282 test eax, eax .text:7C802284 jl short loc_7C80229B .text:7C802286 .text:7C802286 loc_7C802286: ; CODE XREF: WriteProcessMemory+E1↓j .text:7C802286 push ebx ; NumberOfBytesToFlush .text:7C802287 push [ebp+lpBaseAddress] ; BaseAddress .text:7C80228A push edi ; ProcessHandle .text:7C80228B call ds:NtFlushInstructionCache .text:7C802291 xor eax, eax .text:7C802293 inc eax .text:7C802294 .text:7C802294 loc_7C802294: ; CODE XREF: WriteProcessMemory+108↓j .text:7C802294 ; WriteProcessMemory+119↓j .text:7C802294 pop edi .text:7C802295 pop esi .text:7C802296 pop ebx .text:7C802297 leave .text:7C802298 retn 14h .text:7C80229B ; --------------------------------------------------------------------------- .text:7C80229B .text:7C80229B loc_7C80229B: ; CODE XREF: WriteProcessMemory+39↑j .text:7C80229B ; WriteProcessMemory+71↑j .text:7C80229B push eax ; Status .text:7C80229C call sub_7C8093FD .text:7C8022A1 jmp loc_7C80232A .text:7C8022A6 ; --------------------------------------------------------------------------- .text:7C8022A6 .text:7C8022A6 loc_7C8022A6: ; CODE XREF: WriteProcessMemory+35↑j .text:7C8022A6 lea eax, [ebp+OldAccessProtection] .text:7C8022A9 push eax ; OldAccessProtection .text:7C8022AA push 4 ; NewAccessProtection .text:7C8022AC lea eax, [ebp+NumberOfBytesToProtect] .text:7C8022AF push eax ; NumberOfBytesToProtect .text:7C8022B0 lea eax, [ebp+BaseAddress] .text:7C8022B3 push eax ; BaseAddress .text:7C8022B4 push edi ; ProcessHandle .text:7C8022B5 call esi ; NtProtectVirtualMemory .text:7C8022B7 jmp short loc_7C80224A .text:7C8022B9 ; --------------------------------------------------------------------------- .text:7C8022B9 .text:7C8022B9 loc_7C8022B9: ; CODE XREF: WriteProcessMemory+40↑j .text:7C8022B9 test al, 3 .text:7C8022BB jnz short loc_7C8022FD .text:7C8022BD lea eax, [ebp+ProcessHandle] .text:7C8022C0 push eax ; NumberOfBytesWritten .text:7C8022C1 push ebx ; NumberOfBytesToWrite .text:7C8022C2 push [ebp+Buffer] ; Buffer .text:7C8022C5 push [ebp+lpBaseAddress] ; BaseAddress .text:7C8022C8 push edi ; ProcessHandle .text:7C8022C9 call ds:NtWriteVirtualMemory .text:7C8022CF mov [ebp+Buffer], eax .text:7C8022D2 mov eax, [ebp+lpNumberOfBytesWritten] .text:7C8022D5 test eax, eax .text:7C8022D7 jz short loc_7C8022DE .text:7C8022D9 mov ecx, [ebp+ProcessHandle] .text:7C8022DC mov [eax], ecx .text:7C8022DE .text:7C8022DE loc_7C8022DE: ; CODE XREF: WriteProcessMemory+C4↑j .text:7C8022DE lea eax, [ebp+OldAccessProtection] .text:7C8022E1 push eax ; OldAccessProtection .text:7C8022E2 push [ebp+OldAccessProtection] ; NewAccessProtection .text:7C8022E5 lea eax, [ebp+NumberOfBytesToProtect] .text:7C8022E8 push eax ; NumberOfBytesToProtect .text:7C8022E9 lea eax, [ebp+BaseAddress] .text:7C8022EC push eax ; BaseAddress .text:7C8022ED push edi ; ProcessHandle .text:7C8022EE call esi ; NtProtectVirtualMemory .text:7C8022F0 cmp [ebp+Buffer], 0 .text:7C8022F4 jge short loc_7C802286 .text:7C8022F6 mov esi, 0C0000005h .text:7C8022FB jmp short loc_7C80230F .text:7C8022FD ; --------------------------------------------------------------------------- .text:7C8022FD .text:7C8022FD loc_7C8022FD: ; CODE XREF: WriteProcessMemory+A8↑j .text:7C8022FD lea ecx, [ebp+OldAccessProtection] .text:7C802300 push ecx ; OldAccessProtection .text:7C802301 push eax ; NewAccessProtection .text:7C802302 lea eax, [ebp+NumberOfBytesToProtect] .text:7C802305 push eax ; NumberOfBytesToProtect .text:7C802306 lea eax, [ebp+BaseAddress] .text:7C802309 push eax ; BaseAddress .text:7C80230A push edi ; ProcessHandle .text:7C80230B call esi ; NtProtectVirtualMemory .text:7C80230D xor esi, esi .text:7C80230F .text:7C80230F loc_7C80230F: ; CODE XREF: WriteProcessMemory+E8↑j .text:7C80230F push 0C0000005h ; Status .text:7C802314 call sub_7C8093FD .text:7C802319 mov eax, esi .text:7C80231B jmp loc_7C802294 .text:7C802320 ; --------------------------------------------------------------------------- .text:7C802320 .text:7C802320 loc_7C802320: ; CODE XREF: WriteProcessMemory+69↑j .text:7C802320 mov edx, [ebp+ProcessHandle] .text:7C802323 mov [ecx], edx .text:7C802325 jmp loc_7C802282 .text:7C80232A ; --------------------------------------------------------------------------- .text:7C80232A .text:7C80232A loc_7C80232A: ; CODE XREF: WriteProcessMemory+8E↑j .text:7C80232A xor eax, eax .text:7C80232C jmp loc_7C802294 .text:7C80232C WriteProcessMemory endp
发现该函数调用过程会先检查该虚拟地址的状态,然后在调用NTWriteProcessMemory这个函数,当我们在查看导入表的时候,会发现这个函数在ntdll.dll里面,所以我们查看ntdll.dll里NTWriteProcessMemory这个函数
ZwWriteVirtualMemory proc near mov eax, 115h ; NtWriteVirtualMemory mov edx, 7FFE0300h call dword ptr [edx] retn 14h ZwWriteVirtualMemory endp
发现他调用了7ffe0300这个位置的函数,这里其实是一个结构体_KUSER_SHARED_DATA
_KUSER_SHARED_DATA
先来看看成员
0: kd> dt _KUSER_SHARED_DATA ntdll!_KUSER_SHARED_DATA +0x000 TickCountLow : Uint4B +0x004 TickCountMultiplier : Uint4B +0x008 InterruptTime : _KSYSTEM_TIME +0x014 SystemTime : _KSYSTEM_TIME +0x020 TimeZoneBias : _KSYSTEM_TIME +0x02c ImageNumberLow : Uint2B +0x02e ImageNumberHigh : Uint2B +0x030 NtSystemRoot : [260] Uint2B +0x238 MaxStackTraceDepth : Uint4B +0x23c CryptoExponent : Uint4B +0x240 TimeZoneId : Uint4B +0x244 Reserved2 : [8] Uint4B +0x264 NtProductType : _NT_PRODUCT_TYPE +0x268 ProductTypeIsValid : UChar +0x26c NtMajorVersion : Uint4B +0x270 NtMinorVersion : Uint4B +0x274 ProcessorFeatures : [64] UChar +0x2b4 Reserved1 : Uint4B +0x2b8 Reserved3 : Uint4B +0x2bc TimeSlip : Uint4B +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE +0x2c8 SystemExpirationDate : _LARGE_INTEGER +0x2d0 SuiteMask : Uint4B +0x2d4 KdDebuggerEnabled : UChar +0x2d5 NXSupportPolicy : UChar +0x2d8 ActiveConsoleId : Uint4B +0x2dc DismountCount : Uint4B +0x2e0 ComPlusPackage : Uint4B +0x2e4 LastSystemRITEventTickCount : Uint4B +0x2e8 NumberOfPhysicalPages : Uint4B +0x2ec SafeBootMode : UChar +0x2f0 TraceLogging : Uint4B +0x2f8 TestRetInstruction : Uint8B +0x300 SystemCall : Uint4B +0x304 SystemCallReturn : Uint4B +0x308 SystemCallPad : [3] Uint8B +0x320 TickCount : _KSYSTEM_TIME +0x320 TickCountQuad : Uint8B +0x330 Cookie : Uint4B
在偏移0x300处有syscall这个成员,通过windbg查看一下,发现了ntdll!KiFastSystemCall函数
通过调用了sysenter指令,从而进入了0环
这里还有个小细节,是否支持快调用
快调用
快调用是什么呢?在CPU中还有一个寄存器叫做MSR寄存器,这个寄存器里面存储了在提权时需要改变寄存器的值,查看msr寄存器 rdmsr xx(偏移)
既然有快调用,肯定会有比他慢的调用,之前我们学习中断门的时候,都是通过中断进入0环的,那么我们也可以通过中断进入
中断调用
当机器不知道快调用的时候,则会使用ntdll.dll!KiIntSystemCall()函数进入0环,来看看他的内容
通过调用中断2e来中断,我们来看看中断会运行去哪里
804dee00`0008eff0
众所周知,中断提权填入各个段寄存器的是用TSS任务门的值填入的,而快调用则是用MSR调用的,一个是寄存器一个是内存,寄存器肯定是比内存块的
接下来,我们来试试自己写一个简单的WriteProcessMemory函数,这里参考了hambaga师傅的博客,因为我一开始写的真的很垃圾,看完他的后,才发现自己的完全错误了
分析WriteProcessMemory
#include<stdio.h> #include<Windows.h> BOOL OverWriteProcessMemory_Fast(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesWritten) { LONG NtStatus; __asm { push lpNumberOfBytesWritten//压参数 push nSize push lpBuffer push lpBaseAddress push hProcess sub esp,4 //提升栈 mov eax,0x115 mov edx,0x7ffe0300 push NtWriteProcessMemoryRet mov edx,esp _emit 0x0f _emit 0x34 mov NtStatus,eax NtWriteProcessMemoryRet: add esp,0x18 } if(lpNumberOfBytesWritten!=NULL) { *lpNumberOfBytesWritten=nSize; } if(NtStatus<0) { return FALSE; } return TRUE; } BOOL OverWriteProcessMemory_Int(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesWritten) { LONG NtStatus; __asm { push lpNumberOfBytesWritten//压参数 push nSize push lpBuffer push lpBaseAddress push hProcess sub esp,4 //提升栈 mov eax,0x115 mov edx,0x7ffe0300 push NtWriteProcessMemoryRet lea edx,[esp+8] int 0x2E NtWriteProcessMemoryRet: add esp,0x18 mov NtStatus,eax } if(lpNumberOfBytesWritten!=NULL) { *lpNumberOfBytesWritten=nSize; } if(NtStatus<0) { return FALSE; } return TRUE; } BOOL WINAPI ReadProcessMemory_FAST(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead) { LONG NtStatus; __asm { // 模拟 ReadProcessMemory lea eax,nSize; push eax; push nSize; push lpBuffer; push lpBaseAddress; push hProcess; sub esp, 0x04; // 模拟 ReadProcessMemory 里的 CALL NtReadVirtualMemory // 模拟 NtReadVirtualMemory mov eax, 0xBA; push NtReadVirtualMemoryReturn; // 模拟 NtReadVirtualMemory 函数里的 CALL [0x7FFE0300] // 模拟 KiFastSystemCall mov edx, esp; _emit 0x0F; // sysenter _emit 0x34; NtReadVirtualMemoryReturn: add esp, 0x18; // 模拟 NtReadVirtualMemory 返回到 ReadProcessMemory 时的 RETN 0x14 mov NtStatus, eax; } if (lpNumberOfBytesRead != NULL) { *lpNumberOfBytesRead = nSize; } // 错误检查 if (NtStatus < 0) { return FALSE; } return TRUE; } BOOL EnableDebugPrivilege() { HANDLE hToken; BOOL fOk=FALSE; if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken)) { TOKEN_PRIVILEGES tp; tp.PrivilegeCount=1; LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid); tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL); fOk=(GetLastError()==ERROR_SUCCESS); CloseHandle(hToken); } return fOk; } int main() { EnableDebugPrivilege(); DWORD pid,addr,dwRead,dwWrite; PVOID buffer[256]; scanf("%x%x",&pid,&addr); getchar(); printf("start:"); ReadProcessMemory_FAST(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)addr,buffer,4,&dwRead); printf(" Writeing "); OverWriteProcessMemory_Fast(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPVOID)addr,"ppp ",4,&dwWrite); printf("end:"); ReadProcessMemory_FAST(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)addr,buffer,4,&dwRead); printf("%s",(char*)buffer); getchar(); }