diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/analysis/__init__.py | 0 | ||||
-rw-r--r-- | tests/analysis/contents/__init__.py | 0 | ||||
-rw-r--r-- | tests/analysis/contents/restricted.py | 141 | ||||
-rw-r--r-- | tests/arch/__init__.py | 0 | ||||
-rwxr-xr-x | tests/arch/vmpa.py | 35 | ||||
-rw-r--r-- | tests/chrysacase.py | 48 | ||||
-rw-r--r-- | tests/format/__init__.py | 0 | ||||
-rw-r--r-- | tests/format/elf/Makefile | 5 | ||||
-rw-r--r-- | tests/format/elf/__init__.py | 0 | ||||
-rw-r--r-- | tests/format/elf/non_existing_binary.py | 20 | ||||
-rw-r--r-- | tests/format/elf/oob_section_name.py | 36 | ||||
-rwxr-xr-x | tests/run.sh | 8 | ||||
-rw-r--r-- | tests/test.py | 39 |
13 files changed, 256 insertions, 76 deletions
diff --git a/tests/analysis/__init__.py b/tests/analysis/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/analysis/__init__.py diff --git a/tests/analysis/contents/__init__.py b/tests/analysis/contents/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/analysis/contents/__init__.py diff --git a/tests/analysis/contents/restricted.py b/tests/analysis/contents/restricted.py new file mode 100644 index 0000000..e8d3e07 --- /dev/null +++ b/tests/analysis/contents/restricted.py @@ -0,0 +1,141 @@ +#!/usr/bin/python3-dbg +# -*- coding: utf-8 -*- + + +# Tests minimalistes pour valider l'intégration des contenus restreints +# depuis Python. + + +from chrysacase import ChrysalideTestCase +from pychrysalide.analysis.contents import FileContent, RestrictedContent +from pychrysalide.arch import vmpa, mrange +import tempfile + + +class TestRestrictedContent(ChrysalideTestCase): + """TestCase for analysis.contents.RestrictedContent.""" + + @classmethod + def setUpClass(cls): + + super(TestRestrictedContent, cls).setUpClass() + + cls._out = tempfile.NamedTemporaryFile() + + cls._out.write(b'\x01\x02\x03\x04') + cls._out.write(b'\x05\x06\x07\x08') + cls._out.write(b'\x11\x12\x13\x14') + cls._out.write(b'\x15\x16\x17\x18') + cls._out.write(b'\x21\x22\x23\x24') + cls._out.write(b'\x25\x26\x27\x28') + cls._out.write(b'\x31\x32\x33\x34') + cls._out.write(b'\x35\x36\x37\x38') + + cls._out.flush() + + cls.log('Using temporary file "%s"' % cls._out.name) + + + @classmethod + def tearDownClass(cls): + + super(TestRestrictedContent, cls).tearDownClass() + + cls.log('Delete file "%s"' % cls._out.name) + + cls._out.close() + + + def testReadAccess(self): + """Check valid accesses to restricted content.""" + + fcnt = FileContent(self._out.name) + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + covered = mrange(start, 12) # 0x15 ... 0x28 + + rcnt = RestrictedContent(fcnt, covered) + self.assertIsNotNone(rcnt) + + val = rcnt.read_u8(start) + self.assertEqual(val, b'\x15') + + val = rcnt.read_u8(start) + self.assertEqual(val, b'\x16') + + val = rcnt.read_u16(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x17\x18') + + val = rcnt.read_u32(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x21\x22\x23\x24') + + + def testBorderLineAccess(self): + """Check valid border line accesses to restricted content.""" + + fcnt = FileContent(self._out.name) + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + covered = mrange(start, 12) # 0x15 ... 0x28 + + rcnt = RestrictedContent(fcnt, covered) + self.assertIsNotNone(rcnt) + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u8(start) + self.assertEqual(val, b'\x15') + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u16(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x15\x16') + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u32(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x15\x16\x17\x18') + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u64(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x15\x16\x17\x18\x21\x22\x23\x24') + + start = vmpa(23, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u8(start) + self.assertEqual(val, b'\x28') + + start = vmpa(22, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u16(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x27\x28') + + start = vmpa(20, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u32(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x25\x26\x27\x28') + + start = vmpa(16, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u64(start, vmpa.SRE_LITTLE) + self.assertEqual(val, b'\x21\x22\x23\x24\x25\x26\x27\x28') + + + def testWrongAccess(self): + """Check invalid accesses to restricted content.""" + + fcnt = FileContent(self._out.name) + + start = vmpa(12, vmpa.VMPA_NO_VIRTUAL) + covered = mrange(start, 12) # 0x15 ... 0x28 + + rcnt = RestrictedContent(fcnt, covered) + self.assertIsNotNone(rcnt) + + with self.assertRaisesRegex(Exception, 'Invalid read access.'): + + start = vmpa(1, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u8(start) + + with self.assertRaisesRegex(Exception, 'Invalid read access.'): + + start = vmpa(11, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u16(start, vmpa.SRE_LITTLE) + + with self.assertRaisesRegex(Exception, 'Invalid read access.'): + + start = vmpa(23, vmpa.VMPA_NO_VIRTUAL) + val = rcnt.read_u16(start, vmpa.SRE_LITTLE) diff --git a/tests/arch/__init__.py b/tests/arch/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/arch/__init__.py diff --git a/tests/arch/vmpa.py b/tests/arch/vmpa.py index 9814b20..def61ea 100755 --- a/tests/arch/vmpa.py +++ b/tests/arch/vmpa.py @@ -1,32 +1,35 @@ #!/usr/bin/python3-dbg # -*- coding: utf-8 -*- -import pychrysalide -from pychrysalide.arch import vmpa -from test import TestSuite + +# Tests minimalistes pour valider l'intégration des adresses et espaces mémoire +# depuis Python. -######################## +from chrysacase import ChrysalideTestCase +from pychrysalide.arch import vmpa -TestSuite.print_sep() -addr = vmpa() +class TestVmpa(ChrysalideTestCase): + """TestCase for arch.vmpa.""" -print('repr():', repr(addr)) -print('str(): ', str(addr)) + def testInit(self): + """VMPA values are left uninitialized by default.""" -######################## + v = vmpa() -TestSuite.print_sep() + self.assertIsNone(v.phys) + self.assertIsNone(v.virt) -TestSuite.check_true('Create a virtual memory or physical address', lambda: vmpa()) -v = vmpa() + def testAdd(self): + """Verify the commutative property of addition.""" -TestSuite.check_true('VMPA values are left uninitialized by default', lambda: v.phy == None and v.virt == None) + a = vmpa(0, 0) + 1 -a = vmpa(0, 0) + 1 + b = 1 + vmpa(0, 0) -b = 1 + vmpa(0, 0) + c = vmpa(1, 1) -TestSuite.check_true('Verify the commutative property of addition', lambda: a == b) + self.assertEqual(a, b) + self.assertEqual(a, c) diff --git a/tests/chrysacase.py b/tests/chrysacase.py new file mode 100644 index 0000000..26625e7 --- /dev/null +++ b/tests/chrysacase.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3-dbg +# -*- coding: utf-8 -*- + + +import unittest +import os +import sys + + +class ChrysalideTestCase(unittest.TestCase): + """Base class for all Chrysalide test cases.""" + + @classmethod + def setUpClass(cls): + + fullname = sys.modules[ChrysalideTestCase.__module__].__file__ + filename = os.path.basename(fullname) + + cls._baselen = len(fullname) - len(filename) + + + def shortDescription(self): + """Discard the direct use of __doc__ strings.""" + + return None + + + def __str__(self): + """Display the description of the current tested case.""" + + fullname = sys.modules[self.__class__.__module__].__file__ + + origin = fullname[self._baselen:] + + title = self._testMethodDoc and self._testMethodDoc or self._testMethodName + + return '[%s] "%s"' % (origin, title) + + + @classmethod + def log(cls, msg): + """Display an information message.""" + + fullname = sys.modules[cls.__module__].__file__ + + origin = fullname[cls._baselen:] + + print('[%s] *** %s ***' % (origin, msg)) diff --git a/tests/format/__init__.py b/tests/format/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/format/__init__.py diff --git a/tests/format/elf/Makefile b/tests/format/elf/Makefile index b14ff47..c32392f 100644 --- a/tests/format/elf/Makefile +++ b/tests/format/elf/Makefile @@ -1,11 +1,8 @@ -EXECUTABLES=tiny oob_section_name +EXECUTABLES=oob_section_name all: $(EXECUTABLES) -tiny: tiny.o - $(ARM_CROSS)objcopy $< -O binary $@ - oob_section_name: oob_section_name.o $(ARM_CROSS)objcopy $< -O binary $@ diff --git a/tests/format/elf/__init__.py b/tests/format/elf/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/format/elf/__init__.py diff --git a/tests/format/elf/non_existing_binary.py b/tests/format/elf/non_existing_binary.py index 47c9028..6111f03 100644 --- a/tests/format/elf/non_existing_binary.py +++ b/tests/format/elf/non_existing_binary.py @@ -5,22 +5,20 @@ # Eprouve quelques mécanismes de construction côté Python. -import pychrysalide - +from chrysacase import ChrysalideTestCase from pychrysalide.analysis.contents import FileContent from pychrysalide.format.elf import ElfFormat -cnt = FileContent("non_existing_binary") -print(cnt) +class TestNonExistingBinary(ChrysalideTestCase): + """TestCase for non existent binary loading.""" -print(cnt == None) + def testNonExistent(self): + """Try to load a non existent binary without crashing.""" -try: - fmt = ElfFormat(cnt) -except TypeError as e: - fmt = None + cnt = FileContent('non_existing_binary') + self.assertIsNone(cnt) -print(fmt) + with self.assertRaisesRegex(TypeError, 'The argument must be an instance of BinContent.'): -print(fmt == None) + fmt = ElfFormat(cnt) diff --git a/tests/format/elf/oob_section_name.py b/tests/format/elf/oob_section_name.py index da58e29..8f91efd 100644 --- a/tests/format/elf/oob_section_name.py +++ b/tests/format/elf/oob_section_name.py @@ -10,15 +10,39 @@ # lors de l'accès concret, au moment de l'appel à strlen(). -import pychrysalide - +from chrysacase import ChrysalideTestCase from pychrysalide.analysis.contents import FileContent from pychrysalide.format.elf import ElfFormat +import os +import sys + + +class TestNonExistingBinary(ChrysalideTestCase): + """TestCase for corrupted ELF binaries with wrong section names.""" + + @classmethod + def setUpClass(cls): + + super(TestNonExistingBinary, cls).setUpClass() + + cls.log('Compile binary "oob_section_name" if needed...') + + fullname = sys.modules[cls.__module__].__file__ + dirpath = os.path.dirname(fullname) + + os.system('make -C %s oob_section_name 2>&1 > /dev/null' % dirpath) + + + def testOOBSectionName(self): + """Avoid crashing when dealing with OutOfBound section names.""" -cnt = FileContent("oob_section_name") + fullname = sys.modules[self.__class__.__module__].__file__ + filename = os.path.basename(fullname) -fmt = ElfFormat(cnt) + baselen = len(fullname) - len(filename) -print(fmt) + cnt = FileContent(fullname[:baselen] + 'oob_section_name') + self.assertIsNotNone(cnt) -print(isinstance(fmt, ElfFormat)) + fmt = ElfFormat(cnt) + self.assertIsInstance(fmt, ElfFormat) diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..24f586d --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ -z "$ARM_CROSS" ]; then + echo "ARM_CROSS is not set!" + exit 1 +fi + +LANG=C python3 -m unittest discover -v -p '*py' diff --git a/tests/test.py b/tests/test.py deleted file mode 100644 index 895c4f6..0000000 --- a/tests/test.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - - -class TestSuite: - - - - - - - - - def print_sep(): - """Print a separator line.""" - - print('------------------------') - - - def check_true(desc, code): - """Check if an expression is true.""" - - try: - test = code() - except: - test = False - - if test: - print('[+] %s: OK' % desc) - else: - print('[+] %s: nok...' % desc) - raise Exception('Unexpected result!') - - - - - - |