一、追踪malloc以及free的调用地址(不含调用栈)
import sys
import frida
def on_message(message, data):
if message['type'] == 'send':
print(message['payload'])
pid = int(input("请输入目标进程的PID: "))
session = frida.attach(pid)
print("attach suscess")
script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, "malloc"), {
onEnter: function(args) {
send("malloc called with: " + args[0]);
}
});
Interceptor.attach(Module.findExportByName(null, "free"), {
onEnter: function(args) {
send("free called with: " + args[0]);
}
});
""")
script.on('message', on_message)
script.load()
print("js load failed")
sys.stdin.read()
session.detach()
二、追踪malloc以及free的函数调用栈
import sys
import frida
def on_message(message, data):
if message['type'] == 'send':
print(message['payload'])
pid = int(input("请输入目标进程的PID: "))
session = frida.attach(pid)
print("attach success")
script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, "malloc"), {
onEnter: function(args) {
send("malloc called with: " + args[0]);
send("Call stack: " + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));
}
});
Interceptor.attach(Module.findExportByName(null, "free"), {
onEnter: function(args) {
send("free called with: " + args[0]);
send("Call stack: " + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));
}
});
""")
script.on('message', on_message)
script.load()
print("js load success")
sys.stdin.read()
session.detach()
三、追踪malloc以及free调用栈(输出到文件中)
import sys
import frida
pid = int(input("请输入目标进程的PID: "))
session = frida.attach(pid)
print("attach success")
# 打开文件并将标准输出重定向到该文件
f_output = open('output.txt', 'w')
f_stdout = sys.stdout
sys.stdout = f_output
script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, "malloc"), {
onEnter: function(args) {
send("malloc called with: " + args[0]);
send("Call stack: " + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));
}
});
Interceptor.attach(Module.findExportByName(null, "free"), {
onEnter: function(args) {
send("free called with: " + args[0]);
send("Call stack: " + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));
}
});
""")
def on_message(message, data):
if message['type'] == 'send':
print(message['payload'])
script.on('message', on_message)
script.load()
try:
sys.stdin.read()
except KeyboardInterrupt:
f_stdout.write(f" exit\n")
f_output.flush()
session.detach()
四、统计调用栈次数与详细信息并输出到文件中:
import sys
import frida
pid = int(input("请输入目标进程的PID: "))
session = frida.attach(pid)
print("attach success")
call_stacks = []
def on_message(message, data):
if message['type'] == 'send':
print(message['payload'])
if "Call stack" in message['payload']:
call_stacks.append(message['payload'])
script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, "malloc"), {
onEnter: function(args) {
send("Call stack: \\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));
}
});
Interceptor.attach(Module.findExportByName(null, "free"), {
onEnter: function(args) {
send("Call stack: \\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));
}
});
""")
script.on('message', on_message)
script.load()
print("js load success")
try:
sys.stdin.read()
except KeyboardInterrupt:
# 打开文件并将标准输出重定向到该文件
f_output = open('output.txt', 'w')
f_stdout = sys.stdout
sys.stdout = f_output
# 将调用栈信息整理成火焰图格式
flamegraph_data = {}
for stack in call_stacks:
stack_lines = stack.split("\n")
for line in stack_lines:
if line.startswith("0x"):
if line in flamegraph_data:
flamegraph_data[line] += 1
else:
flamegraph_data[line] = 1
# 输出火焰图格式数据
for addr, count in flamegraph_data.items():
print(f"次数:{count} 详细信息:{addr}")
f_stdout.write(" exit\n")
f_output.flush()
session.detach()
关于Frida目前研究的总结:
1、在Windows下无论是Release还是Debug都可以分析malloc以及free的调用栈,只不过Debug信息更详细,如果将Release的优化关闭
两者信息基本一致。
2、在Windows下符号信息仍需要pdb文件协助,这不同于Linux,好消息是只需要将pdb与测试的PE文件放在同一目录Frida可以自动搜寻到符号信息。
目前时间只够我研究到上面四个情况,第四种已经基本满足需求了,如果后续需要转换为火焰图的话还需要加以改进,但是整体脚本框架无需很大变动了,可以参考内存增长与火焰图这篇文章。