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:
Daniel S. 2020-08-04 18:05:34 +02:00
parent aabacafd9c
commit 8d92f25b8c
47 changed files with 2744 additions and 411 deletions

60
frida/frida_hook_net.js Normal file
View 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;
}
})

View 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
View 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()

View 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
View 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
View 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()

View 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)

View 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()