经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
驱动开发:内核读写内存浮点数
来源:cnblogs  作者:lyshark  时间:2023/5/30 9:08:59  对本文有异议

如前所述,在前几章内容中笔者简单介绍了内存读写的基本实现方式,这其中包括了CR3切换读写,MDL映射读写,内存拷贝读写,本章将在如前所述的读写函数进一步封装,并以此来实现驱动读写内存浮点数的目的。内存浮点数的读写依赖于读写内存字节的实现,因为浮点数本质上也可以看作是一个字节集,对于单精度浮点数来说这个字节集列表是4字节,而对于双精度浮点数,此列表长度则为8字节。

如下代码片段摘取自本人的LyMemory驱动读写项目,函数ReadProcessMemoryByte用于读取内存特定字节类型的数据,函数WriteProcessMemoryByte则用于写入字节类型数据,完整代码如下所示;

这段代码中依然采用了《驱动开发:内核MDL读写进程内存》中所示的读写方法,通过MDL附加到进程并RtlCopyMemory拷贝数据,至于如何读写字节集只需要循环读写即可实现;

  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <windef.h>
  7. // 读取内存字节
  8. BYTE ReadProcessMemoryByte(HANDLE Pid, ULONG64 Address, DWORD Size)
  9. {
  10. KAPC_STATE state = { 0 };
  11. BYTE OpCode;
  12. PEPROCESS Process;
  13. PsLookupProcessByProcessId((HANDLE)Pid, &Process);
  14. // 绑定进程对象,进入进程地址空间
  15. KeStackAttachProcess(Process, &state);
  16. __try
  17. {
  18. // ProbeForRead 检查内存地址是否有效, RtlCopyMemory 读取内存
  19. ProbeForRead((HANDLE)Address, Size, 1);
  20. RtlCopyMemory(&OpCode, (BYTE *)Address, Size);
  21. }
  22. __except (EXCEPTION_EXECUTE_HANDLER)
  23. {
  24. // 调用KeUnstackDetachProcess解除与进程的绑定,退出进程地址空间
  25. KeUnstackDetachProcess(&state);
  26. // 让内核对象引用数减1
  27. ObDereferenceObject(Process);
  28. // DbgPrint("读取进程 %d 的地址 %x 出错", ptr->Pid, ptr->Address);
  29. return FALSE;
  30. }
  31. // 解除绑定
  32. KeUnstackDetachProcess(&state);
  33. // 让内核对象引用数减1
  34. ObDereferenceObject(Process);
  35. DbgPrint("[内核读字节] # 读取地址: 0x%x 读取数据: %x \n", Address, OpCode);
  36. return OpCode;
  37. }
  38. // 写入内存字节
  39. BOOLEAN WriteProcessMemoryByte(HANDLE Pid, ULONG64 Address, DWORD Size, BYTE *OpCode)
  40. {
  41. KAPC_STATE state = { 0 };
  42. PEPROCESS Process;
  43. PsLookupProcessByProcessId((HANDLE)Pid, &Process);
  44. // 绑定进程,进入进程的地址空间
  45. KeStackAttachProcess(Process, &state);
  46. // 创建MDL地址描述符
  47. PMDL mdl = IoAllocateMdl((HANDLE)Address, Size, 0, 0, NULL);
  48. if (mdl == NULL)
  49. {
  50. return FALSE;
  51. }
  52. //使MDL与驱动进行绑定
  53. MmBuildMdlForNonPagedPool(mdl);
  54. BYTE* ChangeData = NULL;
  55. __try
  56. {
  57. // 将MDL映射到我们驱动里的一个变量,对该变量读写就是对MDL对应的物理内存读写
  58. ChangeData = (BYTE *)MmMapLockedPages(mdl, KernelMode);
  59. }
  60. __except (EXCEPTION_EXECUTE_HANDLER)
  61. {
  62. // DbgPrint("映射内存失败");
  63. IoFreeMdl(mdl);
  64. // 解除映射
  65. KeUnstackDetachProcess(&state);
  66. // 让内核对象引用数减1
  67. ObDereferenceObject(Process);
  68. return FALSE;
  69. }
  70. // 写入数据到指定位置
  71. RtlCopyMemory(ChangeData, OpCode, Size);
  72. DbgPrint("[内核写字节] # 写入地址: 0x%x 写入数据: %x \n", Address, OpCode);
  73. // 让内核对象引用数减1
  74. ObDereferenceObject(Process);
  75. MmUnmapLockedPages(ChangeData, mdl);
  76. KeUnstackDetachProcess(&state);
  77. return TRUE;
  78. }

实现读取内存字节集并将读入的数据放入到LySharkReadByte字节列表中,这段代码如下所示,通过调用ReadProcessMemoryByte都内存字节并每次0x401000 + i在基址上面增加变量i以此来实现字节集读取;

  1. // 驱动入口地址
  2. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  3. {
  4. DbgPrint("Hello LyShark \n");
  5. // 读内存字节集
  6. BYTE LySharkReadByte[8] = { 0 };
  7. for (size_t i = 0; i < 8; i++)
  8. {
  9. LySharkReadByte[i] = ReadProcessMemoryByte(4884, 0x401000 + i, 1);
  10. }
  11. // 输出读取的内存字节
  12. for (size_t i = 0; i < 8; i++)
  13. {
  14. DbgPrint("[+] 打印数据: %x \n", LySharkReadByte[i]);
  15. }
  16. Driver->DriverUnload = UnDriver;
  17. return STATUS_SUCCESS;
  18. }

运行如上代码片段,你会看到如下图所示的读取效果;

那么如何实现写内存字节集呢?其实写入内存字节集与读取基本类似,通过填充LySharkWriteByte字节集列表,并调用WriteProcessMemoryByte函数依次循环字节集列表即可实现写出字节集的目的;

  1. // 驱动入口地址
  2. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  3. {
  4. DbgPrint("Hello LyShark \n");
  5. // 内存写字节集
  6. BYTE LySharkWriteByte[8] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
  7. for (size_t i = 0; i < 8; i++)
  8. {
  9. BOOLEAN ref = WriteProcessMemoryByte(4884, 0x401000 + i, 1, LySharkWriteByte[i]);
  10. DbgPrint("[*] 写出状态: %d \n", ref);
  11. }
  12. Driver->DriverUnload = UnDriver;
  13. return STATUS_SUCCESS;
  14. }

运行如上代码片段,即可将LySharkWriteByte[8]中的字节集写出到内存0x401000 + i的位置处,输出效果图如下所示;

接下来不如本章的重点内容,首先如何实现读内存单精度与双精度浮点数的目的,实现原理是通过读取BYTE类型的前4或者8字节的数据,并通过*((FLOAT*)buffpyr)将其转换为浮点数,通过此方法即可实现字节集到浮点数的转换,而决定是单精度还是双精度则只是一个字节集长度问题,这段读写代码实现原理如下所示;

  1. // 读内存单精度浮点数
  2. FLOAT ReadProcessFloat(DWORD Pid, ULONG64 Address)
  3. {
  4. BYTE buff[4] = { 0 };
  5. BYTE* buffpyr = buff;
  6. for (DWORD x = 0; x < 4; x++)
  7. {
  8. BYTE item = ReadProcessMemoryByte(Pid, Address + x, 1);
  9. buff[x] = item;
  10. }
  11. return *((FLOAT*)buffpyr);
  12. }
  13. // 读内存双精度浮点数
  14. DOUBLE ReadProcessMemoryDouble(DWORD Pid, ULONG64 Address)
  15. {
  16. BYTE buff[8] = { 0 };
  17. BYTE* buffpyr = buff;
  18. for (DWORD x = 0; x < 8; x++)
  19. {
  20. BYTE item = ReadProcessMemoryByte(Pid, Address + x, 1);
  21. buff[x] = item;
  22. }
  23. return *((DOUBLE*)buffpyr);
  24. }
  25. // 驱动卸载例程
  26. VOID UnDriver(PDRIVER_OBJECT driver)
  27. {
  28. DbgPrint("Uninstall Driver \n");
  29. }
  30. // 驱动入口地址
  31. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  32. {
  33. DbgPrint("Hello LyShark \n");
  34. // 读取单精度
  35. FLOAT fl = ReadProcessFloat(4884, 0x401000);
  36. DbgPrint("[读取单精度] = %d \n", fl);
  37. // 读取双精度浮点数
  38. DOUBLE fl = ReadProcessMemoryDouble(4884, 0x401000);
  39. DbgPrint("[读取双精度] = %d \n", fl);
  40. Driver->DriverUnload = UnDriver;
  41. return STATUS_SUCCESS;
  42. }

如上代码就是实现浮点数读写的关键所在,这段代码中的浮点数传值如果在内核中会提示无法解析的外部符号 _fltused此处只用于演示核心原理,如果想要实现不报错,该代码中的传值操作应在应用层进行,而传入参数也应改为字节类型即可。

同理,对于写内存浮点数而言依旧如此,只是在接收到用户层传递参数后应对其dtoc双精度浮点数转为CHAR或者ftoc单精度浮点数转为CHAR类型,再写出即可;

  1. // 将DOUBLE适配为合适的Char类型
  2. VOID dtoc(double dvalue, unsigned char* arr)
  3. {
  4. unsigned char* pf;
  5. unsigned char* px;
  6. unsigned char i;
  7. // unsigned char型指针取得浮点数的首地址
  8. pf = (unsigned char*)&dvalue;
  9. // 字符数组arr准备存储浮点数的四个字节,px指针指向字节数组arr
  10. px = arr;
  11. for (i = 0; i < 8; i++)
  12. {
  13. // 使用unsigned char型指针从低地址一个字节一个字节取出
  14. *(px + i) = *(pf + i);
  15. }
  16. }
  17. // 将Float适配为合适的Char类型
  18. VOID ftoc(float fvalue, unsigned char* arr)
  19. {
  20. unsigned char* pf;
  21. unsigned char* px;
  22. unsigned char i;
  23. // unsigned char型指针取得浮点数的首地址
  24. pf = (unsigned char*)&fvalue;
  25. // 字符数组arr准备存储浮点数的四个字节,px指针指向字节数组arr
  26. px = arr;
  27. for (i = 0; i < 4; i++)
  28. {
  29. // 使用unsigned char型指针从低地址一个字节一个字节取出
  30. *(px + i) = *(pf + i);
  31. }
  32. }
  33. // 写内存单精度浮点数
  34. BOOL WriteProcessMemoryFloat(DWORD Pid, ULONG64 Address, FLOAT write)
  35. {
  36. BYTE buff[4] = { 0 };
  37. ftoc(write, buff);
  38. for (DWORD x = 0; x < 4; x++)
  39. {
  40. BYTE item = WriteProcessMemoryByte(Pid, Address + x, buff[x], 1);
  41. buff[x] = item;
  42. }
  43. return TRUE;
  44. }
  45. // 写内存双精度浮点数
  46. BOOL WriteProcessMemoryDouble(DWORD Pid, ULONG64 Address, DOUBLE write)
  47. {
  48. BYTE buff[8] = { 0 };
  49. dtoc(write, buff);
  50. for (DWORD x = 0; x < 8; x++)
  51. {
  52. BYTE item = WriteProcessMemoryByte(Pid, Address + x, buff[x], 1);
  53. buff[x] = item;
  54. }
  55. return TRUE;
  56. }
  57. // 驱动卸载例程
  58. VOID UnDriver(PDRIVER_OBJECT driver)
  59. {
  60. DbgPrint("Uninstall Driver \n");
  61. }
  62. // 驱动入口地址
  63. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  64. {
  65. DbgPrint("Hello LyShark \n");
  66. // 写单精度
  67. FLOAT LySharkFloat1 = 12.5;
  68. INT fl = WriteProcessMemoryFloat(4884, 0x401000, LySharkFloat1);
  69. DbgPrint("[写单精度] = %d \n", fl);
  70. // 读取双精度浮点数
  71. DOUBLE LySharkFloat2 = 12.5;
  72. INT d1 = WriteProcessMemoryDouble(4884, 0x401000, LySharkFloat2);
  73. DbgPrint("[写双精度] = %d \n", d1);
  74. Driver->DriverUnload = UnDriver;
  75. return STATUS_SUCCESS;
  76. }

原文链接:https://www.cnblogs.com/LyShark/p/17182633.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号