Add r2_analyze script to parse and label interesting stuff
This commit is contained in:
parent
d539d83648
commit
a0b08b7544
2 changed files with 200 additions and 0 deletions
|
@ -4,6 +4,7 @@
|
|||
* `parse_save.py`: Dumps information extracted from Save file
|
||||
* `scrapper.py`: Extractor and Repacker for *.packed files, needs the `construct` and `tqdm` python modules and python 3.x
|
||||
- Run `scrapper.py -h` for help
|
||||
* `r2_analyze.py`: uses radare2 to parse and label a lot of interesting stuff in the `Scrap.exe` binary
|
||||
* `lib/dbg.py`: general Script for poking around inside the game's scripting system
|
||||
- Run `import dbg` inside the Game's Console,
|
||||
this will load all builtin modules and enable godmode
|
||||
|
|
199
r2_analyze.py
Normal file
199
r2_analyze.py
Normal file
|
@ -0,0 +1,199 @@
|
|||
import r2pipe
|
||||
import os
|
||||
import json
|
||||
from tqdm import tqdm
|
||||
from pprint import pprint
|
||||
import os
|
||||
import sys
|
||||
r2cmds=[]
|
||||
scrap_exe=sys.argv[1]
|
||||
folder=os.path.join(os.path.dirname(scrap_exe))
|
||||
r2 = r2pipe.open(scrap_exe)
|
||||
|
||||
assert r2.cmdj("itj")['sha1'] == "d2dde960e8eca69d60c2e39a439088b75f0c89fa","Hash mismatch"
|
||||
assert r2.cmdj("itj")['md5'] == "a934c85dca5ab1c32f05c0977f62e186","Hash mismatch"
|
||||
|
||||
def r2_cmd(cmd):
|
||||
global r2,r2cmds
|
||||
r2cmds.append(cmd)
|
||||
return r2.cmd(cmd)
|
||||
|
||||
def r2_cmdj(cmd):
|
||||
global r2,r2cmds
|
||||
r2cmds.append(cmd)
|
||||
return r2.cmdj(cmd)
|
||||
|
||||
def r2_cmdJ(cmd):
|
||||
global r2,r2cmds
|
||||
r2cmds.append(cmd)
|
||||
return r2.cmdJ(cmd)
|
||||
|
||||
|
||||
print("[*] Running 'aaaa'")
|
||||
|
||||
r2_cmd("aaaa")
|
||||
|
||||
flags = {0x7FE944: ("World_Ptr", 4), 0x79C698: ("Py_Mods", 4)}
|
||||
|
||||
functions = {
|
||||
0x404A50: "find_entity",
|
||||
0x404BB0: "ht_hash",
|
||||
0x404460: "reg_c_callback",
|
||||
0x417470: "load_game",
|
||||
0x5E3800: "fopen_1",
|
||||
0x419950: "fopen_2",
|
||||
0x403370: "debug_init",
|
||||
0x401770: "init",
|
||||
0x4026D0: "init_py",
|
||||
0x5A8FB0: "init_py_mod",
|
||||
0x41AB50: "open_pak",
|
||||
0x5A8390: "py_exec",
|
||||
0x414570: "setup_game_vars",
|
||||
0x5FBC50: "throw_assertion_1",
|
||||
0x414070: "throw_assertion_2",
|
||||
0x5F7000: "load_m3d_ini",
|
||||
0x650F80: "load_sm3",
|
||||
0x6665A0: "load_m3d_1",
|
||||
0x666900: "load_m3d_2",
|
||||
0x479B20: "world_constructor",
|
||||
0x479B40: "world_init",
|
||||
0x402510: "world_deinit",
|
||||
0x479870: "make_world",
|
||||
}
|
||||
|
||||
for addr, args in flags.items():
|
||||
name, size = args
|
||||
r2_cmd(f"f {name} {size} {hex(addr)}")
|
||||
|
||||
for addr, name in functions.items():
|
||||
r2_cmd(f"afr {name} {hex(addr)}")
|
||||
|
||||
def vtables():
|
||||
ret={}
|
||||
print("[*] Analyzing VTables")
|
||||
vtables = r2_cmdJ("avj")
|
||||
for c in tqdm(vtables,ascii=True):
|
||||
methods=[]
|
||||
for m in tqdm(c.methods,ascii=True,leave=False):
|
||||
methods.append(hex(m.offset))
|
||||
r2.cmd(f"afr @{hex(m.offset)} 2>NUL")
|
||||
ret[hex(c.offset)]=methods
|
||||
return ret
|
||||
|
||||
def c_callbacks():
|
||||
print("[*] Parsing C Callbacks")
|
||||
funcs={}
|
||||
res = r2_cmd("/r 0x404460 ~CALL[1]").splitlines()
|
||||
for addr in tqdm(res,ascii=True):
|
||||
func,name=r2_cmdJ(f"s {addr};so -3;pdj 2")
|
||||
func=func.refs[0].addr
|
||||
name=r2_cmd(f"psz @{hex(name.refs[0].addr)}").strip()
|
||||
r2_cmd(f"afr CB_{name} {hex(func)} 2>NUL")
|
||||
funcs[name]=hex(func)
|
||||
return funcs
|
||||
|
||||
def assertions():
|
||||
assertions = {}
|
||||
for a_addr in ['0x414070','0x5fbc50']:
|
||||
print(f"[*] Parsing C assertions for {a_addr}")
|
||||
res = r2_cmd(f"/r {a_addr} ~CALL[1]").splitlines()
|
||||
print()
|
||||
for line in tqdm(res, ascii=True):
|
||||
addr = line.strip()
|
||||
file, msg = r2_cmdJ(f"s {addr};so -2;pij 2") # seek and print disassembly
|
||||
try:
|
||||
file = r2_cmd(f"psz @{file.refs[0].addr}").strip()
|
||||
msg = r2_cmd(f"psz @{msg.refs[0].addr}").strip()
|
||||
path = os.path.abspath(file.replace("\\\\", "\\"))
|
||||
assertions.setdefault(path, [])
|
||||
assertions[path].append([int(addr, 16), msg])
|
||||
except:
|
||||
pass
|
||||
return assertions
|
||||
|
||||
|
||||
def world():
|
||||
print("[*] Parsing World offsets")
|
||||
res = r2_cmd("/r 0x7fe944 ~&fcn,DATA[0,1]").splitlines()
|
||||
print()
|
||||
for hit in res:
|
||||
func, offset = hit.split()
|
||||
offset = int(offset, 16)
|
||||
print("=" * 5, func, "=" * 5)
|
||||
for op in r2_cmdJ(f"pdfj @{func}")["ops"]:
|
||||
if op.offset >= offset:
|
||||
# print(op.disasm,op.get('refs',[]))
|
||||
print(op.disasm)
|
||||
|
||||
|
||||
def py_mods():
|
||||
print("[*] Parsing Python modules")
|
||||
res = r2_cmd("/r 0x5a8fb0 ~CALL[1]").splitlines()
|
||||
print()
|
||||
py_mods={}
|
||||
for call_loc in tqdm(res,ascii=True):
|
||||
args = r2_cmdJ(f"s {call_loc};so -3;pdj 3")
|
||||
refs = []
|
||||
if not all([arg.type=="push" for arg in args]):
|
||||
continue
|
||||
for arg in args:
|
||||
refs.append(hex(arg.val))
|
||||
doc,methods,name=refs
|
||||
doc=r2_cmd(f"psz @{doc}").strip()
|
||||
name=r2_cmd(f"psz @{name}").strip()
|
||||
r2_cmd(f"s {methods}")
|
||||
r2_cmd(f"f PyMethodDef_{name} 4 {methods}")
|
||||
py_mods[name]={'methods_addr':methods,'doc':doc,'methods':{}}
|
||||
while True:
|
||||
m_name,m_func,_,m_doc=[v.value for v in r2_cmdJ(f"pfj xxxx")]
|
||||
if m_name==0:
|
||||
break
|
||||
m_name,m_func,m_doc=map(hex,(m_name,m_func,m_doc))
|
||||
m_name=r2_cmd(f"psz @{m_name}").strip()
|
||||
r2_cmd(f"f Py_{name}_{m_name}_doc 4 {m_doc}")
|
||||
m_doc=r2_cmd(f"psz @{m_doc}").strip()
|
||||
py_mods[name]['methods'][m_name]={'addr':m_func,'doc':m_doc}
|
||||
r2_cmd(f"afr Py_{name}_{m_name} {m_func} 2>NUL")
|
||||
r2_cmd("s +16")
|
||||
return py_mods
|
||||
|
||||
|
||||
def game_vars():
|
||||
print("[*] Parsing Game variables")
|
||||
res = r2_cmd("/r 0x414570 ~CALL[1]").splitlines()
|
||||
print()
|
||||
for line in tqdm(res,ascii=True):
|
||||
addr = line.strip()
|
||||
args = r2_cmdJ(f"s {addr};so -4;pdj 4") # seek and print disassembly
|
||||
args_a = []
|
||||
for arg in args:
|
||||
if "refs" in arg:
|
||||
addr = hex(arg.refs[0].addr)
|
||||
s = r2_cmd(f"ps @{hex(arg.refs[0].addr)}").strip()
|
||||
args_a.append((addr, s))
|
||||
print(args_a)
|
||||
|
||||
ret=dict(
|
||||
py_mods=py_mods(),
|
||||
assertions=assertions(),
|
||||
c_callbacks=c_callbacks(),
|
||||
vtables=vtables(),
|
||||
#game_vars=game_vars(),
|
||||
#world=world(),
|
||||
)
|
||||
|
||||
|
||||
with open(os.path.join(folder,"Scrap_dissect.json"),"w") as of:
|
||||
json.dump(ret,of,indent=4)
|
||||
print("[+] Wrote Scrap_dissect.json")
|
||||
with open(os.path.join(folder,"Scrap_dissect.r2"),"w") as of:
|
||||
wcmds=[]
|
||||
for cmd in r2cmds:
|
||||
for start in ['f ','afr ','aaaa']:
|
||||
if cmd.startswith(start):
|
||||
wcmds.append(cmd)
|
||||
break
|
||||
of.write("\n".join(wcmds))
|
||||
print("[+] Wrote Scrap_dissect.r2")
|
||||
print("[*] Done!")
|
||||
print("[*] Run 'r2 -i Scrap_dissect.r2 Scrap.exe' to load parsed infos into radare2")
|
Loading…
Reference in a new issue