-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathextract_c2.py
93 lines (75 loc) · 2.93 KB
/
extract_c2.py
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from __main__ import *
import socket
import struct
SIZE_OF_IP = 4
SIZE_OF_SINGLE_CONFIG = 8
def convert_dword_to_ip(dword_ip):
return socket.inet_ntoa(struct.pack('!I', dword_ip)).encode('utf-8')
class InvalidConfig(Exception):
pass
def parse_single_config(addr):
''' parse single structure of config from given address,
and return ip address and port.
'''
# get 4 bytes from top of the given address
# this shuld be IP address
ip = convert_dword_to_ip(getInt(addr))
# '0.0.0.0' is a mark of the end of config
if ip == '0.0.0.0':
raise InvalidConfig('Invalid Config or found Config end')
# get 2 bytes from offset 4 of the given address,
# this should be port number
port = getShort(addr.add(SIZE_OF_IP))
return ip, port
def iterate_config(config_addr):
''' parse all configs at given address.'''
while True:
try:
ip, port = parse_single_config(config_addr)
config_addr = config_addr.add(SIZE_OF_SINGLE_CONFIG)
yield (ip, port)
except InvalidConfig:
# when parse_single_config throw InvalidConfig exceptoin,
# it should be that it finished iteration of config
break
except Exception as e:
print('[!] {}'.format(e))
def find_config_address():
''' find the address of emotet config embedded in data section.'''
asm_ptns = [
# Ptn 1 (since 2020-08~)
# 1000e267 89 72 28 MOV dword ptr [EDX + 0x28],ESI=>DAT_1001f000
# 1000e26a 89 72 24 MOV dword ptr [EDX + 0x24],ESI=>DAT_1001f000
# 1000e26d 89 4a 10 MOV dword ptr [EDX + 0x10],ECX
# 1000e270 eb 04 JMP LAB_1000e276
'\\x89.{1}\\x28\\x89.{2}\\x89.{2}\\xeb\\x04',
# Ptn 2 (since 2021-01-28~)
# 007718a4 c7 40 04 MOV dword ptr [EAX + pCVar4->field_0x4],DAT_0077d390
# 007718ab c7 40 10 MOV dword ptr [EAX + pCVar4->c2_info],DAT_0077d390
# 007718b2 c7 40 0c MOV dword ptr [EAX + pCVar4->field_0xc],0x0
'\\xc7.{5}\\x00\\xc7.{5}\\x00\\xc7.{5}\\x00',
]
# try all patterns
for asm in asm_ptns:
found = findBytes(None, asm, -1)
if not found:
continue
elif len(found) != 1:
raise ValueError('False detection of config')
inst = getInstructionAt(found[0])
config_addr = inst.getAddress(1)
return config_addr
# if nothing found, it comes here.
raise RuntimeError('Config not found')
def extract_c2():
''' extract all the C2 configs of emotet. you can give a config address,
otherwise it will search it by regex.
'''
config_addr = find_config_address()
for (ip, port) in iterate_config(config_addr):
yield 'http://{}:{}'.format(ip, port)
def main():
for c2_server in extract_c2():
print(c2_server)
if __name__ == '__main__':
main()