read digest_view the readme file

read digest_view the readme file一、本文大纲系统调用的两种方式:中断门和快速调用_KUSER_SHARED_DATA结构使用cpuid指令判断当前CPU是否支持快速调用3环进0环需要更改的4个寄存器以ReadProcessMemory为例说明系统调用全过程重写ReadProcessMemory和WriteProcessMemoryint0x2e和sysenter都做了什么工作?二、中断门和快速调用以我的理解,系统调用,即从调用操作系统提供的3环API开始,到进0环,再到返回结果到3环的全过程

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、本文大纲

  • 系统调用的两种方式:中断门和快速调用
  • _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
	{ 
   
		// 直接模拟 KiIntSystemCall
		lea 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
	{ 
   
		// 模拟 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 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
	{ 
   
		// 模拟 WriteProcessMemory
		lea eax,nSize;
		push eax;
		push nSize;
		push lpBuffer;
		push lpBaseAddress;
		push hProcess;
		sub esp, 0x04; // 模拟 WriteProcessMemory 里的 CALL NtWriteVirtualMemory
		// 模拟 NtWriteVirtualMemory
		mov eax, 0x115;
		push NtWriteVirtualMemoryReturn; // 模拟 NtWriteVirtualMemory 函数里的 CALL [0x7FFE0300]
		// 模拟 KiFastSystemCall
		mov edx, esp;
		_emit 0x0F; // sysenter 
		_emit 0x34;
NtWriteVirtualMemoryReturn:		
		add esp, 0x18; // 模拟 NtWriteVirtualMemory 返回到 WriteProcessMemory 时的 RETN 0x14
		mov 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();

	// 测试两个版本的 ReadProcessMemory
	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);
	
	// 测试两个版本的 WriteProcessMemory
	HbgWriteProcessMemory_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;
}

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194444.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • [Halcon&标定] 单相机标定「建议收藏」

    [Halcon&标定] 单相机标定「建议收藏」为什么要进行单相机标定?广义:畸变矫正和一维和二维测量1)畸变矫正:    在几何光学和阴极射线管(CRT)显示中。畸变是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了。这是一种光学畸变(opticalaberration)。畸变是一种相差…

    2022年5月28日
    27
  • webgame开发中的文件加密

    webgame开发中的文件加密一般的webgame中都会对资源、消息进行加密,这里只是简单记录一下对文件的加密过程。上图为实际项目中所使用的加密工具(较为简单的一个air项目)输入加密key+需要加密的文件–>加密–>将加密后的文件保存至另一目录(后缀名视自己的项目的规则进行修改)实现步骤:1、读取文件(flash.filesystem.File),获取文件流(…

    2022年6月4日
    38
  • 一个概括性关于维数约简的论文[通俗易懂]

    一个概括性关于维数约简的论文[通俗易懂]
    http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.107.1327&rep=rep1&type=pdf

    2022年6月1日
    25
  • 黑客技术之初学者编程入门「建议收藏」

    黑客技术之初学者编程入门「建议收藏」你是否曾经在用别人开发的工具尝试“入侵”.你是否希望开发出自己的黑器……相信很多人有着这种近似相同的经历。本章将简单介绍黑客编程及工具开发。如果你是初学编程,如果你从来没有接触过黑客软件的开发,如果你急于想了解黑客编程方面的知识……那么就请继续往下阅读。编程语言和开发环境的选择初学者刚开始学习编程语言最头疼的问题就是如何选择编程语言及合适的开发环境,下面就来具体介绍一下。有

    2022年5月18日
    73
  • java递归和迭代_Java中的迭代与递归

    java递归和迭代_Java中的迭代与递归递归提到迭代,不得不提一个数学表达式:n!=n*(n-1)*(n-2)*…*1有很多方法来计算阶乘。有肯定数学基础的人都知道n!=n*(n-1)!因而,代码的实现可以直接写成:代码一intfactorial(intn){if(n==1){return1;}else{returnn*factorial(n-1);}}在执行以上代码的时候,其实机器是要执行一系列乘法的:…

    2022年7月13日
    15
  • Mathpix | Typora | 实现快速公式编辑[通俗易懂]

    Mathpix | Typora | 实现快速公式编辑[通俗易懂]这篇文章是之前做过的一期视频的改进版本,写这个文章的原因有两个:1、mathpix的模式有所变化2、我发现了更简单的使用方法需要环境mathpixtyporawindows10使用介绍当我们遇到一个公式的时候,我们可以使用mathpix去截图获取其公式编码1、点击截图按钮,框出想要选择复制的公式2、选择好后,已自动copy好latex格式,手动选择copy也可3、打开typora,输入$$按回车,创建公式模块;也可以右键,插入公式4、将之前copy的latex

    2022年5月18日
    72

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号