逆向学习笔记 之 drinkSomeTea

  1. 写在前面
    1. method 1th
    2. method 2nd
    3. method 3rd

写在前面

​ 这两天一直再看逆向相关的书,也在B站上看小甲鱼的视频学了一下OD(Ollydbg),但一直还没有实际尝试过逆向的解题,今天刚好是DASCTF的march月赛,所以做了其中一道比较简单的逆向题来入了个门。

​ 源程序在这里,这里应该是有三个解法,首先我先介绍方法一

method 1th

首先用IDA载入,

image-1

流程很简单,我们看到main(我按了一下反斜杠键,看着清爽些)

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
56
57
58
59
60
61
62
63
64
65
66
int __cdecl main(int argc, const char **argv, const char **envp)
{
HANDLE v3; // eax
void *v4; // esi
int result; // eax
DWORD v6; // edi
char *v7; // esi
DWORD v8; // ebx
HANDLE v9; // eax
void *v10; // esi
DWORD NumberOfBytesRead; // [esp+Ch] [ebp-8h] BYREF
DWORD NumberOfBytesWritten; // [esp+10h] [ebp-4h] BYREF

sub_401000();
sub_4013B2(aWelcomeToMyTea);
v3 = CreateFileA(FileName, 0xC0000000, 0, 0, 3u, 0x80u, 0);
v4 = v3;
if ( v3 == -1 )
{
sub_4013B2(aIThinkYouDoNot);
result = 0;
}
else
{
v6 = GetFileSize(v3, 0);
if ( v6 < 0xEA60 )
{
SetFilePointer(v4, 0, 0, 0);
NumberOfBytesRead = 0;
ReadFile(v4, &unk_409988, v6, &NumberOfBytesRead, 0);
CloseHandle(v4);
if ( v6 >> 3 )
{
v7 = &unk_409988;
v8 = v6 >> 3;
do
{
(loc_4010A0)(v7, &unk_407030);
v7 += 8;
--v8;
}
while ( v8 );
}
v9 = CreateFileA(aTeaPngOut, 0xC0000000, 0, 0, 2u, 0x80u, 0);
v10 = v9;
if ( v9 == -1 )
{
sub_4013B2(aIThinkYouDoNot);
}
else
{
NumberOfBytesWritten = 0;
WriteFile(v9, &unk_409988, v6, &NumberOfBytesWritten, 0);
CloseHandle(v10);
sub_4013B2(aNowThisCupOfTe);
}
result = 0;
}
else
{
sub_4013B2(aYourTeaIsTooHo);
result = 0;
}
}
return result;
}

逻辑很简单:

首先读入一个文件tea.png【判断是否读入成功,否则退出】

获取文件大小【如果文件大于等于0xEA60字节,退出】

然后将文件大小除以8,作为循环躺数(以8字节为单位对文件内容进行加密)

【加密函数】(文件地址,&unk_407030(key))

最后写入一个新的文件tea.png.out。

那我们就来看看这个加密函数

image-20210327194150988

这里发现IDA反编译失败了,此时,要么就去啃汇编,要么就去找到原因

继续往下拉会发现image-20210327194236596

这里有个神奇的指令,看不懂,据说是什么花指令(这个坑以后填叭),void教我的,在这里先按U(undefined),然后把00401116处 的值改为90(在hex-view里,F2,改掉,F2保存),也就是nop掉,

然后往上找push ebp【函数起点】,按p,创建函数

image-20210327195409251

然后就可以快乐F5了

image-20210327195428766

可以发现这个函数的两个参数,一个是int类型的指针(4字节),一个是_DWORD类型的指针(4字节)

并且在这里的操作中,我们可以看到,对第一个参数只用了a1[0],a1[1]这两个参数,对第二个参数,是用到了四个值

我们在思考一下这两个参数原本的意义,一个是8字节的文件,另一个是key,去看一下key

image-20210327194012170

发现是个字符串,数一数是有16个字节的,所以估计是4个字节为一组作为一个_DWORD,那么第一个参数就是4个字符为一组作为一个int。然后在加密函数里进行操作。

那么字符怎么转换为数字呢?在.data块按d键就可以转化类型了,转化之后是

image-20210327195956419

可以发现这个顺序怪怪的,是小端序,所以对于文件的数据而言,也是用的小端序了。

这里是对于前八字节的加密代码(我们能确定png文件的前八字节)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main() {
int key[4] = { 0x67616C66, 0x6b61667b, 0x6c665f65, 0x7d216761 };
int v3 = 0; // [esp+Ch] [ebp-14h]
int i; // [esp+14h] [ebp-Ch]
int v5; // [esp+18h] [ebp-8h]
int v6; // [esp+1Ch] [ebp-4h]
int m[2] = { 0x474e5089, 0x0a1a0a0d };
v5 = 0;
v6 = m[1];
v3 = m[0];
for (i = 0; i < 32; ++i)
{
printf("%x %x %x\n", v5, v3, v6);
v5 -= 0x61C88647;
v3 += (key[1] + (v6 >> 5)) ^ (v5 + v6) ^ (key[0] + 16 * v6);
v6 += (key[3] + (v3 >> 5)) ^ (v5 + v3) ^ (key[2] + 16 * v3);

}
m[1] = v6;
m[0] = v3;
printf("%x %x %x\n", v5, v3, v6);
return 0;
}

运行结果

image-20210327200350437

image-20210327200434464

可以看到,前八字节的密文与他给的加密文件的头八字节是一致的。那么至此我们已经可以选择写这个算法的解密算法了。

这就是方法一,解密脚本暂时没有,因为C语言快忘光了,不怎么会操作,python写起来又不太对,python复现加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#enc
from Crypto.Util.number import *
v5=0
key = [0x67616C66, 0x6b61667b, 0x6c665f65, 0x7d216761]
m = [0x474e5089, 0x0a1a0a0d]
v3 = m[0]
v6 = m[1]
for j in range(32):
print(hex(v5),hex(v3),hex(v6))
v5 += 0x9E3779B9
v5 &= 0xffffffff
v3 += (key[1] + (v6 >> 5) ^ (v5 + v6)) ^ (key[0] + 16 * v6)
v3 &= 0xffffffff
v6 += (key[3] + (v3 >> 5) ^ (v5 + v3)) ^ (key[2] + 16 * v3)
v6 &= 0xffffffff

print(hex(v3),hex(v6))

结果

image-20210327203639223

第三行的v6开始出错,估计是sar的问题,也就是那个算术位移操作【带符号的】

所以就放弃了,我选择patch这个程序,让他从加密程序,变成解密程序。

method 2nd

他的加密逻辑很简单,

先加v5

然后处理v3【v3与v5和v6有关】

最后处理v6【v6与v5和已经被处理的v3有关】

那么解密逻辑也不难,

先处理v6【最后的v6与最后的v5,v3的值有关,且这两个值已知,所以v6可以还原为上一轮的值】

然后处理v3【最后的v3与最后v5和上一轮的v6值有关,上一轮的v6值我们已经处理出来了,最后的v5我们也知道,因此v3也可以还原为为上一轮的值】

然后v5减去那个常数

所以我们可以选择从底层(汇编)改他的程序流程

但是比赛的时候我又失败了,改流程得稍微处理下,最后我是在原程序的基础上,patch了程序处理的值

method 3rd

把v5的初始值改为v5的最终值,然后把v5的add改为sub,那么程序一开始就是sub一下,意味着v5的值一开始就会被还原为上一轮,这是我们不期望的,我们还没用它呢,所以我们得在v5的最终值的基础上,在加一轮先【也就是改成第33轮的v5】,方便程序去sub

然后就是把v3 patch成v6,把v6 patch成v3。

与此同时我们也要把他们用到的key调换一下,但调换的时候会遇到问题,好像是代码的长度对不上,那么我们就直接在.data段【hex-view】把key前后两段的顺序换一下,这样效果还是一样的。

结果为

image-20210327202523573

image-20210327202536357

注:patch:【Edit-Patch Program-Asemble】

​ patch的时候给v5赋值的时候要用dword ptr,不然会报错

image-20210327202109607

​ patch完了保存【Edit-Patch Program-Apply patches to input file】

这样子这个程序就变成了解密程序了,将加密图片的后缀去掉,点击这个程序,然后就会生成一个tea.png.out文件,再把这个文件后缀去掉,就是flag的图片了。

patch好的程序也一起塞着这里

【回头补一下前两个方法的做法】


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 643713081@qq.com

文章标题:逆向学习笔记 之 drinkSomeTea

文章字数:1.7k

本文作者:Van1sh

发布时间:2021-03-27, 19:22:00

最后更新:2021-08-19, 21:13:15

原始链接:http://jayxv.github.io/2021/03/27/逆向学习笔记之drinkSomeTea/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏