Y1rannn 发表于 2022-1-24 18:32

BUUCTF 部分中等难度题总结

来论坛也一年有余,一直是从各位师傅处学习。深觉自己能力浅薄不应随意发表误人子弟,但近来发现有时即使是不那么正确的内容,对于初学者来说有就比没有好。这里把自己一月以来刷题的记录总结一下,一方面是部分题难以找到 WriteUp,在这里记载也希望能让后来的人发觉一线思路,抛砖引玉,另一方面也是鞭策我自己,让大家看看我做题的过程中是否有一些想当然的错误。废话就说到这儿,以下是正文,所有题目均可以在 BUUCTF 上找到,大部分分数处于 90-100 之间

---

## QCTF2018 - asong

提前说明我做的时候命名有问题, 实际的密码cipher应该是content里面存的, 而我命名的cipher是密文.一开始命名错了就懒得改了

请原谅大懒蛋我甚至因为懒得传图而没放图

题目给了三个文件, 一个明文一个密文一个ELF

老样子, 丢IDA,F12找到一个文件名一个QCTF{, 直接定位关键函数

函数0x400936 :

把读入的flag第i位按下表转换为cur,并读取文件内容的第cur位赋值给v5的第i位, 这是步骤I

但是总感觉Symbol缺了一些东西,IDA的F5没有成功分析出来,打算第二步再看

| 0-9| a-zA-Z | Symbols |
| ---- | ------ | ------- |
| 0-9| 10-35| 略      |

第一个函数是步骤II, 一个轮换,大概意思是v5 = v5], i = table1

直接把v2看成i即可

```c
while ( table1 ) {
    cipher1 = cipher1];
    i = table1;
}
result = cipher;
cipher1 = v2;
```

table1的值是这样的

```c
unsigned char table1[] =
{
0x16, 0x00, 0x06,
0x02, 0x1E,
0x18, 0x09, 0x01,
0x15, 0x07,
0x12, 0x0A, 0x08,
0x0C, 0x11,
0x17, 0x0D, 0x04,
0x03, 0x0E,
0x13, 0x0B, 0x14,
0x10, 0x0F,
0x05, 0x19, 0x24,
0x1B, 0x1C,
0x1D, 0x25, 0x1F,
0x20, 0x21,
0x1A, 0x22, 0x23
};
```

```python
table1 = [
0x16, 0x00, 0x06,
0x02, 0x1E,
0x18, 0x09, 0x01,
0x15, 0x07,
0x12, 0x0A, 0x08,
0x0C, 0x11,
0x17, 0x0D, 0x04,
0x03, 0x0E,
0x13, 0x0B, 0x14,
0x10, 0x0F,
0x05, 0x19, 0x24,
0x1B, 0x1C,
0x1D, 0x25, 0x1F,
0x20, 0x21,
0x1A, 0x22, 0x23
]
cur = 1
for i in range(len(table1)) :
    # i 在 table1 中的下标的值就是前一个 i
    print(cur, end=",")
    cur = table1.index(cur)
   
'''
1,7,9,6,2,
3,18,10,11,21,
8,12,13,16,23,
15,24,5,25,26,
35,37,31,32,33,
34,36,27,28,29,
30,4,17,14,19,
20,22,0
'''
```

把table1的轮换逆掉

第二个函数是把v5变成v5的低5位拼v5的高3位,前者在高位后者在低位,最后把最后一位拼成v5的低五位和v5的高三位, 步骤III

```c
3 = *cipher >> 5;
for ( i = 0; flaglen - 1 > i; ++i )
   cipher = (8 * cipher) | (cipher >> 5);
result = &cipher;
*result = (8 * *result) | v3;
```



第三个函数是输出

这种移位加密的脚本还是用C比较好写

先把III转回来

```cpp
#include <cstdio>
unsigned char des[] = {
    0xEC, 0x29, 0xE3, 0x41, 0xE1,
    0xF7, 0xAA, 0x1D, 0x29, 0xED,
    0x29, 0x99, 0x39, 0xF3, 0xB7,
    0xA9, 0xE7, 0xAC, 0x2B, 0xB7,
    0xAB, 0x40, 0x9F, 0xA9, 0x31,
    0x35, 0x2C, 0x29, 0xEF, 0xA8,
    0x3D, 0x4B, 0xB0, 0xE9, 0xE1,
    0x68, 0x7B, 0x41
};

int main() {
    char temp = des;
    for(int i = 37; i > 0; i -- ) {
      des = (des >> 3) | (des << 5);
    }
    des = (des >> 3) | (temp << 5);
    for(int i = 0; i < 38; i ++ ) {
      printf("0x%x, ", des);
    }
}
/*
0x3d, 0x85, 0x3c, 0x68, 0x3c,
0x3e, 0xf5, 0x43, 0xa5, 0x3d,
0xa5, 0x33, 0x27, 0x3e, 0x76,
0xf5, 0x3c, 0xf5, 0x85, 0x76,
0xf5, 0x68, 0x13, 0xf5, 0x26,
0x26, 0xa5, 0x85, 0x3d, 0xf5,
0x07, 0xa9, 0x76, 0x1d, 0x3c,
0x2d, 0x0f, 0x68
*/
```

我略微疑惑了一下, 步骤II是轮换,那这里出现的字符应该还是全可见字符,但是里面的0xf5\0xf什么的明显不是可见字符, 不过先做下去再说.

瞎翻的过程中发现readin函数也调用过那个全篇Switch的函数..晕, 太不细心了,这说明 Thatgirl原文被改过.

不过先把III和II转到I吧

```python
table = [
1,7,9,6,2,3,18,10,11,21,8,12,13,16,23,15,24,5,25,26,35,37,31,32,33,34,36,27,28,29,30,4,17,14,19,20,22,0,1
]
Src = [
0x3d, 0x85, 0x3c, 0x68, 0x3c,
0x3e, 0xf5, 0x43, 0xa5, 0x3d,
0xa5, 0x33, 0x27, 0x3e, 0x76,
0xf5, 0x3c, 0xf5, 0x85, 0x76,
0xf5, 0x68, 0x13, 0xf5, 0x26,
0x26, 0xa5, 0x85, 0x3d, 0xf5,
0x07, 0xa9, 0x76, 0x1d, 0x3c,
0x2d, 0x0f, 0x68
]
Dst = * 40
print(len(table))
print(len(Src))
for i in range(0, len(table)-1) :
    Dst] = Src]
print(Dst)
'''
[133, 67, 104, 133, 245,
38, 60, 61, 39, 245,
51, 104, 62, 60, 118,
38, 245, 118, 165, 245,
19, 165, 61, 245, 62,
165, 45, 61, 245, 7,
60, 118, 29, 60, 15,
104, 133, 169]
'''
```

最后差400936, 这个要和读入结合起来看

content ++

cipher= content)]

switch分析过了,实在是懒得动调出第一步,

switch就像一个离散化函数,content就是一个词频统计桶

0-9根本不用计,只需要计azAZ和_即可,\_是46

这样之前的疑惑也迎刃而解,根本没有什么可不可见字符的事情,只是一词频统计的数字.

```c
#include <cstdio>
int bukit;
int main() {
    freopen("that_girl", "r", stdin);
    char buf;
    while((buf = getchar()) != EOF) {
      if(buf == '_') bukit ++;
      else {
            if(buf >= 'a' && buf <= 'z') bukit ++;
            else bukit ++;
      }
    }
    for(int i = 0; i < 26; i ++ ) {
      printf("%c : %d\n", i+'a', bukit);
    }
    printf("_ : %d\n", bukit);
}
```

```c
a : 104
b : 30
c : 15
d : 29
e : 169
f : 19
g : 38
h : 67
i : 60
j : 0
k : 20
l : 39
m : 28
n : 118
o : 165
p : 26
q : 0
r : 61
s : 51
t : 133
u : 45
v : 7
w : 34
x : 0
y : 62
z : 0
_ : 245
```

最后懒得写脚本了,直接对着翻译好了

flag{that_girl_saying_no_for_your_vindicate}

---
## FLEEGO

下载下来是一堆32位exe, 随便打开一个

![屏幕截图 2022-01-15 142654.png](https://tianyu.xin/usr/uploads/2022/01/59233850.png)

读一个wchar数组,扔在941240里,需要和word_944380比对结果一致,会给一张png

![屏幕截图 2022-01-15 142839.png](https://tianyu.xin/usr/uploads/2022/01/2802175353.png)


长这样

!(https://tianyu.xin/usr/uploads/2022/01/1514685671.png)

这样的话肯定要把所有exe的这部分都解出来

![屏幕截图 2022-01-15 143124.png](https://tianyu.xin/usr/uploads/2022/01/637664949.png)


检查对比数组的交叉引用,发现用的是resource,可以直接010 editor看之

![屏幕截图 2022-01-15 143315.png](https://tianyu.xin/usr/uploads/2022/01/286010558.png)


在2Ab0处,用python直接提取了每个执行一次, 注意win的resource中间隔了一个0x0, 需要去掉

```python
import os
import subprocess
base_dir = r"E:\ctf\work\FLEGGO"
file_name = os.listdir(base_dir)
for name in file_name :
    if name[-3:] != "exe" :
      continue
    key = ""
    with open(name, "rb") as f:
      key = f.read()
    file_dir = base_dir+"\\"+name
    p = subprocess.Popen(, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    p.stdin.write(key)
    p.stdin.close()
    print(p.stdout.read().decode())
    p.stdout.close()
```

这样我们就获得了48张图片,每张图片上有个数字,每个图片对应一个字母

```plaintext
What is the password?
Everything is awesome!
65141174.png => w

What is the password?
Everything is awesome!
85934406.png => m

What is the password?
Everything is awesome!
67782682.png => m

What is the password?
Everything is awesome!
75072258.png => r

What is the password?
Everything is awesome!
16544936.png => e

What is the password?
Everything is awesome!
67322218.png => _

What is the password?
Everything is awesome!
58770751.png => o

What is the password?
Everything is awesome!
64915798.png => 3

What is the password?
Everything is awesome!
88763595.png => e

What is the password?
Everything is awesome!
18376743.png => _

What is the password?
Everything is awesome!
36870498.png => m

What is the password?
Everything is awesome!
72501159.png => c

What is the password?
Everything is awesome!
47619326.png => p

What is the password?
Everything is awesome!
70037217.png => m

What is the password?
Everything is awesome!
18309310.png => @

What is the password?
Everything is awesome!
15566524.png => e

What is the password?
Everything is awesome!
82100368.png => m

What is the password?
Everything is awesome!
60075496.png => s

What is the password?
Everything is awesome!
71290032.png => a

What is the password?
Everything is awesome!
33718379.png => .

What is the password?
Everything is awesome!
42255131.png => t

What is the password?
Everything is awesome!
16295588.png => a

What is the password?
Everything is awesome!
61333226.png => f

What is the password?
Everything is awesome!
13147895.png => w

What is the password?
Everything is awesome!
16785906.png => 4

What is the password?
Everything is awesome!
80333569.png => o

What is the password?
Everything is awesome!
37723511.png => n

What is the password?
Everything is awesome!
44958449.png => _

What is the password?
Everything is awesome!
30171375.png => s

What is the password?
Everything is awesome!
72263993.png => h

What is the password?
Everything is awesome!
82236857.png => e

What is the password?
Everything is awesome!
33098947.png => _

What is the password?
Everything is awesome!
33662866.png => r

What is the password?
Everything is awesome!
47893007.png => _

What is the password?
Everything is awesome!
61006829.png => l

What is the password?
Everything is awesome!
89295012.png => 0

What is the password?
Everything is awesome!
87730986.png => 0

What is the password?
Everything is awesome!
65626704.png => 3

What is the password?
Everything is awesome!
72562746.png => -

What is the password?
Everything is awesome!
36494753.png => 0

What is the password?
Everything is awesome!
79545849.png => s

What is the password?
Everything is awesome!
63223880.png => a

What is the password?
Everything is awesome!
51227743.png => a

What is the password?
Everything is awesome!
73903128.png => u

What is the password?
Everything is awesome!
52817899.png => n

What is the password?
Everything is awesome!
19343964.png => o

What is the password?
Everything is awesome!
12268605.png => s

What is the password?
Everything is awesome!
47202222.png => n
```

应该是要自动化一次的, 识别一下数字,但是没错, 我懒了, 决定做更多的手工动作

mor3_awes0m3_th4n_an_awes0me_p0ssum@flare-on.com

---

## Code_Interpreter

看名字就知道是VM题,打开关键函数分析OPCode

```plain
0x0 -> HALT
0x1 -> PUSH(EXPAND(,,,)) OP+=4
0x2 -> POP
0x3 -> += OP += 2
0x4 -> -=
0x5 -> *=
0x6 -> >>=
0x7 -> MOV
0x8 -> MOV , SP+ OP += 2
0x9 -> ^=
0xA -> |=
```

先把code转16进制然后用Python反编译一下

```cpp
#include <cstdio>
int main() {
    freopen("code", "rb", stdin);
    int ch;
    while(~scanf("%c", &ch)) {
      printf("0x%02x,", ch);
    }
}
```

```python
opcode = [
    0x09,0x04,0x04,0x09,0x00,0x00,0x08,0x01,0x00,0x08,0x02,0x01,0x08,0x03,0x02,0x06,0x01,0x04,0x05,0x01,0x15,0x07,0x00,0x01,0x04,0x00,0x03,0x01,0x6b,0xcc,0x7e,0x1d,0x08,0x01,0x03,0x04,0x00,0x01,0x02,0x0a,0x04,0x00,0x09,0x00,0x00,0x08,0x01,0x00,0x08,0x02,0x01,0x08,0x03,0x02,0x06,0x03,0x08,0x05,0x03,0x03,0x07,0x00,0x03,0x03,0x00,0x02,0x01,0x7c,0x79,0x79,0x60,0x08,0x01,0x03,0x04,0x00,0x01,0x02,0x0a,0x04,0x00,0x09,0x00,0x00,0x08,0x01,0x00,0x08,0x02,0x01,0x08,0x03,0x02,0x06,0x01,0x08,0x07,0x00,0x01,0x03,0x00,0x02,0x01,0xbd,0xbd,0xbc,0x5f,0x08,0x01,0x03,0x04,0x00,0x01,0x02,0x0a,0x04,0x00,0x00
]
vStack = * 10
ip, flag, top = 0, 1, 2
while flag != 0 :
    if opcode == 0x0:
      flag = 0
      print("HALT")
    elif opcode == 0x1 :
      temp= "( " + str(opcode) + " << 24 )+"
      temp += "( " + str(opcode) + " << 16 )+"
      temp += "( " + str(opcode) + " << 8 )+"
      temp += str(opcode)
      print("PUSH", temp)
      ip+=4
    elif opcode == 0x2 :
      print("POP")
    elif opcode == 0x3 :
      print("ADD [", opcode, "], [", opcode, "]")
      ip+=2
    elif opcode == 0x4 :
      print("SUB [", opcode, "], [", opcode, "]")
      ip+=2
    elif opcode == 0x5 :
      print("MUL [", opcode, "],", opcode)
      ip+=2
    elif opcode == 0x6 :
      print("SHR(U) [", opcode, "], ", opcode)
      ip+=2
    elif opcode == 0x7 :
      print("MOV [", opcode, "], [", opcode, "]")
      ip+=2
    elif opcode == 0x9 :
      print("XOR [", opcode, "], [", opcode, "]")
      ip+=2
    elif opcode == 0xA :
      print("OR [", opcode, "], [", opcode, "]")
      ip+=2
    elif opcode == 0x8 :
      print("LOD [", opcode, "], Stack:[", opcode, "]")
      ip+=2
    else :
      print("FAULT")
    ip += 1

```

翻译结果如下:

```asm
XOR [ 4 ], [ 4 ]
XOR [ 0 ], [ 0 ]
LOD [ 1 ], Stack:[ 0 ]
LOD [ 2 ], Stack:[ 1 ]
LOD [ 3 ], Stack:[ 2 ]
SHR(U) [ 1 ],4
MUL [ 1 ], 21
MOV [ 0 ], [ 1 ]
SUB [ 0 ], [ 3 ]
PUSH ( 29 << 24 )+( 126 << 16 )+( 204 << 8 )+107
LOD [ 1 ], Stack:[ 3 ]
SUB [ 0 ], [ 1 ]
POP
OR [ 4 ], [ 0 ]
XOR [ 0 ], [ 0 ]
LOD [ 1 ], Stack:[ 0 ]
LOD [ 2 ], Stack:[ 1 ]
LOD [ 3 ], Stack:[ 2 ]
SHR(U) [ 3 ],8
MUL [ 3 ], 3
MOV [ 0 ], [ 3 ]
ADD [ 0 ], [ 2 ]
PUSH ( 96 << 24 )+( 121 << 16 )+( 121 << 8 )+124
LOD [ 1 ], Stack:[ 3 ]
SUB [ 0 ], [ 1 ]
POP
OR [ 4 ], [ 0 ]
XOR [ 0 ], [ 0 ]
LOD [ 1 ], Stack:[ 0 ]
LOD [ 2 ], Stack:[ 1 ]
LOD [ 3 ], Stack:[ 2 ]
SHR(U) [ 1 ],8
MOV [ 0 ], [ 1 ]
ADD [ 0 ], [ 2 ]
PUSH ( 95 << 24 )+( 188 << 16 )+( 189 << 8 )+189
LOD [ 1 ], Stack:[ 3 ]
SUB [ 0 ], [ 1 ]
POP
OR [ 4 ], [ 0 ]
HALT
```

```cpp
mem[ 4 ] ^=mem[ 4 ];
mem[ 0 ] ^=mem[ 0 ];
mem[ 1 ]=stk[ 0 ];
mem[ 2 ]=stk[ 1 ];
mem[ 3 ]=stk[ 2 ];
mem[ 1 ] >>=4 ;
mem[ 1 ] *=21 ;
mem[ 0 ] = mem[ 1 ];
mem[ 0 ] -= mem[ 3 ];
stk[++top] =( 29 << 24 )+( 126 << 16 )+( 204 << 8 )+107;
mem[ 1 ]=stk[ 3 ];
mem[ 0 ] -= mem[ 1 ];
top --;
mem[ 4 ] |= mem[ 0 ];
mem[ 0 ] ^=mem[ 0 ];
mem[ 1 ]=stk[ 0 ];
mem[ 2 ]=stk[ 1 ];
mem[ 3 ]=stk[ 2 ];
mem[ 3 ] >>=8 ;
mem[ 3 ] *=3 ;
mem[ 0 ] = mem[ 3 ];
mem[ 0 ] += mem[ 2 ];
stk[++top] =( 96 << 24 )+( 121 << 16 )+( 121 << 8 )+124;
mem[ 1 ]=stk[ 3 ];
mem[ 0 ] -= mem[ 1 ];
top --;
mem[ 4 ] |= mem[ 0 ];
mem[ 0 ] ^=mem[ 0 ];
mem[ 1 ]=stk[ 0 ];
mem[ 2 ]=stk[ 1 ];
mem[ 3 ]=stk[ 2 ];
mem[ 1 ] >>=8 ;
mem[ 0 ] = mem[ 1 ];
mem[ 0 ] += mem[ 2 ];
stk[++top] =( 95 << 24 )+( 188 << 16 )+( 189 << 8 )+189;
mem[ 1 ]=stk[ 3 ];
mem[ 0 ] -= mem[ 1 ];
top --;
mem[ 4 ] |= mem[ 0 ];
```

得出以下约束条件:

```plaintext
x / 16 * 21 - z == ( 29 << 24 )+( 126 << 16 )+( 204 << 8 )+107
z / 256 * 3 + y == ( 96 << 24 )+( 121 << 16 )+( 121 << 8 )+124
x / 256 + y == ( 95 << 24 )+( 188 << 16 )+( 189 << 8 )+189
x & 0xFF == 0x5E
y & 0xFF000000 == 0x5E0000
z & 0xFF == 0x5E
```

嗯这些东西丢Z3一解即可

---

## encrypt

老样子, IDA

三步处理,第一步输入无关,可以动调获得栈里的数据

第二步是把预处理的数组和输入做第一次变换,然后把输入做第二次变换赋给Dst

Dst最后和目标内存比对.

先动调拿到预处理数组(后附)

额有好多0x0, 有一点点诡异但是暂时先不管它.

回头看第二次变换, 三位变四位, 不足补 =, 好明显的魔改Base64

```c
Dst = Input + 0x3D
Dst = Input | (Input << 4) + 0x3D
Dst = Input | (Input << 2) + 0x3D
Dst = Input + 0x3D
```

我们回头去看第一个变换, 第一个变换是一个固定的异或,可知异或的值与输入无关,我们可以写个脚本找出来每次异或的值, 如果你熟悉常见算法, 应该很容易能看得出来这是一个RC4的打乱Sbox和加密的过程.

```cpp
#include <cstdio>
unsigned char Table[] =
{
0xB0, 0x31, 0x75,
0x70, 0xF8, 0xDF, 0x07, 0x3C,
0x78, 0x71, 0x50, 0x29, 0x2C,
0x16, 0x69, 0x12, 0xC8, 0x2B,
0x3B, 0x7F, 0xB2, 0xE7, 0x4B,
0x68, 0x8C, 0xC5, 0xA6, 0x15,
0x03, 0x58, 0x47, 0x04, 0x13,
0x8D, 0x87, 0x26, 0x09, 0xED,
0x17, 0x8A, 0xC2, 0xF2, 0x43,
0xC0, 0xAC, 0x59, 0x97, 0xF5,
0x3F, 0x67, 0x5E, 0x39, 0x86,
0xD5, 0x72, 0x61, 0xDA, 0xF7,
0x01, 0x05, 0x8B, 0xC3, 0xB1,
0x77, 0xAF, 0x1D, 0x30, 0xC6,
0x45, 0x0E, 0x5F, 0xEE, 0xAE,
0xF0, 0x28, 0xCE, 0xCD, 0xA7,
0x9B, 0x2A, 0x19, 0x48, 0x08,
0x44, 0x20, 0xFE, 0x6D, 0xB5,
0x2E, 0x6A, 0xF1, 0x34, 0xBC,
0x1E, 0x3E, 0xCC, 0x41, 0x92,
0xD8, 0xBD, 0xA5, 0xE8, 0x4D,
0x0A, 0x49, 0x0D, 0xA2, 0xFA,
0x62, 0x74, 0xD4, 0x83, 0x96,
0x94, 0x3D, 0xCB, 0x18, 0x63,
0x99, 0x46, 0xCA, 0xB7, 0x8E,
0xCF, 0xFB, 0xA3, 0x6C, 0x7E,
0x51, 0x27, 0x60, 0x9A, 0x11,
0xF3, 0x5C, 0x6E, 0xBA, 0x42,
0x76, 0x2F, 0xEF, 0xBF, 0x21,
0xAA, 0xE4, 0xD6, 0x1B, 0x55,
0x7D, 0xBE, 0xEA, 0xD3, 0x10,
0xF4, 0xC7, 0x4A, 0x23, 0x79,
0x84, 0xA4, 0x1C, 0xAB, 0x14,
0xDB, 0x4C, 0x3A, 0xB8, 0x52,
0xEC, 0x37, 0x38, 0xB6, 0xD2,
0xA0, 0x5A, 0x5B, 0x98, 0x66,
0x54, 0x9E, 0x4E, 0x4F, 0xB4,
0xC4, 0xC9, 0xD0, 0x25, 0x9C,
0x80, 0xDE, 0x2D, 0x06, 0x22,
0x0B, 0x91, 0x6B, 0x9F, 0xF6,
0xE6, 0xE2, 0xC1, 0x0F, 0x93,
0x90, 0x7B, 0x9D, 0x8F, 0xDD,
0xE5, 0x65, 0x35, 0xAD, 0xA9,
0xDC, 0x82, 0xBB, 0x00, 0x53,
0xD1, 0xA8, 0x33, 0xE9, 0x40,
0x1A, 0xFF, 0xA1, 0x95, 0x36,
0xD9, 0xEB, 0x89, 0xE3, 0x7C,
0x73, 0x85, 0x88, 0x7A, 0xE0,
0xFD, 0x64, 0x0C, 0x57, 0x32,
0xB3, 0xB9, 0x1F, 0xD7, 0xFC,
0x81, 0xE1, 0x02, 0xF9, 0x5D,
0x56, 0x6F, 0x24
};
unsigned char Target[] =
{
0x5A, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44, 0x7C, 0x66,
0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56, 0x4C, 0x7C, 0x79, 0x6E,
0x65, 0x55, 0x52, 0x79, 0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56,
0x4A, 0x67, 0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,
0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C, 0x76, 0x5A,
0x45, 0x3D
};
int main() {
    int i1 = 0, i2 = 0;
    for(int i = 0; i < 52/4*3; i ++ ) {
      i1 ++;
      int t1 = Table;
      i2 = (i2+t1)&0xff;
      int t2 = Table;
      Table = t2;
      Table = t1;
      printf("input[%d] ^= %d AND i1 = %d, i2 = %d\n", i, Table[(t1+t2)&0xff], i1, i2);
    }
}
```

最后Exp

```plaintext
Xor_array = [
    0x10, 0x59, 0x9c, 0x92, 0x06,
    0x22, 0xcf, 0xa5, 0x72, 0x1e,
    0x45, 0x6a, 0x06, 0xcb, 0x08,
    0xc3, 0xe4, 0x49, 0x5a, 0x63,
    0x0c, 0xdf, 0xf6, 0x5f, 0x08,
    0x28, 0xbd, 0xe2, 0x10, 0x15,
    0x1f, 0x6e, 0xaa, 0x5a, 0xca,
    0xec, 0x80, 0xaf, 0x9b, 0x16,
    0xbb, 0x3d, 0x13, 0x2f, 0x6a,
    0xa4, 0xc7, 0x2e, 0xbc, 0x4b,
    0x60, 0x9a
]
Dst = [
0x5A, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44, 0x7C, 0x66,
0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56, 0x4C, 0x7C, 0x79, 0x6E,
0x65, 0x55, 0x52, 0x79, 0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56,
0x4A, 0x67, 0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,
0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C, 0x76, 0x5A,
0x45, 0x3D
]
Flag, cur = * 100, 0
flag = ""
for i in range(0, len(Dst), 4) :
    Flag = ((((Dst - 0x3D) & 0x3F) << 2) | (((Dst - 0x3D) & 0x30) >> 4)) ^ Xor_array
    Flag = ((((Dst - 0x3D) & 0xF) << 4) | (((Dst - 0x3D) & 0x3C) >> 2)) ^ Xor_array
    Flag = ((((Dst - 0x3D) & 0x3) << 6) | (((Dst - 0x3D) & 0x3F) )) ^ Xor_array
    cur += 3
for i in Flag :
    print(chr(i), end="")
```

---

## BabyRe

可恶! 事 Rust 逆向

这个 subA110 事真正的主函数, 不能反编译



![屏幕截图 2022-01-16 194629.png](https://tianyu.xin/usr/uploads/2022/01/3353815346.png)


主函数逻辑好像还可以, 我们主要关注cmp

loc_A1B5 有一个 failed read的报错, 猜测这里是读入结束, 15CD0 可能是读入函数, 进而猜测sp+1E8+var_158应该是输入的地址

A259 有一个 RAX和32的cmp, 失败跳到最后, 猜测应该是长度, 那么关键函数就是右侧这一个分支了, 这个分支十节竟然调用了十个函数

看了一会没什么头绪,没能分析出来这些函数传参到底有什么规律, 输入一些东西动调一下

在A273打一个断点, 我晕, 这输入怎么不是在这儿?

回头在15CD0打个断点, 没错, 它就是输入, 可是我输入之后, 我的输入呢???? 我翻了一年,死活没在栈里和内存里找到输入, 搜索也没有什么用, 然后发现是搜索方式的问题, 应该向上搜索,在6010处找到输入...(实际可能因为基址不同地址不同)

可是... 找倒是找到了, 它一点也没动... 但是结合地址可以推测出来栈里存放的也是地址, 这应该是个双间址, 我们可以通过这个来找到变换之后的地址, ESP+1E8+var158 确实是输入的地址. 我们跟两个函数看看它到底对输入干了什么

终于, 我们在 A110 + 232 这个地址的函数参数里发现了 rsp+1E8+varB8 中存放了 7FA3608B0A0, 找到了这个变换到底特么的在哪
![屏幕截图 2022-01-16 204234.png](https://tianyu.xin/usr/uploads/2022/01/3512868993.png)


这时我多少有点破防,不过第一个变换只是四位一组做了一个轮换(1 2 4 3), 还好, 后面还有三个代码块, 猜测应该还有几次变换

下一个代码块后, 变成了

```c
0x4A, 0x53, 0x9C, 0xC3,
0x4E, 0x57, 0xA0, 0xC7,
0x52, 0x5B, 0xA4, 0xCB,
0x56, 0x5F, 0xA8, 0xCF,
0x5A, 0x63, 0xAC, 0xD3,
0x5E, 0x67, 0xB0, 0xD7,
0x68, 0x6B, 0xBA, 0xDB,
0x6C, 0x75, 0xBE, 0xE5
```

很显然,竖着两个差了4, 和最开始的是一样的, 这时候就该猜了,众所周知,逆向是6分肝4分猜

四位应该是分别加 0x09, 0x12, 0x58, 0x81

然后变成了

```c
0x52, 0xD4, 0x39, 0x3C,
0x72, 0xD5, 0x41, 0x7C,
0x92, 0xD6, 0x49, 0xBC,
0xB2, 0xD7, 0x51, 0xFC,
0xD2, 0xD8, 0x59, 0x3D,
0xF2, 0xD9, 0x61, 0x7D,
0x43, 0xDA, 0x75, 0xBD,
0x63, 0x5D, 0x7D, 0x5E
```

超, 这怎么猜,不过有一个很明显的是第四位,把两个16进制位换过来了, 猜测还是4个为一组,然后每个都有不同的变换方式,其中第四个位就是 i = (i >> 4) | (i <<4) & 0xFF

有了这个导向其实就能猜了,我们把16进制都变成bin看, 看看一组到底按位进行了怎么样的变换

先看4A变52和4E变72

```c
0100 1010 -> 0101 0010
0100 1110 -> 0111 0010
```

看上去像是把整个数字倒过来但是到第三个就不对了,

0x52 -> 0x92 :

0101 0010 -> 1001 0010

看上去像是4567 8321 这样, 再看一个

0x6C -> 0x63 :

0110 1100 -> 0110 0011

这样看上去像是 4567 8123, 循环左移三位(右移五位),这看起来更靠谱一点

再看第二位 0x53 0x57 变 0xD4, 0xD5

```c
0101 0011 -> 1101 0100
0101 0111 -> 1101 0101
```

循环右移两位(左移六位)

同理第三位9c->39 : 1001 1100 -> 0011 1001 循环左移一位

第四位就是循环左移四位了



在函数9F50处出现了wrong, 猜测这个函数就是判定函数了, 我们重来一次然后步进, 看看是怎么对比的

terminate之后先直接去看了看9F50, 这..这有点太明显了吧, 就是明文比对就好了

```asm
mov   byte ptr , 0DAh
mov   byte ptr , 0D8h
mov   byte ptr , 3Dh ; '='
mov   byte ptr , 4Ch ; 'L'
mov   byte ptr , 0E3h
mov   byte ptr , 63h ; 'c'
mov   byte ptr , 97h
mov   byte ptr , 3Dh ; '='
mov   byte ptr , 0C1h
mov   byte ptr , 91h
mov   byte ptr , 97h
mov   byte ptr , 0Eh
mov   byte ptr , 0E3h
mov   byte ptr , 5Ch ; '\'
mov   byte ptr , 8Dh
mov   byte ptr , 7Eh ; '~'
mov   byte ptr , 5Bh ; '['
mov   byte ptr , 91h
mov   byte ptr , 6Fh ; 'o'
mov   byte ptr , 0FEh
mov   byte ptr , 0DBh
mov   byte ptr , 0D0h
mov   byte ptr , 17h
mov   byte ptr , 0FEh
mov   byte ptr , 0D3h
mov   byte ptr , 21h ; '!'
mov   byte ptr , 99h
mov   byte ptr , 4Bh ; 'K'
mov   byte ptr , 73h ; 's'
mov   byte ptr , 0D0h
mov   byte ptr , 0ABh
mov   byte ptr , 0FEh
```

直接写脚本即可

```python
target = [
    0x0DA, 0x0D8, 0x3D, 0x4C, 0x0E3, 0x63, 0x97, 0x3D, 0x0C1, 0x91, 0x97, 0x0E, 0x0E3, 0x5C, 0x8D, 0x7E, 0x5B, 0x91, 0x6F, 0x0FE, 0x0DB, 0x0D0, 0x17, 0x0FE, 0x0D3, 0x21, 0x99, 0x4B, 0x73, 0x0D0, 0x0AB, 0x0FE
]

def ROL(x, n) :
    return ((x << n) | (x >> (8-n))) & 0xFF

for i in range(0, len(target), 4) :
    target = ROL(target, 5) - 0x09
    target = ROL(target, 2) - 0x12
    target = ROL(target, 7) - 0x58
    target = ROL(target, 4) - 0x81
    target, target, target, target = \
    target, target, target, target
for i in target :
    print(chr(i&0xFF), end="")
# QCRF{Rss4_/s_fsn4nb_1nr3r3qt1ne}
```

多少有点像了,但是肯定不对, 感觉是每组第三位错了, 这个R应该是T, 哦, 犯蠢了, 0xA-0x3 = 7 不等于 9

Flag :QCTF{Rus4_1s_fun4nd_1nt3r3st1ng}

wanlinwo 发表于 2022-1-25 08:55

qsc2857132 发表于 2022-1-25 11:09

大佬 牛逼 收藏了

wmufo123 发表于 2022-1-25 14:22

厉害了          哈哈哈

Jason599 发表于 2022-1-25 14:44

Forgo7ten2020 发表于 2022-1-25 14:58

师傅牛哇,带带我呜呜呜呜

adgjm孬蛋 发表于 2022-1-25 16:27

66666啊,感谢楼主

Colarier 发表于 2022-1-27 09:18

不多说了,点一波收听
页: [1]
查看完整版本: BUUCTF 部分中等难度题总结