Skip to content

Commit 5684bdd

Browse files
authored
Code cleanup and coverage (#1035)
* dead code elimination, __init__ cleanup * `binary.Elf` bugfix, add `binary` package tests
1 parent e6833ab commit 5684bdd

File tree

10 files changed

+154
-172
lines changed

10 files changed

+154
-172
lines changed

.coveragerc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[run]
2-
source = .
3-
omit = tests/*
2+
source = manticore
3+
omit =
4+
*__init__.py

manticore/binary/__init__.py

Lines changed: 1 addition & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -15,124 +15,7 @@
1515
1616
'''
1717

18-
from elftools.elf.elffile import ELFFile
19-
import io
20-
21-
22-
class Binary:
23-
magics = {}
24-
25-
def __new__(cls, path):
26-
if cls is Binary:
27-
cl = cls.magics[open(path, 'rb').read(4)]
28-
return cl(path)
29-
else:
30-
return super(Binary, cls).__new__(cls)
31-
32-
def __init__(self, path):
33-
self.path = path
34-
self.magic = Binary.magics[open(path, 'rb').read(4)]
35-
36-
def arch(self):
37-
pass
38-
39-
def maps(self):
40-
pass
41-
42-
def threads(self):
43-
pass
44-
45-
46-
class CGCElf(Binary):
47-
48-
@staticmethod
49-
def _cgc2elf(filename):
50-
# hack begin so we can use upstream Elftool
51-
with open(filename, 'rb') as fd:
52-
stream = io.BytesIO(fd.read())
53-
stream.write(b'\x7fELF')
54-
stream.name = fd.name
55-
return stream
56-
57-
def __init__(self, filename):
58-
super().__init__(filename)
59-
stream = self._cgc2elf(filename)
60-
self.elf = ELFFile(stream)
61-
self.arch = {'x86': 'i386', 'x64': 'amd64'}[self.elf.get_machine_arch()]
62-
63-
assert 'i386' == self.arch
64-
assert self.elf.header.e_type in ['ET_EXEC']
65-
66-
def maps(self):
67-
for elf_segment in self.elf.iter_segments():
68-
if elf_segment.header.p_type not in ['PT_LOAD', 'PT_NULL', 'PT_PHDR', 'PT_CGCPOV2']:
69-
raise Exception("Not Supported Section")
70-
71-
if elf_segment.header.p_type != 'PT_LOAD' or elf_segment.header.p_memsz == 0:
72-
continue
73-
74-
flags = elf_segment.header.p_flags
75-
# PF_X 0x1 Execute - PF_W 0x2 Write - PF_R 0x4 Read
76-
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags & 7]
77-
if 'r' not in perms:
78-
raise Exception("Not readable map from cgc elf not supported")
79-
80-
# CGCMAP--
81-
assert elf_segment.header.p_filesz != 0 or elf_segment.header.p_memsz != 0
82-
yield((elf_segment.header.p_vaddr,
83-
elf_segment.header.p_memsz,
84-
perms,
85-
elf_segment.stream.name, elf_segment.header.p_offset, elf_segment.header.p_filesz))
86-
87-
def threads(self):
88-
yield(('Running', {'EIP': self.elf.header.e_entry}))
89-
90-
91-
class Elf(Binary):
92-
def __init__(self, filename):
93-
super().__init__(filename)
94-
self.elf = ELFFile(open(filename))
95-
self.arch = {'x86': 'i386', 'x64': 'amd64'}[self.elf.get_machine_arch()]
96-
assert self.elf.header.e_type in ['ET_DYN', 'ET_EXEC', 'ET_CORE']
97-
98-
# Get interpreter elf
99-
self.interpreter = None
100-
for elf_segment in self.elf.iter_segments():
101-
if elf_segment.header.p_type != 'PT_INTERP':
102-
continue
103-
self.interpreter = Elf(elf_segment.data()[:-1])
104-
break
105-
if self.interpreter is not None:
106-
assert self.interpreter.arch == self.arch
107-
assert self.interpreter.elf.header.e_type in ['ET_DYN', 'ET_EXEC']
108-
109-
def maps(self):
110-
for elf_segment in self.elf.iter_segments():
111-
if elf_segment.header.p_type != 'PT_LOAD' or elf_segment.header.p_memsz == 0:
112-
continue
113-
114-
flags = elf_segment.header.p_flags
115-
# PF_X 0x1 Execute - PF_W 0x2 Write - PF_R 0x4 Read
116-
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags & 7]
117-
if 'r' not in perms:
118-
raise Exception("Not readable map from cgc elf not supported")
119-
120-
# CGCMAP--
121-
assert elf_segment.header.p_filesz != 0 or elf_segment.header.p_memsz != 0
122-
yield((elf_segment.header.p_vaddr,
123-
elf_segment.header.p_memsz,
124-
perms,
125-
elf_segment.stream.name, elf_segment.header.p_offset, elf_segment.header.p_filesz))
126-
127-
def getInterpreter(self):
128-
return self.interpreter
129-
130-
def threads(self):
131-
yield(('Running', {'EIP': self.elf.header.e_entry}))
132-
133-
134-
Binary.magics = {b'\x7fCGC': CGCElf,
135-
b'\x7fELF': Elf}
18+
from .binary import Binary, CGCElf, Elf # noqa
13619

13720

13821
if __name__ == '__main__':

manticore/binary/binary.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import io
2+
3+
from elftools.elf.elffile import ELFFile
4+
5+
6+
class Binary:
7+
magics = {}
8+
9+
def __new__(cls, path):
10+
if cls is Binary:
11+
cl = cls.magics[open(path, 'rb').read(4)]
12+
return cl(path)
13+
else:
14+
return super(Binary, cls).__new__(cls)
15+
16+
def __init__(self, path):
17+
self.path = path
18+
self.magic = Binary.magics[open(path, 'rb').read(4)]
19+
20+
def arch(self):
21+
pass
22+
23+
def maps(self):
24+
pass
25+
26+
def threads(self):
27+
pass
28+
29+
30+
class CGCElf(Binary):
31+
32+
@staticmethod
33+
def _cgc2elf(filename):
34+
# hack begin so we can use upstream Elftool
35+
with open(filename, 'rb') as fd:
36+
stream = io.BytesIO(fd.read())
37+
stream.write(b'\x7fELF')
38+
stream.name = fd.name
39+
return stream
40+
41+
def __init__(self, filename):
42+
super().__init__(filename)
43+
stream = self._cgc2elf(filename)
44+
self.elf = ELFFile(stream)
45+
self.arch = {'x86': 'i386', 'x64': 'amd64'}[self.elf.get_machine_arch()]
46+
47+
assert 'i386' == self.arch
48+
assert self.elf.header.e_type in ['ET_EXEC']
49+
50+
def maps(self):
51+
for elf_segment in self.elf.iter_segments():
52+
if elf_segment.header.p_type not in ['PT_LOAD', 'PT_NULL', 'PT_PHDR', 'PT_CGCPOV2']:
53+
raise Exception("Not Supported Section")
54+
55+
if elf_segment.header.p_type != 'PT_LOAD' or elf_segment.header.p_memsz == 0:
56+
continue
57+
58+
flags = elf_segment.header.p_flags
59+
# PF_X 0x1 Execute - PF_W 0x2 Write - PF_R 0x4 Read
60+
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags & 7]
61+
if 'r' not in perms:
62+
raise Exception("Not readable map from cgc elf not supported")
63+
64+
# CGCMAP--
65+
assert elf_segment.header.p_filesz != 0 or elf_segment.header.p_memsz != 0
66+
yield ((elf_segment.header.p_vaddr,
67+
elf_segment.header.p_memsz,
68+
perms,
69+
elf_segment.stream.name, elf_segment.header.p_offset, elf_segment.header.p_filesz))
70+
71+
def threads(self):
72+
yield (('Running', {'EIP': self.elf.header.e_entry}))
73+
74+
75+
class Elf(Binary):
76+
def __init__(self, filename):
77+
super().__init__(filename)
78+
self.elf = ELFFile(open(filename, 'rb'))
79+
self.arch = {'x86': 'i386', 'x64': 'amd64'}[self.elf.get_machine_arch()]
80+
assert self.elf.header.e_type in ['ET_DYN', 'ET_EXEC', 'ET_CORE']
81+
82+
# Get interpreter elf
83+
self.interpreter = None
84+
for elf_segment in self.elf.iter_segments():
85+
if elf_segment.header.p_type != 'PT_INTERP':
86+
continue
87+
self.interpreter = Elf(elf_segment.data()[:-1])
88+
break
89+
if self.interpreter is not None:
90+
assert self.interpreter.arch == self.arch
91+
assert self.interpreter.elf.header.e_type in ['ET_DYN', 'ET_EXEC']
92+
93+
def maps(self):
94+
for elf_segment in self.elf.iter_segments():
95+
if elf_segment.header.p_type != 'PT_LOAD' or elf_segment.header.p_memsz == 0:
96+
continue
97+
98+
flags = elf_segment.header.p_flags
99+
# PF_X 0x1 Execute - PF_W 0x2 Write - PF_R 0x4 Read
100+
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags & 7]
101+
if 'r' not in perms:
102+
raise Exception("Not readable map from cgc elf not supported")
103+
104+
# CGCMAP--
105+
assert elf_segment.header.p_filesz != 0 or elf_segment.header.p_memsz != 0
106+
yield ((elf_segment.header.p_vaddr,
107+
elf_segment.header.p_memsz,
108+
perms,
109+
elf_segment.stream.name, elf_segment.header.p_offset, elf_segment.header.p_filesz))
110+
111+
def getInterpreter(self):
112+
return self.interpreter
113+
114+
def threads(self):
115+
yield (('Running', {'EIP': self.elf.header.e_entry}))
116+
117+
118+
Binary.magics = {b'\x7fCGC': CGCElf,
119+
b'\x7fELF': Elf}

manticore/core/smtlib/__init__.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,3 @@
66

77
import logging
88
logger = logging.getLogger(__name__)
9-
10-
'''
11-
class OperationNotPermited(SolverException):
12-
def __init__(self):
13-
super().__init__("You cant build this expression") #no childrens
14-
15-
class ConcretizeException(SolverException):
16-
def __init__(self, expression):
17-
super().__init__("Need to concretize the following and retry\n"+str(expression)) #no childrens
18-
self.expression = expression
19-
'''
20-
21-
22-
class VisitorException(Exception):
23-
pass

manticore/core/smtlib/solver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import shlex
2424
import time
2525
from .visitors import *
26-
from ...utils.helpers import issymbolic, istainted, taint_with, get_taints, memoized
26+
from ...utils.helpers import issymbolic, istainted, taint_with, get_taints
2727
import io
2828
import collections
2929

manticore/platforms/decree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ..core.smtlib import *
88
from ..core.executor import TerminateState
99
from ..utils.helpers import issymbolic
10-
from ..binary import CGCElf
10+
from manticore.binary import CGCElf
1111
from ..platforms.platform import Platform
1212
import logging
1313
import random

manticore/platforms/evm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import copy
44
import inspect
55
from functools import wraps
6-
from ..utils.helpers import issymbolic, memoized, get_taints, taint_with, istainted
6+
from ..utils.helpers import issymbolic, get_taints, taint_with, istainted
77
from ..platforms.platform import *
88
from ..core.smtlib import solver, BitVec, Array, Operators, Constant, ArrayVariable, BitVecConstant, translate_to_smtlib
99
from ..core.state import Concretize, TerminateState

manticore/utils/helpers.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,38 +72,6 @@ def taint_with(arg, taint, value_bits=256, index_bits=256):
7272
return arg
7373

7474

75-
class memoized(object):
76-
'''Decorator. Caches a function's return value each time it is called.
77-
If called later with the same arguments, the cached value is returned
78-
(not reevaluated).
79-
'''
80-
81-
def __init__(self, func):
82-
self.func = func
83-
self.cache = {}
84-
85-
def __call__(self, *args, **kwargs):
86-
key = args + tuple(sorted(kwargs.items()))
87-
if not isinstance(key, collections.Hashable):
88-
# uncacheable. a list, for instance.
89-
# better to not cache than blow up.
90-
return self.func(*args, **kwargs)
91-
if key in self.cache:
92-
return self.cache[key]
93-
else:
94-
value = self.func(*args, **kwargs)
95-
self.cache[key] = value
96-
return value
97-
98-
def __repr__(self):
99-
'''Return the function's docstring.'''
100-
return self.func.__doc__
101-
102-
def __get__(self, obj, objtype):
103-
'''Support instance methods.'''
104-
return functools.partial(self.__call__, obj)
105-
106-
10775
class CacheDict(OrderedDict):
10876
def __init__(self, *args, max_size=30000, flush_perc=30, **kwargs):
10977
self._max_size = max_size

tests/test_binaries.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,37 @@
77
import hashlib
88
import subprocess
99
import time
10+
from manticore.binary import Elf, CGCElf
1011

1112
#logging.basicConfig(filename = "test.log",
1213
# format = "%(asctime)s: %(name)s:%(levelname)s: %(message)s",
1314
# level = logging.DEBUG)
1415

16+
17+
class TestBinaryPackage(unittest.TestCase):
18+
_multiprocess_can_split_ = True
19+
20+
def test_elf(self):
21+
filename = os.path.join(os.path.dirname(__file__), 'binaries', 'basic_linux_amd64')
22+
f = Elf(filename)
23+
self.assertTrue(
24+
[(4194304, 823262, 'r x', 'tests/binaries/basic_linux_amd64', 0, 823262),
25+
(7118520, 16112, 'rw ', 'tests/binaries/basic_linux_amd64', 827064, 7320)],
26+
list(f.maps())
27+
)
28+
self.assertTrue([('Running', {'EIP': 4196624})], list(f.threads()))
29+
self.assertIsNone(f.getInterpreter())
30+
31+
def test_decree(self):
32+
filename = os.path.join(os.path.dirname(__file__), 'binaries', 'cadet_decree_x86')
33+
f = CGCElf(filename)
34+
self.assertTrue(
35+
[(134512640, 1478, 'r x', 'tests/binaries/cadet_decree_x86', 0, 1478)],
36+
list(f.maps())
37+
)
38+
self.assertTrue([('Running', {'EIP': 134513708})], list(f.threads()))
39+
40+
1541
class IntegrationTest(unittest.TestCase):
1642
_multiprocess_can_split_ = True
1743
def setUp(self):

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ envlist = py3{6,7}
44
[testenv]
55
deps =
66
.[dev]
7-
commands = nosetests
7+
commands = nosetests tests
88
install_command = pip install --no-binary keystone-engine {opts} {packages}
99

1010
[testenv:pep8]
@@ -20,4 +20,4 @@ exclude = docs/,examples/,scripts/,tests/
2020
[flake8]
2121
ignore = E265,E501,F403,F405,E266,E712,F841,E741,E722,E731
2222
max-line-length = 160
23-
exclude = .tox,.*.egg,.git,docs/,examples/,scripts/,tests/
23+
exclude = .tox,.*.egg,.git,docs/,examples/,scripts/,tests/,parsetab.py,iterpickle.py

0 commit comments

Comments
 (0)