做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,然后放在同目录下即可
    181203 逆向-基于Frida的VM_log工具

例如令test_input=0x77
181203 逆向-基于Frida的VM_log工具
执行程序可以看到log
181203 逆向-基于Frida的VM_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()

相关文章:

  • 2021-06-13
  • 2021-09-17
  • 2021-12-25
  • 2022-12-23
  • 2021-06-30
  • 2022-12-23
  • 2021-08-01
  • 2022-03-07
猜你喜欢
  • 2021-10-20
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-16
  • 2022-01-05
  • 2022-12-23
相关资源
相似解决方案