├── 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 | [](https://github.com/ht0Ruial/RChainer/stargazers) [](https://github.com/ht0Ruial/RChainer/network) [](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 | 
13 |
14 | 2. 选择合适的深度和最大偏移量
15 | 这里写 *4 3000* 获取不到可行链路,后面改成 *4 4000*
16 | 
17 |
18 | 3. 经过校验后的可行链路
19 | 
20 |
21 | 4. 选择合适的判断条件
22 | 
23 |
24 | 5. 输入合适的判断值
25 | 
26 |
27 | 6. 保存文件成功
28 | 2个文件是指配置文件和动态基址脚本
29 | 
30 |
31 | 7. 执行 *动态基址脚本* 后,打印信息和基址链路
32 | 
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------