2019安洵杯

学习逆向之后,在github找题目进行复现的。

Easy_Encryption

提示很多,直接找到main函数,简单分析一下,对输入flag进行2个加密函数后,最后与一个已知密文比较。其中第二个明显的base64加密。

来到一个加密函数,由于ida栈顶分析失败导致,直接alt+k修改一下就可以了。image-20200815162136204

愉快的看伪代码,一个注意的地方:image-20200815162346063

直接简单的爆破。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <math.h>

int main(void)
{
char s[] = "artqkoehqpkbihv", i = 0, j = 0;
char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

for(i = 0; base[i]; i++)
base[i] = abs(base[i]-97);

for(i = 0; i <= s[i]; i++)
for(j = 97; j <= 122; j++)
if((j+base[i]-97)%26+97 == s[i])
putchar(j);

return 0;
}
//flag: umpnineissogood

game

ida载入后,发现伪代码十分难看。。。看了一会儿,实在不想分析了,所有函数都是一种形式,感觉是什么混淆代码的技术。

百度后,发现是ollvm,控制流平坦化技术。

控制流平坦化(control flow flattening)的基本思想主要是通过一个主分发器来控制程序基本块的执行流程,这个主分发器就是函数中的switch语句。

且目前可以利用符号执行来去除控制流平坦化。利用符号执行去除控制流平坦化

而这也需要angr,网上说的都是建议安装在python虚拟环境中。

跟着教程安装完虚拟环境并创建后,但angr又安装失败。。。后面发现还可以直接用dokcer。

了解一波docker,docker,折腾一些时间(谷歌大法帮了大忙👍)后成功执行deflat.py脚本。image-20200815165930112

又是愉快的伪代码,简单就不分析了,使用gdb动调出2个对比的数独表,找出差异的数字,即0所在位置。image-20200815170400494

然后使用notepad++找不不同:image-20200815170436332

最后简单解密一下:

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
#include <stdio.h>
#include <string.h>

int main(void)
{
char s[] = "4693641762894685722843556137219876255986";
int i = 0, j = 0, k = 0, count = 0;

for(i = 0; s[i]; i++)
{
for(j = 33; j <= 122; j++)
{
if(((j&0xf3 | ~j&0xc)-20) == s[i])
{
s[i] = j;
break;
}
}
}
for(i = 0; i < strlen(s); i += 2)
{
char temp = s[i];
s[i] = s[i+1];
s[i+1] = temp;
}

k = strlen(s)/2;
for(i = 0; i < strlen(s)/2; i++)
{
char temp = s[k];
s[k] = s[i];
s[i] = temp;
k++;
}
puts(s);

return 0;
}
//flag: KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J

crackme

打开程序,出现hooked,表示已经对程序中的的函数hook成功。

而之前我也在逆向工程核心原理一书中看到过该章节的一点内容,它的介绍:

代码逆向分析中,钩取(hooking)是一种截取信息,更改程序流程,添加新功能的技术。

ida中找到main函数后,会发现很奇怪,有一个异常,且没有操作输入flag的函数,猜测是hook改变了程序的程序流程。但可以通过数据交叉引用找到关键函数。

看了一下所有与计算输入数据与比较数据相关的函数,了解大致流程后,OD进行动调看看:

其中main函数中的MessageBox函数执行后并没有正常的执行,而是跳到了一个新的函数,也是ida中分析过的过数据加密的函数。结合ida继续动调:image-20200815173123252

OD中看到跳到的main函数触发异常的地方,但是程序无法处理的异常。。。image-20200815173417257

那上图中的TopLevelExceptionFilter关键加密函数不执行的嘛。。使用PEtools来dump出正常执行的程序看到数据改变了的,那是执行了的。。

百度一下SetUnhandledExceptionFilter(TopLevelExceptionFilter)函数:简单来说就是设置一个发生异常处理的函数,且它会将原来默认处理异常的函数hook掉。且我们的程序要不处于调试状态才会去执行它设置的异常处理函数。介绍

参数:lpTopLevelExceptionFilter 函数指针。当异常发生时,且程序不处于调试模式(在vs或者别的调试器里运行)则首先调用该函数。image-20200815175235431

程序流程清楚了,开始分析算法:

第二个加密函数变种的base64,但第一个看不出什么名堂,自己逆的话感觉工程量肯定大且很大几率搞不出来。。

就想着先把变种的base64先解密,但一直解密乱码。。不能啊,自己也写了2个函数来测试,能将变种加密的base64还原为正常base64加密的,但题中的密文不行,。。

模拟本题的变种base64加密(也是从模拟发现是base64的):

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
int k; // [esp+E4h] [ebp-5Ch]
int v3; // [esp+F0h] [ebp-50h]
signed int j; // [esp+FCh] [ebp-44h]
int v5; // [esp+108h] [ebp-38h]
signed int i; // [esp+114h] [ebp-2Ch]
char *v7; // [esp+120h] [ebp-20h]
signed int v8; // [esp+12Ch] [ebp-14h]
int v9; // [esp+138h] [ebp-8h]
int temp = 0;

char str[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/";
char Str[] = "123456";
v5 = 0;
v8 = strlen(Str);
if ( v8 % 3 )
v9 = 4 * (v8 / 3) + 4;
else
v9 = 4 * (v8 / 3);
v7 = (char *)malloc(sizeof(char)*(v9+1));
v7[v9] = 0;
for ( i = 0; i < v8; i += 3 )
{
v3 = 0;
for ( j = 0; j < 3; ++j )
v3 |= (unsigned __int8)Str[j + i] << 8 * (2 - j);
for ( k = 0; k < 4; ++k )
{
if ( k >= 4 - (i + 3 - v8) && i + 3 > v8 )
v7[v5] = '!';
else
temp = (((v3 >> 6 * (3 - k)) & 0x3F)+24)%64, v7[v5] = str[temp], printf("%d ", temp);;
++v5;
}
}
puts(v7);
}
//加密后:KRGlLBSo

还原成正常加密的base64密文:

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
#include <stdio.h>

int main(void)
{
int i = 0, j = 0, k = 0;
char a[] = "KRGlLBSo";
char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char a1[] = {20, 53, 0, 19, 8, 14, 41, 36,
14, 50, 22, 18, 47, 6, 38, 63, 24, 14, 24, 5, 17, 56};
for(i = 0; a[i]; i++)
for(j = 0; j < 64; j++)
if(a[i] == s[j])
{
int t = j;
if(t <= 25)
t += 26;
else if(t <= 51)
t -= 26;

for(k = 0; k < 64; k++)
if((k+24)%64 == t)
{
putchar(s[k]);
break;
}
}

return 0;
}

//还原为正常base64加密密文:MTIzNDU2

正常base64解密:可以看到,解密出明文。image-20200815181032181

就这样卡住了。。。也无心看第一个加密了。

不行,去看了看writeup,原来这个解密出本来就是乱码,转化为16进制就好。。。又直接看了第一个加密是sm4。。

它在加密中其实有标志的:一是表,二是0xA3B1BAC6image-20200815181639575

image-20200815181823633

最后python安装pysm4进行解密:

首先base64解密且转化为16进制:

1
2
3
4
5
6
7
8
9
10
from base64 import b64decode

def str_to_hex(s):
return ''.join([hex(c).replace('0x', '') for c in s])

s = "WdCVKQ3yQAYU9I0naQaHTg=="
s1 = b64decode(s)

ans = str_to_hex(s1)
print(ans)

sm4解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pysm4 import encrypt, decrypt
#密钥
mk = 0x77686572655f6172655f755f6e6f773f

#密文
cipher_num = 0x59d095290df2400614f48d276906874e

clear_num = decrypt(cipher_num, mk)
s = hex(clear_num)[2:]
print(s)

def hex_to_str(s):
return ''.join([chr(i) for i in [int(s[j]+s[j+1], 16) for j in range(0, len(s), 2)]])
print(hex_to_str(s))

#flag:SM4foRExcepioN?!
-------------本文结束感谢您的阅读-------------