做CTF中的VM题一直都是在python中手写一遍所有的handler来模拟执行VM,然后打log并结合代码来猜测逻辑
然后慢慢地产生一个想法、本身程序也有在执行这个VM,何苦还要再手写VM呢,反正大多数情况下需要的只是log–再具体说的话就只是程序的reg和data而已
通过在VM的每次loop之间插桩拿到data,也同样可以打出log
由于CTF中的VM大多数情况下都不会很复杂,因此完全可以通过观察input数据和data的变化来猜测算法
于是以前两天的鹏城杯为例,写出了这个工具(的雏形)
由于缺乏项目经验,所以完善度比较差_(:з」∠)_还请大佬见谅
简单来说给定地址和长度,即可将数据send回py的callback函数,然后通过py进行格式化输出,可选参数都在代码开头部分,linux下基于py2
- program_name
程序名称 - hook_addr
要Hook的地址,一般位于VM_loop的开头 - data_addr
要dump的数据地址 - data_length
数据的长度 - data_fromat
打印数据的进制,仅接受16和10 - test_input
猜测的输入 - input_length
输入长度,当test_input小于该长度时会以comp_char补足 - anti_debug
程序若有反调试代码,则需要根据实际情况进行反调试。由于Frida是通过ptrace来实现的,因此对于ptrace(TRACE_ME)会没有办法。最简单的办法当然是Patch,如果不便需要Hook的话,本程序提供了LD_PRELOAD的方法来绕过一些函数。将下述代码编译成so,然后放在同目录下即可
例如令test_input=0x77
执行程序可以看到log
第17条指令执行前0x77被装入mem,下一条指令变为0x3f
简单起见可以直接猜测0x77^0x3f=>0x48,正好是前一个mem的数据
如果没猜对的话可以去查看第17条指令,值为12时对应的handler
然后猜测与之后的0x2e进行比较,于是反推出第一个输入应该是0x2e^0x48=>0x66
将test_input的第一个值改为0x66,再次运行,发现成功执行到了第二个字符的循环中,以此类推,获知算法
之后有空再试验一下Windows的,感觉Frida的API和python代码应该是不用改可以通用的
from __future__ import print_function
import frida
import sys
import frida
import string
import subprocess
import time
# make color work on Win10
import os
os.system("")
############# INIT SETTING ##########
program_name = "badblock"
hook_addr = 0x4415
data_addr = 0x206340
data_length = 20
data_format = 16
test_input = "flag{Y0u_ar3_S0co0L}"
comp_char = "\xaa"
input_length = 20
anti_debug = True
anti_debug_path = "./ptrace_and_getppid.so"
#####################################
# cause of change of input before VM, need to hook and write memory after change.
s = """
// data init
var base = Module.findBaseAddress('%s');
var target = base.add(%d)
var dump = base.add(%d)
String.prototype.format = function () {
var values = arguments;
return this.replace(/\{(\d+)(:\d+)?\}/g, function (match, index, width) {
if (values.length > index) {
return values[index];
} else {
return "";
}
});
};
//var printf = new NativeFunction(Module.findExportByName("libc.so.6", "printf"), "int", ["pointer", "int", "int"]);
// write input_data to mem
var input = new Array(20);
input[0] = 0x66;
input[1] = 0xaa;
// input_data init
for(var i=0;i<20;i++)
if(input[i]==undefined){
input[i] = 0x77;
}
Interceptor.attach(ptr(Number(base)+0x2f77), function(){
addr = Memory.readPointer(ptr(this.context["rdi"]));
console.log("addr:", addr.toString(16));
//console.log("ptr to:", Memory.readPointer(addr));
//console.log("value0:", Memory.readU8(ptr(addr)));
for(var i=0;i<20;i++)
Memory.writeU8(addr.add(i), input[i]);
});
// dump data
Interceptor.attach(target, function() {
//send(JSON.stringify(this.context));
i_ = this.context["rdx"] //i
value_ptr = ptr(this.context["rsi"]) //value_ptr
value = Memory.readU8(value_ptr);
data = ""
for(var i=0;i<%d;i++){
buff = Memory.readU8(dump.add(i)).toString(%d);
while((buff).length<%d)
buff = "0" + buff
data += buff + " ";
}
send("{0}:{1}:{2}".format(parseInt(i_/3), value, data));
/*make program later over for frida-server sending data
for(i=0;i<0xffff;i++)
{;}*/
});
"""%(program_name, hook_addr, data_addr, data_length, data_format, 2 if data_format==16 else 3)
def hook():
session = frida.attach(program_name)
script = session.create_script(s)
script.on('message', on_message)
script.load()
return session
def on_message(message, data):
global n, msg, last
if message['type'] == 'send':
p = message['payload'].split(":")
tmp = ""
if(last!=""):
cmp = 0
for i in range(len(p[2])):
if( p[2][i]!=last[i]):
if(cmp==0):
tmp += "\033[1;31m"
cmp = 1
tmp += p[2][i]
else:
if(cmp==1):
tmp += "\033[0m"
cmp = 0
tmp += p[2][i]
else:
tmp = p[2]
msg = "[\033[1;33m%3s\033[0m]:\033[1;32m%5s\033[0m\t%s"%(p[0], p[1],tmp)
print(msg)
last = p[2]
def main():
global char
global n
f = 1
stat = 0
input = test_input + (input_length-len(test_input))*comp_char
p = subprocess.Popen(CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, env={"LD_PRELOAD":anti_debug_path} if anti_debug else None)
time.sleep(0.5)
session = hook()
pin, pout = p.stdin, p.stdout
pin.write(input+"\n")
pin.flush()
for k in range(9):
a = pout.readline()
session.detach()
p.kill()
time.sleep(0.5)
print("finish")
n = 0
msg = ""
char = "1"
last = ""
CMD = "./%s"%program_name
if(len(comp_char)!=1):
raise Exception("Complement Character must be one character.")
main()