一、本文大纲

  • 系统调用的两种方式:中断门和快速调用
  • _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 结构

7ffe0000

ffdf0000

此结构体由操作系统负责初始化,其偏移 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 处填写不同的函数指针。

当CPU支持快读调用,SystemCall 指向 ntdll.dll!KiFastSystemCall()
当CPU不支持快速调用,SystemCall 指向 ntdll.dll!KiIntSystemCall()

观察该结构体的名字,意思为“内核-用户共享内存”。
3环通过地址 0x7ffe0000 可以访问到这个结构体,3环PTE属性是只读;
0环通过地址 0xffdf0000 可以访问到这个结构体,0环PTE属性是可读写。

这两个线性地址映射的是同一个物理页。


四、CPUID 指令

通过CPUID指令查看当前CPU是否支持快速调用,方法是将EAX值设置为1,然后调用CPUID指令,指令执行结果存储在ECX和EDX中,其中EDX的SEP位(11位)表明CPU是否支持快速调用指令 sysenter / sysexit。

可以看到,在我的电脑中执行CPUID指令后,EDX(…BFF)的11位是1。


五、3环进0环需要更改的4个寄存器

  • CS的权限由3变为0 意味着需要新的CS

  • SS与CS的权限永远一致 需要新的SS

  • 权限发生切换的时候,堆栈也一定会切换,需要新的ESP

  • 进0环后代码的位置,需要EIP

简单复习一下,中断门进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 干了啥?

_NtReadVirtualMemory@20 proc near
mov     eax, 0BAh       ; NtReadVirtualMemory
mov     edx, 7FFE0300h
call    dword ptr [edx]
retn    14h
_NtReadVirtualMemory@20 endp

NtReadVirtualMemory 把系统调用号(服务号?)存到EAX,然后 call [7FFE0300h],实际上就是调用了 KiFastSystemCall 函数(因为我的CPU支持快速调用的,所以 7FFE0300h 存的是 KiFastSystemCall)

再看看 KiFastSystemCall 干了啥?

_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

通过上面的分析,我们已经了解了系统调用3环部分的过程,下面我重写了 ReadProcessMemory 和 WriteProcessMemory 函数。重写3环API的意义在于,可以防3环HOOK API的检测。

注意,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;
}

[检测过检测] 重写 ReadProcessMemory 、WriteProcessMemory相关推荐

  1. matlab瓶盖盖严检查,盖检机|瓶盖检测|外观检测设备|誉阵科技

    盖检机--五加仑盖系列 设备型号YZ-JPG10K-1YZ-JPG10K-5YZ-JPG10K-6相机组数1组5组6组检测速度10000个/小时检测位置瓶盖正面瓶盖正面,360°外圆面及盖耳朵 瓶盖正 ...

  2. yolo 负样本_SSD——样本正负比控制+多尺度检测 (目标检测)(one-stage)(深度学习)(ECCV 2016)...

    SSD--样本正负比控制+多尺度检测 (目标检测)(one-stage)(深度学习)(ECCV 2016) 发布时间:2018-11-23 20:57, 浏览次数:1399 , 标签: SSD one ...

  3. DBnet检测知识蒸馏+tensorrt推理(文字检测+条形码检测)

    向AI转型的程序员都关注了这个号???????????? 人工智能大数据与深度学习  公众号:datayx 一.DBnet 提出了 Differentiable Binarization (DB),它 ...

  4. 有未经处理的异常(在 xx.exe 中): 堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出。

    有未经处理的异常(在 xx.exe 中): 堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出. 参考文章: (1)有未经处理的异常(在 xx.exe 中): 堆栈 Cookie 检测代码检测到基 ...

  5. 行人检测/人体检测综述

    行人检测/人体检测综述 更详细的资料整理到了个人的github上,地址为 https://github.com/whitenightwu/11-Algorithm_and_Data\11-Paper ...

  6. VJ框架 与 人脸检测/物体检测 详解

    Viola-Jones Object Detection Framework 1. VJ Framework 1.1 Overview 本文详细阐述 Viola-Joines 人脸检测/物体检测 实时 ...

  7. 基于AI+RT-THREAD的人检测入侵检测摄像头(一 AI模型的导入及处理)

    RT-AK+RT-THREAD+ART-PI的人检测入侵检测摄像头 简介:该项目基于ART-PI+RT-THREAD平台开发了一套基于人检测AI的网络摄像头,当遥控开启时将自动检测人的特征并判断,若有 ...

  8. lm393 过零检测 功率因数检测

    lm393 过零检测 功率因数检测 引脚图 multism仿真 实际电路 注意事项: 引脚图 multism仿真 实际电路 vcc 12V in+ 幅值4V的正弦信号 IN- 接地 注意事项: 两个二 ...

  9. 螺纹检测案例-螺距测量-大径小径检测-螺牙检测

    齿轮检测分析 Gear inspection and analysis 螺纹结构及检测要素 1 大径(宽径) 2 小径(窄径) 3 中经 4 螺距(间距) 5 螺牙高度(深度) VisionBank ...

最新文章

  1. bzoj 4012: [HNOI2015]开店 主席树
  2. 转载:static的初始化顺序
  3. you must reset your password using alter table
  4. 慕课乐学python单元测试答案_中国大学慕课第三章单元测试答案_乐学软件工程免费答案...
  5. Hibernate hql getHibernateTemplate()常用方法汇总
  6. mybatis ------ mybatis和spring整合(十一)
  7. python输出字符_python输出字符
  8. 微软紧急修复FIP-FS中的 “Year 2022” 漏洞
  9. Python类、模块、包的区别
  10. 求合体电脑版_网红界宋祖儿找了个年轻版言承旭当男友,一张合照就收割百万cp粉...
  11. 自动驾驶的Pipline -- 如何打造自动驾驶的数据闭环?(下)
  12. 【Hadoop】:Windows下使用IDEA搭建Hadoop开发环境
  13. html 嵌入word 插件,利用VenoBox插件在网页中悬浮显示word文档
  14. oracle报1405,【案例】Oracle报错ORA-15054 asm diskgroup无法mount的解决办法
  15. unity 裙子摆动_【Unity Shader】摇摆的小草——顶点动画
  16. PHP 依赖注入 容器,PHP 依赖注入容器 Pimple 笔记
  17. deny后加to do还是doing_常见的后面只能接to do的词组(不能接doing)有哪些
  18. 基于企业战略的业务流程重组与外包(2) (转载)
  19. 解决微软拼音不显示备选字的问题
  20. python3 破解 geetest(极验)的滑块验证码

热门文章

  1. Kevin Mitnick 小故事
  2. Ubuntu系统强制安全重启
  3. xml相关php函数,php常用XML函数集
  4. 计算机考证操作题文件
  5. 计算机集体备课的好处,信息技术对教师专业发展的促进作用
  6. 如何在 Python 中隐藏和加密密码?
  7. 怎样安装 手机撑控汽车一键启动无钥匙进入系统?
  8. v-show条件渲染
  9. Flex开发实战(二)--Flash Builder 4.7下载和安装及破解方法
  10. JavaScript将时间戳转为日期