forked from ReScrap/ScrapHacks
		
	Lots of Updates (expand for more):
- Started implementing new parser for chunked data - Started documenting data formats - Started dissector for network protocol - Added AI-Graph renderer (converts .pth files to python data you can import into Blender) - Added Script to convert savefile to JSON - Added (old) parser for chunked data format - Added basic parser for LFVF data section (Vertex Data) - Added script to analyze and filter read trace generated with frida script - Added various Frida scripts
This commit is contained in:
		
							parent
							
								
									aabacafd9c
								
							
						
					
					
						commit
						8d92f25b8c
					
				
					 47 changed files with 2744 additions and 411 deletions
				
			
		
							
								
								
									
										60
									
								
								frida/frida_hook_net.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								frida/frida_hook_net.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| var sendto = Module.getExportByName("WSOCK32.dll", "sendto") | ||||
| var recvfrom = Module.getExportByName("WSOCK32.dll", "recvfrom") | ||||
| 
 | ||||
| Interceptor.attach(ptr("0x004f9300"), { | ||||
|     onEnter: function (args) { | ||||
|         console.log("[SendUsrString]", JSON.stringify({ | ||||
|             data: args[0].readCString(), | ||||
|             dst: args[1].toInt32(), | ||||
|             chat: args[2].toInt32() | ||||
|         })); | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| Interceptor.attach(ptr(sendto), { | ||||
|     onEnter: function (args) { | ||||
|         this.socket = args[0]; | ||||
|         this.buffer = args[1]; | ||||
|         this.size = args[2].toInt32(); | ||||
|         this.flags = args[3].toInt32(); | ||||
|         this.sock_addr = args[4]; | ||||
|         this.to_len = args[5].toInt32(); | ||||
|     }, | ||||
|     onLeave: function (ret) { | ||||
|         var port = this.sock_addr.add(2).readU16(); | ||||
|         var addr = this.sock_addr.add(4).readU32(); | ||||
|         var data = Memory.readByteArray(this.buffer, ret.toInt32()) | ||||
|         send({ | ||||
|             type: "SEND", | ||||
|             ptr: this.buffer.toInt32(), | ||||
|             addr, | ||||
|             port | ||||
|         }, data); | ||||
|         return ret; | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| Interceptor.attach(ptr(recvfrom), { | ||||
|     onEnter: function (args) { | ||||
|         this.socket = args[0]; | ||||
|         this.buffer = args[1]; | ||||
|         this.size = args[2].toInt32(); | ||||
|         this.flags = args[3].toInt32(); | ||||
|         this.sock_addr = args[4]; | ||||
|         this.from_len = args[5].toInt32(); | ||||
|     }, | ||||
|     onLeave: function (ret) { | ||||
|         if (!ret.equals(ptr("0xffffffff"))) { | ||||
|             var port = this.sock_addr.add(2).readU16(); | ||||
|             var addr = this.sock_addr.add(4).readU32(); | ||||
|             var data = Memory.readByteArray(this.buffer, ret.toInt32()) | ||||
|             send({ | ||||
|                 type: "RECV", | ||||
|                 ptr: this.buffer.toInt32(), | ||||
|                 addr, | ||||
|                 port | ||||
|             }, data); | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| }) | ||||
							
								
								
									
										174
									
								
								frida/frida_hook_read_trace.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								frida/frida_hook_read_trace.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | |||
| var pak_files = {} | ||||
| var ftypes = {} | ||||
| var record=false; | ||||
| 
 | ||||
| var current_block_id; | ||||
| var filename; | ||||
| var t0 = performance.now(); | ||||
| 
 | ||||
| Interceptor.attach(ptr("0x5e3b50"), { //read_block_header
 | ||||
|     onEnter: function (args) { | ||||
|         filename = pak_files[this.context.ecx] || this.context.ecx; | ||||
|         current_block_id = args[0].readUtf8String(); | ||||
|     }, | ||||
| 
 | ||||
|     onLeave: function(ret) { | ||||
|         return ret; | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| // Interceptor.attach(ptr("0x5e3c50"), { // read_block_id
 | ||||
| //     onEnter: function (args) {
 | ||||
| //         var filename=pak_files[this.context.ecx]||this.context.ecx;
 | ||||
| //         var id=args[1].readUtf8String();
 | ||||
| //         console.log("[+read_block("+filename+")]",id,args[1]);
 | ||||
| //     },
 | ||||
| 
 | ||||
| //     // onLeave: function(ret) {
 | ||||
| //     //     console.log("[-read_ini_block] Ret:",ret);
 | ||||
| //     // }
 | ||||
| // })
 | ||||
| 
 | ||||
| Interceptor.attach(ptr("0x7B43B020"),{ | ||||
|     onEnter: function(args) { | ||||
|         var info={}; | ||||
|         info['this']=args[0]; | ||||
|         info['Length']=args[1]; | ||||
|         info['Usage']=args[2]; | ||||
|         info['FVF']=args[3]; | ||||
|         info['Pool']=args[4]; | ||||
|         info['ppVertexBuffer']=args[4]; | ||||
|         send({CreateVertexBuffer:info}); | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| Interceptor.attach(ptr("0x5e3bb0"), { // read_stream_wrapper
 | ||||
|     onEnter: function (args) { | ||||
|         this.args = {}; | ||||
|         this.args[0] = args[0]; | ||||
|         this.args[1] = args[1]; | ||||
|         this.timestamp = performance.now()-t0; | ||||
|     }, | ||||
|     onLeave: function (ret) { | ||||
|         var data=Memory.readByteArray(this.args[0],this.args[1].toInt32()); | ||||
|         var stack = Thread.backtrace(this.context,Backtracer.ACCURATE); | ||||
|         var obj={ | ||||
|             filename, | ||||
|             timestamp: this.timestamp, | ||||
|             block_id: current_block_id, | ||||
|             stack | ||||
|         }; | ||||
|         send(obj,data); | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| 
 | ||||
| Interceptor.attach(ptr("0x5e3800"), { // fopen_from_pak
 | ||||
|     onEnter: function (args) { | ||||
|         this.filename = args[0].readUtf8String(); | ||||
|     }, | ||||
|     onLeave: function (ret) { | ||||
|         if (ret != 0) { | ||||
|             pak_files[ret] = this.filename; | ||||
|         } | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| // Interceptor.attach(ptr("0x5e3c50"), { // read_block_id
 | ||||
| //     onEnter: function (args) {
 | ||||
| //         console.log("[+read]",args[0],args[1]);
 | ||||
| //     },
 | ||||
| //     onLeave: function(ret) {
 | ||||
| //         console.log("[-read] Ret:",ret);
 | ||||
| //     }
 | ||||
| // })
 | ||||
| 
 | ||||
| // Interceptor.attach(ptr("0x6665a0"), { // load_m3d_1
 | ||||
| //     onEnter: function (args) {
 | ||||
| //         console.log("[M3D_1]",args[0].readUtf8String());
 | ||||
| //     }
 | ||||
| // })
 | ||||
| 
 | ||||
| 
 | ||||
| // Interceptor.attach(ptr("0x666900"), { // load_m3d_2
 | ||||
| //     onEnter: function (args) {
 | ||||
| //         console.log("[M3D_2]",args[0].readUtf8String());
 | ||||
| //     }
 | ||||
| // })
 | ||||
| 
 | ||||
| 
 | ||||
| function dasm(addr, size) { | ||||
|     var size = size || 8; | ||||
|     var offset = 0; | ||||
|     var ret = []; | ||||
|     while (ret.length != size) { | ||||
|         var inst = Instruction.parse(ptr(addr).add(offset)); | ||||
|         ret.push(("[" + inst.address + "] " + inst.mnemonic + " " + inst.opStr).trim()); | ||||
|         offset += inst.size; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| function r(addr, options) { | ||||
|     var options = options || {} | ||||
|     var max_depth = options.max_depth || 4; | ||||
|     var num = options.num || 4; | ||||
|     var ret = {}; | ||||
|     var vals = [ | ||||
|         "S8", | ||||
|         "U8", | ||||
|         "S16", | ||||
|         "U16", | ||||
|         "S32", | ||||
|         "U32", | ||||
|         "Float", | ||||
|         "Double", | ||||
|         "Pointer", | ||||
|         "CString", | ||||
|         "Utf8String", | ||||
|         "Utf16String", | ||||
|         "AnsiString" | ||||
|     ]; | ||||
|     vals.forEach(function (k) { | ||||
|         try { | ||||
|             ret[k] = ptr(addr)['read' + k]() | ||||
|         } catch (e) { | ||||
|             ret[k] = undefined; | ||||
|         } | ||||
|     }) | ||||
|     try { | ||||
|         ret["code"] = dasm(addr, 8); | ||||
|     } catch (e) { | ||||
|         ret["code"] = undefined; | ||||
|     } | ||||
| 
 | ||||
|     if (max_depth > 1) { | ||||
|         var p = {}; | ||||
|         var read_ptr = false; | ||||
|         for (var i = 0; i < num; ++i) { | ||||
|             if (ret["Pointer"] === undefined) { | ||||
|                 continue; | ||||
|             } | ||||
|             p[i * Process.pointerSize] = r(ret["Pointer"].add(i * Process.pointerSize), { | ||||
|                 max_depth: max_depth - 1, | ||||
|                 num | ||||
|             }); | ||||
|             read_ptr = true; | ||||
|         } | ||||
|         if (read_ptr) { | ||||
|             ret["p"] = p; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // function test() {
 | ||||
| //     for (var p = 0; p < 4; ++p) {
 | ||||
| //         var player = ptr(0x7FE944).readPointer().add(0x288 + p * 4).readPointer();
 | ||||
| //         if (!player.isNull()) {
 | ||||
| //             console.log("Player " + (p+1) + ":", player);
 | ||||
| //             console.log(JSON.stringify(r(player),null,4));
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| // }
 | ||||
							
								
								
									
										56
									
								
								frida/frida_inject_net.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								frida/frida_inject_net.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| import frida | ||||
| import psutil | ||||
| from binascii import hexlify | ||||
| import subprocess as SP | ||||
| import string | ||||
| import ipaddress | ||||
| from dissect_net import packet,printable_chars,hexdump,is_printable | ||||
| 
 | ||||
| def on_message(msg, data=None): | ||||
|     if not data: | ||||
|         return | ||||
|     msg = msg["payload"] | ||||
|     IP = ipaddress.IPv4Address(msg["addr"]) | ||||
|     IP = ipaddress.IPv4Address(IP.packed[::-1]) | ||||
|     direction = msg["type"] | ||||
|     port = msg["port"] | ||||
|     ptr = msg["ptr"] | ||||
| 
 | ||||
|     with open("netlog.txt","a",encoding="utf8") as of: | ||||
|         print( | ||||
|             "{} {}:{} 0x{:x} {}".format(msg["type"], IP, port, ptr, str(hexlify(data),"utf8")), | ||||
|             file=of | ||||
|         ) | ||||
|          | ||||
|     if is_printable(data): | ||||
|         print(direction, addr, buffer_addr, data) | ||||
|         return | ||||
|      | ||||
|     try: | ||||
|         parsed_data = packet.parse(data) | ||||
|         print( | ||||
|             "{} {}:{} 0x{:x}".format(msg["type"], IP, port, ptr) | ||||
|         ) | ||||
|         print(hexdump(data)) | ||||
|         print(parsed_data) | ||||
|         print() | ||||
|     except Exception as e: | ||||
|         print(e) | ||||
|         pass | ||||
| 
 | ||||
| def main(): | ||||
|     pid = frida.spawn(sys.argv[1:]) | ||||
|     session = frida.attach(pid) | ||||
|     session.enable_jit() | ||||
|     script = session.create_script(open("frida_hook_net.js").read()) | ||||
|     open(f"netlog.txt","w",encoding="utf8").close() | ||||
|     script.on("message", on_message) | ||||
|     script.load() | ||||
|     frida.resume(pid) | ||||
|     proc = psutil.Process(pid) | ||||
|     proc.wait() | ||||
|     session.detach() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										59
									
								
								frida/frida_inject_read_trace.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								frida/frida_inject_read_trace.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| from __future__ import print_function | ||||
| import frida | ||||
| import os | ||||
| import sys | ||||
| import psutil | ||||
| import binascii | ||||
| import sqlite3 | ||||
| import json | ||||
| import time | ||||
| import msgpack | ||||
| from multiprocessing import JoinableQueue | ||||
| import threading | ||||
| 
 | ||||
| 
 | ||||
| q = JoinableQueue() | ||||
| 
 | ||||
| 
 | ||||
| def db_worker(q): | ||||
|     with open("dump.mp", "wb") as of: | ||||
|         while True: | ||||
|             args = q.get() | ||||
|             if args is None: | ||||
|                 q.task_done() | ||||
|                 break | ||||
|             msgpack.dump(args, of) | ||||
|             q.task_done() | ||||
| 
 | ||||
| 
 | ||||
| db_w = threading.Thread(target=db_worker, args=(q,)) | ||||
| 
 | ||||
| db_w.start() | ||||
| 
 | ||||
| 
 | ||||
| def on_message(msg, data): | ||||
|     filename = msg.get("payload", {}).get("filename", "<UNKNOWN>").replace("\\", "/") | ||||
|     block_id = msg.get("payload", {}).get("block_id", "<UNKNOWN>") | ||||
|     print(filename,block_id,data) | ||||
|     msg["payload"]["data"] = data | ||||
|     q.put(msg["payload"]) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     pid = frida.spawn(sys.argv[1:]) | ||||
|     session = frida.attach(pid) | ||||
|     script = session.create_script(open("frida_hook_read_trace.js").read()) | ||||
|     script.on("message", on_message) | ||||
|     script.load() | ||||
|     frida.resume(pid) | ||||
|     proc = psutil.Process(pid) | ||||
|     proc.wait() | ||||
|     session.detach() | ||||
|     q.put(None) | ||||
|     q.join() | ||||
|     q.close() | ||||
|     db_w.join() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										8
									
								
								frida/frida_mem_mon.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								frida/frida_mem_mon.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| MemoryAccessMonitor.enable({ | ||||
|     base: ptr("0x7fe944"), | ||||
|     size: 4 | ||||
| }, { | ||||
|     onAccess: function (details) { | ||||
|         console.log(details.operation, details.from, details.address) | ||||
|     }, | ||||
| }) | ||||
							
								
								
									
										22
									
								
								frida/frida_mem_mon.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								frida/frida_mem_mon.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| import frida | ||||
| import sys | ||||
| import psutil | ||||
| 
 | ||||
| def on_message(msg, data=None): | ||||
|     print(msg,data) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     pid = frida.spawn(sys.argv[1:]) | ||||
|     session = frida.attach(pid) | ||||
|     session.enable_jit() | ||||
|     script = session.create_script(open("frida_mem_mon.js").read()) | ||||
|     script.on("message", on_message) | ||||
|     script.load() | ||||
|     frida.resume(pid) | ||||
|     proc = psutil.Process(pid) | ||||
|     proc.wait() | ||||
|     session.detach() | ||||
|      | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										43
									
								
								frida/frida_stalker_test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								frida/frida_stalker_test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| var stalked_threads = []; | ||||
| var excluded_modules = [] | ||||
| var sent=false; | ||||
| setInterval(() => { | ||||
|     Process.enumerateModules().forEach(mod => { | ||||
|         if (mod.name == "Scrap.exe") { | ||||
|             if (!sent) { | ||||
|                 send({ | ||||
|                     mod: mod | ||||
|                 }) | ||||
|                 sent=true; | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|         if (excluded_modules.indexOf(mod.name) == -1) { | ||||
|             Stalker.exclude(mod); | ||||
|             excluded_modules.push(mod.name); | ||||
|         } | ||||
|     }) | ||||
|     Process.enumerateThreads().forEach(thread => { | ||||
|         if (stalked_threads.indexOf(thread.id) != -1) { | ||||
|             return; | ||||
|         } | ||||
|         Stalker.follow(thread.id, { | ||||
|             events: { | ||||
|                 call: true, | ||||
|                 block: true, | ||||
|                 compile: true, | ||||
|                 ret: true, | ||||
|                 exec: true | ||||
|             }, | ||||
|             onReceive: function (events) { | ||||
|                 send({ | ||||
|                     stalker: Stalker.parse(events, { | ||||
|                         annotate: true, | ||||
|                         stringify: true | ||||
|                     }) | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         stalked_threads.push(thread.id); | ||||
|     }) | ||||
| }, 0) | ||||
							
								
								
									
										67
									
								
								frida/frida_stalker_test.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								frida/frida_stalker_test.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| import frida | ||||
| import sys | ||||
| import psutil | ||||
| import subprocess as SP | ||||
| import threading | ||||
| from multiprocessing import JoinableQueue | ||||
| import msgpack | ||||
| 
 | ||||
| 
 | ||||
| q = JoinableQueue() | ||||
| 
 | ||||
| 
 | ||||
| def db_worker(q): | ||||
|     events = 0 | ||||
|     with open("trace.mp", "wb") as of: | ||||
|         while True: | ||||
|             args = q.get() | ||||
|             if args is None: | ||||
|                 q.task_done() | ||||
|                 break | ||||
|             events += 1 | ||||
|             msgpack.dump(args, of) | ||||
|             q.task_done() | ||||
|     print("Wrote", events, "events") | ||||
| 
 | ||||
| 
 | ||||
| db_w = threading.Thread(target=db_worker, args=(q,)) | ||||
| 
 | ||||
| db_w.start() | ||||
| modules = {} | ||||
| mem_range = None | ||||
| 
 | ||||
| 
 | ||||
| def on_message(msg, data=None): | ||||
|     global mem_range | ||||
|     data = msg["payload"] | ||||
|     if "stalker" in data: | ||||
|         for val in data["stalker"]: | ||||
|             q.put(val) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     pid = frida.spawn(sys.argv[1:]) | ||||
|     session = frida.attach(pid) | ||||
|     session.enable_jit() | ||||
|     script = session.create_script(open("frida_stalker_test.js").read()) | ||||
|     script.on("message", on_message) | ||||
|     script.load() | ||||
|     frida.resume(pid) | ||||
|     proc = psutil.Process(pid) | ||||
|     proc.wait() | ||||
|     session.detach() | ||||
|     q.put(None) | ||||
|     q.join() | ||||
|     q.close() | ||||
|     db_w.join() | ||||
| 
 | ||||
| 
 | ||||
| """ | ||||
| import msgpack as mp | ||||
| from collections import Counter | ||||
| data=list(mp.Unpacker(open("trace.mp","rb"), raw=False)) | ||||
| Counter(v[1] for v in data).most_common(10) | ||||
| """ | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue