让我们简单先分析下混淆的原理,根据原理来完成去混淆,通过分析,有大部分函数是通过触发异常来完成解密方法体的调用的,如下所示:
flare_71方法使用DynamicMethod根据传入的字节码数组来进行动态调用:
这类受保护的方法还有一个很明显的特征就是开头是两个NOP,而且调用了flare_71,知道这些后就可以编写代码还原第一层混淆:
代码如下所示,自动寻找符合特征的函数,调用目标exe里面的相关方法并修复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
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
67
68
private static void flareon_wrap_decrypt(IList<TypeDef> typeDefs)
{
foreach (var typeDef
in
typeDefs)
foreach (var methodDef
in
typeDef.Methods)
if
(methodDef.Module.Name
=
=
Assembly.ManifestModule.ScopeName && methodDef.HasBody &&
methodDef.Body.Instructions.Count >
2
&& methodDef.Body.Instructions[
0
].OpCode
=
=
OpCodes.Nop &&
methodDef.Body.Instructions[
1
].OpCode
=
=
OpCodes.Nop)
{
var is_wrap
=
false;
var find_true_call
=
false;
MethodDef true_call_MethodDef
=
null;
var is_get_all_args
=
false;
var args_token
=
new
int
[
2
];
var Instructions
=
methodDef.Body.Instructions;
for
(var i
=
0
; i < Instructions.Count; i
+
+
)
{
if
(!find_true_call && Instructions[i].OpCode
=
=
OpCodes.Call)
{
find_true_call
=
true;
true_call_MethodDef
=
(MethodDef)Instructions[i].Operand;
}
if
(Instructions[i].OpCode
=
=
OpCodes.Ldsfld && Instructions[i
+
1
].OpCode
=
=
OpCodes.Ldsfld)
{
args_token[
0
]
=
((FieldDef)Instructions[i].Operand).MDToken.ToInt32();
args_token[
1
]
=
((FieldDef)Instructions[i
+
1
].Operand).MDToken.ToInt32();
Console.WriteLine(
"---------------------"
);
Console.WriteLine(Instructions[i].Operand.ToString());
Console.WriteLine(Instructions[i
+
1
].Operand.ToString());
Console.WriteLine(
"---------------------"
);
is_get_all_args
=
true;
}
if
(Instructions[i].OpCode
=
=
OpCodes.Call && Instructions[i].Operand.ToString().Contains(
"flare_71"
) && is_get_all_args)
{
is_wrap
=
true;
}
}
if
(is_wrap && find_true_call)
{
CurrentMethod
=
methodDef;
var fieldInfo0
=
Assembly.Modules.FirstOrDefault().ResolveField(args_token[
0
]);
var fieldInfo1
=
Assembly.Modules.FirstOrDefault().ResolveField(args_token[
1
]);
var arg0
=
(Dictionary<uint,
int
>)fieldInfo0.GetValue(null);
var arg1
=
(byte[])fieldInfo1.GetValue(null);
Console.WriteLine(methodDef.FullName);
var dm
=
flare.flare_71(Assembly.Modules.FirstOrDefault(), true_call_MethodDef.MDToken.ToInt32(), arg0, arg1);
var methodBody
=
MethodBodyReader.CreateCilBody(AssemblyWriter.moduleDef, arg1, null, true_call_MethodDef.Parameters,
1
, true_call_MethodDef.Body.MaxStack, (uint)(arg1.Length), true_call_MethodDef.Body.LocalVarSigTok, GenericParamContext.Create(true_call_MethodDef));
true_call_MethodDef.FreeMethodBody();
true_call_MethodDef.Body
=
methodBody;
Console.WriteLine(true_call_MethodDef.Name);
}
}
}
在解密出第一层混淆,很快又发现了第二层混淆, 通过方法体的token算出hash,然后通过hash索引PE文件中区段数据,经过RC4解密解密出方法体。
第二层混淆被保护的方法体的名称都是flared打头的:
接下来编写程序自动化找到这些函数,并调用RC4解密进行修复,代码如下所示:
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
private static void flareon_decrypt(IList<TypeDef> typeDefs)
{
foreach (var typeDef
in
typeDefs)
foreach (var methodDef
in
typeDef.Methods)
if
(methodDef.Module.Name
=
=
Assembly.ManifestModule.ScopeName &&
methodDef.ToString().Contains(
"flared"
))
{
Console.WriteLine(methodDef.Name);
var token
=
methodDef.MDToken.ToInt32();
var method
=
Assembly.Modules.FirstOrDefault()?.ResolveMethod(token);
var ILcode
=
method.GetMethodBody().GetILAsByteArray();
var hash_text
=
flare.flared_66(Assembly.Modules.FirstOrDefault(), token);
byte[] sec_data
=
GetSectionData(hash_text);
byte[] decrypted_IL_code
=
flare.rc4(new byte[] {
18
,
120
,
171
,
223
}, sec_data);
var dm
=
flare.flared_67(Assembly.Modules.FirstOrDefault(), decrypted_IL_code, token);
Console.WriteLine(sec_data.Length);
var methodBody
=
MethodBodyReader.CreateCilBody(AssemblyWriter.moduleDef, decrypted_IL_code, null, methodDef.Parameters,
1
, methodDef.Body.MaxStack, (uint)(decrypted_IL_code.Length), methodDef.Body.LocalVarSigTok, GenericParamContext.Create(methodDef));
if
(methodDef.Body.HasExceptionHandlers)
{
Console.WriteLine(methodDef.Name
+
": "
+
methodDef.Body.ExceptionHandlers.Count);
}
methodDef.FreeMethodBody();
methodDef.Body
=
methodBody;
}
}
在运行程序后,使用wireshark观察其网络活动,一开始我就注意到dns活动,猜测该后门和C&C交互使用DNS隧道进行通信:
编写一个DNS server模拟C&C的响应包并对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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from
dnslib
import
*
from
dnslib.server
import
*
import
sys
import
time
class
TestResolver:
def
__init__(
self
):
self
.data
=
[]
op
=
[
2
,
10
,
8
,
19
,
11
,
1
,
15
,
13
,
22
,
16
,
5
,
12
,
21
,
3
,
18
,
17
,
20
,
14
,
9
,
7
,
4
]
for
i
in
op:
op_str
=
str
(i)
payload_len
=
len
(op_str)
s
=
[
'43'
]
for
k
in
range
(payload_len):
s.append(
str
(
ord
(op_str[k])))
s
=
s
+
(
4
-
len
(s))
*
[
"0"
]
pl
=
'.'
.join(s)
self
.data
+
=
([
'192.0.0.%d'
%
(payload_len
+
1
)]
+
[pl])
self
.data
=
100
*
self
.data
print
(
self
.data)
self
.pos
=
0
def
resolve(
self
,request,handler):
reply
=
request.reply()
qname
=
request.q.qname
qtype
=
request.q.qtype
if
"flare-on.com"
in
str
(qname)
and
QTYPE[qtype]
=
=
'A'
:
answer
=
RR(rname
=
qname,ttl
=
60
, rdata
=
A(
self
.data[
self
.pos]))
self
.pos
+
=
1
reply.add_answer(answer)
return
reply
reply.header.rcode
=
getattr
(RCODE,
'NXDOMAIN'
)
return
reply
def
main():
resolver
=
TestResolver()
logger
=
DNSLogger(prefix
=
False
)
dns_server
=
DNSServer(resolver,port
=
53
, address
=
'0.0.0.0'
, logger
=
logger)
dns_server.start_thread()
try
:
while
True
:
time.sleep(
600
)
sys.stderr.flush()
sys.stdout.flush()
except
KeyboardInterrupt:
sys.exit(
0
)
if
__name__
=
=
'__main__'
:
main()
程序中处理数据包的地方如下所示,根据不同的功能码执行不同的cmd:
通过动态调试,分析清楚了协议响应包的格式,
[agent_id]+[payload size]+[opcode]+[pad]
前面分析了那么多,那么flag究竟在哪呢?Rc4解密函数被调用了2次,一次用于解密方法体,另一次用于解密flag:
RC4密钥是在处理数据包的同时,不断对append的数据,最后得到哈希值作为key:
只有这些操作全部调用完成,才会触发解密逻辑:
加密的数据和方法体一样,也是在区段数据里面,知道这些,就可以直接编写脚本解密出flag了,脚本如下所示:
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
import
hashlib
from
Crypto.Cipher
import
ARC4
def
to_ps(c):
return
"powershell -exec bypass -enc \""
+
c
+
"\""
op_str
=
[
(
19
,
"146"
,
"JChwaW5nIC1uIDEgMTAuNjUuNDUuMyB8IGZpbmRzdHIgL2kgdHRsKSAtZXEgJG51bGw7JChwaW5nIC1uIDEgMTAuNjUuNC41MiB8IGZpbmRzdHIgL2kgdHRsKSAtZXEgJG51bGw7JChwaW5nIC1uIDEgMTAuNjUuMzEuMTU1IHwgZmluZHN0ciAvaSB0dGwpIC1lcSAkbnVsbDskKHBpbmcgLW4gMSBmbGFyZS1vbi5jb20gfCBmaW5kc3RyIC9pIHR0bCkgLWVxICRudWxs"
),
(
18
,
"939"
,
"JAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4AMQAwAC4AMgAyAC4ANAAyACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgAxADAALgAyADMALgAyADAAMAAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4AMQAwAC4ANAA1AC4AMQA5ACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgAxADAALgAxADkALgA1ADAAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA="
),
(
16
,
"e87"
,
"JAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANQAxAC4AMQAxACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgA2ADUALgA2AC4AMQAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANQAyAC4AMgAwADAAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA7ACQAKABwAGkAbgBnACAALQBuACAAMQAgADEAMAAuADYANQAuADYALgAzACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwA"
),
(
15
,
"197"
,
"JAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4AMQAwAC4AMQAwAC4ANAAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4AMQAwAC4ANQAwAC4AMQAwACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgAxADAALgAyADIALgA1ADAAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA7ACQAKABwAGkAbgBnACAALQBuACAAMQAgADEAMAAuADEAMAAuADQANQAuADEAOQAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsAA=="
),
(
14
,
"3a7"
,
"JAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4AMQAwAC4AMgAxAC4AMgAwADEAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA7ACQAKABwAGkAbgBnACAALQBuACAAMQAgADEAMAAuADEAMAAuADEAOQAuADIAMAAxACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgAxADAALgAxADkALgAyADAAMgAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4AMQAwAC4AMgA0AC4AMgAwADAAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA="
),
(
10
,
"f38"
,
"hostname"
),
(
17
,
"2e4"
,
"JAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANAA1AC4AMQA4ACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgA2ADUALgAyADgALgA0ADEAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA7ACQAKABwAGkAbgBnACAALQBuACAAMQAgADEAMAAuADYANQAuADMANgAuADEAMwAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANQAxAC4AMQAwACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwA"
),
(
13
,
"e38"
,
"bnNsb29rdXAgZmxhcmUtb24uY29tIHwgZmluZHN0ciAvaSBBZGRyZXNzO25zbG9va3VwIHdlYm1haWwuZmxhcmUtb24uY29tIHwgZmluZHN0ciAvaSBBZGRyZXNz"
),
(
12
,
"570"
,
"JAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANAAuADUAMAAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANAAuADUAMQAgAHwAIABmAGkAbgBkAHMAdAByACAALwBpACAAdAB0AGwAKQAgAC0AZQBxACAAJABuAHUAbABsADsAJAAoAHAAaQBuAGcAIAAtAG4AIAAxACAAMQAwAC4ANgA1AC4ANgA1AC4ANgA1ACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwAOwAkACgAcABpAG4AZwAgAC0AbgAgADEAIAAxADAALgA2ADUALgA1ADMALgA1ADMAIAB8ACAAZgBpAG4AZABzAHQAcgAgAC8AaQAgAHQAdABsACkAIAAtAGUAcQAgACQAbgB1AGwAbAA7ACQAKABwAGkAbgBnACAALQBuACAAMQAgADEAMAAuADYANQAuADIAMQAuADIAMAAwACAAfAAgAGYAaQBuAGQAcwB0AHIAIAAvAGkAIAB0AHQAbAApACAALQBlAHEAIAAkAG4AdQBsAGwA"
),
(
11
,
"818"
,
"RwBlAHQALQBOAGUAdABUAEMAUABDAG8AbgBuAGUAYwB0AGkAbwBuACAAfAAgAFcAaABlAHIAZQAtAE8AYgBqAGUAYwB0ACAAewAkAF8ALgBTAHQAYQB0AGUAIAAtAGUAcQAgACIARQBzAHQAYQBiAGwAaQBzAGgAZQBkACIAfQAgAHwAIABTAGUAbABlAGMAdAAtAE8AYgBqAGUAYwB0ACAAIgBMAG8AYwBhAGwAQQBkAGQAcgBlAHMAcwAiACwAIAAiAEwAbwBjAGEAbABQAG8AcgB0ACIALAAgACIAUgBlAG0AbwB0AGUAQQBkAGQAcgBlAHMAcwAiACwAIAAiAFIAZQBtAG8AdABlAFAAbwByAHQAIgA="
),
(
4
,
"ea5"
,
"WwBTAHkAcwB0AGUAbQAuAEUAbgB2AGkAcgBvAG4AbQBlAG4AdABdADoAOgBPAFMAVgBlAHIAcwBpAG8AbgAuAFYAZQByAHMAaQBvAG4AUwB0AHIAaQBuAGcA"
),
(
5
,
"bfb"
,
"net user"
),
(
3
,
"113"
,
"whoami"
),
(
1
,
"c2e"
,
"RwBlAHQALQBOAGUAdABJAFAAQQBkAGQAcgBlAHMAcwAgAC0AQQBkAGQAcgBlAHMAcwBGAGEAbQBpAGwAeQAgAEkAUAB2ADQAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAEkAUABBAGQAZAByAGUAcwBzAA=="
),
(
7
,
"b"
,
"RwBlAHQALQBDAGgAaQBsAGQASQB0AGUAbQAgAC0AUABhAHQAaAAgACIAQwA6AFwAUAByAG8AZwByAGEAbQAgAEYAaQBsAGUAcwAiACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIABOAGEAbQBlAA=="
),
(
8
,
"2b7"
,
"RwBlAHQALQBDAGgAaQBsAGQASQB0AGUAbQAgAC0AUABhAHQAaAAgACcAQwA6AFwAUAByAG8AZwByAGEAbQAgAEYAaQBsAGUAcwAgACgAeAA4ADYAKQAnACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIABOAGEAbQBlAA=="
),
(
9
,
"9b2"
,
"RwBlAHQALQBDAGgAaQBsAGQASQB0AGUAbQAgAC0AUABhAHQAaAAgACcAQwA6ACcAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAE4AYQBtAGUA"
),
(
2
,
"d7d"
,
"RwBlAHQALQBOAGUAdABOAGUAaQBnAGgAYgBvAHIAIAAtAEEAZABkAHIAZQBzAHMARgBhAG0AaQBsAHkAIABJAFAAdgA0ACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIAAiAEkAUABBAEQARAByAGUAcwBzACIA"
),
(
22
,
"709"
,
"systeminfo | findstr /i \"Domain\""
),
(
20
,
"3c9974"
,
"RwBlAHQALQBOAGUAdABJAFAAQwBvAG4AZgBpAGcAdQByAGEAdABpAG8AbgAgAHwAIABGAG8AcgBlAGEAYwBoACAASQBQAHYANABEAGUAZgBhAHUAbAB0AEcAYQB0AGUAdwBhAHkAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAE4AZQB4AHQASABvAHAA"
),
(
21
,
"8e6"
,
"RwBlAHQALQBEAG4AcwBDAGwAaQBlAG4AdABTAGUAcgB2AGUAcgBBAGQAZAByAGUAcwBzACAALQBBAGQAZAByAGUAcwBzAEYAYQBtAGkAbAB5ACAASQBQAHYANAAgAHwAIABTAGUAbABlAGMAdAAtAE8AYgBqAGUAYwB0ACAAUwBFAFIAVgBFAFIAQQBkAGQAcgBlAHMAcwBlAHMA"
)]
def
get_info(key):
no_ps
=
[
10
,
5
,
3
,
22
]
for
i
in
op_str:
num
=
i[
0
]
if
num
=
=
key:
s
=
i[
2
]
if
num
not
in
no_ps:
s
=
to_ps(s)
return
(num,i[
1
],s)
FLARE15_c
=
[
250
,
242
,
240
,
235
,
243
,
249
,
247
,
245
,
238
,
232
,
253
,
244
,
237
,
251
,
234
,
233
,
236
,
246
,
241
,
255
,
252
]
sh
=
''
stack
=
'System.Object InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)System.Object Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)'
key_str
=
''
d
=
[]
for
i
in
FLARE15_c:
op
=
i^
248
print
(op)
d.append(op)
info
=
get_info(op)
sh
+
=
info[
1
]
if
op!
=
4
:
key_str
+
=
(stack
+
info[
2
])
print
(sh[::
-
1
][
0
:
8
])
print
(d)
hx
=
hashlib.sha256(key_str.encode(
'utf8'
)).digest()
cipher
=
ARC4.new(hx)
with
open
(
'enc_data.bin'
,
'rb'
) as fp:
enc_data
=
fp.read()
dec_data
=
cipher.decrypt(enc_data)
with
open
(
'dec_data.bin'
,
'wb'
) as fp:
fp.write(dec_data)