-- =============𝓖𝓖 𝓛𝓾𝓪================================ -- 全局统一配置 其它游戏模块自改即可 冷门ov指针啥的自己添加 -- =================================================== --此脚本只输出0x6位数的主偏移 cb段可自定义输出地址数量 --可固定cb主偏移扫描 直接写入第四个框即可 例0x123456,记得0x也要写 --不填16384 XA内存 即可直接扫Cd的so模块 local Nc = 8|16|32 -- 统一定义内存范围 正常就这几个指针类型 --local Nc = 4|8|16|32|1048576|-2080896 local BSSMK = '^/data/*libUE4.so:bss$' -- Cb运算指定模块定义 local SOMK = '^/data/*libUE4.so$' -- so运算指定模块定义 local Zpycd = 0x100000 -- 锁链主偏移位宽长度 0x6位数默认0x100000,短位修改此处即可 --报错自行更换专用修改器 模块是啥都不懂 你还扫个🐔8️⃣ --[[=========脚本支持32,64通用============ Jh内存 gg.REGION_JAVA_HEAP 数字值 2 Ch内存 gg.REGION_C_HEAP 数字值 1 Ca内存 gg.REGION_C_ALLOC 数字值 4 Cd内存 gg.REGION_C_DATA 数字值 8 Cb内存 gg.REGION_C_BSS 数字值 16 Ps内存 gg.REGION_PPSSPP 数字值 262144 A内存 gg.REGION_ANONYMOUS 数字值 32 J内存 gg.REGION_JAVA 数字值 65536 S内存 gg.REGION_STACK 数字值 64 As内存 gg.REGION_ASHMEM 数字值 524288 V内存 gg.REGION_VIDEO 数字值 1048576 O内存 gg.REGION_OTHER 数字值 -2080896 B内存 gg.REGION_BAD 数字值 131072 Xa内存 gg.REGION_CODE_APP 数字值 16384 Xs内存 gg.REGION_CODE_SYS 数字值 32768 ======================================]] gg.loadResults(gg.getSelectedListItems(1)) gg.setRanges(Nc) function nium() local nmnb local nmmm=gg.getTargetInfo() local dqws = nmmm and nmmm.x64 and 64 or 32 if nmmm==nil then nmnb = "基于: [Chainer] 牛马纯C版\n当前时间: "..os.date("%Y-%m-%d [%H:%M]") else nmnb = "基于: [Chainer] 牛马纯C版\n当前应用: "..nmmm['label'].." ["..dqws.."位]".."\n应用包名: "..nmmm['cmdLine'].."\n当前版本: "..nmmm['versionName'] end return nmnb end local file_application if gg.getTargetInfo() then file_application=gg.getTargetInfo()['label'] else file_application="" end function shuchupz(basePath, prefix, suffix) --自动删除带空格的应用名 确保txt文件完整 local zdbckg = prefix:gsub("%s+", "") local filePath = basePath .. zdbckg .. suffix if suffix == ".txt" and not filePath:find("%.txt$") then filePath = filePath .. ".txt" end if not io.open(filePath) then return filePath end for i = 2, 999 do local uniquePath = basePath .. zdbckg .. i .. suffix if suffix == ".txt" and not uniquePath:find("%.txt$") then uniquePath = uniquePath .. ".txt" end if not io.open(uniquePath) then return uniquePath end end local finalPath = basePath .. zdbckg .. os.time() .. suffix if suffix == ".txt" and not finalPath:find("%.txt$") then finalPath = finalPath .. ".txt" end return finalPath end function JT() function QQXX(QX) if type(QX) == "number" and QX == math.floor(QX) then return math.tointeger(QX) end return QX end function shuchupz(shuc1, prefix, suffix) local zdbckg = prefix:gsub("%s+", "") local shucgs = shuc1 .. zdbckg .. suffix if suffix == ".lua" and not shucgs:find("%.lua$") then shucgs = shucgs .. ".lua" end if not io.open(shucgs) then return shucgs end for i = 2, 999 do local shuc3 = shuc1 .. zdbckg .. i .. suffix if suffix == ".lua" and not shuc3:find("%.lua$") then shuc3 = shuc3 .. ".lua" end if not io.open(shuc3) then return shuc3 end end local shuc2 = shuc1 .. zdbckg .. os.time() .. suffix if suffix == ".lua" and not shuc2:find("%.lua$") then shuc2 = shuc2 .. ".lua" end return shuc2 end local a = gg.getResults(gg.getResultCount()) local ranges = gg.getRangesList() if #a == 0 then gg.alert("当前列表无数据,🤡请把静态地址替换到GG搜索列表,XA Cd cb为静态,A内存你提取个鸡毛") return end local mt = shuchupz("/storage/emulated/0/", "静态输出", ".lua") local tdm = io.open(mt, "w") tdm:write([[function setvalue(address,flags,value) local tt={} tt[1]={} tt[1].address=address tt[1].flags=flags tt[1].value=value gg.setValues(tt) end --头代码 --GG直接运行即可食用 偏移➠类型➠修改值 ]]) for i, result in ipairs(a) do local original_value = QQXX(result.value) local value_type = result.flags local address = result.address local target_range = nil for _, range in ipairs(ranges) do if address >= range.start and address <= range["end"] then target_range = range break end end if not target_range then goto continue end local range_name = target_range.name:match('[^/]*$') local module_ranges = gg.getRangesList(range_name) if #module_ranges == 0 then goto continue end local module_base = module_ranges[1].start local offset = address - module_base local output = "so=gg.getRangesList('" .. range_name .. "')[1].start\n" .. "setvalue(so+0x" .. string.format("%X", offset) .. "," .. value_type .."," .. original_value .. ")\n".."\n" print(output) tdm:write(output) ::continue:: end tdm:close() local nm = #a gg.alert("静态基址数: " .. nm .."\n".."输出路径为:" .. mt) end function Ay() local Hs={ c_gglua, TXTwjdb, JT } local AA = gg.choice({ "🍋动态基址扫描", "🥒输出文件对比", "🥦静态基址提取" },nil,nium()) if AA then Hs[AA]() return end As=0 end function TXTwjdb() gg.setVisible(false) local shuclj = "/storage/emulated/0/" local shuchu = shuchupz(shuclj, "文件对比输出", ".txt") local ret = gg.prompt({ "请选择要[对比]的文本1:", "请选择要[对比]的文本2:", "请选择要[输出]的路径:" }, { shuclj.."文件1.txt", shuclj.."文件2.txt", shuchu }, { "file", "file", "file" }) if not ret or ret[1] == "" or ret[2] == "" or ret[3] == "" then gg.alert("选择了取消") gg.setVisible(true) return end local inputFile1 = ret[1] local inputFile2 = ret[2] local outputFile = ret[3] local contentMap = {} local file1LineCount = 0 local BLOCK_SIZE = 4 * 1024 * 1024 local buffer = "" local inFile1, err1 = io.open(inputFile1, "rb") if not inFile1 then gg.alert("无法打开文件1:" .. inputFile1 .. "\n错误:" .. (err1 or "权限不足/文件不存在")) gg.setVisible(true) return end while true do local chunk = inFile1:read(BLOCK_SIZE) if not chunk then break end buffer = buffer .. chunk while true do local lineEnd = buffer:find("\n") if not lineEnd then break end local rawLine = buffer:sub(1, lineEnd - 1) local cleanLine = rawLine:gsub("^%s+", ""):gsub("%s+$", "") buffer = buffer:sub(lineEnd + 1) if cleanLine ~= "" then contentMap[cleanLine] = true file1LineCount = file1LineCount + 1 end end collectgarbage("collect") end if buffer ~= "" then local rawLine = buffer local cleanLine = rawLine:gsub("^%s+", ""):gsub("%s+$", "") if cleanLine ~= "" then contentMap[cleanLine] = true file1LineCount = file1LineCount + 1 end end inFile1:close() inFile1 = nil collectgarbage("collect") if file1LineCount == 0 then gg.alert("文件1无有效内容,请检查文件是否为空或全为空白行") gg.setVisible(true) return end local outFile, err2 = io.open(outputFile, "wb") if not outFile then gg.alert("无法创建输出文件:" .. outputFile .. "\n错误:" .. (err2 or "权限不足/路径无效")) gg.setVisible(true) return end local matchCount = 0 local inFile2, err3 = io.open(inputFile2, "rb") if not inFile2 then gg.alert("无法打开文件2:" .. inputFile2 .. "\n错误:" .. (err3 or "权限不足/文件不存在")) outFile:close() gg.setVisible(true) return end buffer = "" while true do local chunk = inFile2:read(BLOCK_SIZE) if not chunk then break end buffer = buffer .. chunk while true do local lineEnd = buffer:find("\n") if not lineEnd then break end local rawLine = buffer:sub(1, lineEnd - 1) local cleanLine = rawLine:gsub("^%s+", ""):gsub("%s+$", "") buffer = buffer:sub(lineEnd + 1) if cleanLine ~= "" and contentMap[cleanLine] then outFile:write(rawLine .. "\n") outFile:flush() matchCount = matchCount + 1 end end collectgarbage("collect") end if buffer ~= "" then local rawLine = buffer local cleanLine = rawLine:gsub("^%s+", ""):gsub("%s+$", "") if cleanLine ~= "" and contentMap[cleanLine] then outFile:write(rawLine) outFile:flush() matchCount = matchCount + 1 end end inFile2:close() outFile:close() inFile2 = nil outFile = nil contentMap = nil collectgarbage("collect") gg.alert("对比完成!\n文件1有效行数:"..file1LineCount.."\n共找到 " .. matchCount .. " 条相同项\n已保存到:" .. outputFile) gg.setVisible(true) end function c_gglua() lx=gg.choice({ "🥦Cb 模块运算(墙裂推荐BSS)🥦", "🥦Cd 模块运算(不常用纯so)🥦" },nil,"当前脚本定义:所有libUE4,其他游戏要是模块不同,即可在文件开头自定义模块") if lx==nil then return elseif lx==1 then CBYS() elseif lx==2 then CDYS() end end function CDYS() gg.loadResults(gg.getSelectedListItems(1)) gg.setRanges(Nc) local depth, maxOffset, level, file, chains local ti = gg.getTargetInfo() gg.loadResults(gg.getSelectedListItems(1)) if gg.getResultsCount() == 1 then gg.setRanges(Nc) else gg.alert("请勾选保存列表中的一条值,一个内存页输出一条值就全够用了,死小白🤡") return end function printChain(pre, u) if u.offset == nil then chains = (chains + 1) file:write(pre.."\n") return pre else for offset, v in pairs(u.offset) do printChain(pre..string.format('+0x%X', offset), v) end end end function loadChain(lvl, p) local fix, maxo, lev = true, maxOffset, level local targetBitness = ti.x64 and 64 or 32 local addrMask = targetBitness == 64 and 0xFFFFFFFFFFFFFFFF or 0xFFFFFFFF for k = lvl, 1, -1 do local levk, p2, stop = lev[k], {}, true for j, u in pairs(p) do if u.offset == nil then u.offset = {} u.value = math.tointeger(u.value) or 0 u.value = u.value & addrMask for i, v in ipairs(levk) do local offset = v.address - u.value if offset >= 0 and offset <= maxo then u.offset[offset], p2[v], stop = v, v, false end end end end if stop then break end p = p2 end end function getRanges() local ranges = {} local archs = {[0x3] = 'x86', [0x28] = 'ARM', [0xC000003E] = 'ARM64', [0x80000000] = 'x86_64'} local t = gg.getRangesList(SOMK) local arch = 'unknown' for i, v in ipairs(t) do if v.type:sub(2, 2) == '-' then local t_val = gg.getValues({ {address = v.start, flags = 4}, {address = v.start + 0x12, flags = 32} }) if t_val[1].value == 0x464C457F then arch = archs[t_val[2].value] or 'unknown' end end if v.type:sub(2, 2) == 'w' then v.arch = arch table.insert(ranges, v) end end return ranges end local cfg_file = gg.EXT_FILES_DIR.."/"..gg.getFile():match("[^/]+$")..'1.cfg' local chunk = loadfile(cfg_file) local cfg = nil if chunk ~= nil then cfg = chunk() end if cfg == nil then cfg = {} end local pkg = gg.getTargetPackage() if pkg == nil then pkg = 'none' end local def = cfg[pkg] if def == nil then def = {3, 0x100} end local p = gg.prompt({ '深度: ', '最大偏移量: ' }, def, { 'number', 'number' }) if p == nil then return end level = {} cfg[pkg] = p gg.saveVariable(cfg, cfg_file) depth, maxOffset = p[1], tonumber(p[2]) local old = gg.getResults(gg.getResultCount()) local scpz1 = "/storage/emulated/0/" local scpz = ti['label'] .. "_so输出" local scpzlj1 = shuchupz(scpz1, scpz, ".txt") local ret while true do ret = gg.prompt({ '基址的输出文件', }, { scpzlj1, }, { 'new_file', }) if ret and ret[1] ~= "" then break end gg.alert("请选择有效的输出路径!") end p[3] = ret[1] file, err = io.open(p[3], 'w') if not file then gg.alert("无法创建输出文件:"..err.."\n请选择其他路径", "重试", "取消") return end chains = 0 gg.setVisible(false) local x = os.clock() local ranges = getRanges() if #ranges == 0 then gg.alert("未找到libUE4.so模块,请确认模块存在,游戏不是UE4请在脚本开头替换", "退出", "重试") file:close() os.remove(p[3]) gg.setVisible(true) return end while true do for lvl = 0, depth do if lvl > 0 then local t = gg.getResults(1000000) level[lvl] = t if (lvl == depth) then gg.setRanges(Nc) end gg.searchPointer(maxOffset) end for m, r in ipairs(ranges) do local p_res = gg.getResults(1000000, 0, r.start, r['end']) if #p_res > 0 then local out = {} gg.removeResults(p_res) loadChain(lvl, p_res) p_res.map = r table.insert(out, p_res) for j, p_item in ipairs(out) do for i, u in ipairs(p_item) do local sopy = string.format('0x%X', ( (u.address) - (p_item.map.start) )) printChain(p_item.map.internalName:gsub('^.*/', '').."+"..sopy, u) end end end end if gg.getResultsCount() == 0 then break end end gg.loadResults(old) file:close() local fm = sjxspz(os.clock() - x) if chains ~= 0 then gg.alert("耗时: "..fm.."\n".."深度: "..depth.." 最大偏移量: "..maxOffset.."\n".."输出基址数: "..(chains).." \n".."输出路径为: "..p[3]) else os.remove(p[3]) gg.alert("耗时: "..fm.."\n".."未找到符合条件的基址(libUE4.so段)或增加偏移量") end break end gg.setVisible(true) return end function CBYS() gg.loadResults(gg.getSelectedListItems(1)) gg.setRanges(Nc) local pathdjxh = os.getenv("EXTERNAL_STORAGE").."/Android/[C]配置数据对接信号" local pathdjpz = os.getenv("EXTERNAL_STORAGE").."/Android/[C]链表数据DATA.txt" local depth, maxOffset, iii, saomdz, fixedBaseOffset local ti = gg.getTargetInfo() gg.loadResults(gg.getSelectedListItems(1)) if gg.getResultsCount() == 1 then gg.setRanges(Nc) else gg.alert("请勾选保存列表中的一条值,一个内存页输出一条值就全够用了,死小白🤡") return end function take_Some() local data = {} local Somame = {} local t = gg.getRangesList(BSSMK) for i, v in ipairs(t) do if v.type:sub(2, 2) == 'w' then data[#data+1] = v.internalName:match("[^/]+$") end end for i = 1, #data do if (#data ~= i) then if data[i] ~= data[i+1] then Somame[#Somame+1] = data[i] end else Somame[#Somame+1] = data[i] end end return Somame end function getRanges() local ranges = {} local archs = {[0x3] = 'x86', [0x28] = 'ARM', [0xC000003E] = 'ARM64', [0x80000000] = 'x86_64'} local t = gg.getRangesList(BSSMK) local arch = 'unknown' for i, v in ipairs(t) do if v.type:sub(2, 2) == '-' then local t_val = gg.getValues({ {address = v.start, flags = 4}, {address = v.start + 0x12, flags = 32} }) if t_val[1].value == 0x464C457F then arch = archs[t_val[2].value] or 'unknown' end end if v.type:sub(2, 2) == 'w' then v.arch = arch table.insert(ranges, v) end end return ranges end local cfg_file = gg.EXT_FILES_DIR.."/"..gg.getFile():match("[^/]+$")..'1.cfg' local chunk = loadfile(cfg_file) local cfg = nil if chunk ~= nil then cfg = chunk() end if cfg == nil then cfg = {} end local pkg = gg.getTargetPackage() if pkg == nil then pkg = 'none' end local def = cfg[pkg] if def == nil then def = {3, 0x160, 1024, ""} end local p = gg.prompt({ '深度: ', '最大偏移量: (针对后面小指针)', '扫描地址数量: (留空默认扫描Cb全段)', '固定主偏移(就是链条第一个偏移,自身通杀专用,要填0x,留空默认): ' }, def, { 'number', 'number', 'number', 'text' }) if p == nil then return end cfg[pkg] = p gg.saveVariable(cfg, cfg_file) depth = tonumber(p[1]) or def[1] maxOffset = tonumber(p[2]) or def[2] saomdz = tonumber(p[3]) if not saomdz or saomdz <= 0 then saomdz = 1000000000 end fixedBaseOffset = p[4] and p[4] ~= "" and tonumber(p[4]) or nil if fixedBaseOffset then saomdz = 1 end local old = gg.getResults(1) local scpz1 = gg.getFile():gsub('[^/]*$', '') local scpz = file_application .. "_bss输出" local scpzlj = shuchupz(scpz1, scpz, ".txt") local scpzlj1 = shuchupz(scpz1, scpz, ".txt") local bss local ret = gg.prompt({ '基址的输出文件', '仅bss' }, { scpzlj1, true }, { 'new_file', 'checkbox' }) if not ret then ret = {false, false} return end bss = 1 ret[1] = scpzlj1 if ret[1] then p[3] = ret[1] end gg.setVisible(false) os.remove(pathdjxh, "e") os.remove(pathdjpz, "e") local x = os.clock() local ranges = getRanges() local Basenumber , Pointernumber = "", "" iii = 0 while true do for lvl = 0, depth do if lvl > 0 then local bl_1, bl_2, bl_3 = saomdz, 0, 0 if (gg.ExclusiveoutputG ~= nil) then bl_3 = gg.ExclusiveoutputG(pathdjpz) else local fileLE = io.open(pathdjpz, 'a') while true do local datak = gg.getResults(bl_1, bl_2) if (#datak ~= 0) then bl_2 = bl_2 + bl_1 for i, PointerData in ipairs(datak) do bl_3 = bl_3 + 1 local ptrAddr = math.tointeger(PointerData.address) or 0 local ptrValue = math.tointeger(PointerData.value) or 0 fileLE:write(string.format('Pointer|%d|%d\n', ptrAddr, ptrValue)) end else break end end fileLE:close() end Pointernumber = string.format('%s2000|%d|%d|\n', Pointernumber, lvl, bl_3) if (lvl == depth) then gg.setRanges(Nc) end gg.searchPointer(maxOffset) end local fileLE = io.open(pathdjpz, 'a') for m, r in ipairs(ranges) do local p_res if fixedBaseOffset then local fixedAddr = r.start + fixedBaseOffset p_res = gg.getResults(saomdz, 0, fixedAddr, fixedAddr) else p_res = gg.getResults(saomdz, 0, r.start, r['end']) end if #p_res > 0 then for j, u in pairs(p_res) do local mainOffset = u.address - r.start if mainOffset >= Zpycd then iii = iii + 1 local uValue = math.tointeger(u.value) or 0 fileLE:write(string.format('Base|%d|%s+0x%X\n', uValue, r.internalName:gsub('^.*/', ''), mainOffset)) end end gg.removeResults(p_res) end end fileLE:close() if lvl >= 1 then Basenumber = string.format('%s3500|%d|%d|\n', Basenumber, lvl, iii) end if gg.getResultsCount() == 0 then if (io.open(p[3])) then os.remove(p[3]) end if gg.alert("没有数据符合\n建议再试一遍","重试","取消")==1 then CBYS() break end break end end local fm = sjxspz(os.clock() - x) or "0 秒" if iii ~= 0 then gg.alert("耗时: "..fm.."\n".."深度: "..depth.." 最大偏移量: "..maxOffset.."\n".."输出基址数: "..(iii).." \n".."输出路径为: "..p[3]) gg.clearResults() end break end io.open(pathdjxh, "w+"):write(string.format('%d<>%d<>%d<>%s\n%s%s', depth, ti.x64 and 1 or 0, maxOffset, p[3], Basenumber, Pointernumber )):close() local path = gg.FILES_DIR.."/" local c_name = "基址C运算.c" local pathCprint = (p and p[3]) and p[3].."print" or os.getenv("EXTERNAL_STORAGE").."/Android/[C]运算完Printf数据.txt" if (io.open(path .. c_name)) then os.remove(path .. c_name) end gg.getRaw(path .. c_name, "chunk_cpp3") local printf = gg.command("cd " .. path .. "\n chmod 777 " .. c_name .. "\n " .. "'" .. "./" .. c_name .. "'" .. " " .. "'" .. "'" .. " " .. "'" .. "交互验证" .. "'" .. "\n rm -f " .. c_name ) gg.setVisible(true) return end function sjxspz(x) local fm, fs, ms if x >= 60.0 then fs = x // 60.0 ms = x - (fs * 60.0) fm = fs .. " 分 " .. ms .." 秒" else fm = x .. " 秒" end return fm end while true do gg.showUiButton() if gg.isClickedUiButton() then gg.hideUiButton() Ay() end end