• 【软件逆向-求解flag】内存获取、逆变换操作、线性变换、约束求解


    目录

    一、直接内存获取

    1.1、简介:

    1.2、示例:

    1.3、分析:

    二、对算法进行逆变换操作

    2.1、示例:

    三、线性变换的求解

    3.1、简介:

    3.2、示例:

    四、约束求解

    4.1、简介:

    4.2、示例:


    一、直接内存获取

    1.1、简介:

    简单的情况,直接查看内存的方式获取flag

    即只需要在比较的地方下个断点,然后通过查看内存即可得到flag

    伪代码:

    input = get_input()
    if(input == calc_flag())
    {
            puts(flag is input)
    }

    1.2、示例:

    main函数(反编译代码):

    1.3、分析:

    循环计算出了一个dest,然后与输入的参数argv[1]比较,如果相等,则argv[1]就是flag

    选择在调用memcmp的地方下断点,然后运行程序。在断点断下之后,RDI寄存器指向的内容
    即为flag,在GDB中读取flag



    二、对算法进行逆变换操作

    2.1、示例:

    一个判断过程的代码:

    要分析convert的算法,然后分析结果编写出对应的逆算法,通过reverse_convert(stardard)方式求得flag

    input = get_input()
    if(standard == convert(input))
    {
            puts(flag is input)
    }


    定位程序比较的地方:

    是base64编码的程序

    先分析main函数,其中change函数根据输入input得到一个output字符串,然后将output字符串与“ms4otszPhcr7tMmz GMkHyFn=”进行比较--->需要分析change函数


    change函数(反编译代码):

    变种的base64:

    建立了一个to_string(i)与v22[i]的map,然后,将input转化为二进制的字符串,每次取6字节,转化为一个整数,接着查询map,得到对应的输出字节


    base64逆变换:

    import base64
    s1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    s2 = 'ELF8n0BKxOCbj/WU9mwle4cG6hytqD+P3kZ7AzYsag2NufopRSIVQHMXJri51Tdv'
    dict = {}
    for i in range(len(s1)):
            dict[s2[i]] = s1[i]
    dict['='] = '='
    output = 'ms4otszPhcr7tMmzGMkHyFn='
    s3 = ' '
    for i in range(len(output)):
            s3 += dict[output[i]]
    flag = base64.b64decode(s3)
    print flag



    三、线性变换的求解

    3.1、简介:

    如果convert是一个线性变换,那么在output=convert(input)中,output的第i位只能由input的第i位决定。通过获取input[i]的所有可能输入对应的输出output[i],即可求出input[i]。

    对于这种变换,可以进行单字符爆破

    3.2、示例:

    提供了一个cipher可执行程序和ciphertext密文数据。运行cipher,会要求输入明文,并将加密后的结果保存到out文件中

    cipher程序运行结果


    尝试发现当输入只有第1字节不同时,输出也只有第1字节不同

    多次尝试,可以确定其为线性变换


    采用单字节爆破

    代码:

    from zio import *
    with open('./ciphertext') as f:
    d = f.read()
    flag = ' '
    for i in range(len(d)):
            for c in range(0x21, 0x80):
                    try_input = flag + chr(c)
                    io = zio('./cipher')
                    io.writeline(try_input)
                    io.close()
                    f = open('./out', 'rb')
                    d2 = f.read()
                    if d2[i] == d[i]:
                            flag += chr(c)
                            break
    print flag



    四、约束求解

    4.1、简介:

    如果output=convert(input)之后,需要output满足多个约束条件

    通常会选择约束求解,通常会用到的约束求解器为z3。

    4.2、示例:

    运行程序,弹出错误对话框

    用OD加载,下断点GetWindowsTextA,按下check键,程序成功断下来

    调用堆栈,可以知道函数返回地址为0x40bd7b。

    在IDA中查看0x40bd7b地址,发现该函数被识别为CWnd::GetWindowTextA,所以还要再回溯一层,最终到达地址0x4017AD。


    0x4017AD函数的反编译代码

    (除了对长度进行判断,要求小于40字节之外,还调用了3个子函数,对输入进行变换)

    定位到程序的主要判断逻辑:

    第一个函数sub_401380(反编译代码)

    熟悉的base64字符串--->该函数为base64加密

    第二个函数sub_401000(反编译代码)

    对每个字符做了一个减3的操作

    第三个函数sub_401040(反编译代码)


     需要满足条件:

    a2[i]+a2[i+1] == v5[i]
    a2[9]-a2[20]==22
    a2[40]==0

    条件较难直接计算,故采用约束求解的方式进行求解

    代码:

    from z3 import *
    import base64
    s2 = [151, 130, 175, 190, 163, 189, 149, 132, 192, 188, 159, 162, 131, 99, 168, 197, 151, 151, 164, 164, 152, 166, 205, 188, 1

    s1 = [BitVec('s1_%d' % i, 8) for i in range(41)]
    s = Solver()
    for i in range(39):
            s.add(s1[i]+s1[i+1] == s2[i])
    s.add(s1[9] - s1[20] == 22)
    s.add(s1[40] == 0)
    s3 = ' '
    if s.check() == z3.sat:
            m = s.model()
            for i in range(40):
                    s3 += chr(m[s1[i]].as_long())
    s4 = ' '.join([chr(ord(s3[i])+3) for i in range(len(s3))])
    flag = base64.b64decode(s4)
    print flag

  • 相关阅读:
    idea 类注释模板和方法注释模板设置
    踩坑了!0作为除数,不一定会抛出异常!
    openwrt开启SSH远程访问与开启WEB远程访问——三种方法
    java项目-第144期ssm农产品供销服务系统-java毕业设计_计算机毕业设计
    pytorch 43 基于面向对象思想使用4行代码实现yolov8分类模型、目标检测模型、实例分割的TensorRT c++部署
    【如何构建自己的基于Arduino的Scara 机器人】
    Kafka/Spark-01消费topic到写出到topic
    Kubernetes-三大开放接口-初见
    LSTM 词语模型上的动态量化
    MySQL查询性能优化七种武器之索引潜水
  • 原文地址:https://blog.csdn.net/qq_53079406/article/details/125625497