├── Images ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg └── 7.jpg ├── LICENSE ├── RChainer.lua └── README.md /Images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/1.jpg -------------------------------------------------------------------------------- /Images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/2.jpg -------------------------------------------------------------------------------- /Images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/3.jpg -------------------------------------------------------------------------------- /Images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/4.jpg -------------------------------------------------------------------------------- /Images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/5.jpg -------------------------------------------------------------------------------- /Images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/6.jpg -------------------------------------------------------------------------------- /Images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ht0Ruial/RChainer/43e2b5056b3167f44d81c18fd7e9a9d70c91c30f/Images/7.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Randall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RChainer.lua: -------------------------------------------------------------------------------- 1 | function printChain(pre, u) 2 | if u.offset == nil then 3 | return u.value 4 | else 5 | local ret = '' 6 | for offset, v in pairs(u.offset) do 7 | table.insert(l_table_down2, offset) 8 | ret = ret .. '\n\n' .. 9 | printChain( 10 | pre .. 11 | string.format(' -> 0x%X + 0x%X', u.value, offset), 12 | v) 13 | end 14 | if ret ~= '' then ret = ret:sub(3) end 15 | return ret 16 | end 17 | end 18 | 19 | function S_Pointer(t_So, t_Offset, _bit) 20 | local function getRanges() 21 | local ranges = {} 22 | local t = gg.getRangesList('^/data/*.so*$') 23 | for i, v in pairs(t) do 24 | if v.type:sub(2, 2) == 'w' then table.insert(ranges, v) end 25 | end 26 | return ranges 27 | end 28 | local function Get_Address(N_So, Offset, ti_bit) 29 | local ti = gg.getTargetInfo() 30 | local S_list = getRanges() 31 | local t = {} 32 | local _t 33 | local _S = nil 34 | if ti_bit then 35 | _t = 32 36 | else 37 | _t = 4 38 | end 39 | for i in pairs(S_list) do 40 | local _N = S_list[i].internalName:gsub('^.*/', '') 41 | if N_So[1] == _N and N_So[2] == S_list[i].state then 42 | _S = S_list[i] 43 | break 44 | end 45 | end 46 | if _S then 47 | t[#t + 1] = {} 48 | t[#t].address = _S.start + Offset[1] 49 | t[#t].flags = _t 50 | if #Offset ~= 1 then 51 | for i = 2, #Offset do 52 | local S = gg.getValues(t) 53 | t = {} 54 | for _ in pairs(S) do 55 | if not ti.x64 then 56 | S[_].value = S[_].value & 0xFFFFFFFF 57 | end 58 | t[#t + 1] = {} 59 | t[#t].address = S[_].value + Offset[i] 60 | t[#t].flags = _t 61 | end 62 | end 63 | end 64 | _S = t[#t].address 65 | end 66 | return _S 67 | end 68 | local _A = string.format('0x%X', Get_Address(t_So, t_Offset, _bit)) 69 | return _A 70 | end 71 | 72 | local ti = gg.getTargetInfo() 73 | local x64 = ti.x64 74 | 75 | if gg.getResultsCount() ~= 1 then 76 | print('当前搜索列表不为1') 77 | os.exit() 78 | end 79 | 80 | local ac_ = gg.getResults(1)[1] 81 | local ac_flags = ac_.flags 82 | local ac_value = ac_.value 83 | 84 | local depth, minOffset, maxOffset, level, out 85 | 86 | function loadChain(lvl, p) 87 | local fix, mino, maxo, lev = not x64, minOffset, maxOffset, level 88 | for k = lvl, 1, -1 do 89 | local levk, p2, stop = lev[k], {}, true 90 | for j, u in pairs(p) do 91 | if u.offset == nil then 92 | u.offset = {} 93 | if fix then u.value = u.value & 0xFFFFFFFF end 94 | for i, v in ipairs(levk) do 95 | local offset = v.address - u.value 96 | if offset >= mino and offset <= maxo then 97 | u.offset[offset], p2[v], stop = v, v, false 98 | end 99 | end 100 | end 101 | end 102 | if stop then break end 103 | p = p2 104 | end 105 | end 106 | 107 | function getRanges() 108 | local archs = { 109 | [0x3] = 'x86', 110 | [0x28] = 'ARM', 111 | [0x3E] = 'x86-64', 112 | [0xB7] = 'AArch64' 113 | } 114 | local ranges = {} 115 | local t = gg.getRangesList('^/data/*.so*$') 116 | local arch = 'unknown' 117 | for i, v in ipairs(t) do 118 | if v.type:sub(2, 2) == '-' then 119 | local t = gg.getValues({ 120 | {address = v.start, flags = gg.TYPE_DWORD}, 121 | {address = v.start + 0x12, flags = gg.TYPE_WORD} 122 | }) 123 | if t[1].value == 0x464C457F then 124 | arch = archs[t[2].value] 125 | if arch == nil then arch = 'unknown' end 126 | end 127 | end 128 | if v.type:sub(2, 2) == 'w' then 129 | v.arch = arch 130 | table.insert(ranges, v) 131 | end 132 | end 133 | return ranges 134 | end 135 | 136 | local ranges = getRanges() 137 | 138 | gg.setRanges(gg.REGION_C_HEAP | gg.REGION_C_ALLOC | gg.REGION_C_DATA | 139 | gg.REGION_C_BSS | gg.REGION_ANONYMOUS) 140 | 141 | local cfg_file = gg.getFile() .. '.cfg' 142 | local chunk = loadfile(cfg_file) 143 | local cfg = nil 144 | if chunk ~= nil then cfg = chunk() end 145 | if cfg == nil then cfg = {} end 146 | 147 | local pkg = gg.getTargetPackage() 148 | if pkg == nil then pkg = 'none' end 149 | 150 | gg.setVisible(false) 151 | while true do 152 | local def = cfg[pkg] 153 | if def == nil then def = {3, 0, 256} end 154 | local p = gg.prompt({'深度', '最小偏移量', '最大偏移量'}, def, 155 | {'number', 'number', 'number'}) 156 | 157 | if p == nil then 158 | gg.setVisible(true) 159 | os.exit() 160 | end 161 | cfg[pkg] = p 162 | gg.saveVariable(cfg, cfg_file) 163 | 164 | depth = p[1] 165 | minOffset = tonumber(p[2]) 166 | maxOffset = tonumber(p[3]) 167 | 168 | level, out = {}, {} 169 | 170 | local old = gg.getResults(100000) 171 | local x = os.clock() 172 | 173 | for lvl = 0, depth do 174 | if lvl > 0 then 175 | local t = gg.getResults(100000) 176 | level[lvl] = t 177 | gg.toast(lvl .. ' from ' .. depth) 178 | gg.internal3(maxOffset) 179 | end 180 | 181 | for m, r in ipairs(ranges) do 182 | local p = gg.getResults(100000, 0, r.start, r['end']) 183 | if #p > 0 then 184 | gg.removeResults(p) 185 | loadChain(lvl, p) 186 | p.map = r 187 | table.insert(out, p) 188 | end 189 | end 190 | 191 | if gg.getResultsCount() == 0 then break end 192 | end 193 | 194 | gg.loadResults(old) 195 | 196 | -- 入表 197 | t_table = {} 198 | for j, p in ipairs(out) do 199 | for i, u in ipairs(p) do 200 | local l_table_down1 = {} 201 | table.insert(t_table, l_table_down1) 202 | table.insert(l_table_down1, string.format('%s', 203 | p.map.internalName:gsub( 204 | '^.*/', ''))) 205 | table.insert(l_table_down1, string.format('%s', p.map.state)) 206 | 207 | l_table_down2 = {} 208 | table.insert(t_table, l_table_down2) 209 | table.insert(l_table_down2, u.address - p.map.start) 210 | printChain(string.format('%s + 0x%X [0x%X]', 211 | p.map.internalName:gsub('^.*/', ''), 212 | u.address - p.map.start, u.address), u) 213 | end 214 | end 215 | 216 | gg.toast("正在校验" .. #t_table .. "条链路,请稍候") 217 | -- 检验 218 | last_table = {} 219 | for i = 1, #t_table, 2 do 220 | local t = t_table[i] 221 | local tt = t_table[i + 1] 222 | local ttt = S_Pointer(t, tt, true) 223 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value == 224 | ac_value then 225 | table.insert(last_table, t) 226 | table.insert(last_table, tt) 227 | gg.toast("第" .. string.format('%s', (i + 1) / 2) .. 228 | "条链路可用") 229 | end 230 | end 231 | 232 | local chain = '' 233 | if #last_table == 0 then chain = '\n\n没有结果' end 234 | gg.toast("已找到" .. #last_table .. "条链路") 235 | -- 重现链路 236 | 237 | for i = 1, #last_table, 2 do 238 | chain = chain .. "\n\n" .. string.format('%s', (i + 1) / 2) .. ":" .. 239 | string.format('%s', last_table[i][1]) .. " + " .. 240 | string.format('0x%X', last_table[i + 1][1]) 241 | for j = 2, #last_table[i + 1] do 242 | chain = chain .. string.format(' -> 0x%X', last_table[i + 1][j]) 243 | end 244 | chain = chain .. " = " .. string.format('%s', ac_value) 245 | end 246 | 247 | x = string.format('%.2f', os.clock() - x) 248 | print(depth, minOffset, maxOffset, x) 249 | 250 | local chains = #last_table / 2 251 | p = gg.alert('在' .. x .. '秒内找到了' .. chains .. '条链路 (' .. 252 | depth .. ', ' .. minOffset .. ', ' .. maxOffset .. '):' .. 253 | chain, '保存', '重试', '退出') 254 | if p == 1 then break end 255 | if p ~= 2 then 256 | print(last_table) 257 | print(chain) 258 | gg.setVisible(true) 259 | os.exit() 260 | 261 | end 262 | end 263 | 264 | function main(last_table, ac_flags, ac_value, control_str) 265 | for i = 1, #last_table do 266 | local t = last_table[i][1] 267 | local tt = last_table[i][2] 268 | local ttt = S_Pointer(t, tt, true) 269 | if control_str == "==" then 270 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value == 271 | ac_value then 272 | control_flag = true 273 | gg.searchNumber(ac_value, ac_flags, false, gg.SIGN_EQUAL, ttt, 274 | ttt) 275 | end 276 | elseif control_str == "~=" then 277 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value ~= 278 | ac_value then 279 | control_flag = true 280 | gg.searchNumber(ac_value, ac_flags, false, gg.SIGN_NOT_EQUAL, 281 | ttt, ttt) 282 | end 283 | elseif control_str == ">" then 284 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value > 285 | ac_value then 286 | control_flag = true 287 | gg.searchNumber(ac_value, ac_flags, false, 288 | gg.SIGN_GREATER_OR_EQUAL, ttt, ttt) 289 | end 290 | elseif control_str == ">=" then 291 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value >= 292 | ac_value then 293 | control_flag = true 294 | gg.searchNumber(ac_value, ac_flags, false, 295 | gg.SIGN_GREATER_OR_EQUAL, ttt, ttt) 296 | end 297 | elseif control_str == "<" then 298 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value < 299 | ac_value then 300 | control_flag = true 301 | gg.searchNumber(ac_value, ac_flags, false, 302 | gg.SIGN_LESS_OR_EQUAL, ttt, ttt) 303 | end 304 | elseif control_str == "<=" then 305 | if gg.getValues({{address = ttt, flags = ac_flags}})[1].value <= 306 | ac_value then 307 | control_flag = true 308 | gg.searchNumber(ac_value, ac_flags, false, 309 | gg.SIGN_LESS_OR_EQUAL, ttt, ttt) 310 | end 311 | end 312 | if control_flag then 313 | li_str = "\n{{" 314 | for l, m in ipairs(t) do 315 | li_str = li_str .. "'" .. string.format('%s', m) .. "'," 316 | end 317 | li_str = li_str .. "},{" 318 | for l, m in ipairs(tt) do 319 | li_str = li_str .. string.format('0x%X', m) .. "," 320 | end 321 | li_str = li_str .. "}}\n\n" 322 | print("序号: " .. i) 323 | print(gg.getValues({{address = ttt, flags = ac_flags}})[1]) 324 | print(li_str) 325 | end 326 | end 327 | end 328 | 329 | -- 文件名 330 | local script = gg.getFile():gsub('[^/]*$', '') .. ti.packageName 331 | for i = 1, 1000 do 332 | local f = io.open(script .. i .. "-" .. string.format('%s', depth) .. "-" .. 333 | string.format('%s', minOffset) .. "-" .. 334 | string.format('%s', maxOffset) .. '.lua') 335 | if f == nil then 336 | script = script .. i .. "-" .. string.format('%s', depth) .. "-" .. 337 | string.format('%s', minOffset) .. "-" .. 338 | string.format('%s', maxOffset) .. '.lua'; 339 | break 340 | end 341 | f:close() 342 | end 343 | 344 | function Con_choise() 345 | local control_str_T = {"==", "~=", ">", ">=", "<", "<="} 346 | local control_str_num = gg.choice(control_str_T, 1, 347 | "选择合适的判断条件,取消则选取默认值") 348 | if control_str_num == nil then control_str_num = 1 end 349 | local control_str = control_str_T[control_str_num] 350 | ac_value_S = gg.prompt({ 351 | "当前判断条件为: " .. string.format('%s', control_str) .. 352 | "\n当前默认值为: " .. string.format('%s', ac_value) .. 353 | "\n请输入合适的判断值,取消则返回上一步" 354 | }, {[1] = ac_value}, {[1] = "number"}) 355 | if ac_value_S == nil or ac_value_S[1] == "" then Con_choise() end 356 | ac_value = tonumber(ac_value_S[1]) 357 | return control_str, ac_value 358 | end 359 | local control_str, ac_value = Con_choise() 360 | 361 | local p = gg.prompt({'输出文件'}, {script}, {'file'}) 362 | if p ~= nil then 363 | script = p[1] 364 | 365 | code = '' 366 | 367 | local cpi = {} 368 | for i, u in ipairs({S_Pointer, main}) do cpi[i] = debug.getinfo(u) end 369 | local n = 0 370 | for line in io.lines(gg.getFile()) do 371 | n = n + 1 372 | for i, u in ipairs(cpi) do 373 | if n >= u.linedefined and n <= u.lastlinedefined then 374 | code = code .. '\n' .. line 375 | break 376 | end 377 | end 378 | end 379 | 380 | -- 拼接 381 | code = code .. "\n\nlocal last_table = {" 382 | for i = 1, #last_table, 2 do 383 | code = code .. "\n\t{{" 384 | for l, m in ipairs(last_table[i]) do 385 | code = code .. "'" .. string.format('%s', m) .. "'," 386 | end 387 | code = code .. "},{" 388 | for l, m in ipairs(last_table[i + 1]) do 389 | code = code .. string.format('0x%X', m) .. "," 390 | end 391 | code = code .. "}}," 392 | end 393 | code = code .. "\n}\nlocal ac_flags = " .. string.format('%s', ac_flags) .. 394 | "\nlocal ac_value = " .. string.format('%s', ac_value) .. 395 | "\nlocal control_str = '" .. string.format('%s', control_str) .. 396 | "'" 397 | 398 | code = code .. '\n\nmain(last_table, ac_flags, ac_value, control_str)\n' 399 | 400 | -- 写入 401 | local f = io.open(script, 'w+') 402 | f:write(code) 403 | f:close() 404 | gg.toast("保存成功") 405 | end 406 | gg.setVisible(true) 407 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # RChainer 3 | [![GitHub stars](https://img.shields.io/github/stars/ht0Ruial/RChainer)](https://github.com/ht0Ruial/RChainer/stargazers) [![GitHub forks](https://img.shields.io/github/forks/ht0Ruial/RChainer)](https://github.com/ht0Ruial/RChainer/network) [![GitHub issues](https://img.shields.io/github/issues/ht0Ruial/RChainer)](https://github.com/ht0Ruial/RChainer/issues) 4 | 5 | + 适用于GameGuardian的动态基址获取工具,支持自动检验可行链路、自定义判断条件和导出基址脚本。 6 | 7 | ## 简介 8 | + RChainer是在chainer *v0.2* 以及chainer *v0.46* 的基础上做了些许优化,使得在GG修改器上寻找动态基址的过程变得更加简单。 9 | 10 | ## 使用 11 | 1. 搜索列表的数据量为1,执行 **RChainer.lua** 12 | ![](Images/1.jpg) 13 | 14 | 2. 选择合适的深度和最大偏移量
15 | 这里写 *4 3000* 获取不到可行链路,后面改成 *4 4000* 16 | ![](Images/2.jpg) 17 | 18 | 3. 经过校验后的可行链路 19 | ![](Images/3.jpg) 20 | 21 | 4. 选择合适的判断条件 22 | ![](Images/4.jpg) 23 | 24 | 5. 输入合适的判断值 25 | ![](Images/5.jpg) 26 | 27 | 6. 保存文件成功
28 | 2个文件是指配置文件和动态基址脚本 29 | ![](Images/6.jpg) 30 | 31 | 7. 执行 *动态基址脚本* 后,打印信息和基址链路 32 | ![](Images/7.jpg) 33 | 34 | 35 | 36 | 37 | 38 | --------------------------------------------------------------------------------