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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
import logging
log = logging.getLogger('loader_common')
hnd = logging.StreamHandler()
hnd.setFormatter(logging.Formatter("[%(levelname)s]: %(message)s"))
log.addHandler(hnd)
log.setLevel(logging.CRITICAL)
def canon_libname_libfunc(libname, libfunc):
dn = libname.split('.')[0]
if type(libfunc) == str:
return "%s_%s" % (dn, libfunc)
else:
return str(dn), libfunc
class libimp:
def __init__(self, lib_base_ad=0x71111000, **kargs):
self.name2off = {}
self.libbase2lastad = {}
self.libbase_ad = lib_base_ad
self.lib_imp2ad = {}
self.lib_imp2dstad = {}
self.fad2cname = {}
self.fad2info = {}
self.all_exported_lib = []
def lib_get_add_base(self, name):
name = name.lower().strip(' ')
if not "." in name:
log.debug('warning adding .dll to modulename')
name += '.dll'
log.debug('%s' % name)
if name in self.name2off:
ad = self.name2off[name]
else:
ad = self.libbase_ad
log.debug('new lib %s %s' % (name, hex(ad)))
self.name2off[name] = ad
self.libbase2lastad[ad] = ad + 0x1
self.lib_imp2ad[ad] = {}
self.lib_imp2dstad[ad] = {}
self.libbase_ad += 0x1000
return ad
def lib_get_add_func(self, libad, imp_ord_or_name, dst_ad=None):
if not libad in self.name2off.values():
raise ValueError('unknown lib base!', hex(libad))
# test if not ordinatl
# if imp_ord_or_name >0x10000:
# imp_ord_or_name = vm_get_str(imp_ord_or_name, 0x100)
# imp_ord_or_name = imp_ord_or_name[:imp_ord_or_name.find('\x00')]
#/!\ can have multiple dst ad
if not imp_ord_or_name in self.lib_imp2dstad[libad]:
self.lib_imp2dstad[libad][imp_ord_or_name] = set()
self.lib_imp2dstad[libad][imp_ord_or_name].add(dst_ad)
if imp_ord_or_name in self.lib_imp2ad[libad]:
return self.lib_imp2ad[libad][imp_ord_or_name]
# log.debug('new imp %s %s' % (imp_ord_or_name, dst_ad))
ad = self.libbase2lastad[libad]
self.libbase2lastad[libad] += 0x11 # arbitrary
self.lib_imp2ad[libad][imp_ord_or_name] = ad
name_inv = dict([(x[1], x[0]) for x in self.name2off.items()])
c_name = canon_libname_libfunc(name_inv[libad], imp_ord_or_name)
self.fad2cname[ad] = c_name
self.fad2info[ad] = libad, imp_ord_or_name
return ad
def check_dst_ad(self):
for ad in self.lib_imp2dstad:
all_ads = self.lib_imp2dstad[ad].values()
all_ads.sort()
for i, x in enumerate(all_ads[:-1]):
if x is None or all_ads[i + 1] is None:
return False
if x + 4 != all_ads[i + 1]:
return False
return True
def add_export_lib(self, e, name):
self.all_exported_lib.append(e)
# will add real lib addresses to database
if name in self.name2off:
ad = self.name2off[name]
else:
log.debug('new lib %s' % name)
ad = e.NThdr.ImageBase
libad = ad
self.name2off[name] = ad
self.libbase2lastad[ad] = ad + 0x1
self.lib_imp2ad[ad] = {}
self.lib_imp2dstad[ad] = {}
self.libbase_ad += 0x1000
ads = get_export_name_addr_list(e)
todo = ads
# done = []
while todo:
# for imp_ord_or_name, ad in ads:
imp_ord_or_name, ad = todo.pop()
# if export is a redirection, search redirected dll
# and get function real addr
ret = is_redirected_export(e, ad)
if ret:
exp_dname, exp_fname = ret
# log.debug('export redirection %s' % imp_ord_or_name)
# log.debug('source %s %s' % (exp_dname, exp_fname))
exp_dname = exp_dname + '.dll'
exp_dname = exp_dname.lower()
# if dll auto refes in redirection
if exp_dname == name:
libad_tmp = self.name2off[exp_dname]
if not exp_fname in self.lib_imp2ad[libad_tmp]:
# schedule func
todo = [(imp_ord_or_name, ad)] + todo
continue
elif not exp_dname in self.name2off:
raise ValueError('load %r first' % exp_dname)
c_name = canon_libname_libfunc(exp_dname, exp_fname)
libad_tmp = self.name2off[exp_dname]
ad = self.lib_imp2ad[libad_tmp][exp_fname]
# log.debug('%s' % hex(ad))
# if not imp_ord_or_name in self.lib_imp2dstad[libad]:
# self.lib_imp2dstad[libad][imp_ord_or_name] = set()
# self.lib_imp2dstad[libad][imp_ord_or_name].add(dst_ad)
# log.debug('new imp %s %s' % (imp_ord_or_name, hex(ad)))
self.lib_imp2ad[libad][imp_ord_or_name] = ad
name_inv = dict([(x[1], x[0]) for x in self.name2off.items()])
c_name = canon_libname_libfunc(
name_inv[libad], imp_ord_or_name)
self.fad2cname[ad] = c_name
self.fad2info[ad] = libad, imp_ord_or_name
def gen_new_lib(self, target_pe, filter=lambda _: True):
"""Gen a new DirImport description
@target_pe: PE instance
@filter: (boolean f(address)) restrict addresses to keep
"""
new_lib = []
for lib_name, ad in self.name2off.items():
# Build an IMAGE_IMPORT_DESCRIPTOR
# Get fixed addresses
out_ads = dict() # addr -> func_name
for func_name, dst_addresses in self.lib_imp2dstad[ad].items():
out_ads.update({addr:func_name for addr in dst_addresses})
# Filter available addresses according to @filter
all_ads = [addr for addr in out_ads.keys() if filter(addr)]
log.debug('ads: %s' % map(hex, all_ads))
if not all_ads:
continue
# Keep non-NULL elements
all_ads.sort()
for i, x in enumerate(all_ads):
if x not in [0, None]:
break
all_ads = all_ads[i:]
while all_ads:
# Find libname's Import Address Table
othunk = all_ads[0]
i = 0
while i + 1 < len(all_ads) and all_ads[i] + 4 == all_ads[i + 1]:
i += 1
# 'i + 1' is IAT's length
# Effectively build an IMAGE_IMPORT_DESCRIPTOR
funcs = [out_ads[addr] for addr in all_ads[:i + 1]]
try:
rva = target_pe.virt2rva(othunk)
except pe.InvalidOffset:
pass
else:
new_lib.append(({"name": lib_name,
"firstthunk": rva},
funcs)
)
# Update elements to handle
all_ads = all_ads[i + 1:]
return new_lib
|