绿城杯

⏳:2021-9-29

easy_re

查壳(工具 Exeinfo PE) 发现无壳 32位

 

1.静态分析

扔进ida32打开 找到main函数 f5反编译一下

 

出现JUMPOUT问题反汇编不了 首先考虑是不是花指令

shift+f12打开字符串窗口

 

找到与flag有关的字符串双击 找到IDA View窗口中标红的地方

 

关键点为 jz跳转命令是一个无效指令 导致下面adc [edx],esp无法执行

选中jz指令使用Edit->Patch program->Assemble nop掉

 

并将出现的黄色指令用C键强行转换成代码 直到黄色指令消失

 

这时f5进入main函数的伪代码

观察发现函数中含有RC4加密算法(函数中多次出现%256要注意是否为RC4加密)

 

与一般RC4不同的是 此题中在加密过程异或了0x37

找到关键点:

v26=”tallmewhy” v4=v26的长度且加密中出现%v4 可断定v26为密钥

 

回到最前面 找到开头赋值的五组数 五组数在内存空间中连续出现

 

 

v20-v21若非十六进制显示可按H键切换

进入xmmword_4021B0和xmmword_4021C0

 

发现是赋值 选中两条 shift+e 发现是小端存储

1A 91 A9 24 D3 EB F4 30 65 28 A5 9F E4 8D 8C F5h变成了

F5 8C 8D E4 9F A5 28 65 30 F4 EB D3 24 A9 91 1A

 

说明v20-v21所存放的数据也是小端存储 写脚本时需要转过来

分析完 依据伪代码中加密解密过程改动成脚本

 

脚本如下

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main()
{
int b[256] = {0};
int s[256] = {0}; //RC4加密后的s盒
char key[] = "tallmewhy"; //密钥


char flag[42] = {0xF5,0x8C,0x8D,0xE4,0x9F,0xA5,0x28,0x65,
0x30,0xF4,0xEB,0xD3,0x24,0xA9,0x91,0x1A,
0x6F,0xD4,0x6A,0xD7,0x0B,0x8D,0xE8,0xB8,
0x83,0x4A,0x5A,0x6E,0xBE,0xCB,0xF4,0x4B,
0x99,0xD6,0xE6,0x54,0x7A,0x4F,0x50,0x14,0xE5,0xEC}; //flag 小端存储获得


int i,t,w;
int v6 = 0;
int v7 = 0;


for(i=0;i<256;i++) //初始化
{
b[i] = key[i%9];
s[i] = i;
}


for(i=0;i<256;i++) //置换s
{
v6 = s[i];
v7 = (v7 + b[i] + v6) % 256;
s[i] = s[v7];
s[v7] = v6 ^ 0x37; //此题中为改RC4 加密过程多了一步异或操作
}


for(i=0,t=0,v7=0;t<42;t++) //密钥流
{
i = (i + 1) % 256;
v7 = (v7 + s[i]) % 256;
v6 = s[i];
s[i] = s[v7];
s[v7] = v6;
w = (s[i] + s[v7]) % 256;
flag[i-1] ^= s[w];
}


for(i=0;i<42;i++)
putchar(flag[i]);
}

运行得到flag

2.静态分析+动态分析

动态分析好处是可以越过部分代码分析 只找出关键代码点并设下断点进行动态调试得到值

使用32位od打开文件

运行之后f8 直到没有反应 此时就处于main函数入口处

 

 

其实可以验证是不是main函数 od里面停在的指令的标志位是1040 ida里面主函数的标志位也为1040

 

在主函数处下断点并重新运行到断点处

 

f7步进主函数里面

 

继续f8单步执行到不动 (标志位相同)

 

 

 

因为main函数里面gets是我们输入的值

现在可以缕清此题的逻辑:内部有密钥和声明的值进行RC4加密,我们输入的值与此结果进行比较 相同则ok

此时我们从ida找到加密算法的最后一步 即最后进行异或的操作(此时代表加密结束 出现结果) 找到相对应的标志位并在od中设置断点

 

 

 

于是我们现在可以任意输入一串值(建议与flag长度相等)来获得函数内与之异或的值,而且我们知到声明的值

而且异或的逻辑比较简单 异或回去就可

我们输入43位长的字符串(应该要42位)

 

找到断点处直接f4执行到此位置 选择在内存窗口中转到->地址

 

 

我们可以看到输入的值还有函数声明的值

 

而我们要关注的是al的值 它里面存放的是与加密算法异或的值

 

进行动态调试(f8)并记录每次运行到断点处指令时al的值

93 e0 ec 83 e4 c6 1d 0 0 92 de b5 12 84 f7 2d 56 b1 47 e2 69 b4 8a 95 ba 72 62 8 93 f9 cc 2d a9 e2 d0 65 4b 78 68 24 d7 91

接下来就是写脚本求flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
str1 = [0x93,0xe0,0xec,0x83,0xe4,0xc6,0x1d,0x00,0x00,0x92,
0xde,0xb5,0x12,0x84,0xf7,0x2d,0x56,0xb1,0x47,0xe2,
0x69,0xb4,0x8a,0x95,0xba,0x72,0x62,0x08,0x93,0xf9,
0xcc,0x2d,0xa9,0xe2,0xd0,0x65,0x4b,0x78,0x68,0x24,0xd7,0x91]


str2 = [0xF5,0x8C,0x8D,0xE4,0x9F,0xA5,0x28,0x65,
0x30,0xF4,0xEB,0xD3,0x24,0xA9,0x91,0x1A,
0x6F,0xD4,0x6A,0xD7,0x0B,0x8D,0xE8,0xB8,
0x83,0x4A,0x5A,0x6E,0xBE,0xCB,0xF4,0x4B,
0x99,0xD6,0xE6,0x54,0x7A,0x4F,0x50,0x14,0xE5,0xEC]


for i in range(42):
print(chr(str1[i] ^ str2[i]),end='')

flag{c5e0f5f6-f79e-5b9b-988f-28f046117802}


helloworld

主要难点在于去除花指令

自己写的教程可能没有那么详细

推荐一下:

绿城杯-Reverse(逆向)-Green-babyvxworks 浅谈花指令_哔哩哔哩_bilibili

脚本:

1
2
3
4
5
6
7
flag = [188,10,187,193,213,134,127,10,201,185,81,78,136,10,130,
185,49,141,10,253,201,199,127,185,17,78,185,232,141,87]

for i in range(len(flag)):
for j in range(len(flag)):
flag[i] = ((flag[i] - 3) ^ 0x22) & 0xff #防止溢出
print(chr(flag[i]), end='')

flag{helo_w0rld_W3lcome_70_R3}