1
2
3
4 import os, time, atexit
5 from signal import signal, SIGTERM
6 import requests
7 from subprocess import Popen
8 from mmap import mmap
9 from urlparse import urlparse, parse_qs
10 from tempfile import NamedTemporaryFile
11 import json
12 import adt, arm, asm, bil
13
14 import threading
15
16 from pprint import pprint
17
18 __all__ = ["disasm", "image"]
19
20 DEBUG_LEVEL = ["Critical", "Error"]
21
22 storage = threading.local()
23 servers = dict()
24 server_lock = threading.Lock()
25 requests_lock = threading.Lock()
26 request = None
27
29 global request
30 with requests_lock:
31 if request == None:
32 request = requests.Session()
33 adapter = requests.adapters.HTTPAdapter(
34 pool_connections=1000,
35 pool_maxsize=1000,
36 max_retries=10,
37 pool_block=True)
38 request.mount('http://', adapter)
39
40 init_requests()
41
42
44 instance = getattr(storage, 'instance', None)
45 if instance is not None:
46 instance.close()
47
54
55 atexit.register(del_instance)
56 signal(SIGTERM, lambda x,y: del_instance)
57
58
60 port = str(kwargs.get('port', 8080))
61 name = kwargs.get('name', 'bap-server')
62 with server_lock:
63 if port in servers:
64 return servers[port]
65 else:
66 process = Popen([name, '--port=' + port])
67 server = {
68 'server' : process,
69 'url' : "http://127.0.0.1:{0}".format(port)
70 }
71 servers[port] = server
72 return server
73
74
76 r""" disasm(obj) disassembles provided object.
77 Returns a generator object yielding instructions.
78 """
79 def run(obj):
80 return get_instance(**kwargs).insns(obj, **kwargs)
81 if isinstance(obj, Id):
82 return run(obj)
83 elif isinstance(obj, Resource):
84 return run(obj.ident)
85 else:
86 return run(load_chunk(obj, **kwargs))
87
93
96
97
98
99
102 self.ident = Id(ident)
103 self.bap = bap
104 self.msg = None
105 self._name = name
106
108 if self.msg is None:
109 self.msg = self.bap.get_resource(self.ident)
110 if not self._name in self.msg:
111 if 'error' in msg:
112 raise ServerError(response)
113 else:
114 msg = "Expected {0} msg but got {1}".format(
115 self._name, msg)
116 raise RuntimeError(msg)
117
118 - def get(self, child):
119 self.load()
120 return self.msg[self._name].get(child)
121
122
136
137
141
143 ss = self.get('segments')
144 self.segments = [Segment(s, self) for s in ss]
145
147 for sec in self.segments:
148 sym = sec.get_symbol(name, d)
149 if sym is not d:
150 return sym
151 return d
152
159
164
166 self.symbols = [Symbol(s, self) for s in self.get('symbols')]
167
169 try:
170 return (s for s in self.symbols if s.name == name).next()
171 except StopIteration:
172 return d
173
175 if name == 'symbols':
176 self.load_symbols()
177 return self.symbols
178 elif name == 'addr' or name == 'size':
179 return self.get('memory')[name]
180 elif name == 'memory':
181 self.memory = Memory(self.get('memory'), self)
182 return self.memory
183 else:
184 return self.get(name)
185
188 super(Symbol, self).__init__('symbol', ident, parent.bap)
189 self.parent = parent
190
192 self.chunks = [Memory(s, self) for s in self.get('chunks')]
193
195 if name == 'chunks':
196 self.load_chunks()
197 return self.chunks
198 elif name == 'addr':
199 self.load_chunks()
200 return self.chunks[0].addr
201 else:
202 return self.get(name)
203
206 self.parent = parent
207 self.size = int(mem['size'])
208 self.addr = int(mem['addr'])
209 self.links = mem['links']
210
212 try:
213 url = (urlparse(url) for url in self.links
214 if urlparse(url).scheme == 'mmap').next()
215 qs = parse_qs(url.query)
216 offset = int(qs['offset'][0])
217 with open(url.path, "rw+b") as f:
218 mm = mmap(f.fileno(), length=0)
219 mm.seek(offset)
220 self.data = mm.read(self.size)
221 mm.close()
222 except StopIteration:
223 self.data = None
224
230
231
234 self.msg = str(Error(err))
235
238
241 self.__dict__.update(err)
242 self.__dict__.update(err['error'])
243
245 return "{severity}: {description}".format(**self.error)
246
251 return str(self.value)
252
253 RETRIES = 10
254
257 if isinstance(server, dict):
258 self.__dict__.update(spawn_server(**server))
259 else:
260 self.url = server
261
262 self.last_id = 0
263 for attempt in range(RETRIES):
264 try:
265 self.capabilities = self.call({'init' : {
266 'version' : '0.1'}}).next()['capabilities']
267 break
268 except Exception:
269 if attempt + 1 == RETRIES:
270 raise
271 else:
272 time.sleep(0.1 * attempt)
273
274 if not "capabilities" in self.__dict__:
275 raise RuntimeError("Failed to connect to BAP server")
276 self.data = {}
277 self.temp = NamedTemporaryFile('rw+b', prefix="bap-")
278
279 - def insns(self, src, **kwargs):
280 req = {'resource' : src}
281 req.update(kwargs)
282 res = self.call({'get_insns' : req})
283 for msg in res:
284 if 'error' in msg:
285 err = Error(msg)
286 if err.severity in DEBUG_LEVEL:
287 print(err)
288 else:
289 return (parse_insn(js) for js in msg['insns'])
290
293
297
300
302 kwargs.setdefault('url', self.mmap(data))
303 kwargs.setdefault('arch', 'i386')
304 kwargs.setdefault('addr', 0)
305 addr = kwargs['addr']
306 if isinstance(addr, str):
307 addr = int(addr, 0)
308 kwargs['addr'] = '0x{0:x}'.format(addr)
309
310 return self._load_resource({'load_memory_chunk' : kwargs})
311
313 if 'server' in self.__dict__:
314 self.server.terminate()
315 self.temp.close()
316
318 self.last_id += 1
319 dic['id'] = Id(self.last_id)
320 return json.dumps(dic, default=str)
321
322 - def call(self, data):
328
329
330 - def mmap(self, data):
331 url = "mmap://{0}?offset=0&length={1}".format(
332 self.temp.name, len(data))
333 os.ftruncate(self.temp.fileno(), 4096)
334 mm = mmap(self.temp.fileno(), 4096)
335 mm.write(data)
336 mm.close()
337 return url
338
340 rep = self.call(res).next()
341 if 'error' in rep:
342 raise ServerError(rep)
343 return Id(rep['resource'])
344
345
347 dec = json.JSONDecoder(encoding='utf-8')
348 while True:
349 obj,p = dec.scan_once(r.text,p)
350 yield obj
351
353 if 'target' in js:
354 return arm.loads(js['target'])
355 else:
356 return None
357
359 if 'bil' in js:
360 return [bil.loads(s) for s in js['bil']]
361 else:
362 return None
363
367
369 return ' '.join(x.encode('hex') for x in data)
370