免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
本次游戏没法给
内容参考于:微尘网络安全
上一个内容:12.Windows驱动-R3到R0的系统调用
上一个内容里通过ReadProcessMemory和OpenProcess两个函数查看了它们进入内核的过程,还有一个东西就只我们通过GetLastError可以得到一个错误号,如下图框框它有两个ret,函数运行成功是通过下图红框的ret进行返回的,如果函数运行失败会执行下图蓝框的代码然后ret返回

下图红框的代码是用来执行注册GetLastError的错误码的

如下图红框,它执行了两个函数RtlNtStatusToDosError和RtlRestoreLastWin32Error

说明:
RtlNtStatusToDosError用于把内核的错误翻译成用户态的 Win32 错误码
RtlRestoreLastWin32Error用来保存错误码,方便后续我们调用GetLastError函数得到错误码进行排错
然后分析下图红框的代码,我们调用的 OpenProcess(123, TRUE, 555);

代码分析:如果下方的代码说明看不明白,那就复制给ai,让ai一行一行的更细致的解释
; ======================================
; kernelbase!OpenProcess 函数入口
; 入参寄存器分布(x64调用约定):
; RCX = 第一个参数 123(dwDesiredAccess)
; RDX = 第二个参数 TRUE(1,bInheritHandle)
; R8 = 第三个参数 555(dwProcessId)
; ======================================
00007FF881FAF310 <kernelbase.OpenProcess> | 4C 8B DC | mov r11,rsp ; 【栈基准】保存原始栈顶到r11,后续用r11定位栈上的参数存储位置(避免rsp变动影响寻址)
00007FF881FAF313 | 48 83 EC 68 | sub rsp,68 ; 【栈空间】分配0x68字节栈空间,用于存放123/TRUE/555的转换后数据、临时值
00007FF881FAF317 | 45 33 C9 | xor r9d,r9d ; 【临时值初始化】r9d清零(后续用于给栈上的临时位置赋0,辅助参数转换)
00007FF881FAF31A | 49 63 C0 | movsxd rax,r8d ; 【处理555】把r8d中的555(32位)扩展为64位存入rax(555从32位转64位,适配x64系统)
00007FF881FAF31D | 45 89 4B E4 | mov dword ptr ds:[r11-1C],r9d ; 【辅助初始化】把0写入r11-0x1C(栈位置),为后续TRUE的转换预留干净的内存
00007FF881FAF321 | 4D 8D 43 C8 | lea r8,qword ptr ds:[r11-38] ; 【处理TRUE】计算r11-0x38的栈地址存入r8(后续将TRUE转换后的标志位存入该地址,作为NtOpenProcess的参数地址)
00007FF881FAF325 | 4D 89 4B D0 | mov qword ptr ds:[r11-30],r9 ; 【辅助初始化】把0写入r11-0x30(栈位置),为555的存储预留干净的内存
00007FF881FAF329 | F7 DA | neg edx ; 【处理TRUE】对edx中的TRUE(1)取反,得到edx=-1(0xFFFFFFFF),这是转换TRUE为内核标志位的第一步
00007FF881FAF32B | 49 89 43 B8 | mov qword ptr ds:[r11-48],rax ; 【存储555】把rax中64位的555写入r11-0x48(栈位置),保存555的转换后数据
00007FF881FAF32F | 0F 57 C0 | xorps xmm0,xmm0 ; 【辅助初始化】xmm0寄存器清零(后续用于清空栈上的连续内存,保证参数数据干净)
00007FF881FAF332 | 49 C7 43 C8 3 | mov qword ptr ds:[r11-38],30 ; 【处理TRUE】把0x30写入r11-38(栈位置,即上面r8指向的地址),完成TRUE转换前的内存初始化
00007FF881FAF33A | 1B C0 | sbb eax,eax ; 【处理TRUE】利用借位标志计算,将eax置为-1(0xFFFFFFFF),这是转换TRUE为内核标志位的第二步
00007FF881FAF33C | 83 E0 02 | and eax,2 ; 【处理TRUE】eax(-1)与2做与运算,得到eax=2(TRUE最终转换为标志位0x2,这是NtOpenProcess需要的格式)
00007FF881FAF33F | 4D 89 4B 20 | mov qword ptr ds:[r11+20],r9 ; 【辅助初始化】把0写入r11+0x20(栈位置),为123的传递预留干净的内存
00007FF881FAF343 | 89 44 24 48 | mov dword ptr ss:[rsp+48],eax ; 【存储TRUE转换结果】把eax中的标志位2写入rsp+0x48(栈位置),保存TRUE的最终转换值
00007FF881FAF347 | 8B D1 | mov edx,ecx ; 【处理123】把ecx中的123存入edx(123从RCX转移到RDX,作为NtOpenProcess的参数值)
00007FF881FAF349 | 4D 89 4B D8 | mov qword ptr ds:[r11-28],r9 ; 【辅助初始化】把0写入r11-0x28(栈位置),辅助123的参数传递
00007FF881FAF34D | 49 8D 4B 20 | lea rcx,qword ptr ds:[r11+20] ; 【辅助参数】计算r11+0x20的栈地址存入rcx(作为NtOpenProcess的第一个参数地址,接收内核返回的句柄)
00007FF881FAF351 | 4D 89 4B C0 | mov qword ptr ds:[r11-40],r9 ; 【辅助初始化】把0写入r11-0x40(栈位置),辅助555的参数传递
00007FF881FAF355 | 4D 8D 4B B8 | lea r9,qword ptr ds:[r11-48] ; 【处理555】计算r11-0x48的栈地址(即存储555的位置)存入r9(555从数值变为地址,作为NtOpenProcess的第四个参数地址)
00007FF881FAF359 | F3 0F 7F 44 2 | movdqu xmmword ptr ss:[rsp+50],xmm0 ; 【辅助初始化】把xmm0(0)写入rsp+0x50(栈位置,16字节),清空内存避免脏数据影响参数
00007FF881FAF35F | 48 FF 15 72 1 | call qword ptr ds:[<&NtOpenProcess>] ; 【参数传递完成】调用NtOpenProcess,此时:
; RCX = r11+20(句柄地址)、RDX=123(权限值)、R8=r11-38(TRUE转换后的标志位地址)、R9=r11-48(555的地址)
; 123、TRUE、555已全部转换为NtOpenProcess需要的格式(数值/地址)
00007FF881FAF366 | 0F 1F 44 00 0 | nop dword ptr ds:[rax+rax] ; 【指令对齐】空指令,不影响参数,仅为CPU执行效率
00007FF881FAF36B | 85 C0 | test eax,eax ; 【结果判断】检查NtOpenProcess的返回值(eax),判断参数转换后的调用是否成功
00007FF881FAF36D | 78 0E | js kernelbase.7FF881FAF37D ; 【分支跳转】若调用失败(eax为负数),跳转到错误处理
00007FF881FAF36F | 48 8B 84 24 8 | mov rax,qword ptr ss:[rsp+88] ; 【成功处理】取出内核返回的进程句柄,作为OpenProcess的返回值(参数转换后的最终结果)
00007FF881FAF377 | 48 83 C4 68 | add rsp,68 ; 【栈恢复】释放之前分配的0x68字节栈空间(参数转换用的临时空间)
00007FF881FAF37B | C3 | ret ; 【函数返回】回到调用处,返回句柄(参数转换完成后的结果)
00007FF881FAF37C | CC | int3 ; 【调试标记】断点指令,无参数处理逻辑
; ======================================
; 错误处理分支(参数转换后的调用失败时执行)
; ======================================
00007FF881FAF37D | 8B C8 | mov ecx,eax ; 【错误处理】把失败的返回值存入ecx,准备传递给错误处理函数
00007FF881FAF37F | E8 DC F2 FF F | call kernelbase.7FF881FAE660 ; 【错误处理】调用错误函数(处理参数转换后调用失败的情况)
00007FF881FAF384 | 33 C0 | xor eax,eax ; 【返回设置】eax清零,OpenProcess返回NULL(参数转换后的调用失败)
00007FF881FAF386 | EB EF | jmp kernelbase.7FF881FAF377 ; 【栈恢复】跳转到释放栈空间的指令,结束错误处理
上方前部分的代码是把 OpenProcess(123, TRUE, 555); 的三个参数转换成NtOpenProcess的4个参数,NtOpenProcess函数的第一个参数是一个内存地址,用来存放返回值句柄、第二个参数是权限也就是123,第三个参数是要不要继承句柄这里是TRUE,第四个参数是要打卡的进程id,这里是555,然后这些操作是在Kernelbase.dll里做的,做这个的原因是为了兼容,在早期的Windows系统(比如Windows xp、Windows 7)是没有Kernelbase.dll的它是直接kernel32.dll到ntdll.dll然后进入内核,所以为了兼容老版本的系统,在新版本的操作系统(比如Windows10、Windows11)做了一个Kernelbase.dll进行扩展

5300

被折叠的 条评论
为什么被折叠?



