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