已知密文利用gdb进行的明文爆破
1461886022645831 发表于 四川 CTF 447浏览 · 2024-08-26 01:10

前提提醒:
这个方法不是原创
https://bbs.kanxue.com/thread-282105.htm
跟着这篇文章进行的学习
如果可以,大家还是多多去学习原文章
这篇文章就是写出我对这个方法的心得体会以及一些理解来帮助大家理解

我的理解
其实就是黑盒测试中,只不过黑盒必须是单字节才能进行爆破,因为最多只是128 n 次就可以
如果是双字节其实也行, 128**2
n
具体就看你们的大家的需求了

利用gdb得到下断点,我们监控到我们输入的明文最后通过程序加密后的地址,然后在断点处进行和密文的比较,从而得到我们想要的答案

准备工作
我们要去gdb的官网下载
minggw:https://www.mingw-w64.org/。
然后在里面的python包里面
导入我们需要的Crypto(就是标志密码包,我改名字了)和libnum包


这个版本
代码部分
再次提醒,本文的知识点是由国资师傅(上面的链接)学习的,并非原创

# 此脚本未测试
from time import *
import gdb,os
import libnum

from Crypto.Util.number import long_to_bytes, bytes_to_long, getPrime, isPrime
'''
黑盒测试脚本:
目标:有一段代码,多次执行,改变某些输入,观察某些输出,
1.函数开始、结束分别设置断点
2.执行到函数开始处记录状态
循环执行如下步骤
    3.设置状态为原始状态,设置change状态,并执行
    4.结束后,取出 moniter 状态

从输入开始黑盒测试脚本
1.设置结束断点
2.重定向输入
3.每次修改输入后执行函数
4.执行到断点处取出结果    
'''

General_regs_list = ["ax", "bx", "cx", "dx", "di", "si", "sp", "bp", "ip"]
Segment_regs_list = ["es", "ds", "cs", "ss", "fs", "gs"]
x64_regs_list = ["r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"]
flag_reg = ["eflags"]


class Black_Test:
    def __init__(self, start_addr: int, end_addr: int, change_context: dict, moniter_target: list):
        """
        start_addr : 起始地址
        end_addr : 结束地址
        change_context : 改变的数据,格式为{"eax":0,"0x400080":1},每次修改的数据只有1个
        moniter_target : 监视内容,格式为["eax",[0x40080,0x400a0]]
        """
        self.start_addr = start_addr
        self.end_addr = end_addr
        self.moniter_target = moniter_target
        self.change_context = change_context
        self.stdin_path = "test.txt"

        # 设置架构,目前只支持x86 x64
        tmp = gdb.selected_inferior().architecture().name()
        if "i386:x86-64" == tmp:
            self.arch = "x64"
            self.pc = "rip"
        elif "i386" == tmp:
            self.arch = "x86"
            self.pc = "eip"
        # 架构所用寄存器列表,不支持 xmm 寄存器
        self.regs_list = self.get_regs_list()

    def get_data_long(self, StartSeclectAddr, len):
        '''
        从开始地址取得一定长度的数据
        :param StartSeclectAddr: 开始地址
        :param len: 获取的长度
        :return:
        '''
        mem_obj = gdb.selected_inferior().read_memory(StartSeclectAddr, len)
        data = bytes_to_long(mem_obj.tobytes()[::-1])
        return data

    def set_func_state(self, context: dict):
        """
        设置函数状态
        """
        for key in context.keys():
            # 设置寄存器值
            if key in self.regs_list:
                # print(key)
                # print(context[key])
                if key == "eflags":
                    gdb.execute(f"set ${key} = {context[key]}")
                else:
                    gdb.execute(f"set ${key} = (long long int){context[key]}")
            # 否则就按照内存进行设置
            else:
                # print("long_to_bytes(context[key])[::-1] is" ,long_to_bytes(context[key])[::-1])
                # print("key is ",key)
                gdb.selected_inferior().write_memory(int(key, 16), long_to_bytes(context[key])[::-1])
                # gdb.execute("x/20gx {}".format(key))

    def get_regs_list(self) -> list:
        """
        获得寄存器列表
        """
        regs_list = []
        # 判断架构前缀
        if self.arch == "x86":
            regs_prefix = "e"
        elif self.arch == "x64":
            regs_prefix = "r"
            regs_list = x64_regs_list
            regs_list += flag_reg

        for i in General_regs_list:
            regs_list.append(regs_prefix + i)
        # 段寄存器
        regs_list += Segment_regs_list

        return regs_list

    def get_all_regs_value(self) -> dict:
        """
        获得寄存器的值
        """
        regs_context = {}
        for i in self.regs_list:
            # print(i)
            regs_context[i] = int(hex(gdb.selected_frame().read_register(i)), 16)
        return regs_context

    def get_context(self) -> dict:
        # 获取所有寄存器的值
        context = self.get_all_regs_value()
        # 获取监视内存的值
        # 监视内存的结构为[0x400080,0x400100]
        # 所以长度为 target[1] - target[0]
        for i in self.moniter_target:
            if isinstance(i, list):
                context[hex(i[0])] = self.get_data_long(i[0], i[1] - i[0])

        return context

    def get_moniter_context(self) -> dict:
        """
        这个可以和上面整合,懒得写了
        获得监视的值
        """
        context = {}
        for i in self.moniter_target:
            if isinstance(i, list):
                context[hex(i[0])] = self.get_data_long(i[0], i[1] - i[0])
            elif isinstance(i, str):
                context[i] = hex(gdb.selected_frame().read_register(i))

        return context

    def delet_same_bp(self, delet_bp_addr, delet_bp_type=1):
        """
        :param delet_bp_addr:
        :param delet_bp_type: 1为 bp,7位 watch,9为 awatch
        :return:
        """
        # 获取 bp 列表
        bp_list = gdb.breakpoints()
        # print(type(bp_list))
        for bp in bp_list:
            # print(type(bp))
            # 现判断类型
            bp_type = bp.type
            if bp_type == delet_bp_type:
                # if bp.location != None: # 有些断点获取不了地址
                bp_addr = int(bp.location[1:], 16)
                if bp_addr == delet_bp_addr:
                    # print(bp.number)
                    bp.delete()

    def black_test(self, start_orig_context=""):
        """
        执行此函数需要在监视前后手动分别加断点,防止错误
        """
        # 添加断点
        gdb.execute("b *0x{0:X}".format(self.start_addr))
        gdb.execute("b *0x{0:X}".format(self.end_addr))
        # 如果 start_orig_context 为空表示第一次执行
        if start_orig_context == "":
            # 执行到起始断点处
            gdb.execute("c")
            # 获得原始状态
            self.start_orig_context = self.get_context()
        # 如果 start_orig_context 不为空表示多次执行
        else:
            # 获得原始状态
            self.start_orig_context = start_orig_context

        # 删除起始断点
        self.delet_same_bp(self.start_addr)
        # print(self.start_orig_context)
        start_context = self.start_orig_context
        # 添加变量的状态
        for key in self.change_context.keys():
            start_context[key] = self.change_context[key]
        # 设置为原始状态
        # print(start_context)
        self.set_func_state(start_context)
        # 继续执行到结束断点
        gdb.execute("c")
        # 删除结束断点
        self.delet_same_bp(self.end_addr)
        # 获取最终状态
        moniter_context = self.get_moniter_context()
        return moniter_context, self.start_orig_context

    def write_context2shm(self):
        # 写入 /dev/shm
        if not os.path.exists(self.stdin_path):
            os.system("dd if=/dev/zero of={} bs=1K count=1024".format(self.stdin_path))
        for key in self.change_context.keys():
            with open(self.stdin_path ,"wb") as fd:
                fd.write(long_to_bytes(self.change_context[key])[::-1])

    def black_test_from_start(self):
        """
        执行此函数需要在监视前后手动分别加断点,防止错误
        """
        # 添加断点
        self.delet_same_bp(self.end_addr)
        # gdb.execute("start < {}".format(self.stdin_path))
        gdb.execute("b *0x{0:X}".format(self.end_addr))
        self.write_context2shm()

        # 因为 windows下没有符号
        # 建议执行 run ,取消 start 和 c
        gdb.execute("run < {}".format(self.stdin_path))
        # gdb.execute("c")
        # 获取最终状态
        moniter_context = self.get_moniter_context()
        return moniter_context


def get_change_context(context_len: int):
    """
    获取改变内容的函数,需要根据测试情况修改
    :return:
    """
    for i in range(256 ** context_len):
        context = i
        yield context


def generate_flag(flag_len,prefix = b"",suffix = b""):
    """
    生成 flag
    :param flag_len: flag总长度
    :param prefix:
    :param suffix:
    :return:
    """

    flag_bytes = b"\x01" * (flag_len - len(prefix) - len(suffix))
    flag_bytes = prefix + flag_bytes + suffix
    flag = bytes_to_long(flag_bytes[::-1])

    return flag


def check_flag():

    return 0

def duchao_black_test():
    # 执行黑盒测试前需要在监视前后手动分别加断点,防止错误
    enc = [0x8A, 0x81, 0xA3, 0xBB, 0xE4, 0x82, 0x65, 0xDA, 0xC9, 0x85, 0x6C, 0xA9, 0xA4]
    flag = 0
    # 初始化状态的位置一定要注意
    # 因为循环中的爆破都不是第一次执行,所以要放在最外面
    start_context = ""
    # 没有实现前缀、后缀
    prefix = b""
    suffix = b""
    for i in range(len(enc)):
        moniter_context = {"rax": [], "0x7fffffffdb70": []}
        for j in range(128):
            # 如果是寄存器则使用字符串,如"eax"
            # 改变的数据,格式为{"eax":0,"0x400080":123}
            change_addr_1 = 0x7fffffffdb70
            change_value = flag + (j << (i * 8))
            change_test = {hex(change_addr_1): change_value, hex(
                0x555555558060): 0xd487c7b95dcb55fbff7bb8c8c0ef461b3d172af23a8f65befc7a7f620791de94a5eba77149b31a161376b25a43acb6dd20e9fad0684f12ecab95b01f45022853483142d5e0cde6e45783c6006ee747fd924a2d190a234bc5bd3e44a6a9f6d975ca744d3b30bc9a50d89cae9f40c9af2f64fef954f03896272b78ad9b1e8d5897dc0cb55693c48a7c052e8909857ef16a5cea3541a233c17dce8ce3d70186c3a1cc0ba303b124985f1d4c39df8090baa4526b3ff725aa0f7029517799726c2c675e73d6f3a808a069329d1cd336040661c234d2f5dbcfda5960e8148481edd163f488e13c6d18ee11e28b22265bf8798e8215bf0eb74ee537bb106621b40d9e6f}

            # 监视内容,格式为["eax",[0x40080,0x400a0]]
            monitor_addr = change_addr_1
            moniter_target = ["rax", [monitor_addr, monitor_addr + len(enc)]]
            black_test_start_addr = 0x555555555A0A
            black_test_end_addr = 0x555555555A0F

            black_test_proj = Black_Test(black_test_start_addr, black_test_end_addr, change_test, moniter_target)
            # 获取黑盒测试后的数据
            moniter_context_tmp, start_context = black_test_proj.black_test(start_context)
            # print(start_context)
            # moniter_context[hex(monitor_addr)].append(hex(( moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) &0xff ))
            print(i)
            print(j)
            if (moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) & 0xff == enc[i]:
                flag += j << (i * 8)
                break
        # print(moniter_context)
    print(long_to_bytes(flag)[::-1])



def duchao_blach_test_stdin():
    # 执行黑盒测试前需要在监视前后手动分别加断点,防止错误
    enc = [ 0x60, 0x6C, 0x66, 0x67, 0x63, 0x2D, 0x79, 0x60, 0x62, 0x7D,
  0x76, 0x21]
    # flag = 0x01010101010101010101010101
    # 初始化状态的位置一定要注意
    # 因为循环中的爆破都不是第一次执行,所以要放在最外面
    start_context = ""
    prefix = b""
    suffix = b""
    flag = generate_flag(len(enc),prefix=prefix,suffix=suffix)
    test={}
    for i in range(len(prefix) , len(enc)-len(suffix)):
        moniter_context = {"0x19FEBC": []}
        # 爆破的范围需要特别注意
        # scanf 等输入函数会不接受 \x09 \x0a \x0d 等字符
        for j in range(128):
            # 如果是寄存器则使用字符串,如"eax"
            # 改变的数据,格式为{"eax":0,"0x400080":123}
            change_addr_1 = 0x0407080
            change_value = flag + (j << (i * 8))
            print(libnum.n2s(change_value)[::-1])
            change_test = {hex(change_addr_1): change_value}
            # 监视内容,格式为["eax",[0x40080,0x400a0]]
            monitor_addr = change_addr_1
            moniter_target = [ [monitor_addr, monitor_addr + len(enc)]]
            black_test_start_addr = 0x555555555a0a
            black_test_end_addr = 0x40170E

            black_test_proj = Black_Test(black_test_start_addr, black_test_end_addr, change_test, moniter_target)
            # 获取黑盒测试后的数据
            moniter_context_tmp = black_test_proj.black_test_from_start()
            # print(start_context)
            # moniter_context[hex(monitor_addr)].append(hex(( moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) &0xff ))
            print(i)
            print(j)
            # print(moniter_context_tmp)
            print(long_to_bytes(flag)[::-1])
            # print(hex((moniter_context_tmp[hex(monitor_addr)]>>(i * 8)) &0xff))
            if (moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) & 0xff == enc[i]:
                print("ok")
                flag += j << (i * 8)
                break
        # print(moniter_context)
    print(long_to_bytes(flag)[::-1])


if __name__ == "__main__":
    # 指定程序段进行黑盒测试
    # duchao_black_test()
    # 从程序开始进行黑盒测试,可以控制输入
    duchao_blach_test_stdin()

其实注释写得很详细了
这段代码写得也不是很复杂
举例
我们随便举一个单字节的例子吧
就拿RC4


可以很明显发现整体逻辑


V5找到我们输入的地址(因为最后比较也是通过V5,如果赋值给其他了,需要定位要cmp的地址)


找到密文


找到比较密文的断点

国资师傅也给了对应的代码

# 此脚本未测试
from time import *
import gdb,os
import libnum

from Crypto.Util.number import long_to_bytes, bytes_to_long, getPrime, isPrime
'''
黑盒测试脚本:
目标:有一段代码,多次执行,改变某些输入,观察某些输出,
1.函数开始、结束分别设置断点
2.执行到函数开始处记录状态
循环执行如下步骤
    3.设置状态为原始状态,设置change状态,并执行
    4.结束后,取出 moniter 状态

从输入开始黑盒测试脚本
1.设置结束断点
2.重定向输入
3.每次修改输入后执行函数
4.执行到断点处取出结果    
'''

General_regs_list = ["ax", "bx", "cx", "dx", "di", "si", "sp", "bp", "ip"]
Segment_regs_list = ["es", "ds", "cs", "ss", "fs", "gs"]
x64_regs_list = ["r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"]
flag_reg = ["eflags"]


class Black_Test:
    def __init__(self, start_addr: int, end_addr: int, change_context: dict, moniter_target: list):
        """
        start_addr : 起始地址
        end_addr : 结束地址
        change_context : 改变的数据,格式为{"eax":0,"0x400080":1},每次修改的数据只有1个
        moniter_target : 监视内容,格式为["eax",[0x40080,0x400a0]]
        """
        self.start_addr = start_addr
        self.end_addr = end_addr
        self.moniter_target = moniter_target
        self.change_context = change_context
        self.stdin_path = "test.txt"

        # 设置架构,目前只支持x86 x64
        tmp = gdb.selected_inferior().architecture().name()
        if "i386:x86-64" == tmp:
            self.arch = "x64"
            self.pc = "rip"
        elif "i386" == tmp:
            self.arch = "x86"
            self.pc = "eip"
        # 架构所用寄存器列表,不支持 xmm 寄存器
        self.regs_list = self.get_regs_list()

    def get_data_long(self, StartSeclectAddr, len):
        '''
        从开始地址取得一定长度的数据
        :param StartSeclectAddr: 开始地址
        :param len: 获取的长度
        :return:
        '''
        mem_obj = gdb.selected_inferior().read_memory(StartSeclectAddr, len)
        data = bytes_to_long(mem_obj.tobytes()[::-1])
        return data

    def set_func_state(self, context: dict):
        """
        设置函数状态
        """
        for key in context.keys():
            # 设置寄存器值
            if key in self.regs_list:
                # print(key)
                # print(context[key])
                if key == "eflags":
                    gdb.execute(f"set ${key} = {context[key]}")
                else:
                    gdb.execute(f"set ${key} = (long long int){context[key]}")
            # 否则就按照内存进行设置
            else:
                # print("long_to_bytes(context[key])[::-1] is" ,long_to_bytes(context[key])[::-1])
                # print("key is ",key)
                gdb.selected_inferior().write_memory(int(key, 16), long_to_bytes(context[key])[::-1])
                # gdb.execute("x/20gx {}".format(key))

    def get_regs_list(self) -> list:
        """
        获得寄存器列表
        """
        regs_list = []
        # 判断架构前缀
        if self.arch == "x86":
            regs_prefix = "e"
        elif self.arch == "x64":
            regs_prefix = "r"
            regs_list = x64_regs_list
            regs_list += flag_reg

        for i in General_regs_list:
            regs_list.append(regs_prefix + i)
        # 段寄存器
        regs_list += Segment_regs_list

        return regs_list

    def get_all_regs_value(self) -> dict:
        """
        获得寄存器的值
        """
        regs_context = {}
        for i in self.regs_list:
            # print(i)
            regs_context[i] = int(hex(gdb.selected_frame().read_register(i)), 16)
        return regs_context

    def get_context(self) -> dict:
        # 获取所有寄存器的值
        context = self.get_all_regs_value()
        # 获取监视内存的值
        # 监视内存的结构为[0x400080,0x400100]
        # 所以长度为 target[1] - target[0]
        for i in self.moniter_target:
            if isinstance(i, list):
                context[hex(i[0])] = self.get_data_long(i[0], i[1] - i[0])

        return context

    def get_moniter_context(self) -> dict:
        """
        这个可以和上面整合,懒得写了
        获得监视的值
        """
        context = {}
        for i in self.moniter_target:
            if isinstance(i, list):
                context[hex(i[0])] = self.get_data_long(i[0], i[1] - i[0])
            elif isinstance(i, str):
                context[i] = hex(gdb.selected_frame().read_register(i))

        return context

    def delet_same_bp(self, delet_bp_addr, delet_bp_type=1):
        """
        :param delet_bp_addr:
        :param delet_bp_type: 1为 bp,7位 watch,9为 awatch
        :return:
        """
        # 获取 bp 列表
        bp_list = gdb.breakpoints()
        # print(type(bp_list))
        for bp in bp_list:
            # print(type(bp))
            # 现判断类型
            bp_type = bp.type
            if bp_type == delet_bp_type:
                # if bp.location != None: # 有些断点获取不了地址
                bp_addr = int(bp.location[1:], 16)
                if bp_addr == delet_bp_addr:
                    # print(bp.number)
                    bp.delete()

    def black_test(self, start_orig_context=""):
        """
        执行此函数需要在监视前后手动分别加断点,防止错误
        """
        # 添加断点
        gdb.execute("b *0x{0:X}".format(self.start_addr))
        gdb.execute("b *0x{0:X}".format(self.end_addr))
        # 如果 start_orig_context 为空表示第一次执行
        if start_orig_context == "":
            # 执行到起始断点处
            gdb.execute("c")
            # 获得原始状态
            self.start_orig_context = self.get_context()
        # 如果 start_orig_context 不为空表示多次执行
        else:
            # 获得原始状态
            self.start_orig_context = start_orig_context

        # 删除起始断点
        self.delet_same_bp(self.start_addr)
        # print(self.start_orig_context)
        start_context = self.start_orig_context
        # 添加变量的状态
        for key in self.change_context.keys():
            start_context[key] = self.change_context[key]
        # 设置为原始状态
        # print(start_context)
        self.set_func_state(start_context)
        # 继续执行到结束断点
        gdb.execute("c")
        # 删除结束断点
        self.delet_same_bp(self.end_addr)
        # 获取最终状态
        moniter_context = self.get_moniter_context()
        return moniter_context, self.start_orig_context

    def write_context2shm(self):
        # 写入 /dev/shm
        if not os.path.exists(self.stdin_path):
            os.system("dd if=/dev/zero of={} bs=1K count=1024".format(self.stdin_path))
        for key in self.change_context.keys():
            with open(self.stdin_path ,"wb") as fd:
                fd.write(long_to_bytes(self.change_context[key])[::-1])

    def black_test_from_start(self):
        """
        执行此函数需要在监视前后手动分别加断点,防止错误
        """
        # 添加断点
        self.delet_same_bp(self.end_addr)
        # gdb.execute("start < {}".format(self.stdin_path))
        gdb.execute("b *0x{0:X}".format(self.end_addr))
        self.write_context2shm()

        # 因为 windows下没有符号
        # 建议执行 run ,取消 start 和 c
        gdb.execute("run < {}".format(self.stdin_path))
        # gdb.execute("c")
        # 获取最终状态
        moniter_context = self.get_moniter_context()
        return moniter_context


def get_change_context(context_len: int):
    """
    获取改变内容的函数,需要根据测试情况修改
    :return:
    """
    for i in range(256 ** context_len):
        context = i
        yield context


def generate_flag(flag_len,prefix = b"",suffix = b""):
    """
    生成 flag
    :param flag_len: flag总长度
    :param prefix:
    :param suffix:
    :return:
    """

    flag_bytes = b"\x01" * (flag_len - len(prefix) - len(suffix))
    flag_bytes = prefix + flag_bytes + suffix
    flag = bytes_to_long(flag_bytes[::-1])

    return flag


def check_flag():

    return 0

def duchao_black_test():
    # 执行黑盒测试前需要在监视前后手动分别加断点,防止错误
    enc = [0x8A, 0x81, 0xA3, 0xBB, 0xE4, 0x82, 0x65, 0xDA, 0xC9, 0x85, 0x6C, 0xA9, 0xA4]
    flag = 0
    # 初始化状态的位置一定要注意
    # 因为循环中的爆破都不是第一次执行,所以要放在最外面
    start_context = ""
    # 没有实现前缀、后缀
    prefix = b""
    suffix = b""
    for i in range(len(enc)):
        moniter_context = {"rax": [], "0x7fffffffdb70": []}
        for j in range(128):
            # 如果是寄存器则使用字符串,如"eax"
            # 改变的数据,格式为{"eax":0,"0x400080":123}
            change_addr_1 = 0x7fffffffdb70
            change_value = flag + (j << (i * 8))
            change_test = {hex(change_addr_1): change_value, hex(
                0x555555558060): 0xd487c7b95dcb55fbff7bb8c8c0ef461b3d172af23a8f65befc7a7f620791de94a5eba77149b31a161376b25a43acb6dd20e9fad0684f12ecab95b01f45022853483142d5e0cde6e45783c6006ee747fd924a2d190a234bc5bd3e44a6a9f6d975ca744d3b30bc9a50d89cae9f40c9af2f64fef954f03896272b78ad9b1e8d5897dc0cb55693c48a7c052e8909857ef16a5cea3541a233c17dce8ce3d70186c3a1cc0ba303b124985f1d4c39df8090baa4526b3ff725aa0f7029517799726c2c675e73d6f3a808a069329d1cd336040661c234d2f5dbcfda5960e8148481edd163f488e13c6d18ee11e28b22265bf8798e8215bf0eb74ee537bb106621b40d9e6f}

            # 监视内容,格式为["eax",[0x40080,0x400a0]]
            monitor_addr = change_addr_1
            moniter_target = ["rax", [monitor_addr, monitor_addr + len(enc)]]
            black_test_start_addr = 0x555555555A0A
            black_test_end_addr = 0x555555555A0F

            black_test_proj = Black_Test(black_test_start_addr, black_test_end_addr, change_test, moniter_target)
            # 获取黑盒测试后的数据
            moniter_context_tmp, start_context = black_test_proj.black_test(start_context)
            # print(start_context)
            # moniter_context[hex(monitor_addr)].append(hex(( moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) &0xff ))
            print(i)
            print(j)
            if (moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) & 0xff == enc[i]:
                flag += j << (i * 8)
                break
        # print(moniter_context)
    print(long_to_bytes(flag)[::-1])



def duchao_blach_test_stdin():
    # 执行黑盒测试前需要在监视前后手动分别加断点,防止错误
    enc =[ 0x4A, 0x08, 0xD5, 0xF4, 0xF9, 0xD0, 0x3E, 0x1B, 0xEA, 0x46,
  0xCC, 0x17, 0x35, 0x26, 0x1D, 0x7C, 0x61, 0xBB, 0xF1, 0xF6,
  0x23, 0x41, 0xAB, 0x35, 0xDB, 0x0B, 0x43, 0x45, 0x8C, 0x35,
  0x98, 0xC5, 0xCA, 0x59, 0xFE, 0xB1, 0xAD, 0xB5, 0x12, 0x7D,
  0xDC, 0x82, 0x30, 0x02, 0x4A, ]
    # flag = 0x01010101010101010101010101
    # 初始化状态的位置一定要注意
    # 因为循环中的爆破都不是第一次执行,所以要放在最外面
    start_context = ""
    prefix = b""
    suffix = b""
    flag = generate_flag(len(enc),prefix=prefix,suffix=suffix)
    test={}
    for i in range(len(prefix) , len(enc)-len(suffix)):
        #moniter_context = {"0x19FEBC": []}
        # 爆破的范围需要特别注意
        # scanf 等输入函数会不接受 \x09 \x0a \x0d 等字符
        for j in range(128):
            if j == 127:
                print("boom exit")
                exit(0)
            # 如果是寄存器则使用字符串,如"eax"
            # 改变的数据,格式为{"eax":0,"0x400080":123}
            change_addr_1 = 0x5FFD50
            change_value = flag + (j << (i * 8))
            print(change_value)
            change_test = {hex(change_addr_1): change_value}
            # 监视内容,格式为["eax",[0x40080,0x400a0]]
            monitor_addr = change_addr_1
            moniter_target = [ [monitor_addr, monitor_addr + len(enc)]]
            black_test_start_addr = 0x555555555a0a #黑盒还没开始的地方,一般默认可以解决
            black_test_end_addr = 0x140002C12  #密文已经生产的地方就行

            black_test_proj = Black_Test(black_test_start_addr, black_test_end_addr, change_test, moniter_target)
            # 获取黑盒测试后的数据
            moniter_context_tmp = black_test_proj.black_test_from_start()
            # print(start_context)
            # moniter_context[hex(monitor_addr)].append(hex(( moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) &0xff ))
            print(i)
            print(j)
            print(libnum.n2s(change_value)[::-1])
            print(long_to_bytes(flag)[::-1])
            # print(hex((moniter_context_tmp[hex(monitor_addr)]>>(i * 8)) &0xff))
            if (moniter_context_tmp[hex(monitor_addr)] >> (i * 8)) & 0xff == enc[i]:
                print("ok")
                flag += j << (i * 8)
                break
        # print(moniter_context)
    print(long_to_bytes(flag)[::-1])


if __name__ == "__main__":
    # 指定程序段进行黑盒测试
    # duchao_black_test()
    # 从程序开始进行黑盒测试,可以控制输入
    duchao_blach_test_stdin()


在当前目录放入python文件和exe文件


中间按C继续运行


可以看见答案已经出来了

总结
利用gdb,进行黑盒测试,对于短字节的加密的程序,直接进行爆破,从而避免分析了中间的大量加密

0 条评论
某人
表情
可输入 255
目录