baymax0day 发表于 2019-11-21 11:24

2019百越杯线上赛逆向endless_nightmare题目分析

本帖最后由 baymax0day 于 2019-11-21 22:04 编辑

# 2019百越杯线上赛逆向endless_nightmare题目分析

上上周末的线上赛,一开始本来想的是想跟着题目走一下完整的流程的,发现乱七八糟的东西太多,就先放一下解题过程吧~ 主要是想请教一下表哥们前面的东西是如何设计的,主要是想学习一下`混淆机制`和`生成机制`,奈何自己太菜了,分析不出来,只想看解题过程的,移步[解题过程](###解题过程)。。 大白一枚,务必轻喷~

## 题目简述

#### 基础分析

1. 题目运行起来会要求输入username和passwd,除了no以外没有任何提示;
2. ida打开搜索字符串,得出几个可能比较重要的字符串
   



1. ida查看导入表,几个比较有意思的函数,也是让我最开始分析比较`头冷`的(因为不知道是干嘛用的);a. 此处的互斥体不是让程序互斥的,而是增加了一种加密方式?、b. 原子操作 居然会产生ERROR_FILE_NOT_FOUND错误,这个学到了



4. OD载入程序,最开始因为TLS断下了,说明程序中可能用到了多线程(虽然解题的时候没有用到,但是前面看到了就想分析一下来着),并且程序出错或者结束的时候,上次错误居然是0x2错误,我还以为需要什么文件,但是看了一圈也没发现文件操作,于是就学到了上面的原子会产生0x2错误
   


#### 反编译查看

1. 直接指出了main函数,并且逻辑相对清晰,输出username和passwd,两个都接收完之后都进行了sub_487300函数进行处理
   


2. 接下来进入了sub_407960函数,ida反编译之后函数太长,大约1000多行,这也是难住大多数人的地方吧应该,也是我想请教的`第一个地方`, 类似这种的ida混淆的实现方式,有文章推荐么?如果有的话,交流一下。。



3. 于是转入dbg进行分析,到调用sub_407960函数的地方,栈上有输入的username和passwd作为参数,修改函数的返回值eax,程序直接输出flag{我们输入的passwd},即可以简单认为这个函数是我们需要寻找的`校验函数`、



4. 程序中经常调用CreateMutexA,FindAtomA等函数的函数sub_4188A0? 同样sub_418120这个函数会调用上面的函数,并且传入use_fc_key等字符串,我拿类似的字符串度搜了一下,发现别的程序中也有过这个东西,然后再在字符串中发现了多线程的包winpthreads-git20141130-src,所以上面的问题就是我第一次见tdm的多线程,难免大惊小怪,勿怪我~




5. 因为想学习加密和混淆机制来着,最开始尝试过从末尾,即返回值开始回溯(`这里赞一下x64dbg的跟踪机制,太帅了`),寻找校验算法,这种思路纠结了好久但是失败了,路径太多了,所以就先解题吧。。。

## 解题过程

题目比较简单,主要思路就是利用软硬访问断点,对输入的数据进行监控,即可追溯到对输入的数据进行校验的关键代码处

#### 寻找 校验一

   1. 接上得知sub_407960是关键的检验函数,并且在栈上有我们的输入;
   2. 因此,第一步就是对这两个地址下断,发现第一此访问我们的输入是strlen函数就是求长度,说明程序应该有用到这个长度;
   3. 再对这两个长度的地址下断,来到两个校验长度的函数, 在这个地方走了个捷径,在知道这个地方是校验user的长度后,往上翻了一下,发现两个类似,估计是passwd的长度,直接下断分析。
   4. 最终得出user的长度得是0x6,passwd的长度得是0x24




#### 寻找username
   
    1. 我不会告诉你,username也是我在向上翻的过程中发现的
    2. 就算猜不出来也没关系,因为我们输入的username地址处下了断,因此程序会在以下校验处断下



   
    3. 汇编比较清晰,可以直接看出校验方式为:
   
    u * u == 0x3250
    u - u == 0x2f
    u ^ u == 0x33
    u + u == 0xe6
    u % u == 0x33
    u | u == u

   4. 接下来就是z3一把梭,得出用户名, 代码如下(一个小小的递归),同时我发现使用向量求结果的时候,结果居然是依据定义的向量位数来的??比如u、u定义了8位,但是要求的结果是0x3250,第一次求出来的居然是1和80,原因就是取了0x3250的低8位0x50,
```python
def get_user():

    flag = list(string.punctuation + string.ascii_letters + string.digits)

    solver = Solver()

    def reFind(Res):
      list_chr = [(r.name(), chr( int(Res.as_string()))) for r in Res.decls()]
      for r in Res.decls():
            r_chr = chr( int(Res.as_string()))
            if r_chr not in flag:
                t = int( Res.as_string() )
                solver.add( a!=t ) # 增加条件
                if solver.check() == unsat:
                  return
                new_Res = solver.model()
                return reFind(new_Res)

      tmp_a, tmp_b = int(Res.as_string()), int(Res.as_string())
      if(tmp_a * tmp_b != 0x3250):
            t = int(Res.as_string())
            solver.add(a != t) # 说明此时的值并非正确, 重新计算
            if solver.check() == unsat:
                return
            new_Res = solver.model()
            return reFind(new_Res)
      
      return list_chr

    a, b, c, d, e, f = BitVecs("a b c d e f", 8)

    solver.add(a * b == 0x3250)
    solver.add(b - c == 0x2f)
    solver.add(c ^ d == 0x33)
    solver.add(d + e == 0xe6)
    solver.add(e % f == 0x33)
    solver.add(f | a == a)
    print(solver.check())
    Res = solver.model()

    print(reFind(Res))
```

#### 寻找passwd

   因为没有找到生成算法,因此这个部分就相对简单了。
   
   1. 同样依照之前的,向上翻,当然不往上寻找也是可以的,因为有访问断点,所以会断下的;只不过向上翻可以直接看到校验规则,由于汇编代码很简单可以直接得出计算规则:passwd 在 0x8、0xd、0x12、0x17处得有'-'字符
   
   

   2. 接着对输入再进行一次比对'-'的操作,在401e50的地方会因访问断点断下,猜测是去除‘-‘字符;单步跟踪会在栈上看到去除'-'的字符串;

   
   

   3. 对新产生的即去除’-‘的字符串重新下访问断点,最后会在最后的校验函数断下;简单分析发现函数在和输入的passwd进行对比,拿到对比的字符,就是flag
   
   

```python
def get_pass():

    tmp_p = list(string.ascii_letters[:0x24])
    tmp_p = tmp_p = tmp_p = tmp_p = chr(0x2d)
    print("".join(tmp_p))

    passwd = "89ab3210fedc98ba54761032d8badcfe"
    list_p = list(passwd)
    list_p.insert(0x8, '-')
    list_p.insert(0xD, '-')
    list_p.insert(0x12, '-')
    list_p.insert(0x17, '-')
    print("".join(list_p))
```


题目蓝奏云链接:https://www.lanzouj.com/i7hi9cd

liltn 发表于 2019-11-21 15:37

感谢楼主分享!:loveliness:

dongfang155 发表于 2019-11-21 13:00

混淆部分是一堆while1吗 如果是的话 这是OLLVM的控制流平坦化,可以用去平坦化工具deflat.py来去除一些的

AIctiy 发表于 2019-11-21 11:36

very nice

txq18363001227 发表于 2019-11-21 12:07

不错,谢谢分享

处女-大龙猫 发表于 2019-11-21 13:08

支持,支持,收藏学习

Ginobili 发表于 2019-11-21 14:52

老哥题目文件能不能发一下

刘样andholiday 发表于 2019-11-21 16:47

楼主有人牛,感谢分享啦

CORLEONEY 发表于 2019-11-21 19:24

网上原题,太气人了

wenyuanzh 发表于 2019-11-21 19:26

感谢分享,学习了
页: [1] 2 3 4
查看完整版本: 2019百越杯线上赛逆向endless_nightmare题目分析