首页 > 系统相关 >驱动开发:内核读写内存多级偏移

驱动开发:内核读写内存多级偏移

时间:2023-06-27 09:35:58浏览次数:50  
标签:DbgPrint return Process 读写 pbase 偏移 内核 Offset

让我们继续在《内核读写内存浮点数》的基础之上做一个简单的延申,如何实现多级偏移读写,其实很简单,读写函数无需改变,只是在读写之前提前做好计算工作,以此来得到一个内存偏移值,并通过调用内存写入原函数实现写出数据的目的。

以读取偏移内存为例,如下代码同样来源于本人的LyMemory读写驱动项目,其中核心函数为WIN10_ReadDeviationIntMemory()该函数的主要作用是通过用户传入的基地址与偏移值,动态计算出当前的动态地址。

函数首先将基地址指向要读取的变量,并将其转换为LPCVOID类型的指针。然后将指向变量值的缓冲区转换为LPVOID类型的指针。接下来,函数使用PsLookupProcessByProcessId函数查找目标进程并返回其PEPROCESS结构体。随后,函数从偏移地址数组的最后一个元素开始迭代,每次循环都从目标进程中读取4字节整数型数据,并将其存储在Value变量中。然后,函数将基地址指向Value和偏移地址的和,以便在下一次循环中读取更深层次的变量。最后,函数将基地址指向最终变量的地址,读取变量的值,并返回。

如下案例所示,用户传入进程基址以及offset偏移值时,只需要动态计算出该偏移地址,并与基址相加即可得到动态地址。

#include <ntifs.h>
#include <ntintsafe.h>
#include <windef.h>

// 普通Ke内存读取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
	PEPROCESS SourceProcess = Process;
	PEPROCESS TargetProcess = PsGetCurrentProcess();
	SIZE_T Result;
	if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
		return STATUS_SUCCESS;
	else
		return STATUS_ACCESS_DENIED;
}

// 读取整数内存多级偏移
/*
  Pid: 目标进程的进程ID。
  Base: 变量的基地址。
  offset: 相对基地址的多级偏移地址,用于定位变量。
  len: 偏移地址的数量。
*/
INT64 WIN10_ReadDeviationIntMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len)
{
	INT64 Value = 0;
	LPCVOID pbase = (LPCVOID)Base;
	LPVOID rbuffer = (LPVOID)&Value;

	PEPROCESS Process;
	PsLookupProcessByProcessId((HANDLE)Pid, &Process);

	for (int x = len - 1; x >= 0; x--)
	{
		__try
		{
			KeReadProcessMemory(Process, pbase, rbuffer, 4);
			pbase = (LPCVOID)(Value + offset[x]);
		}
		__except (EXCEPTION_EXECUTE_HANDLER)
		{
			return 0;
		}
	}

	__try
	{
		DbgPrint("读取基址:%x \n", pbase);
		KeReadProcessMemory(Process, pbase, rbuffer, 4);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return 0;
	}

	return Value;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	DWORD PID = 4884;
	LONG PBase = 0x6566e0;
	LONG Size = 4;
	DWORD Offset[32] = { 0 };

	Offset[0] = 0x18;
	Offset[1] = 0x0;
	Offset[2] = 0x14;
	Offset[3] = 0x0c;

	// 读取内存数据
	INT64 read = WIN10_ReadDeviationIntMemory(PID, PBase, Offset, Size);

	DbgPrint("PID: %d 基址: %p 偏移长度: %d \n", PID, PBase, Size);
	DbgPrint("[+] 1级偏移: %x \n", Offset[0]);
	DbgPrint("[+] 2级偏移: %x \n", Offset[1]);
	DbgPrint("[+] 3级偏移: %x \n", Offset[2]);
	DbgPrint("[+] 4级偏移: %x \n", Offset[3]);

	DbgPrint("[ReadMemory] 读取偏移数据: %d \n", read);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

编译并运行如上这段代码,则可获取到PID=4884PBase的动态地址中的数据,如下图所示;

至于如何将数据写出四级偏移的基址上面,则只需要取出pbase里面的基址,并通过原函数WIN10_WriteProcessMemory直接写出数据即可,此出的原函数在《内核MDL读写进程内存》中已经做了详细介绍,实现写出代码如下所示;

#include <ntifs.h>
#include <ntintsafe.h>
#include <windef.h>

// 普通Ke内存读取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
	PEPROCESS SourceProcess = Process;
	PEPROCESS TargetProcess = PsGetCurrentProcess();
	SIZE_T Result;
	if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
		return STATUS_SUCCESS;
	else
		return STATUS_ACCESS_DENIED;
}

// Win10 内存写入函数
BOOLEAN WIN10_WriteProcessMemory(HANDLE Pid, PVOID Address, SIZE_T BYTE_size, PVOID VirtualAddress)
{
	PVOID buff1;
	VOID *buff2;
	int MemoryNumerical = 0;
	KAPC_STATE KAPC = { 0 };

	PEPROCESS Process;
	PsLookupProcessByProcessId((HANDLE)Pid, &Process);

	__try
	{
		//分配内存
		buff1 = ExAllocatePoolWithTag((POOL_TYPE)0, BYTE_size, 1997);
		buff2 = buff1;
		*(int*)buff1 = 1;
		if (MmIsAddressValid((PVOID)VirtualAddress))
		{
			// 复制内存
			memcpy(buff2, VirtualAddress, BYTE_size);
		}
		else
		{
			return FALSE;
		}

		// 附加到要读写的进程
		KeStackAttachProcess((PRKPROCESS)Process, &KAPC);
		if (MmIsAddressValid((PVOID)Address))
		{
			// 判断地址是否可写
			ProbeForWrite(Address, BYTE_size, 1);
			// 复制内存
			memcpy(Address, buff2, BYTE_size);
		}
		else
		{
			return FALSE;
		}
		// 剥离附加的进程
		KeUnstackDetachProcess(&KAPC);
		ExFreePoolWithTag(buff2, 1997);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return FALSE;
	}
	return FALSE;
}

// 写入整数内存多级偏移
INT64 WIN10_WriteDeviationIntMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len, INT64 SetValue)
{
	INT64 Value = 0;
	LPCVOID pbase = (LPCVOID)Base;
	LPVOID rbuffer = (LPVOID)&Value;

	PEPROCESS Process;
	PsLookupProcessByProcessId((HANDLE)Pid, &Process);

	for (int x = len - 1; x >= 0; x--)
	{
		__try
		{
			KeReadProcessMemory(Process, pbase, rbuffer, 4);
			pbase = (LPCVOID)(Value + offset[x]);
		}
		__except (EXCEPTION_EXECUTE_HANDLER)
		{
			return 0;
		}
	}

	__try
	{
		KeReadProcessMemory(Process, pbase, rbuffer, 4);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return 0;
	}

	// 使用原函数写入
	BOOLEAN ref = WIN10_WriteProcessMemory(Pid, (void *)pbase, 4, &SetValue);
	if (ref == TRUE)
	{
		DbgPrint("[内核写成功] # 写入地址: %x \n", pbase);
		return 1;
	}
	return 0;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	DWORD PID = 4884;
	LONG PBase = 0x6566e0;
	LONG Size = 4;
	INT64 SetValue = 100;

	DWORD Offset[32] = { 0 };

	Offset[0] = 0x18;
	Offset[1] = 0x0;
	Offset[2] = 0x14;
	Offset[3] = 0x0c;

	// 写出内存数据
	INT64 write = WIN10_WriteDeviationIntMemory(PID, PBase, Offset, Size, SetValue);

	DbgPrint("PID: %d 基址: %p 偏移长度: %d \n", PID, PBase, Size);
	DbgPrint("[+] 1级偏移: %x \n", Offset[0]);
	DbgPrint("[+] 2级偏移: %x \n", Offset[1]);
	DbgPrint("[+] 3级偏移: %x \n", Offset[2]);
	DbgPrint("[+] 4级偏移: %x \n", Offset[3]);

	DbgPrint("[WriteMemory] 写出偏移数据: %d \n", SetValue);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行如上代码将在0x6566e0所在的基址上,将数据替换为100,实现效果图如下所示;

那么如何实现读写内存浮点数,字节集等多级偏移呢?

其实我们可以封装一个WIN10_ReadDeviationMemory函数,让其只计算得出偏移地址,而所需要写出的类型则根据自己的实际需求配合不同的写入函数完成,也就是将两者分离开,如下则是一段实现计算偏移的代码片段,该代码同样来自于本人的LyMemory驱动读写项目;

#include <ntifs.h>
#include <ntintsafe.h>
#include <windef.h>

// 普通Ke内存读取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
	PEPROCESS SourceProcess = Process;
	PEPROCESS TargetProcess = PsGetCurrentProcess();
	SIZE_T Result;
	if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
		return STATUS_SUCCESS;
	else
		return STATUS_ACCESS_DENIED;
}

// 读取多级偏移内存动态地址
DWORD64 WIN10_ReadDeviationMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len)
{
	INT64 Value = 0;
	LPCVOID pbase = (LPCVOID)Base;
	LPVOID rbuffer = (LPVOID)&Value;

	PEPROCESS Process;
	PsLookupProcessByProcessId((HANDLE)Pid, &Process);

	for (int x = len - 1; x >= 0; x--)
	{
		__try
		{
			KeReadProcessMemory(Process, pbase, rbuffer, 4);
			pbase = (LPCVOID)(Value + offset[x]);
		}
		__except (EXCEPTION_EXECUTE_HANDLER)
		{
			return 0;
		}
	}

	return pbase;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	DWORD PID = 4884;
	LONG PBase = 0x6566e0;
	LONG Size = 4;

	DWORD Offset[32] = { 0 };

	Offset[0] = 0x18;
	Offset[1] = 0x0;
	Offset[2] = 0x14;
	Offset[3] = 0x0c;

	// 写出内存数据
	DWORD64 offsets = WIN10_ReadDeviationMemory(PID, PBase, Offset, Size);

	DbgPrint("PID: %d 基址: %p 偏移长度: %d \n", PID, PBase, Size);
	DbgPrint("[+] 1级偏移: %x \n", Offset[0]);
	DbgPrint("[+] 2级偏移: %x \n", Offset[1]);
	DbgPrint("[+] 3级偏移: %x \n", Offset[2]);
	DbgPrint("[+] 4级偏移: %x \n", Offset[3]);

	DbgPrint("[CheckMemory] 计算偏移地址: %x \n", offsets);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行如上代码将动态计算出目前偏移地址的pbase实际地址,实现效果图如下所示;

标签:DbgPrint,return,Process,读写,pbase,偏移,内核,Offset
From: https://www.cnblogs.com/LyShark/p/17507776.html

相关文章

  • [ARM 汇编]高级部分—系统控制协处理器—3.2.3 控制寄存器的读写操作
    在这一部分,我们将学习如何使用ARM汇编指令在系统控制协处理器(CP15)的控制寄存器上执行读写操作。我们将通过实例来讲解如何使用MCR(MovetoCoprocessorRegister)和MRC(MovefromCoprocessorRegister)指令进行读写操作。MCR指令MCR指令用于将ARM内核寄存器的值写入协处理器寄存......
  • LabVIEW Excel工具包快速读写EXCEL样式模板生成测试报告制作
    LabVIEWExcel工具包快速读写EXCEL样式模板生成测试报告制作原创文章,转载请说明出处,资料来源:http://imgcs.cn/5c/673187774245.html......
  • 升级CentOS 7.9内核
    1.背景不知道大家有没有遇到过这样的问题,在使用docker创建vlan网络时,会提示“Errorresponsefromdaemon:kernelversionfailedtomeettheminimumipvlankernelrequirementof4.2,found3.10.0”,需要最新的稳定版。看一下系统的内核版本,使用以下命令:uname-a先导入......
  • Linux实例常用内核网络参数与常见问题处理
    查看和修改Linux实例内核参数方法一、通过 /proc/sys/ 目录查看内核参数:使用 cat 查看对应文件的内容,例如执行命令 cat/proc/sys/net/ipv4/tcp_tw_recycle 查看 net.ipv4.tcp_tw_recycle 的值。修改内核参数:使用 echo 修改内核参数对应的文件,例如执行命令 echo"......
  • Linux多线程11-读写锁
    当有一个线程已经持有互斥锁时,互斥锁将所有试图进入临界区的线程都阻塞住。但是考虑一种情形,当前持有互斥锁的线程只是要读访问共享资源,而同时有其它几个线程也想读取这个共享资源,但是由于互斥锁的排它性,所有其它线程都无法获取锁,也就无法读访问共享资源了,但是实际上多个线程同时......
  • 驱动开发:内核物理内存寻址读写
    在某些时候我们需要读写的进程可能存在虚拟内存保护机制,在该机制下用户的CR3以及MDL读写将直接失效,从而导致无法读取到正确的数据,本章我们将继续研究如何实现物理级别的寻址读写。首先,驱动中的物理页读写是指在驱动中直接读写物理内存页(而不是虚拟内存页)。这种方式的优点是它能够......
  • Win32k 是 Windows 操作系统中的一个核心组件,它负责处理图形显示、窗口管理和用户交互
    Win32k是Windows操作系统中的一个核心组件,它负责处理图形显示、窗口管理和用户交互等功能。在Windows中,Win32k.sys是一个内核模式驱动程序,它提供了访问图形子系统的接口。因此,Win32k具有较高的权限和特权。作为一个内核模式驱动程序,Win32k有比普通用户程序更高的权限级别......
  • 【野火Linux移植篇】5-重温编译Linux内核命令 make xxx_defconfig 文件位置
    重温编译内核步骤:命令如下:makeARCH=armCROSS_COMPILE=arm-linux-gnueabihf-distcleanmakeARCH=armCROSS_COMPILE=arm-linux-gnueabihf-imx_v7_defconfigmakeARCH=armCROSS_COMPILE=arm-linux-gnueabihf-all-j16第一行命令用于清除工程中之前编译的残留文件,最好这......
  • vue项目在IE内核下打开显示白屏(亲测可用!!!)
    一.安装babel-polyfill库npminstall--savebabel-polyfill 如图二.在main.js中引入(放在最上面,一定要在第一行)import'babel-polyfill'三.在vue.config.js中加入transpileDependencies:process.env.NODE_ENV==="development"?["*"]:["*"......
  • Centos7 内核限制
    一、设置所有用户最大打开文件数和网络连接数1、编辑 /etc/security/limits.conf 文件,可以使用下面的命令打开该文件:sudovim/etc/security/limits.conf2、添加以下四行:第一行和第二行分别设置所有用户的文件打开数的软限制和硬限制均为65535。第三行和第四行分别设置所......