# LyMemory **Repository Path**: AlbertCoder163/LyMemory ## Basic Information - **Project Name**: LyMemory - **Description**: 一款免费的内核级内存读写工具,可突破驱动保护,强制读写任意应用层进程内存数据。 - **Primary Language**: C - **License**: GPL-3.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2023-04-11 - **Last Updated**: 2025-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 内核级内存读写驱动
![image](https://user-images.githubusercontent.com/52789403/210537145-bbf8cc74-64e7-4477-bafd-109437de6131.png)

[![Build status](https://cdn.lyshark.com/archive/LyScript/build.svg)](https://github.com/lyshark/LyMemory) [![Crowdin](https://cdn.lyshark.com/archive/LyScript/email.svg)](mailto:me@lyshark.com) [![OSCS Status](https://cdn.lyshark.com/archive/LyScript/OSCS.svg)](https://www.oscs1024.com/project/lyshark/LyMemory?ref=badge_small)

一款完全免费的内核级内存读写工具,可突破进程驱动保护,强制读写应用层任意进程内存数据,驱动工具目前支持读写整数,字节,字节集,单精度浮点数,双精度浮点数,多级偏移读写,取模块地址,分配远程内存等功能,读写效率高,速度快,兼容性好,使用时需自己签名或在测试模式下。 ## C++ 调用接口 目前驱动读写支持的读写函数如下表所示,需要注意的是`SwitchDriver`在基础版本中不存在,如需使用请购买Pro专业版,专业版与基础版唯一的区别是在读写方式上,专业版具有更强的读写模式,而基础版则只支持Cr3读写模式; | 导出函数 | 函数作用 | | ---- | ---- | | BOOL SwitchDriver(PCHAR pSwitch) | 切换内存条模式(Pro) | | BOOL SetPid(DWORD Pid) | 设置全局进程PID | | BOOL Read(ULONG64 address, T* ret) | 自定义读内存 | | BOOL Write(ULONG64 address, T data) | 自定义读内存 | | BOOL ReadMemoryDWORD(ULONG64 addre, DWORD * ret) | 读内存DWORD | | BOOL ReadMemoryDWORD64(ULONG64 addre, DWORD64 * ret) | 读内存DWORD64 | | BOOL ReadMemoryBytes(ULONG64 addre, BYTE **ret, DWORD sizes) | 读内存字节 | | BOOL ReadMemoryFloat(ULONG64 addre, float* ret) | 读内存浮点数 | | BOOL ReadMemoryDouble(ULONG64 addre, double* ret) | 读内存双精度浮点数 | | BOOL WriteMemoryBytes(ULONG64 addre, BYTE * data, DWORD sizes) | 写内存字节 | | BOOL WriteMemoryDWORD(ULONG64 addre, DWORD ret) | 写内存DWORD | | BOOL WriteMemoryDWORD64(ULONG64 addre, DWORD64 ret) | 写内存DWORD64 | | BOOL WriteMemoryFloat(ULONG64 addre, float ret) | 写内存浮点数 | | BOOL WriteMemoryDouble(ULONG64 addre, double ret) | 写内存双精度浮点数 | | DWORD ReadDeviationMemory32(ProcessDeviationMemory *read_offset_struct) | 计算32位偏移数据基址 | | DWORD64 ReadDeviationMemory64(ProcessDeviationMemory *read_offset_struct) | 计算64位偏移数据基址 | | DWORD64 GetModuleAddress(std::string dllname) | 驱动读取进程模块基地址 | | DWORD64 GetSystemRoutineAddress(std::string funcname) | 获取系统函数内存地址 | | DWORD64 CreateRemoteMemory(DWORD length) | 在对端分配内存空间 | | DWORD DeleteRemoteMemory(DWORD64 address, DWORD length) | 销毁对端内存 | 具体版本及读写模式说明如下表所示,用户可根据实际需求选择适合自己的版本; | 版本 | 读写模式 | 价格 | 作用 | | ---- | ---- | ---- | ---- | | 基础版 | CR3切换 | 永久免费 | 读写保护不太强的进程(直接拉闸) | | 专业版 | 物理内存条寻址 | 500/年 | 读写保护大多数进程(有概率拉闸) | | 旗舰版 | VT虚拟化技术 | 1000/年 | 无痕读写大多数进程(不拉闸) | 新版本读写API接口在读写内存之前需要提前设置进程PID号,后期的调用将不需要再传入进程PID,此类读写适合长期读,某些FPS射击类游戏的人物数组,3D类游戏坐标由于坐标会频繁移动,需持续不间断读取,此读写模块将很适,接下来将带大家分析并简单使用这些API接口实现功能。 在使用`LyMemoryLib`静态库之前请确保您已经正确的配置了`Visual Studio`引用头文件。 ![image](https://user-images.githubusercontent.com/52789403/224695886-391848b0-854f-4f4f-9945-d818eeab25ca.png) **如何安装与卸载驱动:** 读写的第一步是安装驱动并将其运行,当然你可以通过第三方组件对驱动进行安装,也可以使用`LyMemoryLib`中的函数实现安装,如下则是通过`LyMemoryLib.hpp`将驱动加载的完整实现; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") // 安装驱动 BOOL InstallDriver(LyMemoryDrvCtrl Memory) { char szSysFile[MAX_PATH] = { 0 }; char szSvcLnkName[] = "LyMemory";; BOOL ref = FALSE; DWORD index = 0; // 获取完整路径 Memory.GetAppPath(szSysFile); strcat_s(szSysFile, "LyMemory.sys"); printf("驱动路径: %s \n", szSysFile); index = index + 1; // 安装驱动 ref = Memory.Install(szSysFile, szSvcLnkName, szSvcLnkName); printf("安装状态: %d \n", ref); index = index + 1; // 启动驱动 ref = Memory.Start(); printf("启动状态: %d \n", ref); index = index + 1; // 打开 ref = Memory.Open("\\\\.\\LyMemory"); printf("打开状态: %d \n", ref); index = index + 1; if (index == 4 && ref == TRUE) { return TRUE; } return FALSE; } // 卸载驱动 BOOL RemoveDriver(LyMemoryDrvCtrl Memory) { BOOL ref = 0; // 关闭 ref = Memory.Stop(); printf("关闭状态: %d \n", ref); // 移除 ref = Memory.Remove(); printf("移除状态: %d \n", ref); return ref; } int main(int argc, char* argv[]) { LyMemoryDrvCtrl DriveControl; // 加载驱动 BOOL ref = InstallDriver(DriveControl); if (ref == TRUE) { printf("[*] 驱动已加载 \n"); } // 卸载驱动 RemoveDriver(DriveControl); system("pause"); return 0; } ``` 如上代码编译后并以管理员权限运行,则会将驱动`LyMemory.sys`自动加载,并在调试板输出如下图所示的信息; ![image](https://user-images.githubusercontent.com/52789403/224332774-9162ed9b-54c7-4671-9908-a8bfdbbb574f.png) **设置PID进程绑定:** 如果需要使用读写函数,第一步则是设置`进程PID`绑定,通常可通过`SetPid(DWORD Pid)`函数传入进程`PID`进行绑定操作,一旦进程被绑定则后续无需再次打开,提高了读写效率,也可预防多次附加脱离导致应用层异常,如果需要使用设置PID则你可以这样来写; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } system("pause"); return 0; } ``` 运行如上代码所示将自动绑定到进程`6536`并输出绑定状态,如下图所示; ![image](https://user-images.githubusercontent.com/52789403/224332724-bed24e8d-dc47-40d4-a9c2-12a9af5e9ab7.png) **内核读取模块基址:** 由于目前进程已被附加到到驱动上,此时可以调用`GetModuleAddress()`获取进程内特定模块的基址,此函数接收一个模块名; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 取模块基址 DWORD64 user32 = DriveControl.GetModuleAddress("user32.dll"); printf("user32 = 0x%p \n", user32); DWORD64 kernel32 = DriveControl.GetModuleAddress("kernel32.dll"); printf("kernel32 = 0x%p \n", kernel32); system("pause"); return 0; } ``` 如上代码编译并运行,则取出被附加进程内`user32.dll`以及`kernel32.dll`的模块基址,输出效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332675-cc36f1a5-2c3a-4b4d-aaa5-f8aba1f41dca.png) **取内核函数基址:** 与取应用层模块基址类似,函数`GetSystemRoutineAddress`可用于获取到内核模块中特定导出函数的内存基址。 ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 取函数地址 CHAR *SzFunction[3] = { "NtReadFile", "NtClose", "NtSetEvent" }; for (size_t i = 0; i < 3; i++) { DWORD64 ptr = DriveControl.GetSystemRoutineAddress(SzFunction[i]); printf("函数 = %s | 地址 = 0x%p \n", SzFunction[i], ptr); } system("pause"); return 0; } ``` 运行如上方所示的代码片段,则自动取出`"NtReadFile", "NtClose", "NtSetEvent"`三个函数的内存地址,输出效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332629-1f02b238-e6da-403a-8894-a1e9a5500569.png) **分配与释放堆空间:** 在对端内存中开辟一段内存可调用`CreateRemoteMemory`函数实现,释放堆空间则可调用`DeleteRemoteMemory`函数,默认情况下分配的空间自带读写执行属性,为`Hook挂钩`转向提供可能。 ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 分配内存空间 DWORD64 address = DriveControl.CreateRemoteMemory(1024); printf("[+] 已分配内存 = 0x%p \n", address); // 释放内存 BOOL del = DriveControl.DeleteRemoteMemory(address, 1024); if (del == TRUE) { printf("[-] 内存空间 0x%p 已被释放 \n", address); } system("pause"); return 0; } ``` 如上代码片段运行后,将在对端内存中分配`address`的地址,分配后自动将其释放,输出效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332577-361fe96b-49dd-4861-aafe-59e07bf9091d.png) **读/写内存整数型:** 整数类型的读取可调用`ReadMemoryDWORD`读取32位整数,调用`ReadMemoryDWORD64`则读取64位整数型; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 读取32位整数 DWORD read_value = 0; BOOL read_flag = DriveControl.ReadMemoryDWORD(0x0188F828, &read_value); if (read_flag == TRUE) { printf("[*] 读取32位数据 = %d \n", read_value); } // 读取64位整数 DWORD64 read64_value = 0; BOOL read64_flag = DriveControl.ReadMemoryDWORD64(0x0188F828, &read64_value); if (read64_flag == TRUE) { printf("[*] 读取64位数据 = %d \n", read64_value); } system("pause"); return 0; } ``` 编译并运行如上代码片段,则会读取`0x0188F828`处的整数类型数据,读取输出效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332526-4ed685d9-c0cb-4604-9ee9-2e12639d4ed0.png) 写入整数类型同理,调用`WriteMemoryDWORD`写出32位整数,调用`WriteMemoryDWORD64`写出64位整数; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 写入32位整数 BOOL write32 = DriveControl.WriteMemoryDWORD(0x0188F828, 1000); if (write32 == TRUE) { printf("[+] 写出数据完成 \n"); } // 写入64位整数 BOOL write64 = DriveControl.WriteMemoryDWORD64(0x0188F828, 2000); if (write64 == TRUE) { printf("[+] 写出数据完成 \n"); } system("pause"); return 0; } ``` 编译并运行代码,将向目标进程分别写出`1000`及`2000`,代码输出效果如下图所示; ![image](https://user-images.githubusercontent.com/52789403/224332487-da16adca-2508-4ed3-b182-3b227b7a7a54.png) **读/写内存字节集:** 内存读写字节集可调用`ReadMemoryBytes`函数,写出字节集调用`WriteMemoryBytes`函数; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 读取字节集 BYTE buffer[8] = { 0 }; BYTE* bufferPtr = buffer; BOOL flag = DriveControl.ReadMemoryBytes(0x401000, &bufferPtr, sizeof(buffer)); if (flag == TRUE) { for (int x = 0; x < 8; x++) { printf("[+] 读取字节: 0x%x \n", buffer[x]); } } system("pause"); return 0; } ``` 运行如上代码片段,即可在内存`0x401000`处开始读取字节集,向后读取8字节,并存入`buffer`中,输出效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332446-a06ff9f2-0665-4da2-9043-fb2aeaadb183.png) 写出字节集与读取基本一致,函数`WriteMemoryBytes`则用于写出字节集数据,写出是需传递一个定义好的字节数组; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 写内存字节集 BYTE writebuff[4] = { 0x90, 0x90, 0x90, 0x90 }; BOOL flag = DriveControl.WriteMemoryBytes(0x401000, writebuff, sizeof(writebuff)); if (flag == TRUE) { printf("[+] 写出字节集完成 \n"); } system("pause"); return 0; } ``` 运行如上代码片段,则将字节集写出到`0x401000`内存处,写出效果如下图所示; ![image](https://user-images.githubusercontent.com/52789403/224332397-043b185d-fd57-47e2-ab07-08178329785e.png) **读/写内存浮点数:** 浮点数可分为单浮点与双浮点,单浮点可使用`ReadMemoryFloat`实现读写,双浮点则调用`ReadMemoryDouble`实现,两者实现原理完全一致,仅仅只是读写时多出了4个字节的宽度而已。 ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 读取单浮点 float read_float = 0; BOOL float_flag = DriveControl.ReadMemoryFloat(0x01894EF8, &read_float); if (float_flag == TRUE) { printf("[+] 读取单精度 = %f \n", read_float); } // 读取双浮点 double read_double = 0; BOOL double_flag = DriveControl.ReadMemoryDouble(0x01894EF8, &read_double); if (double_flag == TRUE) { printf("[+] 读取双精度 = %f \n", double_flag); } system("pause"); return 0; } ``` 运行后输出两个浮点数,注意双精度此处并不是错误而是输出问题,效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332353-1bae8523-efb3-455f-bbc9-9d9ae8cdfe19.png) 那么如何写出数据呢,只需要调用`WriteMemoryFloat`即可实现写出浮点数的目的; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 写取单浮点 BOOL ref = DriveControl.WriteMemoryFloat(0x01894EF8, 100.245); if (ref == TRUE) { printf("[+] 写出数据完成 \n"); } system("pause"); return 0; } ``` 以单精度浮点数为例,写出数据后输出如下效果; ![image](https://user-images.githubusercontent.com/52789403/224332308-09c7a800-ffd4-4f56-b05f-8e5d7e36daa5.png) **计算多级偏移动态地址:** 函数`ReadDeviationMemory32`可实现动态计算多级偏移的功能,该函数最多可接受32级偏移的计算,计算后可得到一个动态地址,用户得到动态地址后可对其地址执行读写整数,字节,字节集,浮点数等各类操作,我们以整数读写为例子; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 计算四级偏移动态地址 ProcessDeviationMemory read_offset_struct = { 0 }; read_offset_struct.Address = 0x6566e0; // 基地址 read_offset_struct.OffsetSize = 4; // 偏移长度 read_offset_struct.Data = 0; // 读入的数据 read_offset_struct.Offset[0] = 0x18; // 一级偏移 read_offset_struct.Offset[1] = 0x0; // 二级偏移 read_offset_struct.Offset[2] = 0x14; // 三级偏移 read_offset_struct.Offset[3] = 0x0c; // 四级偏移 // 开始计算 DWORD BaseAddress = DriveControl.ReadDeviationMemory32(&read_offset_struct); printf("[+] 得到动态地址 = 0x%016lx \n", BaseAddress); // 读取整数 DWORD GetDWORD = 0; BOOL flag = DriveControl.ReadMemoryDWORD(BaseAddress, &GetDWORD); if (flag == TRUE) { printf("[+] 读取数据 = %d \n", GetDWORD); } system("pause"); return 0; } ``` 如上代码通过调用`ReadDeviationMemory32`计算出当前动态地址的基址,并通过`ReadMemoryDWORD`读取此处的内存DWORD类型,输出效果如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332264-4e9e454d-d997-4d58-81c2-c3dae63a3020.png) **内存读写反汇编:** 读写函数我们可使用`ReadMemoryBytes`实现字节集的读取,通过运用`capstone`反汇编引擎即可对特定内存空间进行反汇编操作; ```c // 署名权 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "LyMemoryLib.h" #include #include #include #include #pragma comment(lib,"capstone64.lib") #pragma comment(lib,"advapi32.lib") #pragma comment(lib,"LyMemoryLib.lib") int main(int argc, char *argv[]) { LyMemoryDrvCtrl DriveControl; DriveControl.InstallAndRun(); BOOL set_pid = DriveControl.SetPid(5588); if (set_pid == TRUE) { printf("[*] 设置PID = %d \n", set_pid); } // 读取前1024个字节 BYTE MyArray[1024] = { 0 }; BYTE* bufferPtr = MyArray; BOOL flag = DriveControl.ReadMemoryBytes(0x401000, &bufferPtr, sizeof(MyArray)); if (flag == TRUE) { printf("[*] 读取完毕 \n"); } csh handle; cs_insn *insn; size_t count; int size = 1023; // 打开句柄 if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK) { return 0; } // 反汇编代码,地址从0x1000开始,返回总条数 count = cs_disasm(handle, (unsigned char *)MyArray, size, 0x401000, 0, &insn); if (count > 0) { size_t index; for (index = 0; index < count; index++) { /* for (int x = 0; x < insn[index].size; x++) { printf("机器码: %d -> %02X \n", x, insn[index].bytes[x]); } */ printf("地址: 0x%"PRIx64" | 长度: %d 反汇编: %s %s \n", \ insn[index].address, insn[index].size, insn[index].mnemonic, insn[index].op_str); } cs_free(insn, count); } /* else { printf("反汇编返回长度为空 \n"); } */ cs_close(&handle); system("pause"); return 0; } ``` 运行后即可对进程中`0x401000`的内存区域向下反汇编`1024`个字节,输出效果图如下所示; ![image](https://user-images.githubusercontent.com/52789403/224332211-6bdfe53f-839a-49b6-84b2-4f6ea5bdb17d.png) ## 项目地址 https://github.com/lyshark/LyMemory