mirror of
https://github.com/vee1e/speakeasy.git
synced 2026-09-01 10:59:07 +00:00
152 lines
5.6 KiB
Python
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()
|