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} 大佬 牛逼 收藏了 厉害了 哈哈哈 师傅牛哇,带带我呜呜呜呜 66666啊,感谢楼主 不多说了,点一波收听
页:
[1]