针对 Linux 平台上远程进程内存读写和搜索的工具实现
BigWhiteRead是一个用于Linux平台的内存操作工具,支持远程进程内存读写和搜索功能。当前版本为1.0,支持多种CPU架构,包括x86、arm、arm64和x86_64。工具利用Linux系统调用process_vm_readv和process_vm_writev实现对远程进程内存的读写操作。
- 支持多种CPU架构(x86、arm、arm64、x86_64)
- 进程内存读写(DWORD、FLOAT、指针等)
- 多种数据类型搜索(DWORD、FLOAT、BYTE、WORD、QWORD、XOR、DOUBLE)
- 支持偏移量搜索
- 支持多级指针操作
- 支持特征码搜索
- Linux操作系统
- 支持的CPU架构:x86、arm、arm64、x86_64
- 需要具有对目标进程的访问权限(通常需要root权限)
- 懒人精灵环境支持
-- 初始化内存操作对象
Mem = {
Pid = 0
}
print("欢迎使用懒人内存插件 当前版本号1.0,懒人精灵交流QQ群:308254130")
work = getWorkPath() -- 脚本工作目录
local type = getCpuArch() -- 获取CPU架构
extractAssets("内存插件.rc", work) -- 释放内存so库
local sopath = ""
print("当前CPU结构"..type)
-- 根据CPU架构选择对应的SO库
if type == 0 then
sopath = work .. "/liblrapi_x86.so"
print("当前系统架构:x86")
elseif type == 1 then
sopath = work .. "/liblrapi_arm.so"
print("当前系统架构:arm")
elseif type == 2 then
sopath = work .. "/liblrapi_arm64.so"
print("当前系统架构:arm64")
elseif type == 3 then
sopath = work .. "/liblrapi_x86_64.so"
print("当前系统架构:x86_64")
end
-- 定义C函数原型
ffi.cdef[[
typedef struct {
long* addrs;
int count;
} AddressData;
typedef struct {
long long value;
int type;
unsigned long offset;
} SearchCondition;
typedef struct {
long* addresses;
int count;
} SearchResult;
SearchResult BigWhite_SearchWithOffset(int BigWhitePid, const SearchCondition* conditions, int conditionCount, int mem);
void BigWhite_FreeSearchResult(SearchResult* result);
int BigWhite_GetPID(const char *packageName);
int BigWhite_GetPID2(const char *packageName);
unsigned long BigWhite_GetModuleBase(int pid, const char *module_name);
unsigned long BigWhite_GetPtr64(int BigWhitePid, unsigned long addr);
unsigned long BigWhite_GetPtr32(int BigWhitePid, unsigned long addr);
int BigWhite_GetDword(int BigWhitePid, unsigned long addr);
float BigWhite_GetFloat(int BigWhitePid, unsigned long addr);
bool BigWhite_WriteDword(int BigWhitePid, unsigned long addr, int data);
bool BigWhite_WriteFloat(int BigWhitePid, unsigned long addr, float data);
AddressData Search_DWORD(int BigWhitePid, int value, int mem);
AddressData Search_FLOAT(int BigWhitePid, float value, int mem);
AddressData Search_BYTE(int BigWhitePid, char value, int mem);
AddressData Search_WORD(int BigWhitePid, short value, int mem);
AddressData Search_QWORD(int BigWhitePid, long long value, int mem);
AddressData Search_XOR(int BigWhitePid, int value, int mem);
AddressData Search_DOUBLE(int BigWhitePid, double value, int mem);
]]
-- 内存区域类型常量定义
--[===[
Mem_Auto = 0 -- 所有内存页
Mem_A = 1 -- 通常为进程主要内存区域
Mem_Ca = 2 -- 堆内存区域(libc_malloc)
Mem_Cd = 3 -- 数据区域(/data/data/)
Mem_Cb = 4 -- BSS段(:bss)
Mem_Jh = 5 -- 堆内存
Mem_J = 6 -- Java堆内存([anon:dalvik)
Mem_S = 7 -- 栈内存([stack])
Mem_V = 8 -- 显存(/dev/kgsl-3d0)
Mem_Xa = 9 -- 应用程序代码区域(/data/app/)
Mem_Xs = 10 -- 系统框架区域(/system/framework/)
Mem_As = 11 -- 共享内存区域(/dev/ashmem/)
Mem_B = 12 -- 系统字体区域(/system/fonts/)
Mem_O = 13 -- 其他内存区域
]===]
-- 加载共享库
local mylib = ffi.load(sopath)
-- 检查是否成功加载
if mylib then
print("内存插件加载成功!")
else
print("内存插件加载失败!")
exitScript()
end
-- Lua包装函数实现
function Mem.GetPID(PackageName)
Mem.Pid = mylib.BigWhite_GetPID(PackageName)
return Mem.Pid
end
function Mem.GetPID2(PackageName)
Mem.Pid = mylib.BigWhite_GetPID2(PackageName)
return Mem.Pid
end
function Mem.GetModuleBase(Lib)
return mylib.BigWhite_GetModuleBase(Mem.Pid, Lib)
end
function Mem.GetPtr64(Addr)
return mylib.BigWhite_GetPtr64(Mem.Pid, Addr)
end
function Mem.GetPtr32(Addr)
return mylib.BigWhite_GetPtr32(Mem.Pid, Addr)
end
function Mem.GetDword(Addr)
return mylib.BigWhite_GetDword(Mem.Pid, Addr)
end
function Mem.GetFloat(Addr)
return mylib.BigWhite_GetFloat(Mem.Pid, Addr)
end
function Mem.WriteDword(Addr, value)
return mylib.BigWhite_WriteDword(Mem.Pid, Addr, value)
end
function Mem.WriteFloat(Addr, value)
return mylib.BigWhite_WriteFloat(Mem.Pid, Addr, value)
end
function Mem.Search_DWORD(value, mem)
return mylib.Search_DWORD(Mem.Pid, value, mem)
end
function Mem.Search_FLOAT(value, mem)
return mylib.Search_FLOAT(Mem.Pid, value, mem)
end
function Mem.Search_BYTE(value, mem)
return mylib.Search_BYTE(Mem.Pid, value, mem)
end
function Mem.Search_WORD(value, mem)
return mylib.Search_WORD(Mem.Pid, value, mem)
end
function Mem.Search_QWORD(value, mem)
return mylib.Search_QWORD(Mem.Pid, value, mem)
end
function Mem.Search_XOR(value, mem)
return mylib.Search_XOR(Mem.Pid, value, mem)
end
function Mem.Search_DOUBLE(value, mem)
return mylib.Search_DOUBLE(Mem.Pid, value, mem)
end
function Mem.SearchWithOffset(conditions, mem)
if not conditions or #conditions == 0 then
return {count = 0, addresses = {}}
end
local c_conditions = ffi.new("SearchCondition[?]", #conditions)
for i, condition in ipairs(conditions) do
if not condition.value or not condition.type or not condition.offset then
return {count = 0, addresses = {}}
end
c_conditions[i-1].value = tonumber(condition.value) or 0
c_conditions[i-1].type = tonumber(condition.type) or 0
c_conditions[i-1].offset = tonumber(condition.offset) or 0
end
local result = mylib.BigWhite_SearchWithOffset(Mem.Pid, c_conditions, #conditions, mem)
if not result or result.count <= 0 or not result.addresses then
return {count = 0, addresses = {}}
end
local results = {
count = 0,
addresses = {}
}
for i = 0, result.count - 1 do
local addr = result.addresses[i]
if addr and addr ~= 0 then
results.count = results.count + 1
table.insert(results.addresses, addr)
end
end
Mem.FreeSearchResult(ffi.new("SearchResult*", result))
return results
end
function Mem.FreeSearchResult(result)
mylib.BigWhite_FreeSearchResult(result)
end
function ReadPointer(...)
local ResultAddr = 0
for i, v in ipairs({...}) do
ResultAddr = Mem.GetPtr64(ResultAddr + v)
end
return ResultAddr
end工具会自动根据系统CPU架构释放对应的SO库文件:
- x86:liblrapi_x86.so
- arm:liblrapi_arm.so
- arm64:liblrapi_arm64.so
- x86_64:liblrapi_x86_64.so
// 搜索结果数据结构
typedef struct {
long* addrs; // 长整型地址数组
int count; // 结果数量
} AddressData;
// 搜索条件结构体
typedef struct {
long long value; // 搜索值
int type; // 数据类型
unsigned long offset; // 偏移量
} SearchCondition;
// 带偏移搜索结果结构体
typedef struct {
long* addresses; // 地址数组
int count; // 结果数量
} SearchResult;-- 方法1: 获取指定包名的进程PID
Mem.GetPID(packageName)
-- 参数:
-- packageName: string - 目标进程的包名
-- 返回值: number - 进程PID,失败返回0
-- 示例:
local pid = Mem.GetPID("com.example.app")
print("进程PID:", pid) -- 输出可能为: 进程PID: 12345-- 方法2: 获取指定包名的最后一个进程PID
Mem.GetPID2(packageName)
-- 参数:
-- packageName: string - 目标进程的包名
-- 返回值: number - 最后一个匹配的进程PID,未找到返回0
-- 示例:
local pid = Mem.GetPID2("com.example.app")
print("最后匹配的进程PID:", pid) -- 输出可能为: 最后匹配的进程PID: 12345两个函数的区别:
GetPID: 返回第一个匹配的进程PIDGetPID2: 返回最后一个匹配的进程PID,适用于有多个同名进程的情况
-- 获取指定进程中模块的基址
Mem.GetModuleBase(libName)
-- 参数:
-- libName: string - 模块名称,例如"libil2cpp.so"
-- 返回值: number - 模块基址,未找到返回0
-- 示例:
local base = Mem.GetModuleBase("libil2cpp.so")
print("模块基址:", string.format("0x%X", base)) -- 输出可能为: 模块基址: 0x7F1234000000-- 读取64位指针
Mem.GetPtr64(addr)
-- 参数:
-- addr: number - 内存地址
-- 返回值: number - 64位指针值
-- 示例:
local ptr = Mem.GetPtr64(0x7F1234000000)
print("64位指针值:", string.format("0x%X", ptr)) -- 输出可能为: 64位指针值: 0x7F1234001000-- 读取32位指针
Mem.GetPtr32(addr)
-- 参数:
-- addr: number - 内存地址
-- 返回值: number - 32位指针值
-- 示例:
local ptr = Mem.GetPtr32(0x7F1234000000)
print("32位指针值:", string.format("0x%X", ptr)) -- 输出可能为: 32位指针值: 0x12340000-- 读取DWORD值(32位整数)
Mem.GetDword(addr)
-- 参数:
-- addr: number - 内存地址
-- 返回值: number - 32位整数值
-- 示例:
local value = Mem.GetDword(0x7F1234000000)
print("DWORD值:", value) -- 输出可能为: DWORD值: 1234567-- 读取浮点数
Mem.GetFloat(addr)
-- 参数:
-- addr: number - 内存地址
-- 返回值: number - 浮点数值
-- 示例:
local value = Mem.GetFloat(0x7F1234000000)
print("浮点数值:", value) -- 输出可能为: 浮点数值: 123.456-- 写入DWORD值(32位整数)
Mem.WriteDword(addr, value)
-- 参数:
-- addr: number - 内存地址
-- value: number - 要写入的32位整数值
-- 返回值: boolean - 写入是否成功
-- 示例:
local success = Mem.WriteDword(0x7F1234000000, 9999)
print("写入是否成功:", success) -- 输出可能为: 写入是否成功: true-- 写入浮点数
Mem.WriteFloat(addr, value)
-- 参数:
-- addr: number - 内存地址
-- value: number - 要写入的浮点数值
-- 返回值: boolean - 写入是否成功
-- 示例:
local success = Mem.WriteFloat(0x7F1234000000, 999.99)
print("写入是否成功:", success) -- 输出可能为: 写入是否成功: true所有搜索函数都遵循相同的参数结构:
-- 通用搜索函数形式
Mem.Search_XXX(value, memArea)
-- 参数:
-- value: 要搜索的值(类型取决于具体函数)
-- memArea: number - 内存区域类型(0表示搜索所有区域)
-- 返回值: table - 包含找到的地址列表和数量
-- 结构: { addrs = {长整型地址数组}, count = 结果数量 }具体搜索函数:
-- 搜索DWORD值(32位整数)
Mem.Search_DWORD(value, memArea)
-- 示例:
local result = Mem.Search_DWORD(12345, 0)
print("找到结果数量:", result.count)
-- 输出第一个找到的地址(如果有)
if result.count > 0 then
print("第一个地址:", string.format("0x%X", result.addrs[1]))
end-- 搜索浮点数
Mem.Search_FLOAT(value, memArea)
-- 示例:
local result = Mem.Search_FLOAT(123.45, 0)
print("找到结果数量:", result.count)-- 搜索字节
Mem.Search_BYTE(value, memArea)
-- 示例:
local result = Mem.Search_BYTE(255, 0) -- 搜索值为255的字节
print("找到结果数量:", result.count)-- 搜索WORD(16位整数)
Mem.Search_WORD(value, memArea)
-- 示例:
local result = Mem.Search_WORD(12345, 0)
print("找到结果数量:", result.count)-- 搜索QWORD(64位整数)
Mem.Search_QWORD(value, memArea)
-- 示例:
local result = Mem.Search_QWORD(1234567890123, 0)
print("找到结果数量:", result.count)-- 搜索XOR加密值
Mem.Search_XOR(value, memArea)
-- 示例:
local result = Mem.Search_XOR(12345, 0)
print("找到结果数量:", result.count)-- 搜索双精度浮点数
Mem.Search_DOUBLE(value, memArea)
-- 示例:
local result = Mem.Search_DOUBLE(123.45678, 0)
print("找到结果数量:", result.count)-- 带偏移量的多条件搜索
Mem.SearchWithOffset(conditions, memArea)
-- 参数:
-- conditions: table - 搜索条件数组,每个条件包含:
-- value: number - 搜索值
-- type: number - 数据类型(0=DWORD, 1=FLOAT等)
-- offset: number - 相对于基址的偏移量
-- memArea: number - 内存区域类型(0表示搜索所有区域)
-- 返回值: table - 包含找到的地址列表和数量
-- 结构: { addresses = {长整型地址数组}, count = 结果数量 }
-- 示例:
local conditions = {
{value = 100, type = 0, offset = 0}, -- 基地址处的DWORD值为100
{value = 3.14, type = 1, offset = 4}, -- 基地址+4处的FLOAT值为3.14
{value = 200, type = 0, offset = 8} -- 基地址+8处的DWORD值为200
}
local results = Mem.SearchWithOffset(conditions, 0)
print("符合条件的地址数量:", results.count)
-- 遍历所有结果
for i = 1, results.count do
print("地址" .. i .. ":", string.format("0x%X", results.addresses[i]))
end内存区域类型常量,用于指定搜索范围:
-- 常量定义
Mem_Auto = 0 -- 所有内存页
Mem_A = 1 -- 通常为进程主要内存区域
Mem_Ca = 2 -- 堆内存区域(libc_malloc)
Mem_Cd = 3 -- 数据区域(/data/data/)
Mem_Cb = 4 -- BSS段(:bss)
Mem_Jh = 5 -- 堆内存
Mem_J = 6 -- Java堆内存([anon:dalvik)
Mem_S = 7 -- 栈内存([stack])
Mem_V = 8 -- 显存(/dev/kgsl-3d0)
Mem_Xa = 9 -- 应用程序代码区域(/data/app/)
Mem_Xs = 10 -- 系统框架区域(/system/framework/)
Mem_As = 11 -- 共享内存区域(/dev/ashmem/)
Mem_B = 12 -- 系统字体区域(/system/fonts/)
Mem_O = 13 -- 其他内存区域-- 读取多级指针
ReadPointer(...)
-- 参数:
-- ...: 可变参数,表示多级指针的偏移链
-- 返回值: number - 最终指针指向的地址
-- 示例:
-- 假设内存结构: 基址 -> 偏移0x10 -> 偏移0x20 -> 目标值
local baseAddr = 0x7F1234000000
local finalAddr = ReadPointer(baseAddr, 0x10, 0x20)
print("最终地址:", string.format("0x%X", finalAddr))
-- 等价于以下手动操作:
local addr1 = Mem.GetPtr64(baseAddr + 0x10)
local finalAddr = Mem.GetPtr64(addr1 + 0x20)-- 释放搜索结果
Mem.FreeSearchResult(result)
-- 参数:
-- result: table - 通过SearchWithOffset函数获得的搜索结果
-- 返回值: 无
-- 示例:
local results = Mem.SearchWithOffset(conditions, 0)
-- 使用结果...
Mem.FreeSearchResult(results) -- 释放资源-- 初始化并连接到指定进程
Mem.GetPID("com.example.game")
if Mem.Pid == 0 then
print("未找到目标进程")
return
end
-- 获取游戏主模块基址
local baseAddr = Mem.GetModuleBase("libil2cpp.so")
if baseAddr == 0 then
print("未找到游戏模块")
return
end
-- 读取角色金币数量(假设在基址+0x12345678的位置)
local goldAddr = baseAddr + 0x12345678
local goldCount = Mem.GetDword(goldAddr)
print("当前金币:", goldCount)
-- 修改金币数量
Mem.WriteDword(goldAddr, 99999)
print("修改后金币:", Mem.GetDword(goldAddr))-- 初始化并连接到指定进程
Mem.GetPID("com.example.game")
if Mem.Pid == 0 then
print("未找到目标进程")
return
end
-- 假设玩家当前生命值为100,搜索这个值
print("请确保角色生命值为100,然后开始搜索...")
local results = Mem.Search_DWORD(100, 0)
print("初次搜索结果:", results.count)
-- 假设玩家受到伤害,生命值变为80
print("请让角色受到伤害,生命值变为80,然后进行第二次搜索...")
-- 筛选出从100变为80的地址
local validAddrs = {}
for i = 1, results.count do
local addr = results.addrs[i]
local value = Mem.GetDword(addr)
if value == 80 then
table.insert(validAddrs, addr)
end
end
print("筛选后可能的生命值地址数量:", #validAddrs)
-- 修改所有可能的地址
for i, addr in ipairs(validAddrs) do
Mem.WriteDword(addr, 999)
print("修改地址:", string.format("0x%X", addr))
end
print("已将所有可能的生命值修改为999")-- 初始化并连接到指定进程
Mem.GetPID("com.example.game")
if Mem.Pid == 0 then
print("未找到目标进程")
return
end
-- 获取游戏主模块基址
local baseAddr = Mem.GetModuleBase("libil2cpp.so")
if baseAddr == 0 then
print("未找到游戏模块")
return
end
-- 使用多级指针找到目标地址
-- 假设指针链为: 基址 + 0x1234000 -> +0x20 -> +0x10 -> +0x8 -> 目标值
local targetAddr = ReadPointer(baseAddr + 0x1234000, 0x20, 0x10, 0x8)
print("目标地址:", string.format("0x%X", targetAddr))
-- 读取目标地址的值
local value = Mem.GetFloat(targetAddr)
print("当前值:", value)
-- 修改值
Mem.WriteFloat(targetAddr, 999.999)
print("修改后的值:", Mem.GetFloat(targetAddr))-- 初始化并连接到指定进程
Mem.GetPID("com.example.game")
if Mem.Pid == 0 then
print("未找到目标进程")
return
end
-- 假设游戏角色数据结构如下:
-- 偏移0: 生命值(DWORD) = 100
-- 偏移4: 魔法值(DWORD) = 50
-- 偏移8: 攻击力(FLOAT) = 25.5
local conditions = {
{value = 100, type = 0, offset = 0}, -- 生命值
{value = 50, type = 0, offset = 4}, -- 魔法值
{value = 25.5, type = 1, offset = 8} -- 攻击力
}
-- 搜索符合上述结构的内存地址
local results = Mem.SearchWithOffset(conditions, 0)
print("找到符合条件的地址数量:", results.count)
-- 修改所有符合条件的地址
for i = 1, results.count do
local baseAddr = results.addresses[i]
-- 修改生命值
Mem.WriteDword(baseAddr, 999)
-- 修改魔法值
Mem.WriteDword(baseAddr + 4, 999)
-- 修改攻击力
Mem.WriteFloat(baseAddr + 8, 999.9)
print("已修改地址:", string.format("0x%X", baseAddr))
end
-- 释放搜索结果
Mem.FreeSearchResult(results)- 使用前请确保已获取正确的目标进程PID
- 内存操作可能需要root权限
- 对内存进行修改可能导致目标应用程序崩溃,请谨慎操作
- 搜索结果使用后需要调用
Mem.FreeSearchResult()释放资源 - 不同的内存区域有不同的权限,请根据实际情况选择合适的内存区域
- 进行内存修改操作前,建议先备份重要数据
- 工具的使用受限于Linux系统权限,某些系统上可能无法正常工作
- 使用FFI库可能会因系统限制导致兼容性问题,请确保系统支持FFI操作
懒人精灵交流QQ群: 308254130
© 2023 BigWhiteRead 项目团队