speakeasy/tests/test_struct.py
2020-08-21 17:05:28 -04:00

152 lines
5.6 KiB
Python

# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
import unittest
import ctypes as ct
from speakeasy.struct import EmuStruct, Ptr
# These are our ground truth test cases
EXPECTED_32BIT_BYTES = b'\x01\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x05\x05' \
b'\x06\x06\x06\x06\x07\x07\x07\x07AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
b'\x08\x08\t\t\t\t\n\n\n\nBBBBBBBBBBBBBBBBBBBBBBBB'
EXPECTED_64BIT_BYTES = b'\x01\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04\x05\x05'\
b'\x06\x06\x06\x06\x07\x07\x07\x07AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
b'\x08\x08\t\t\t\t\t\t\t\t\n\n\n\nBBBBBBBBBBBBBBBBBBBBBBBB'
class DEEP_NEST(EmuStruct):
# Struct nested 2 levels deep
def __init__(self, ptr_size):
super().__init__(ptr_size)
self.Field1 = ct.c_uint32
self.Field2 = ct.c_uint32
self.DeepData = ct.c_uint8 * 32
class SHALLOW_NEST(EmuStruct):
# Struct nested 1 level deep
def __init__(self, ptr_size):
super().__init__(ptr_size, pack=1)
self.Field1 = ct.c_uint16
self.DeepStruct = DEEP_NEST
self.Field2 = ct.c_uint16
class TOP_OBJECT(EmuStruct):
# Top level struct containing nested structs
def __init__(self, ptr_size):
super().__init__(ptr_size=ptr_size, pack=1)
self.Field1 = ct.c_uint16
self.Field2 = ct.c_uint16
self.Field3 = ct.c_uint32
self.Field4 = Ptr # Ptr fields allow you to dynamically create pointer-sized fields
self.NestedStruct = SHALLOW_NEST
self.Field5 = Ptr
self.Field6 = ct.c_uint32
self.Field7 = ct.c_uint8 * 24
class TestStruct(unittest.TestCase):
def test_32bit_emit(self):
'''
Make sure we can convert arbitrary bytes to a speakeasy emu struct and vice versa
'''
# Create a TOP_OBJECT struct with a 32-bit pointer size
ptr_size = 4
top = TOP_OBJECT(ptr_size)
top.Field1 = 0x0101
top.Field2 = 0x0202
top.Field3 = 0x03030303
top.Field4 = 0x04040404
top.NestedStruct.Field1 = 0x0505
top.NestedStruct.DeepStruct.Field1 = 0x06060606
top.NestedStruct.DeepStruct.Field2 = 0x07070707
top.NestedStruct.DeepStruct.DeepData = b'A' * 32
top.NestedStruct.Field2 = 0x0808
top.Field5 = 0x09090909
top.Field6 = 0x0a0a0a0a
top.Field7 = b'B' * 24
bytez = top.get_bytes()
self.assertEqual(bytez, EXPECTED_32BIT_BYTES)
def test_32bit_cast(self):
'''
Now lets flip the same test, bytes should be casted into an equally correct 32bit structure
'''
# Create a TOP_OBJECT struct with a 32-bit pointer size
ptr_size = 4
top = TOP_OBJECT(ptr_size)
# "cast" raw bytes into a python structure for easy mutation
top.cast(EXPECTED_32BIT_BYTES)
# Make sure the struct fields are what we expect
self.assertEqual(top.Field1, 0x0101)
self.assertEqual(top.Field2, 0x0202)
self.assertEqual(top.Field3, 0x03030303)
self.assertEqual(top.Field4, 0x04040404)
self.assertEqual(top.NestedStruct.Field1, 0x0505)
self.assertEqual(top.NestedStruct.DeepStruct.Field1, 0x06060606)
self.assertEqual(top.NestedStruct.DeepStruct.Field2, 0x07070707)
self.assertEqual(bytes(top.NestedStruct.DeepStruct.DeepData[:]), b'A' * 32)
self.assertEqual(top.NestedStruct.Field2, 0x0808)
self.assertEqual(top.Field5, 0x09090909)
self.assertEqual(top.Field6, 0x0a0a0a0a)
self.assertEqual(bytes(top.Field7[:]), b'B' * 24)
def test_64bit_emit(self):
'''
Make sure we can convert arbitrary bytes to a speakeasy emu struct and vice versa
'''
# Create a TOP_OBJECT struct with a 64-bit pointer size
ptr_size = 8
top = TOP_OBJECT(ptr_size)
top.Field1 = 0x0101
top.Field2 = 0x0202
top.Field3 = 0x03030303
top.Field4 = 0x0404040404040404
top.NestedStruct.Field1 = 0x0505
top.NestedStruct.DeepStruct.Field1 = 0x06060606
top.NestedStruct.DeepStruct.Field2 = 0x07070707
top.NestedStruct.DeepStruct.DeepData = b'A' * 32
top.NestedStruct.Field2 = 0x0808
top.Field5 = 0x0909090909090909
top.Field6 = 0x0a0a0a0a
top.Field7 = b'B' * 24
bytez = top.get_bytes()
self.assertEqual(bytez, EXPECTED_64BIT_BYTES)
def test_64bit_cast(self):
'''
Now lets flip the same test, bytes should be casted into an equally correct 64bit structure
'''
# Create a TOP_OBJECT struct with a 64-bit pointer size
ptr_size = 8
top = TOP_OBJECT(ptr_size)
# "cast" raw bytes into a python structure for easy mutation
top.cast(EXPECTED_64BIT_BYTES)
# Make sure the struct fields are what we expect
self.assertEqual(top.Field1, 0x0101)
self.assertEqual(top.Field2, 0x0202)
self.assertEqual(top.Field3, 0x03030303)
self.assertEqual(top.Field4, 0x0404040404040404)
self.assertEqual(top.NestedStruct.Field1, 0x0505)
self.assertEqual(top.NestedStruct.DeepStruct.Field1, 0x06060606)
self.assertEqual(top.NestedStruct.DeepStruct.Field2, 0x07070707)
self.assertEqual(bytes(top.NestedStruct.DeepStruct.DeepData[:]), b'A' * 32)
self.assertEqual(top.NestedStruct.Field2, 0x0808)
self.assertEqual(top.Field5, 0x0909090909090909)
self.assertEqual(top.Field6, 0x0a0a0a0a)
self.assertEqual(bytes(top.Field7[:]), b'B' * 24)
if __name__ == '__main__':
unittest.main()