首页
社区
课程
招聘
[原创]bytectf mordencpp
发表于: 2021-11-8 17:38 6212

[原创]bytectf mordencpp

2021-11-8 17:38
6212

程序分析


最终判断逻辑非常简单,与v10做比较,相同就提示congrats!(真的只是逐个byte比较),不过v10长得比较奇怪。

胡乱输入数据,到达最后判断函数的时候输入的数据不会改变,显然这个题目不会是让你直接输入v10(因为输入是flag)。
我们再查看keyfunc函数。

在keyfunc中,如果发现输入的字符串长度为41且开头为"bytectf{"且结尾为"}",则进行一个错误(exception)的抛(cxa_throw)。
抛出错误后会跳转到entrance处的catch部分(符号被去掉了)(跳转过程省略)

catch下面的汇编代码在伪代码界面并不会显示

 

按照提示构造payload后程序会输出"H4ckingT0Th3Gate"。

在keyfunc下面的虚表函数调用处进行一个断点的打,然后用ida进行一个调试的跟,进入函数process1(自己起的名称)(如果没有走catch的路线,则会进入另外一个函数)。
函数的开头部分将输入的字符串逐个转化为01组成的字符串,并将其串在一起。

我们输入的字符串第一字符是'b',生成的字符串存在了v14里

 

每次循环v14都会将字符串append在v13上

 


函数的后半部分将生成的01字符串当成二进制数,每8位将其转换为16进制数据,并存放在一起

处理后的数据保存在v13里,由v13传递给v9,再由v9传递给v4,再当作参数传递给某虚表函数调用

跟进虚表函数调用,发现是tea加密函数。

tea加密后的数据保存在v14里,由v14传递到v8,再由v8传递到input_1,与v10进行比较。

如果与v10一致则检测通过。

题解

提取码表

首先要解决生成的01字符串与输入字符的对应问题,经过一番乱找,在keyfunc中的某个函数中发现了一个奇怪的数组的初始化。

此数组在执行本函数的时候会将原本的数据逐个与0xAAu进行异或,得到一个字符串。

长得就很像一个码表,接下来只要找到这些字母与生成的01字符串的对应关系即可。
首先利用这些字符构造payload:"bytectf{adghijklmnopqrsuvwxz0123456789!@}"和"bytectf{adghijklmnopqrsuvw#%^&*()_+-=[];}"这样就能生成所有字符对应的01字符串
在ida里动态调试跟进第一个虚表函数调用,在汇编界面中,在call 生成01字符串的下一行打下断点,运行到此位置会发现rax里面存着相应的01字符串以及其长度(rax+8)。

打下断点之后用IDApython提取信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from ida_dbg import *
input="bytectf{adghijklmnopqrsuvw#%^&*()_+-=[];}"
continue_process()
dir={}
for i in range(41):
    tmp=""
    wait_for_next_event(WFNE_SUSP ,-1)
    rax=get_reg_value("rax")
    rax_value=read_dbg_qword(rax)
    len=read_dbg_byte(rax+0x8)
    for j in range(len):
        tmp+=chr(read_dbg_byte(rax_value+j))
    dir[tmp]=input[i]
    continue_process()
print(dir)

把两个payload都跑一遍就能得到所有对应关系了。

1
dir={'00001': 'b', '10011': 'y', '11000': 't', '0011010': 'e', '01110': 'c', '010010': 'f', '10001': '{', '100101': 'a', '11011': 'd', '111011': 'g', '01000': 'h', '10110': 'i', '00110111': 'j', '1111010': 'k', '110010': 'l', '00011': 'm', '10000': 'n', '10100011101': 'o', '0110011': 'p', '011000': 'q', '111110': 'r', '01011': 's', '11110110': 'u', '000001': 'v', '111000': 'w', '00101': 'x', '101000110': 'z', '1110010': '0', '100100': '1', '111111': '2', '01101': '3', '11010': '4', '11110111': '5', '001100': '6', '111010': '7', '00111': '8', '10101': '9', '00110110': '!', '1110011': '@', '1010001111': '}','101001': '#', '00010': '%', '01111': '^', '10100011100': '&', '0110010': '*', '010011': '(', '111100': ')', '01010': '_', '10111': '+', '10100010': '-', '000000': '=', '110011': '[', '00100': ']', '1010000': ';'}

逆向数据

理论上来说只需要对v10进行一个tea的解密,再进行一个码表的查就能得出flag。

tea

只要找到key就行了,v17、v16、v15、v14分别对应key[0]、key[1]、key[2]、key[3]。

v17="etyb",v16="ftc-",v15="clew",v14="~emo"。所以key="tybftc-clew~emo"
从ida里提取出来v10的数据。

1
2
3
4
5
6
7
unsigned char ida_chars[40] =
{
  0x9F, 0x66, 0xD3, 0xC5, 0x1A, 0x17, 0x17, 0xB9, 0x19, 0x7B,
  0xB3, 0xB4, 0x5F, 0x0C, 0xE8, 0x0A, 0x7F, 0x30, 0x80, 0x8D,
  0x80, 0x28, 0x52, 0x21, 0x89, 0x05, 0xD8, 0x34, 0xD1, 0x83,
  0x6C, 0xDE, 0x18, 0x36, 0xB7, 0x59, 0x35, 0x5D, 0xE6, 0xC6
};

一次解密8byte的数据,一共要解密5次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void decrypt(uint32_t* v, uint32_t* key) {
  uint32_t l = v[0], r = v[1], sum = 0, delta = 0x9e3779b9;
  sum = delta *32;
  for (size_t i = 0; i < 32; i++) {
    r -= ((l << 4) + key[2]) ^ (l + sum) ^ ((l >> 5) + key[3]);
    l -= ((r << 4) + key[0]) ^ (r + sum) ^ ((r >> 5) + key[1]);
    sum -= delta;
  }
  v[0] = l;
  v[1] = r;
}
 unsigned char ida_chars[] =
{
  0x9F, 0x66, 0xD3, 0xC5, 0x1A, 0x17, 0x17, 0xB9, 0x19, 0x7B,
  0xB3, 0xB4, 0x5F, 0x0C, 0xE8, 0x0A, 0x7F, 0x30, 0x80, 0x8D,
  0x80, 0x28, 0x52, 0x21, 0x89, 0x05, 0xD8, 0x34, 0xD1, 0x83,
  0x6C, 0xDE, 0x18, 0x36, 0xB7, 0x59, 0x35, 0x5D, 0xE6, 0xC6
};
int main()
{
    //test
    unsigned char k[]="etybftc-clew~emo";
    uint32_t *v=(uint32_t*)ida_chars;
    uint32_t *key=(uint32_t*)k;
    for(int j=0;8*j<0x28;++j)
      decrypt(v+2*j,key);
    // for(int i=0;i<40;i++){
    //   printf("0x%x,",v[i]);
    // }
    unsigned char *res=(unsigned char*)v;
    for(int i=0;i<42;i++){
      printf("0x%x,",res[i]);
    }
    return 0;
}

得出解密后的数据,用python的列表存储,然后将每个byte转化为8位的二进制数据。

1
2
3
list=[0xc,0xf0,0x69,0xd8,0x4a,0x32,0xfb,0x62,0x8e,0xa4,0xcc,0xc,0xc0,0x22,0x63,0xe5,0xb6,0xfd,0x7,0x5e,0xe6,0xfe,0xc6,0x8d,0xfd,0x8d,0x51,0xad,0xe4,0x68,0xfa,0x14,0x78,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0]
for i in list:
    print(bin(i)[2:].rjust(8,'0'),end="")

得出二进制字符串。

1
str="000011001111000001101001110110000100101000110010111110110110001010001110101001001100110000001100110000000010001001100011111001011011011011111101000001110101111011100110111111101100011010001101111111011000110101010001101011011110010001101000111110100001010001111000000000000000000000000000000000000000000000000000000000000000000000000000"

暴力匹配码表

将其与前面得到的字典(输入与01字符串的对应关系)进行一个暴力匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dir={'00001': 'b', '10011': 'y', '11000': 't', '0011010': 'e', '01110': 'c', '010010': 'f', '10001': '{', '100101': 'a', '11011': 'd', '111011': 'g', '01000': 'h', '10110': 'i', '00110111': 'j', '1111010': 'k', '110010': 'l', '00011': 'm', '10000': 'n', '10100011101': 'o', '0110011': 'p', '011000': 'q', '111110': 'r', '01011': 's', '11110110': 'u', '000001': 'v', '111000': 'w', '00101': 'x', '101000110': 'z', '1110010': '0', '100100': '1', '111111': '2', '01101': '3', '11010': '4', '11110111': '5', '001100': '6', '111010': '7', '00111': '8', '10101': '9', '00110110': '!', '1110011': '@', '1010001111': '}','101001': '#', '00010': '%', '01111': '^', '10100011100': '&', '0110010': '*', '010011': '(', '111100': ')', '01010': '_', '10111': '+', '10100010': '-', '000000': '=', '110011': '[', '00100': ']', '1010000': ';'}
str="000011001111000001101001110110000100101000110010111110110110001010001110101001001100110000001100110000000010001001100011111001011011011011111101000001110101111011100110111111101100011010001101111111011000110101010001101011011110010001101000111110100001010001111000000000000000000000000000000000000000000000000000000000000000000000000000"
start=0
end=5
while(1):
    tmp=str[start:end]
    # print(tmp)
    for i in dir.keys():
        if(i==tmp):
            print(dir[i],end='')
            start=end
            end=start+4
            break
    end=end+1

跑一下得出flag。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//