LyMemory 內(nèi)核級內(nèi)存讀寫驅(qū)動

一款完全免費(fèi)的內(nèi)核級內(nèi)存讀寫工具,可突破驅(qū)動保護(hù),強(qiáng)制讀寫應(yīng)用層任意進(jìn)程內(nèi)存數(shù)據(jù),驅(qū)動工具目前支持讀寫整數(shù),字節(jié),字節(jié)集,單精度浮點(diǎn)數(shù),雙精度浮點(diǎn)數(shù),多級偏移讀寫,取模塊地址,分配遠(yuǎn)程內(nèi)存等功能,讀寫效率高,速度快,兼容性好,使用時需自己簽名或在測試模式下。
C++ 調(diào)用接口
目前驅(qū)動讀寫支持的讀寫函數(shù)如下表所示,需要注意的是?SwitchDriver
?在基礎(chǔ)版本中不存在,如需使用請購買 Pro 專業(yè)版,專業(yè)版與基礎(chǔ)版唯一的區(qū)別是在讀寫方式上,專業(yè)版具有更強(qiáng)的讀寫模式,而基礎(chǔ)版則只支持 Cr3 讀寫模式;
導(dǎo)出函數(shù)函數(shù)作用BOOL SwitchDriver(PCHAR pSwitch)切換內(nèi)存條模式 (Pro)BOOL SetPid(DWORD Pid)設(shè)置全局進(jìn)程 PIDBOOL Read(ULONG64 address, T* ret)自定義讀內(nèi)存BOOL Write(ULONG64 address, T data)自定義讀內(nèi)存BOOL ReadMemoryDWORD(ULONG64 addre, DWORD * ret)讀內(nèi)存 DWORDBOOL ReadMemoryDWORD64(ULONG64 addre, DWORD64 * ret)讀內(nèi)存 DWORD64BOOL ReadMemoryBytes(ULONG64 addre, BYTE **ret, DWORD sizes)讀內(nèi)存字節(jié)BOOL ReadMemoryFloat(ULONG64 addre, float* ret)讀內(nèi)存浮點(diǎn)數(shù)BOOL ReadMemoryDouble(ULONG64 addre, double* ret)讀內(nèi)存雙精度浮點(diǎn)數(shù)BOOL WriteMemoryBytes(ULONG64 addre, BYTE * data, DWORD sizes)寫內(nèi)存字節(jié)BOOL WriteMemoryDWORD(ULONG64 addre, DWORD ret)寫內(nèi)存 DWORDBOOL WriteMemoryDWORD64(ULONG64 addre, DWORD64 ret)寫內(nèi)存 DWORD64BOOL WriteMemoryFloat(ULONG64 addre, float ret)寫內(nèi)存浮點(diǎn)數(shù)BOOL WriteMemoryDouble(ULONG64 addre, double ret)寫內(nèi)存雙精度浮點(diǎn)數(shù)DWORD ReadDeviationMemory32(ProcessDeviationMemory *read_offset_struct)計算 32 位偏移數(shù)據(jù)基址DWORD64 ReadDeviationMemory64(ProcessDeviationMemory *read_offset_struct)計算 64 位偏移數(shù)據(jù)基址DWORD64 GetModuleAddress(std::string dllname)驅(qū)動讀取進(jìn)程模塊基地址DWORD64 GetSystemRoutineAddress(std::string funcname)獲取系統(tǒng)函數(shù)內(nèi)存地址DWORD64 CreateRemoteMemory(DWORD length)在對端分配內(nèi)存空間DWORD DeleteRemoteMemory(DWORD64 address, DWORD length)銷毀對端內(nèi)存
新版本讀寫 API 接口在讀寫內(nèi)存之前需要提前設(shè)置進(jìn)程 PID 號,后期的調(diào)用將不需要再傳入進(jìn)程 PID,此類讀寫適合長期讀,某些 FPS 射擊類游戲的人物數(shù)組,3D 類游戲坐標(biāo)由于坐標(biāo)會頻繁移動,需持續(xù)不間斷讀取,此讀寫模塊將很適,接下來將帶大家分析并簡單使用這些 API 接口實(shí)現(xiàn)功能。
在使用?LyMemoryLib
?靜態(tài)庫之前請確保您已經(jīng)正確的配置了?Visual Studio
?引用頭文件。

如何安裝與卸載驅(qū)動:?讀寫的第一步是安裝驅(qū)動并將其運(yùn)行,當(dāng)然你可以通過第三方組件對驅(qū)動進(jìn)行安裝,也可以使用?LyMemoryLib
?中的函數(shù)實(shí)現(xiàn)安裝,如下則是通過?LyMemoryLib.hpp
?將驅(qū)動加載的完整實(shí)現(xiàn);
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com// 安裝驅(qū)動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("驅(qū)動路徑: %s \n", szSysFile);
index = index + 1; // 安裝驅(qū)動
ref = Memory.Install(szSysFile, szSvcLnkName, szSvcLnkName);
printf("安裝狀態(tài): %d \n", ref);
index = index + 1; // 啟動驅(qū)動
ref = Memory.Start();
printf("啟動狀態(tài): %d \n", ref);
index = index + 1; // 打開
ref = Memory.Open("\\\\.\\LyMemory");
printf("打開狀態(tài): %d \n", ref);
index = index + 1; if (index == 4 && ref == TRUE)
{ return TRUE;
} return FALSE;
}// 卸載驅(qū)動BOOL RemoveDriver(LyMemoryDrvCtrl Memory){
BOOL ref = 0; // 關(guān)閉
ref = Memory.Stop();
printf("關(guān)閉狀態(tài): %d \n", ref); // 移除
ref = Memory.Remove();
printf("移除狀態(tài): %d \n", ref); return ref;
}int main(int argc, char* argv[]){
LyMemoryDrvCtrl DriveControl; // 加載驅(qū)動
BOOL ref = InstallDriver(DriveControl); if (ref == TRUE)
{
printf("[*] 驅(qū)動已加載 \n");
} // 卸載驅(qū)動
RemoveDriver(DriveControl);
system("pause"); return 0;
}
如上代碼編譯后并以管理員權(quán)限運(yùn)行,則會將驅(qū)動?LyMemory.sys
?自動加載,并在調(diào)試板輸出如下圖所示的信息;

設(shè)置 PID 進(jìn)程綁定:?如果需要使用讀寫函數(shù),第一步則是設(shè)置進(jìn)程PID
?綁定,通??赏ㄟ^?SetPid(DWORD Pid)
?函數(shù)傳入進(jìn)程?PID
?進(jìn)行綁定操作,一旦進(jìn)程被綁定則后續(xù)無需再次打開,提高了讀寫效率,也可預(yù)防多次附加脫離導(dǎo)致應(yīng)用層異常,如果需要使用設(shè)置 PID 則你可以這樣來寫;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
}
system("pause"); return 0;
}
運(yùn)行如上代碼所示將自動綁定到進(jìn)程?6536
?并輸出綁定狀態(tài),如下圖所示;

內(nèi)核讀取模塊基址:?由于目前進(jìn)程已被附加到到驅(qū)動上,此時可以調(diào)用?GetModuleAddress()
?獲取進(jìn)程內(nèi)特定模塊的基址,此函數(shù)接收一個模塊名;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置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;
}
如上代碼編譯并運(yùn)行,則取出被附加進(jìn)程內(nèi)?user32.dll
?以及?kernel32.dll
?的模塊基址,輸出效果圖如下所示;

取內(nèi)核函數(shù)基址:?與取應(yīng)用層模塊基址類似,函數(shù)?GetSystemRoutineAddress
?可用于獲取到內(nèi)核模塊中特定導(dǎo)出函數(shù)的內(nèi)存基址。
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 取函數(shù)地址
CHAR *SzFunction[3] = { "NtReadFile", "NtClose", "NtSetEvent" }; for (size_t i = 0; i < 3; i++)
{
DWORD64 ptr = DriveControl.GetSystemRoutineAddress(SzFunction[i]); printf("函數(shù) = %s | 地址 = 0x%p \n", SzFunction[i], ptr);
}
system("pause"); return 0;
}
運(yùn)行如上方所示的代碼片段,則自動取出?"NtReadFile", "NtClose", "NtSetEvent"
?三個函數(shù)的內(nèi)存地址,輸出效果圖如下所示;

分配與釋放堆空間:?在對端內(nèi)存中開辟一段內(nèi)存可調(diào)用?CreateRemoteMemory
?函數(shù)實(shí)現(xiàn),釋放堆空間則可調(diào)用?DeleteRemoteMemory
?函數(shù),默認(rèn)情況下分配的空間自帶讀寫執(zhí)行屬性,為?Hook掛鉤
轉(zhuǎn)向提供可能。
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 分配內(nèi)存空間
DWORD64 address = DriveControl.CreateRemoteMemory(1024); printf("[+] 已分配內(nèi)存 = 0x%p \n", address); // 釋放內(nèi)存
BOOL del = DriveControl.DeleteRemoteMemory(address, 1024); if (del == TRUE)
{ printf("[-] 內(nèi)存空間 0x%p 已被釋放 \n", address);
}
system("pause"); return 0;
}
如上代碼片段運(yùn)行后,將在對端內(nèi)存中分配?address
?的地址,分配后自動將其釋放,輸出效果圖如下所示;

讀 / 寫內(nèi)存整數(shù)型:?整數(shù)類型的讀取可調(diào)用?ReadMemoryDWORD
?讀取 32 位整數(shù),調(diào)用?ReadMemoryDWORD64
?則讀取 64 位整數(shù)型;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 讀取32位整數(shù)
DWORD read_value = 0;
BOOL read_flag = DriveControl.ReadMemoryDWORD(0x0188F828, &read_value); if (read_flag == TRUE)
{ printf("[*] 讀取32位數(shù)據(jù) = %d \n", read_value);
} // 讀取64位整數(shù)
DWORD64 read64_value = 0;
BOOL read64_flag = DriveControl.ReadMemoryDWORD64(0x0188F828, &read64_value); if (read64_flag == TRUE)
{ printf("[*] 讀取64位數(shù)據(jù) = %d \n", read64_value);
}
system("pause"); return 0;
}
編譯并運(yùn)行如上代碼片段,則會讀取?0x0188F828
?處的整數(shù)類型數(shù)據(jù),讀取輸出效果圖如下所示;

寫入整數(shù)類型同理,調(diào)用?WriteMemoryDWORD
?寫出 32 位整數(shù),調(diào)用?WriteMemoryDWORD64
?寫出 64 位整數(shù);
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 寫入32位整數(shù)
BOOL write32 = DriveControl.WriteMemoryDWORD(0x0188F828, 1000); if (write32 == TRUE)
{ printf("[+] 寫出數(shù)據(jù)完成 \n");
} // 寫入64位整數(shù)
BOOL write64 = DriveControl.WriteMemoryDWORD64(0x0188F828, 2000); if (write64 == TRUE)
{ printf("[+] 寫出數(shù)據(jù)完成 \n");
}
system("pause"); return 0;
}
編譯并運(yùn)行代碼,將向目標(biāo)進(jìn)程分別寫出?1000
?及?2000
,代碼輸出效果如下圖所示;

讀 / 寫內(nèi)存字節(jié)集:?內(nèi)存讀寫字節(jié)集可調(diào)用?ReadMemoryBytes
?函數(shù),寫出字節(jié)集調(diào)用?WriteMemoryBytes
?函數(shù);
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 讀取字節(jié)集
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("[+] 讀取字節(jié): 0x%x \n", buffer[x]);
}
}
system("pause"); return 0;
}
運(yùn)行如上代碼片段,即可在內(nèi)存?0x401000
?處開始讀取字節(jié)集,向后讀取 8 字節(jié),并存入?buffer
?中,輸出效果圖如下所示;

寫出字節(jié)集與讀取基本一致,函數(shù)?WriteMemoryBytes
?則用于寫出字節(jié)集數(shù)據(jù),寫出是需傳遞一個定義好的字節(jié)數(shù)組;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 寫內(nèi)存字節(jié)集
BYTE writebuff[4] = { 0x90, 0x90, 0x90, 0x90 };
BOOL flag = DriveControl.WriteMemoryBytes(0x401000, writebuff, sizeof(writebuff)); if (flag == TRUE)
{ printf("[+] 寫出字節(jié)集完成 \n");
}
system("pause"); return 0;
}
運(yùn)行如上代碼片段,則將字節(jié)集寫出到?0x401000
?內(nèi)存處,寫出效果如下圖所示;

讀 / 寫內(nèi)存浮點(diǎn)數(shù):?浮點(diǎn)數(shù)可分為單浮點(diǎn)與雙浮點(diǎn),單浮點(diǎn)可使用?ReadMemoryFloat
?實(shí)現(xiàn)讀寫,雙浮點(diǎn)則調(diào)用?ReadMemoryDouble
?實(shí)現(xiàn),兩者實(shí)現(xiàn)原理完全一致,僅僅只是讀寫時多出了 4 個字節(jié)的寬度而已。
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 讀取單浮點(diǎn)
float read_float = 0;
BOOL float_flag = DriveControl.ReadMemoryFloat(0x01894EF8, &read_float); if (float_flag == TRUE)
{ printf("[+] 讀取單精度 = %f \n", read_float);
} // 讀取雙浮點(diǎn)
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;
}
運(yùn)行后輸出兩個浮點(diǎn)數(shù),注意雙精度此處并不是錯誤而是輸出問題,效果圖如下所示;

那么如何寫出數(shù)據(jù)呢,只需要調(diào)用?WriteMemoryFloat
?即可實(shí)現(xiàn)寫出浮點(diǎn)數(shù)的目的;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 寫取單浮點(diǎn)
BOOL ref = DriveControl.WriteMemoryFloat(0x01894EF8, 100.245); if (ref == TRUE)
{ printf("[+] 寫出數(shù)據(jù)完成 \n");
}
system("pause"); return 0;
}
以單精度浮點(diǎn)數(shù)為例,寫出數(shù)據(jù)后輸出如下效果;

計算多級偏移動態(tài)地址:?函數(shù)?ReadDeviationMemory32
?可實(shí)現(xiàn)動態(tài)計算多級偏移的功能,該函數(shù)最多可接受 32 級偏移的計算,計算后可得到一個動態(tài)地址,用戶得到動態(tài)地址后可對其地址執(zhí)行讀寫整數(shù),字節(jié),字節(jié)集,浮點(diǎn)數(shù)等各類操作,我們以整數(shù)讀寫為例子;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(6536); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 計算四級偏移動態(tài)地址
ProcessDeviationMemory read_offset_struct = { 0 };
read_offset_struct.Address = 0x6566e0; ? ? ? ?// 基地址
read_offset_struct.OffsetSize = 4; ? ? ? ? ? ?// 偏移長度
read_offset_struct.Data = 0; ? ? ? ? ? ? ? ? ?// 讀入的數(shù)據(jù)
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("[+] 得到動態(tài)地址 = 0x%016lx \n", BaseAddress); // 讀取整數(shù)
DWORD GetDWORD = 0;
BOOL flag = DriveControl.ReadMemoryDWORD(BaseAddress, &GetDWORD); if (flag == TRUE)
{ printf("[+] 讀取數(shù)據(jù) = %d \n", GetDWORD);
}
system("pause"); return 0;
}
如上代碼通過調(diào)用?ReadDeviationMemory32
?計算出當(dāng)前動態(tài)地址的基址,并通過?ReadMemoryDWORD
?讀取此處的內(nèi)存 DWORD 類型,輸出效果如下所示;

內(nèi)存讀寫反匯編:?讀寫函數(shù)我們可使用?ReadMemoryBytes
?實(shí)現(xiàn)字節(jié)集的讀取,通過運(yùn)用?capstone
?反匯編引擎即可對特定內(nèi)存空間進(jìn)行反匯編操作;
// 署名權(quán)// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comint main(int argc, char *argv[]){
LyMemoryDrvCtrl DriveControl;
DriveControl.InstallAndRun();
BOOL set_pid = DriveControl.SetPid(5588); if (set_pid == TRUE)
{ printf("[*] 設(shè)置PID = %d \n", set_pid);
} // 讀取前1024個字節(jié)
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開始,返回總條數(shù)
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("機(jī)器碼: %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;
}
運(yùn)行后即可對進(jìn)程中?0x401000
?的內(nèi)存區(qū)域向下反匯編?1024
?個字節(jié),輸出效果圖如下所示;

項目地址
https://github.com/lyshark/LyMemory