evilMem

点击此处获得更好的阅读体验


WriteUp来源

题目描述

题目考点

  • 内存取证

解题思路

首先分析Image类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
c.\volatility_2.6_win64_standalone.exe -f image.vmem imageinfo
Volatility Foundation Volatility Framework 2.6
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win7SP1x86_23418, Win7SP0x86, Win7SP1x86
AS Layer1 : IA32PagedMemoryPae (Kernel AS)
AS Layer2 : FileAddressSpace (F:\ichunqiu_MISC\image.vmem)
PAE type : PAE
DTB : 0x185000L
KDBG : 0x8292bc28L
Number of Processors : 1
Image Type (Service Pack) : 1
KPCR for CPU 0 : 0x8292cc00L
KUSER_SHARED_DATA : 0xffdf0000L
Image date and time : 2021-01-17 15:06:35 UTC+0000
Image local date and time : 2021-01-17 23:06:35 +0800

然后查看一下运行中的进程:

1
2
3
4
5
6
7
8
9
10
0x85987160 vmtoolsd.exe           2408   2296      8      191      1      0 2021-01-17 15:03:40 UTC+0000
0x859a34f0 SearchIndexer. 2584 480 11 660 0 0 2021-01-17 15:03:46 UTC+0000
0x857c4d40 svchost.exe 3532 480 5 68 0 0 2021-01-17 15:05:30 UTC+0000
0x84432030 svchost.exe 3560 480 13 337 0 0 2021-01-17 15:05:30 UTC+0000
0x844f7d40 audiodg.exe 1832 780 3 112 0 0 2021-01-17 16:28:55 UTC+0000
0x85835030 SearchProtocol 3368 2584 8 280 0 0 2021-01-17 16:33:33 UTC+0000
0x850f89d0 SearchFilterHo 2424 2584 5 97 ------ 0 2021-01-17 16:33:34 UTC+0000
0x844e2030 cmd.exe 3516 2296 1 22 1 0 2021-01-17 16:33:38 UTC+0000
0x84532030 conhost.exe 2384 376 2 0 1 0 2021-01-17 16:33:38 UTC+0000
0x858e1030 EvilImage.exe 1884 3516 1 7 ------ 0 2021-01-17 16:33:41 UTC+0000

可以看到一个叫做EvilImage.exe的进程,看上去明显不太对,于是首先将exe扣下来看看:

1
2
3
4
5
.\volatility_2.6_win64_standalone.exe -f image.vmem --profile=Win7SP1x86_23418 procdump -p 1884 --dump-dir .
Volatility Foundation Volatility Framework 2.6
Process(V) ImageBase Name Result
---------- ---------- -------------------- ------
0x844304f8 0x00da0000 EvilImage.exe OK: executable.2248.exe

之后检查代码逻辑:

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
v3 = LoadLibraryW(L"Evil.dll");
if ( v3 )
{
v4 = sub_DA13C0(sub_DA1600);
std::ostream::operator<<(v4, v10);
v5 = sub_DA13C0(sub_DA1600);
std::ostream::operator<<(v5, v11);
memset(Filename, 0, 0x200u);
if ( GetModuleFileNameW(v3, Filename, 0x100u) )
{
sub_DA1630(v15);
sub_DA1630(v16);
std::ostream::operator<<(std::cout, sub_DA1600);
}
else
{
v6 = sub_DA13C0(sub_DA1600);
std::ostream::operator<<(v6, v12);
}
checkFlag = GetProcAddress(v3, "checkFlag");
if ( checkFlag )
{
v18 = 0;
v17[0] = 0i64;
v17[1] = 0i64;
puts("Try to input flag");
sub_DA19B0(v17, v14);
((int (__cdecl *)(_OWORD *))checkFlag)(v17);
v8 = sub_DA13C0(sub_DA1600);
}
else
{
v8 = sub_DA13C0(sub_DA1600);
}
std::ostream::operator<<(v8, v13);
}
return 0;

发现是在读取同一目录下的一个叫做Evil.dll的dll,然后调用checkFlag这个函数。那么我们可以尝试去查找这个dll。

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
.\volatility_2.6_win64_standalone.exe -f image.vmem --profile=Win7SP1x86_23418 dlllist -p 2248
Volatility Foundation Volatility Framework 2.6
************************************************************************
EvilImage.exe pid: 1884
Command line : EvilImage.exe
Service Pack 1

Base Size LoadCount Path
---------- ---------- ---------- ----
0x013c0000 0x8000 0xffff C:\Users\link3\Desktop\EvilImage\EvilImage.exe
0x77750000 0x13c000 0xffff C:\Windows\SYSTEM32\ntdll.dll
0x76ae0000 0xd4000 0xffff C:\Windows\system32\kernel32.dll
0x75a40000 0x4a000 0xffff C:\Windows\system32\KERNELBASE.dll
0x74c30000 0x6e000 0xffff C:\Windows\system32\MSVCP140.dll
0x74cb0000 0x13000 0xffff C:\Windows\system32\VCRUNTIME140.dll
0x74ea0000 0x4000 0xffff C:\Windows\system32\api-ms-win-crt-runtime-l1-1-0.dll
0x74dc0000 0xdc000 0xffff C:\Windows\system32\ucrtbase.DLL
0x74db0000 0x3000 0xffff C:\Windows\system32\api-ms-win-core-timezone-l1-1-0.dll
0x74da0000 0x3000 0xffff C:\Windows\system32\api-ms-win-core-file-l2-1-0.dll
0x74d90000 0x3000 0xffff C:\Windows\system32\api-ms-win-core-localization-l1-2-0.dll
0x74d80000 0x3000 0xffff C:\Windows\system32\api-ms-win-core-synch-l1-2-0.dll
0x74d70000 0x3000 0xffff C:\Windows\system32\api-ms-win-core-processthreads-l1-1-1.dll
0x74d60000 0x3000 0xffff C:\Windows\system32\api-ms-win-core-file-l1-2-0.dll
0x74d50000 0x3000 0xffff C:\Windows\system32\api-ms-win-crt-heap-l1-1-0.dll
0x74d30000 0x4000 0xffff C:\Windows\system32\api-ms-win-crt-string-l1-1-0.dll
0x74d20000 0x4000 0xffff C:\Windows\system32\api-ms-win-crt-stdio-l1-1-0.dll
0x74cf0000 0x4000 0xffff C:\Windows\system32\api-ms-win-crt-convert-l1-1-0.dll
0x74ce0000 0x3000 0xffff C:\Windows\system32\api-ms-win-crt-locale-l1-1-0.dll
0x74cd0000 0x5000 0xffff C:\Windows\system32\api-ms-win-crt-math-l1-1-0.dll
0x74d40000 0x3000 0xffff C:\Windows\system32\api-ms-win-crt-filesystem-l1-1-0.dll
0x74c20000 0x3000 0xffff C:\Windows\system32\api-ms-win-crt-time-l1-1-0.dll
0x74d00000 0x3000 0xffff C:\Windows\system32\api-ms-win-crt-environment-l1-1-0.dll
0x74d10000 0x3000 0xffff C:\Windows\system32\api-ms-win-crt-utility-l1-1-0.dll
0x73440000 0x7000 0x1 C:\Users\link3\Desktop\EvilImage\Evil.dll

可以看到dll的路径,于是我们尝试把dll dump到文件中。

1
2
3
4
5
6
7
8
.\volatility_2.6_win64_standalone.exe -f image.vmem --profile=Win7SP1x86_23418 filescan | findstr Evil.dll
Volatility Foundation Volatility Framework 2.6
0x000000003e7b4038 7 0 R--r-d \Device\HarddiskVolume1\Users\link3\AppData\Local\Temp\vmware-link3\VMwareDnD\a623bbe5\EvilImage\Evil.dll
0x000000003fae29d0 7 0 R--r-d \Device\HarddiskVolume1\Users\link3\Desktop\EvilImage\Evil.dll
.\volatility_2.6_win64_standalone.exe -f image.vmem --profile=Win7SP1x86_23418 dumpfiles -Q 0x000000003fae29d0 -D .

Volatility Foundation Volatility Framework 2.6
DataSectionObject 0x3e8e6ba8 None \Device\HarddiskVolume1\Users\link3\Desktop\EvilImage\Evil.dll

分析dll,首先找到我们需要分析的函数为checkFlag

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
int checkFlag()
{
int v0; // eax
char v2[48]; // [esp+8h] [ebp-268h] BYREF
int v3; // [esp+38h] [ebp-238h]
int v4; // [esp+3Ch] [ebp-234h]
int v5[2]; // [esp+40h] [ebp-230h] BYREF
int v6[8]; // [esp+48h] [ebp-228h] BYREF
char v7[516]; // [esp+68h] [ebp-208h] BYREF

v6[0] = 0x3020100;
v6[1] = 117835012;
v6[2] = 185207048;
v6[3] = 252579084;
v6[4] = 319951120;
v6[5] = 387323156;
v6[6] = 454695192;
v6[7] = 522067228;
memset(v7, 0, 0x200u);
v3 = 0;
v4 = 0;
qmemcpy(v5, "fakeivs!", sizeof(v5));
sub_10001000(v2, v6);
sub_10001140(v7);
v0 = 0;
while ( byte_100030C0[v0] == v7[v0] )
{
if ( ++v0 >= 32 )
return 1;
}
return 0;
}

之后直接分析sub_10001000函数,根据关键字可知,使用了chacha20算法。于是这边直接照搬算法解密脚本,写一个解密函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int decrypt_fake()
{
struct chacha_ctx ctx;
uint8_t content[] = { 0xa7,0xc6,0xfd,0xa1,0x56,0xf6,0x4,0xe,0x6f,0x76,0xb9,0x63,0x24,0xcc,0xe5,0xd9,0xa5,0x9a,0xd,0xac,0x38,0x91,0x80,0x78,0x27,0xd5,0x2b,0x3e,0x14,0x4,0xbe,0xe9 };
uint8_t iv[8] = { 'f','a','k','e','i','v','s','!' };
uint8_t key[32] = { 0,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 };
uint8_t keystream[512] = { 0 };
chacha_ivsetup(&ctx, iv, NULL);
chacha_keysetup(&ctx, key, 256);
// chacha_encrypt_bytes(&ctx, keystream, keystream, 32);
chacha_encrypt_bytes(&ctx, (const uint8_t*)content, keystream, 512);
printf("%s\n", keystream);
return 1;
}

但是打印出来的flag却是错误的flag{This_flag___is__fake!_LoL},于是只能重新考虑这个问题。

如果直接选择dump内存中的dll,可能会避开这个坑

然而整个题目本身应该是有解的,于是重新考虑问题,进程中load的dll会不会并不是磁盘上的这个dll呢?

找到内存中的dll:

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
.\volatility_2.6_win64_standalone.exe -f image.vmem --profile=Win7SP1x86_23418 dlldump --pid=1884 --dump-dir=1884
Volatility Foundation Volatility Framework 2.6
Process(V) Name Module Base Module Name Result
---------- -------------------- ----------- -------------------- ------
0x858e1030 EvilImage.exe 0x0013c0000 EvilImage.exe OK: module.1884.3e6e1030.13c0000.dll
0x858e1030 EvilImage.exe 0x077750000 ntdll.dll OK: module.1884.3e6e1030.77750000.dll
0x858e1030 EvilImage.exe 0x074d30000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74d30000.dll
0x858e1030 EvilImage.exe 0x074d70000 api-ms-wi...-1-1.dll OK: module.1884.3e6e1030.74d70000.dll
0x858e1030 EvilImage.exe 0x074d90000 api-ms-wi...-2-0.dll OK: module.1884.3e6e1030.74d90000.dll
0x858e1030 EvilImage.exe 0x074db0000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74db0000.dll
0x858e1030 EvilImage.exe 0x075a40000 KERNELBASE.dll OK: module.1884.3e6e1030.75a40000.dll
0x858e1030 EvilImage.exe 0x074c20000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74c20000.dll
0x858e1030 EvilImage.exe 0x073440000 Evil.dll OK: module.1884.3e6e1030.73440000.dll
0x858e1030 EvilImage.exe 0x074ce0000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74ce0000.dll
0x858e1030 EvilImage.exe 0x074d00000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74d00000.dll
0x858e1030 EvilImage.exe 0x074d20000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74d20000.dll
0x858e1030 EvilImage.exe 0x074d40000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74d40000.dll
0x858e1030 EvilImage.exe 0x074d60000 api-ms-wi...-2-0.dll OK: module.1884.3e6e1030.74d60000.dll
0x858e1030 EvilImage.exe 0x074d80000 api-ms-wi...-2-0.dll OK: module.1884.3e6e1030.74d80000.dll
0x858e1030 EvilImage.exe 0x074da0000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74da0000.dll
0x858e1030 EvilImage.exe 0x074dc0000 ucrtbase.DLL OK: module.1884.3e6e1030.74dc0000.dll
0x858e1030 EvilImage.exe 0x074c30000 MSVCP140.dll OK: module.1884.3e6e1030.74c30000.dll
0x858e1030 EvilImage.exe 0x074d50000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74d50000.dll
0x858e1030 EvilImage.exe 0x074ea0000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74ea0000.dll
0x858e1030 EvilImage.exe 0x074cb0000 VCRUNTIME140.dll OK: module.1884.3e6e1030.74cb0000.dll
0x858e1030 EvilImage.exe 0x074cd0000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74cd0000.dll
0x858e1030 EvilImage.exe 0x076ae0000 kernel32.dll OK: module.1884.3e6e1030.76ae0000.dll
0x858e1030 EvilImage.exe 0x074cf0000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74cf0000.dll
0x858e1030 EvilImage.exe 0x074d10000 api-ms-wi...-1-0.dll OK: module.1884.3e6e1030.74d10000.dll

并且将其dump下来,重新用ida加载:

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
int checkFlag()
{
unsigned __int16 v0; // xmm0_2
int v1; // eax
char Arglist[8]; // [esp+5Ch] [ebp-234h] BYREF
char v4; // [esp+64h] [ebp-22Ch]
int v5; // [esp+68h] [ebp-228h]
int v6; // [esp+6Ch] [ebp-224h]
int v7; // [esp+70h] [ebp-220h]
int v8; // [esp+74h] [ebp-21Ch]
int v9; // [esp+78h] [ebp-218h]
int v10; // [esp+7Ch] [ebp-214h]
int v11; // [esp+80h] [ebp-210h]
int v12; // [esp+84h] [ebp-20Ch]
char v13[516]; // [esp+88h] [ebp-208h] BYREF

v4 = 0;
*(_QWORD *)Arglist = 0i64;
sub_73441AB0("%s", (char)Arglist);
v0 = _mm_loadl_epi64((const __m128i *)&qword_734431E4).m128i_u16[0];
if ( __PAIR64__(561342934 - SHIBYTE(v0), 1853188311 - (char)v0) == *(_QWORD *)Arglist )
{
puts("PASS!");
v5 = -1441843293;
v6 = 558887288;
v7 = 1878051926;
v8 = 1264968977;
v9 = 794857581;
v10 = -899447150;
v11 = -1610488732;
v12 = 736630656;
memset(v13, 0, 0x200u);
sub_73441000();
sub_73441140(v13);
v1 = 0;
while ( byte_734430F8[v1] == v13[v1] )
{
if ( ++v1 >= 32 )
return 1;
}
}
else
{
sub_73441A70("result1:0x%x", v0 + Arglist[0]);
sub_73441A70("result2:0x%x", Arglist[4] + HIBYTE(v0));
puts("FAILED!");
}
return 0;
}

会发现逻辑和之前的完全不一样,于是这里猜测使用了一些奇怪的手段实现的文件换,观察内存中的字符串会发现,这个dll的名字应该也叫做Evil.dll。之后的逻辑就是对这个函数进行逆向。可以看到,除应有的传入的flag之外,这里还会交互读入了一个变量,并且从后文的加密函数中使用这个变量,并且进行一个数学运算,揭开来之后作为后续的算法使用,不过总体来看,应该还是使用的chacha20。于是剩下的就是一些逆向工作(整体还是使用的chacha20)。

1
2
3
4
5
6
7
8
9
struct chacha_ctx ctx;
char iv[9] = "chunqiu!";
uint8_t g_keycontext[64] = { 0xee,0x2a,0xc0,0x8f,0x12,0xaf,0x33,0xc5,0xd1,0x13,0x3e,0x75,0xc8,0x8a,0xad,0xbc,0x3d,0x2,0x0,0x24,0x65,0x22,0x3,0x7e,0x72,0x62,0x33,0x11,0xfc,0x83,0x8f,0xb6 };
uint8_t key[32] = { 163,55,15,170,120,241,79,33,86,204,240,111,17,229,101,75,109,144,96,47,146,134,99,202,100,228,1,160,128,23,232,43 };
uint8_t keystream[512] = { 0 };
chacha_ivsetup(&ctx, (uint8_t*)iv, NULL);
chacha_keysetup(&ctx, key, 256);
chacha_encrypt_bytes(&ctx, (const uint8_t*)g_keycontext, keystream, 64);
printf("%s", keystream);

大佬WP by 魔法少女雪殇

常规取证

1
volatility -f image.vmem --profile=Win7SP1x86 pslist

找到可疑进程EvilImage,然后利用filescan|grep "Evil"寻找程序本体

发现EvilImage.exe与dll,然后filedump出来后,这些dll均为fake,只会解出fakeflag 正确操作:

1
volatility -f image.vmem --profile=Win7SP1x86 dlllist -p 1884

利用dlldump导出正确的dll,导入ida进行逆向 逆向部分: 打开Evil.dll,找到checkFlag,使用反编译

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
int __cdecl checkFlag(char *a1)
{
unsigned __int16 v1; // xmm0_2
int v2; // eax
int v4[17]; // [esp+18h] [ebp-278h] BYREF
char Arglist[8]; // [esp+5Ch] [ebp-234h] BYREF
char v6; // [esp+64h] [ebp-22Ch]
int v7[8]; // [esp+68h] [ebp-228h] BYREF
char v8[516]; // [esp+88h] [ebp-208h] BYREF
v6 = 0;
*(_QWORD *)Arglist = 0i64;
sub_73441AB0("%s", Arglist);
v1 = _mm_loadl_epi64((const __m128i *)"temp.dll").m128i_u16[0];
if ( __PAIR64__(0x217569D6 - SHIBYTE(v1), 0x6E7568D7 - (char)v1) == *(_QWORD*)Arglist )
{
puts("PASS!");
v7[0] = 0xAA0F37A3;
v7[1] = 0x214FF178;
v7[2] = 0x6FF0CC56;
v7[3] = 0x4B65E511;
v7[4] = 0x2F60906D;
v7[5] = 0xCA638692;
v7[6] = 0xA001E464;
v7[7] = 0x2BE81780;
memset(v8, 0, 0x200u);
v4[12] = 0;
v4[13] = 0;
v4[14] = (unsigned __int8)Arglist[0] | (((unsigned __int8)Arglist[1] | (*
(unsigned __int16 *)&Arglist[2] << 8)) << 8);
v4[15] = (unsigned __int8)Arglist[4] | (((unsigned __int8)Arglist[5] | (*
(unsigned __int16 *)&Arglist[6] << 8)) << 8);
sub_73441000(v4, (unsigned __int8 *)v7);
sub_73441140(a1, v4, v8);
v2 = 0;
while ( byte_734430F8[v2] == v8[v2] )
{
if ( ++v2 >= 32 )
return 1;
}
}
else
{
sub_73441A70("result1:0x%x", v1 + Arglist[0]);
sub_73441A70("result2:0x%x", Arglist[4] + HIBYTE(v1));
puts("FAILED!");
}
return 0;
}

这里是真正的dll,和fakedll的密钥扩展方式和加密方式相同,但是密钥和比对的文本不同,并且多了一步验证用户输入的操作。

其中sub_73441000是密钥扩展函数,sub_73441140是加密函数,sub_73441AB0是scanf

在验证第二次输入的地方下断点,得到正确的影响key的输入 chunqiu!

查看加密函数,发现都是异或和移位操作,大概率再加密一次即可解出正确的明文,所以尝试再次加密密文。

在call sub_74331140的地方下断点,把用户输入(第一个参数)改为要比对的密文(byte_734430F8),将使用内存窗口观察第三个参数(v8)的变化,然后直接单步执行,发现v8已经变成了flag。

再次执行程序验证,确是flag。

Flag

1
flag{R3im@aging_1ndir3ctly_LoL}