[转](45)中断门和快速调用进0环详解,重写 WriteProcessMemory
一、本文大纲
- 系统调用的两种方式:中断门和快速调用
- _KUSER_SHARED_DATA 结构
- 使用 cpuid 指令判断当前CPU是否支持快速调用
- 3环进0环需要更改的4个寄存器
- 以 ReadProcessMemory 为例说明系统调用全过程
- 重写 ReadProcessMemory 和 WriteProcessMemory
- int 0x2e 和 sysenter 都做了什么工作?
二、中断门和快速调用
以我的理解,系统调用,即从调用操作系统提供的3环API开始,到进0环,再到返回结果到3环的全过程。
系统调用有中断调用和快速调用两种方式,中断调用是通过中断门进0环,此过程需要查IDT表和TSS表;
快速调用则是使用 sysenter 指令进0环,这种方式不需要查内存,而是直接从CPU的MSR寄存器中获取所需数据,所以称为快速调用。
三、_KUSER_SHARED_DATA 结构
此结构体由操作系统负责初始化,其偏移 0x300 处有一个 SystemCall 属性,是个函数指针。
nt!_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
操作系统启动时,通过CPUID指令,判断CPU是否支持快速调用,根据判断结果,在 +0x300 SystemCall 处填写不同的函数指针。
四、CPUID 指令
可以看到,在我的电脑中执行CPUID指令后,EDX(…BFF)的11位是1。
五、3环进0环需要更改的4个寄存器
CS的权限由3变为0 意味着需要新的CS
SS与CS的权限永远一致 需要新的SS
权限发生切换的时候,堆栈也一定会切换,需要新的ESP
进0环后代码的位置,需要EIP
这部分知识和以前学习的中断门和TSS是串起来的。如果不记得,请看我之前的博客。
中断门博客
TSS博客
简单复习一下,中断门进0环时,我们在IDT表里填的中断门描述符,包含了0环的CS和EIP,而SS和0环的ESP是在TSS里存储的,当时我们还有一个结论,windows里不使用任务,所以TSS的唯一作用就是提权时提供ESP0和SS0。
现在,我们知道了进0环需要更改的4个寄存器,接下来分析 KiFastSystemCall 和 KiIntSystemCall 时,只要明白一点,这两个函数做的事情就是更改这4个寄存器。
六、以 ReadProcessMemory 为例说明系统调用全过程
大家可以看 kernel32.dll 里 ReadProcessMemory 的反汇编,我这里抠出最关键的一条指令:
call ds:__imp__NtReadVirtualMemory@20 ; NtReadVirtualMemory(x,x,x,x,x)
ReadProcessMemory 啥也没干,只是调用了 ntdll.dll 的导出函数 NtReadVirtualMemory 函数。
_NtReadVirtualMemory@20 proc near
mov eax, 0BAh ; NtReadVirtualMemory
mov edx, 7FFE0300h
call dword ptr [edx]
retn 14h
_NtReadVirtualMemory@20 endp
_KiFastSystemCall@0 proc near
mov edx, esp
sysenter
_KiFastSystemCall@0 endp ;
把3环栈顶地址存储到edx中,然后调用sysenter指令,然后就进0环了。
假设,我的CPU不支持快速调用,那么 NtReadVirtualMemory 就会调用另一个函数 KiIntSystemCall
_KiIntSystemCall@0 proc near
arg_4= byte ptr 8
lea edx, [esp+arg_4] ; edx是第一个参数的指针,eax存的是系统调用号
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND; DS:SI -> counted CR-terminated command string
retn
_KiIntSystemCall@0 endp
这个和sysenter稍有不同,它把第一个参数(或者说最后一个压栈的参数)的指针存到edx中,然后触发2E中断进0环。
七、重写 ReadProcessMemory 和 WriteProcessMemory
注意,vs 内联汇编不支持 sysenter 指令,可以用 _emit 代替。
我的代码是在vs2010编译的,实测vc6编译 push NtWriteVirtualMemoryReturn 这条指令时会出错,你可以看一下vc6生成的是什么代码,挺坑的。
// 读写内存_中断门和快速调用实现.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include <Windows.h>// 读进程内存(中断门调用)
BOOL WINAPI HbgReadProcessMemory_INT(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead)
{LONG NtStatus;__asm{// 直接模拟 KiIntSystemCalllea edx,hProcess; // 要求 edx 存储最后入栈的参数mov eax, 0xBA;int 0x2E;mov NtStatus, eax;}if (lpNumberOfBytesRead != NULL){*lpNumberOfBytesRead = nSize; }// 错误检查if (NtStatus < 0){return FALSE;}return TRUE;
}// 读进程内存(快速调用)
BOOL WINAPI HbgReadProcessMemory_FAST(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead)
{LONG NtStatus;__asm{// 模拟 ReadProcessMemorylea eax,nSize;push eax;push nSize;push lpBuffer;push lpBaseAddress;push hProcess;sub esp, 0x04; // 模拟 ReadProcessMemory 里的 CALL NtReadVirtualMemory// 模拟 NtReadVirtualMemorymov eax, 0xBA;push NtReadVirtualMemoryReturn; // 模拟 NtReadVirtualMemory 函数里的 CALL [0x7FFE0300]// 模拟 KiFastSystemCallmov edx, esp;_emit 0x0F; // sysenter _emit 0x34;
NtReadVirtualMemoryReturn: add esp, 0x18; // 模拟 NtReadVirtualMemory 返回到 ReadProcessMemory 时的 RETN 0x14mov NtStatus, eax;}if (lpNumberOfBytesRead != NULL){*lpNumberOfBytesRead = nSize; }// 错误检查if (NtStatus < 0){return FALSE;}return TRUE;
}// 写进程内存(中断门调用)
BOOL WINAPI HbgWriteProcessMemory_INT(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{LONG NtStatus;__asm{lea edx,hProcess;mov eax, 0x115;int 0x2E;mov NtStatus, eax;}if (lpNumberOfBytesWritten != NULL){*lpNumberOfBytesWritten = nSize; }// 错误检查if (NtStatus < 0){return FALSE;}return TRUE;
}// 写进程内存(快速调用)
BOOL WINAPI HbgWriteProcessMemory_FAST(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{LONG NtStatus;__asm{// 模拟 WriteProcessMemorylea eax,nSize;push eax;push nSize;push lpBuffer;push lpBaseAddress;push hProcess;sub esp, 0x04; // 模拟 WriteProcessMemory 里的 CALL NtWriteVirtualMemory// 模拟 NtWriteVirtualMemorymov eax, 0x115;push NtWriteVirtualMemoryReturn; // 模拟 NtWriteVirtualMemory 函数里的 CALL [0x7FFE0300]// 模拟 KiFastSystemCallmov edx, esp;_emit 0x0F; // sysenter _emit 0x34;
NtWriteVirtualMemoryReturn: add esp, 0x18; // 模拟 NtWriteVirtualMemory 返回到 WriteProcessMemory 时的 RETN 0x14mov NtStatus, eax;}if (lpNumberOfBytesWritten != NULL){*lpNumberOfBytesWritten = nSize; }// 错误检查if (NtStatus < 0){return FALSE;}return TRUE;
}// 提权函数:提升为DEBUG权限
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 _tmain(int argc, _TCHAR* argv[])
{EnableDebugPrivilege();DWORD pid,addr,dwRead,dwWritten;char buff[20] = {0};printf("依次输入PID和要读的线性地址(均为16进制)...\n");scanf("%x %x", &pid, &addr);getchar();// 测试两个版本的 ReadProcessMemoryHbgReadProcessMemory_INT(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)addr,buff,4,&dwRead);printf("读取了%d个字节,内容是: \"%s\"\n", dwRead, buff);HbgReadProcessMemory_FAST(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)(addr+4),buff,4,&dwRead);printf("读取了%d个字节,内容是: \"%s\"\n", dwRead, buff);// 测试两个版本的 WriteProcessMemoryHbgWriteProcessMemory_INT(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)addr,"##",2,&dwWritten);printf("写入了%d字节.\n", dwWritten);HbgWriteProcessMemory_FAST(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)(addr+4),"**",2,&dwWritten);printf("写入了%d字节.\n", dwWritten);// 再次读取,验证写入是否成功HbgReadProcessMemory_INT(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)addr,buff,4,&dwRead);printf("读取了%d个字节,内容是: \"%s\"\n", dwRead, buff);HbgReadProcessMemory_FAST(OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid),(LPCVOID)(addr+4),buff,4,&dwRead);printf("读取了%d个字节,内容是: \"%s\"\n", dwRead, buff);printf("bye!\n");getchar();return 0;
}
八、int 0x2e 和 sysenter 都做了什么工作?
https://blog.csdn.net/Kwansy/article/details/109358719
[转](45)中断门和快速调用进0环详解,重写 WriteProcessMemory相关推荐
- (45)中断门和快速调用进0环详解,重写 WriteProcessMemory
一.本文大纲 系统调用的两种方式:中断门和快速调用 _KUSER_SHARED_DATA 结构 使用 cpuid 指令判断当前CPU是否支持快速调用 3环进0环需要更改的4个寄存器 以 ReadPro ...
- Windows系统调用学习笔记(二)—— 3环进0环
Windows系统调用学习笔记(二)-- 3环进0环 要点回顾 基本概念 _KUSER_SHARED_DATA 0x7FFE0300 实验:判断CPU是否支持快速调用 第一步:修改EAX=1 第二步: ...
- 2.API的调用过程(3环进0环)
_KUSER_SHARED_DATA /*这是一个结构体,翻译过来就是: Kernel与User分享的一块数据. 0环与3环共享的一块内存 */ 在User层和Kernel层分别定义了一个_KUSER ...
- 快速傅里叶变换(FFT)详解
快速傅里叶变换(FFT)详解 (这是我第一次写博,不喜勿喷...) 关于FFT已经听闻已久了,这次终于有机会在Function2的介绍下来了解一下FFT了. 快速傅里叶变换(Fast Fourier ...
- Go 语言快速开发入门(基础语法详解,第一节)
Go 语言快速开发入门(基础语法详解,第一节) 一.基础语法详解,第一节 1.HelloWorld 1.1.代码编写和解释 1.2.go语言注意事项 2.Go 语言的转义字符&&Go ...
- python人脸识别opencv_Python基于Opencv来快速实现人脸识别过程详解(完整版)
前言 随着人工智能的日益火热,计算机视觉领域发展迅速,尤其在人脸识别或物体检测方向更为广泛,今天就为大家带来最基础的人脸识别基础,从一个个函数开始走进这个奥妙的世界. 首先看一下本实验需要的数据集,为 ...
- mysql回滚用法_Mysql误操作后利用binlog2sql快速回滚的方法详解
前言 在日常工作或者学习中,操作数据库时候难免会因为"大意"而误操作,需要快速恢复的话通过备份来恢复是不太可能的,下面这篇文章主要给大家介绍关于Mysql误操作后利用binlog2 ...
- Thrift实现C#调用Java开发步骤详解
概述 Thrift实现C#调用Java开发步骤详解 详细 代码下载:http://www.demodashi.com/demo/10946.html Apache Thrift 是 Facebook ...
- python函数定义及调用-python函数声明和调用定义及原理详解
这篇文章主要介绍了python函数声明和调用定义及原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 函数是指代码片段,可以重复调用,比如我们前 ...
最新文章
- keras从入门到放弃(十二)卷积神经网络
- 可视化之为什么要使用箱线图?
- Java 使用GDAL 读写 shapefile
- 【MVC】bootstrap-paginator 分页
- LaTeX tikz初探——基本图形绘制(1)
- java request 原理_JavaWeb response和request对象原理及实例解析
- 用python实现自动签到脚本
- 虚拟软驱_文伟_新浪博客
- 华为手机鸿蒙系统官方下载入口,华为鸿蒙系统升级入口
- 傲腾readyboost_使用SD卡和ReadyBoost提升上网本速度
- 关于使用GHOST进行磁盘对拷整盘镜像以及镜像恢复的一些注意事项
- 智能城市dqn算法交通信号灯调度_滴滴张博:智慧交通大脑是支撑城市可持续发展的重要基础设施...
- F11一键还原精灵使用教程(详细图文步骤)
- ps解决导出图片自动裁剪导致的图像位置偏移的问题
- centos7扫描新硬盘_跟大家讲讲硬盘基础知识
- DIY强大的虚拟化环境-规划思路框架
- ipv6地址概述——带你了解ipv6与ipv4的不同
- 路漫漫其修远兮···VB 来15个数尝尝咸淡
- SSM毕设项目宠物医院预约管理系统wjz80(java+VUE+Mybatis+Maven+Mysql)
- 神技—终端显示Linux系统信息