一个加密后的文件内容hex如下

1
2
3
4
00000000  53 5f 00 f9 1f 08 00 00  6f 4c 69 53 1e b8 ef 08  |S_......oLiS....|
00000010  06 85 66 d2 12 1f e4 bb  93 c6 2a f6 7c 9d a9 7f  |..f.......*.|...|
00000020  49 95 5d 39 73 dd e6 55  30 2b f9 58 a4 16 00 00  |I.]9s..U0+.X....|
00000030  00 00 00 00 4c 43 3e 2b  f2 7f e6 45 30 8b 94 b0  |....LC>+...E0...|

逆向silo.so扩展,可以看到将zend_compile_file替换成了compile_binary_file

1
2
3
4
5
6
if ( !silo_globals )
{
	silo_globals = 1;
	orig_compile_file = (__int64 (__fastcall *)(_QWORD, _QWORD))zend_compile_file;
	zend_compile_file = compile_binary_file;
}

在其中有一个判断和函数调用

1
2
if ( v3 <= 0x33 || *((_DWORD *)v4 + 2) != 'SiLo' || *((unsigned int *)v4 + 1) > v3 || (int)silo_bin_load_file() < 0 )
	goto LABEL_3;

这里的 SiLo 是个可见的明文字符,应该是magic number,之后关注到函数 silo_bin_load_file,从函数名来看也比较像是一个解密文件的,在函数中一开始做了一个crc校验

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
else
{
	v5 = a1 + 1;
	v6 = (__int64)a1 + v4;
	v7 = -1;
	do
	{
		v8 = v7;
		v9 = *v5++ ^ v7;
		v7 = crc32tab[v9] ^ (v8 >> 8);
	}
	while ( (_BYTE *)v6 != v5 );
	v10 = (unsigned int)~v7;
}
v11 = *a1;
if ( (_DWORD)v11 != (_DWORD)v10 )
{
	zend_error(2LL, "CRC mismatch: %d != %d", v10, v11);
	return 0xFFFFFFFFLL;
}

暂时不用管怎么校验,关注到下方的解密函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
v20 = a1[1];
v12 = v20 - 52LL;
v13 = (char *)_emalloc(v12);
memcpy(v13, a1 + 13, v12);
v22 = a1[1] - 52;
v14 = EVP_CIPHER_CTX_new();
if ( !v14 )
	goto LABEL_21;
v15 = EVP_aes_256_cfb128();
if ( (unsigned int)EVP_DecryptInit_ex(v14, v15, 0LL, &silo_bin_key, a1 + 3) != 1
|| (unsigned int)EVP_DecryptUpdate(v14, v13, &v25, v13, v22) != 1
|| (unsigned int)EVP_DecryptFinal_ex(v14, &v13[v25], &v25) != 1 )
{

这里根据后续的解密函数参数可以知道,整个加密文件应该解密的长度应该文件长度-52,其次这里的silo_bin_key是一个硬编码的值,因为加密方式是 EVP_aes_256_cfb128,所以直接从0x20开始一共取32位即可获得decrypt key

之后需要关注到iv的值应该是多少,这里可以看到是 a1+3 看起来好像是直接从文件内容中取,但是不知道此时a1指向哪里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.text:00000000000036F6                 call    _EVP_CIPHER_CTX_new
.text:00000000000036FB                 mov     r14, rax
.text:00000000000036FE                 test    rax, rax
.text:0000000000003701                 jz      loc_38D8
.text:0000000000003707                 call    _EVP_aes_256_cfb128
.text:000000000000370C                 xor     edx, edx
.text:000000000000370E                 mov     r8, r15
.text:0000000000003711                 mov     rdi, r14
.text:0000000000003714                 lea     rcx, silo_bin_key ; " �����ad.Q\x02$��z��XG�������0�}�\t�2"...
.text:000000000000371B                 mov     rsi, rax
.text:000000000000371E                 call    _EVP_DecryptInit_ex

根据函数传递规则可以知道各自参数的值如下

1
2
3
4
5
ctx = rdi => r14 => rax = _EVP_CIPHER_CTX_new()
cipher = rsi => rax => _EVP_aes_256_cfb128()
impl = rdx
key = rcx => silo_bin_key
iv = r8 => r15

向上寻找r15,可以发现这个值来源于 rbx+0xc

rbx的值就是 silo_bin_load_file 的第一个参数,也就是文件内容本身

所以iv的值其实就是文件内容 0xc 开始的地方取16长度

此时解密之后的内容为

1
2
3
4
5
6
gdb-peda$ x/10gx $rbp
0x7ffff3c64800: 0xfe1238db6f6d58d5      0xd854958c30315f9e
0x7ffff3c64810: 0xaf7ad822ce2777f1      0x821dc5d24836d205
0x7ffff3c64820: 0x7d2646e63a25a042      0x33dfbfe417db1522
0x7ffff3c64830: 0xba2ecdb389cb24a4      0xe179c536d6d15dc0
0x7ffff3c64840: 0xd1f3fa750ccf33cc      0xe490a07fa1fc3468

但是这明显不是明文,所以对解密后的内容还需要进行一次处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if ( (unsigned int)inflateInit2_(zlib_ctx, '\xFF\xFF\xFF\xF1', "1.2.11", 112LL) ) {
	zend_error(2LL, "Unable to init zlib for decompression.");
	return 0xFFFFFFFFLL;
}
// ......
memset(v16, 0, v22 + 16);
zlib_ctx[0] = (__int64)decrypt_buf;
LODWORD(zlib_ctx[4]) = v22;
zlib_ctx[3] = (__int64)v16;
LODWORD(zlib_ctx[1]) = v19 - 52;
v20 = inflate(zlib_ctx, 4LL);
inflateEnd(zlib_ctx);
memset(decrypt_buf, 0, size);

这里能发现主要使用了一下zlib解压缩,但是需要注意这里的 inflateInit2_ 中第二个参数设置为 \xff\xff\xff\xf1,好像不太对,但是这里的值其实是-15

所以直接设置 windowBits 为 -15 将其作为无头的纯deflate流解压即可