Separated configuration for analysis into YAML file

This commit is contained in:
Daniel S. 2020-01-03 04:20:18 +01:00
parent d46d725409
commit 7f34e298b8
3 changed files with 219 additions and 233 deletions

View File

@ -2,6 +2,7 @@
- Engine: ScrapEngine
- Ingame Scripting Language: Python 1.5.2
- Interesting memory locations and functions are noted in `config.yml`
# Launch options:
@ -32,11 +33,6 @@
* `idiota`
* `capullo`
## Python Stuff
- `0x79C698`: Modules List (Module Name as `char*` followed by Pointer to Init Function)
- `0x5A8FB0`: InitPyMod
- `0x5A8390`: PyExec
## Other interesting Memory Addresses

195
config.yml Normal file
View File

@ -0,0 +1,195 @@
notes: |
0x7faa4c: temp storage?
0x7d2094: some reference count
comments:
0x6113f9: Check if Window exists
flags:
0x7FE944: P_World
0x7FBE4C: P_Vars
0x79C698: Py_Mods
0x852914: P_D3D8_Dev
0x7FCC00: N_Paks_opened
0x7fcbec: Hash_Index_Size
0x7fcbf0: P_Hash_Index
0x7fcc08: Lst_File
0x7fcc04: Pak_Locked
0x7fc1b0: Pak_Index
0x84cb64: P_ConHandler
0x801e10: num_arrows
0x7fac84: P_Callbacks
0x80b2cc: P_ActClassList
0x807a20: P_Scorer
0x80a398: P_SoundSys
0x84cb58: H_RichEd
0x84cb4c: P_HWND_Console
0x80cb40: Console_Win_Buffer
0x84d400: Lib_preloaded
0x7fac1c: Activate_Viewer
0x8b18f0: P_Models
0x8b18f4: P_Scenes
0x8b18f8: P_ActiveModels
0x803bc0: net_is_server
0x8045e4: net_is_master
0x8038a8: net_is_client
0x7fadd8: is_python
0x7fc084: pak_lock
0x7fbe7c: current_language
0x7d2094: py_refcnt_unk
VMTs:
0x78d4d8: Py_entity
0x78cc6c: World
0x78b680: FilePak_1
0x78b6a4: FilePak_2
0x78b638: AbstractFile
0x78b4d8: App
0x78b480: Window
0x78b5c0: File
0x78b65c: FileMem
0x78b6d0: IDevice_1
0x78b6f4: IDevice_2
0x78b6fc: IDevice_Kb
0x78b720: IDevice_Mouse
0x78b74c: IDevice_Joy
0x7933ac: 3d_Gfx
0x7933a0: NodeFX
types:
- "struct PyMethodDef { char *ml_name; void *ml_meth; int ml_flags; char *ml_doc;};"
- "struct GameVar { struct GameVar* next; const char* name; const char* desc; uint64_t d_type; void* value; void* def_value; };"
- "struct HT_Entry { void* data; const char* key; struct HT_Entry* next;};"
- "struct PakEntry { unsigned char* filename; bool locked; void* data; uint32_t seek;};"
- "struct HashIndexEntry { uint32_t offset; uint32_t size; uint32_t status; const char* name; struct HashIndexEntry* next; };"
- "struct HashIndex { uint32_t size; struct HashIndexEntry** data; };"
- "struct HashTableEntry { void* data; const char *key; struct HashTableEntry* next; };"
- "struct HashTable { uint32_t size; struct HashTableEntry** data; };"
function_signatures:
0x5A8390: "int PyRun_SimpleString(const char* command);"
0x5BB9D0: "int PyArg_ParseTuple(void* PyObj, char* format, ...);"
0x413ee0: "int dbg_log(const char* fmt,...);"
0x4134C0: "int write_log(unsigned int color, const char* msg);"
0x47C1E0: "int ht_hash_ent_list(const char* str);"
0x404BB0: "int ht_hash_ent(const char* str);"
0x4016F0: "int reg_get_val(const char* value);"
0x414280: "int prepare_html_log(const char* filename);"
0x6597d0: "bool read_ini_entry(void* dest,const char* key, const char* section);"
0x5A8FB0: "void* Py_InitModule(const char* name,void* methods);"
0x5E3800: "int fopen_from_pak(const char* filename);"
0x419950: "int fopen_2(const char* filename);"
0x41AB50: "int open_pak(const char* filename, int unk_1,void* unk_ptr);"
0x404460: "int register_c_callback(const char* name,void* func);"
0x414070: "void throw_assertion_2(const char* check,const char* file,const char* date, unsigned int line);"
0x5FBC50: "void throw_assertion_1(const char* check,const char* file, unsigned int line);"
0x5BC140: "static char* convertsimple1(void *arg, char **p_format, void *p_va);"
0x5E3800: "int32_t fopen_from_pak(const char* filename,const char* mode);"
0x5a90f0: "void* Py_BuildValue(const char* format, ...);"
0x5B9E70: "void* PyObject_GetAttrString(void* obj, const char* attr);"
functions:
0x6B1C70: strcmp
0x5BB9D0: PyArg_ParseTuple
0x5DD510: init_engine_3d
0x401180: create_window
0x401240: create_main_window
0x4016F0: reg_get_val
0x4134C0: write_log
0x414280: prepare_html_log
0x418220: get_version_info
0x4137E0: write_html_log
0x402190: handle_console_input
0x5F9520: handle_render_console_input
0x404A50: find_entity
0x47C1E0: ht_hash_ent_list
0x404BB0: ht_hash_ent
0x404460: register_c_callback
0x417470: load_game
0x5E3800: fopen_from_pak
0x5e3500: fopen
0x403370: init_debug
0x401770: init
0x4026D0: init_py
0x405B40: init_py_sub
0x5A8FB0: Py_InitModule
0x41AB50: open_pak
0x5A8390: PyRun_SimpleString
0x414570: setup_game_vars
0x5FBC50: throw_assertion_1
0x414070: throw_assertion_2
0x5F7000: read_ini
0x650F80: load_sm3
0x6665A0: load_m3d_1
0x666900: load_m3d_2
0x479B20: world_constructor
0x479B40: init_world
0x402510: deinit_world
0x479870: make_world
0x602A70: render_frame
0x6B738C: handle_exception
0x5B9E70: PyObject_GetAttrString
0x413ee0: dbg_log
0x5f75e0: init_d3d
0x63a2f0: gdi_draw_line
0x5e3250: read_stream
0x5e3bb0: read_stream_wrapper
0x50b9b0: init_scorer
0x582e10: init_action_class_list
0x528910: init_sound_sys
0x5268d0: try_init_sound_sys
0x404280: cPyFunction_set_func
0x414680: load_config
0x414810: save_config
0x4f42a0: close_server_socket
0x4f4d10: close_server
0x4f48e0: close_client
0x4f4fb0: is_server
0x4f4a10: is_client
0x4fac50: is_master
0x526910: close_sound_sys
0x526520: shutdown_sound_sys
0x5dd700: close_3d_engine
0x5a7320: close_window
0x5dff20: set_exception_handler
0x5a7f20: get_console_wnd
0x5a73a0: show_console
0x666c60: read_m3d
0x417df0: snprintf
0x5fc930: printf
0x6597d0: read_ini_entry
0x5fc0a0: engine_debug_log
0x5a7440: create_console_window
0x6114e0: setup_window
0x404420: clear_functions
0x405ca0: close_py_subsys
0x50bcb0: close_scorer
0x479b20: close_world
0x582e70: close_action_class
0x50b6a0: get_scorer
0x50ea20: scorer_parse_type
0x636580: list_models
0x5a90f0: Py_BuildValue
0x41c5a0: has_lst_file
0x5a8e90: py_error
0x5a9890: get_module_dict
0x5c7bb0: get_current_thread
0x5aa140: preload_lib
0x413c10: sprintf
0x405850: check_is_python
0x47bf90: setup_ent_list
0x474f80: ent_list_get_set
script: |
e asm.cmt.right = true
e cmd.stack = true
e scr.utf8 = true
e asm.describe = false
e graph.cmtright = true
e cfg.sandbox = false
e cfg.newtab = true
e cfg.fortunes.type = tips,fun,creepy,nsfw
e dbg.status = true
e pdb.autoload = true
e emu.str = true
e asm.flags.offset = true

View File

@ -7,14 +7,16 @@ from tqdm import tqdm
from pprint import pprint
import os
import sys
import yaml
r2cmds = []
x64_dbg_script=[]
script_path = os.path.dirname(os.path.abspath(__file__))
scrap_exe = os.path.abspath(sys.argv[1])
folder = os.path.abspath(os.path.dirname(scrap_exe))
script_path=os.path.join(folder, "scrap_dissect.r2")
x64_dbg_script_path=os.path.join(folder, "scrap_dissect.x32dbg.txt")
json_path=os.path.join(folder, "scrap_dissect.json")
scrapland_folder = os.path.abspath(os.path.dirname(scrap_exe))
r2_script_path=os.path.join(scrapland_folder, "scrap_dissect.r2")
x64_dbg_script_path=os.path.join(scrapland_folder, "scrap_dissect.x32dbg.txt")
json_path=os.path.join(scrapland_folder, "scrap_dissect.json")
assert os.path.isfile(scrap_exe), "File not found!"
r2 = r2pipe.open(scrap_exe)
@ -74,238 +76,34 @@ def analysis(full=False):
for ac in steps:
print(f"[*] Running '{ac}'")
r2_cmd(f"{ac} 2>NUL")
# 0x7fac20
# 0x7fac19
# 0x7faa4c
# 0x7fac1c # activate viewer
# 0x84d400 # lib preloaded
# 0x413ee0
with open(os.path.join(script_path,"config.yml")) as cfg:
print("[*] Loading config")
config = type("Config",(object,),yaml.load(cfg,Loader=yaml.SafeLoader))
# 0x7d2094 refcnt
comments= {
0x6113f9:"Check if Window exists"
}
flags = {
0x7FE944: "P_World",
0x7FBE4C: "P_Vars",
0x79C698: "Py_Mods",
0x852914: "P_D3D8_Dev",
0x7FCC00: "N_Paks_opened",
0x7fcbec: "Hash_Index_Size",
0x7fcbf0: "P_Hash_Index",
0x7fcc08: "Lst_File",
0x7fcc04: "Pak_Locked",
0x7fc1b0: "Pak_Index",
0x84cb64: "P_ConHandler",
0x801e10: "num_arrows",
0x7fac84: "P_Callbacks",
0x80b2cc: "P_ActClassList",
0x807a20: "P_Scorer",
0x80a398: "P_SoundSys",
0x84cb58: "H_RichEd",
0x84cb4c: "P_HWND_Console",
0x80cb40: "Console_Win_Buffer",
0x84d400: "Lib_preloaded",
0x7fac1c: "Activate_Viewer",
0x8b18f0: "P_Models",
0x8b18f4: "P_Scenes",
0x8b18f8: "P_ActiveModels",
0x803bc0: "net_is_server",
0x8045e4: "net_is_master",
0x8038a8: "net_is_client",
0x7fadd8: "is_python",
0x7fc084: "pak_lock",
0x7fbe7c: "current_language",
}
VMTs = {
0x78d4d8: "Py_entity",
0x78cc6c: "World",
0x78b680: "FilePak_1",
0x78b6a4: "FilePak_2",
0x78b638: "AbstractFile",
0x78b4d8: "App",
0x78b480: "Window",
0x78b5c0: "File",
0x78b65c: "FileMem",
0x78b6d0: "IDevice_1",
0x78b6f4: "IDevice_2",
0x78b6fc: "IDevice_Kb",
0x78b720: "IDevice_Mouse",
0x78b74c: "IDevice_Joy",
0x7933ac: "3d_Gfx",
0x7933a0: "NodeFX",
}
types = [
"struct PyMethodDef { char *ml_name; void *ml_meth; int ml_flags; char *ml_doc;};",
"struct GameVar { struct GameVar* next; const char* name; const char* desc; uint64_t d_type; void* value; void* def_value; };",
"struct HT_Entry { void* data; const char* key; struct HT_Entry* next;};",
"struct PakEntry { unsigned char* filename; bool locked; void* data; uint32_t seek;};",
"struct HashIndexEntry { uint32_t offset; uint32_t size; uint32_t status; const char* name; struct HashIndexEntry* next; };",
"struct HashIndex { uint32_t size; struct HashIndexEntry** data; };",
"struct HashTableEntry { void* data; const char *key; struct HashTableEntry* next; };",
"struct HashTable { uint32_t size; struct HashTableEntry** data; };",
]
func_sigs = {
0x5A8390: "int py_exec(const char* script);",
0x5BB9D0: "int PyArg_ParseTuple(void* PyObj, char* format, ...);",
0x413ee0: "int dbg_log(const char* fmt,...);",
0x4134C0: "int write_log(unsigned int color, const char* msg);",
0x47C1E0: "int ht_hash_ent_list(const char* str);",
0x404BB0: "int ht_hash_ent(const char* str);",
0x4016F0: "int reg_get_val(const char* value);",
0x414280: "int prepare_html_log(const char* filename);",
0x6597d0: "bool read_ini_entry(void* dest,const char* key, const char* section);",
0x5A8FB0: "void* Py_InitModule(const char* name,void* methods);",
0x5E3800: "int fopen_from_pak(const char* filename);",
0x419950: "int fopen_2(const char* filename);",
0x41AB50: "int open_pak(const char* filename, int unk_1,void* unk_ptr);",
0x404460: "int register_c_callback(const char* name,void* func);",
0x414070: "void throw_assertion_2(const char* check,const char* file,const char* date, unsigned int line);",
0x5FBC50: "void throw_assertion_1(const char* check,const char* file, unsigned int line);",
0x5BC140: "static char* convertsimple1(void *arg, char **p_format, void *p_va);",
0x5E3800: "int32_t fopen_from_pak(const char* filename,const char* mode);",
0x5a90f0: "void* Py_BuildValue(const char* format, ...);"
}
functions = {
0x6B1C70: "strcmp",
0x5BB9D0: "PyArg_ParseTuple",
0x5DD510: "init_engine_3d",
0x401180: "create_window",
0x401240: "create_main_window",
0x4016F0: "reg_get_val",
0x4134C0: "write_log",
0x414280: "prepare_html_log",
0x418220: "get_version_info",
0x4137E0: "write_html_log",
0x402190: "handle_console_input",
0x5F9520: "handle_render_console_input",
0x404A50: "find_entity",
0x47C1E0: "ht_hash_ent_list",
0x404BB0: "ht_hash_ent",
0x404460: "register_c_callback",
0x417470: "load_game",
0x5E3800: "fopen_from_pak",
0x5e3500: "fopen",
0x403370: "init_debug",
0x401770: "init",
0x4026D0: "init_py",
0x405B40: "init_py_sub",
0x5A8FB0: "Py_InitModule",
0x41AB50: "open_pak",
0x5A8390: "py_exec",
0x414570: "setup_game_vars",
0x5FBC50: "throw_assertion_1",
0x414070: "throw_assertion_2",
0x5F7000: "read_ini",
0x650F80: "load_sm3",
0x6665A0: "load_m3d_1",
0x666900: "load_m3d_2",
0x479B20: "world_constructor",
0x479B40: "init_world",
0x402510: "deinit_world",
0x479870: "make_world",
0x602A70: "render_frame",
0x6B738C: "handle_exception",
0x5B9E70: "py_getattr",
0x413ee0: "dbg_log",
0x5f75e0: "init_d3d",
0x63a2f0: "gdi_draw_line",
0x5e3250: "read_stream",
0x5e3bb0: "read_stream_wrapper",
0x50b9b0: "init_scorer",
0x582e10: "init_action_class_list",
0x528910: "init_sound_sys",
0x5268d0: "try_init_sound_sys",
0x404280: "cPyFunction_set_func",
0x414680: "load_config",
0x414810: "save_config",
0x4f42a0: "close_server_socket",
0x4f4d10: "close_server",
0x4f48e0: "close_client",
0x4f4fb0: "is_server",
0x4f4a10: "is_client",
0x4fac50: "is_master",
0x526910: "close_sound_sys",
0x526520: "shutdown_sound_sys",
0x5dd700: "close_3d_engine",
0x5a7320: "close_window",
0x5dff20: "set_exception_handler",
0x5a7f20: "get_console_wnd",
0x5a73a0: "show_console",
0x666c60: "read_m3d",
0x417df0: "snprintf",
0x5fc930: "printf",
0x6597d0: "read_ini_entry",
0x5fc0a0: "engine_debug_log",
0x5a7440: "create_console_window",
0x6114e0: "setup_window",
0x404420: "clear_functions",
0x405ca0: "close_py_subsys",
0x50bcb0: "close_scorer",
0x479b20: "close_world",
0x582e70: "close_action_class",
0x50b6a0: "get_scorer",
0x50ea20: "scorer_parse_type",
0x636580: "list_models",
0x5a90f0: "Py_BuildValue",
0x41c5a0: "has_lst_file",
0x5a8e90: "py_error",
0x5a9890: "get_module_dict",
0x5c7bb0: "get_current_thread",
0x5aa140: "preload_lib",
0x413c10: "sprintf",
0x405850: "check_is_python",
0x47bf90: "setup_ent_list",
0x474f80: "ent_list_get_set",
}
# 0x853954 ??? some obj ptr
# [0x7fbe98]
# [0x853954]+0x2a3cc debug flag, checked in 0x006113a0 called from 0x005dd5ea
cfg="""
e asm.cmt.right = true
e cmd.stack = true
e scr.utf8 = true
e asm.describe = false
e graph.cmtright = true
e cfg.sandbox = false
e cfg.newtab = true
e cfg.fortunes.type = tips,fun,creepy,nsfw
e dbg.status = true
e pdb.autoload = true
e emu.str = true
e asm.flags.offset = true
""".strip().splitlines()
for line in cfg:
for line in config.script.strip().splitlines():
r2_cmd(line)
analysis(False)
for addr,comment in comments.items():
for addr,comment in config.comments.items():
r2_cmd(f"CC {comment} @ {hex(addr)}")
for t in types:
for t in config.types:
r2_cmd(f'"td {t}"')
for addr, name in flags.items():
for addr, name in config.flags.items():
x64_dbg_label(addr,name,"loc")
r2_cmd(f"f loc.{name} 4 {hex(addr)}")
for addr, name in functions.items():
for addr, name in config.functions.items():
x64_dbg_label(addr,name,"fcn")
r2_cmd(f"afr fcn.{name} {hex(addr)}")
if addr in func_sigs:
r2_cmd(f'"afs {func_sigs[addr]}" @{hex(addr)}')
for addr,sig in config.function_signatures:
r2_cmd(f'"afs {config.function_signatures[addr]}" @{hex(addr)}')
def vtables():
@ -314,7 +112,7 @@ def vtables():
vtables = r2_cmdJ("avj")
for c in tqdm(vtables, ascii=True):
methods = []
name=VMTs.get(c.offset,f"{c.offset:08x}")
name=config.VMTs.get(c.offset,f"{c.offset:08x}")
x64_dbg_label(c.offset,name,"vmt")
r2_cmd(f"f vmt.{name} 4 {hex(c.offset)}")
for idx,m in enumerate(tqdm(c.methods, ascii=True, leave=False)):
@ -389,17 +187,14 @@ def bb_refs(addr):
ret[hit]["asm"].append(ins.disasm)
return ret
def world():
print("[*] Parsing World offsets")
return bb_refs("loc.P_World")
def render():
print("[*] Parsing D3D_Device offsets")
return bb_refs("loc.P_D3D8_Dev")
def py_mods():
print("[*] Parsing Python modules")
res = r2_cmd("/r fcn.Py_InitModule ~CALL[1]").splitlines()
@ -498,7 +293,7 @@ with open(x64_dbg_script_path,"w") as of:
print("[+] Wrote scrap_dissect.x32dbg.txt")
with open(script_path, "w") as of:
with open(r2_script_path, "w") as of:
wcmds = []
for cmd in r2cmds:
record=True
@ -524,7 +319,7 @@ print("[+] Analysis took:",datetime.today()-t_start)
print("[+] Executing Cutter")
try:
start_program(['cutter','-A','0','-i',script_path,scrap_exe],cwd=folder,shell=False)
start_program(['cutter','-A','0','-i',r2_script_path,scrap_exe],cwd=scrapland_folder,shell=False)
except FileNotFoundError:
print("[-] cutter not installed, falling back to r2")
start_program(['r2','-i',script_path,scrap_exe],cwd=folder,shell=False)
start_program(['r2','-i',r2_script_path,scrap_exe],cwd=scrapland_folder,shell=False)