Repository: CERT-Polska/malduck (language: python)

Issue:
Missing fixed argument for multiplied IntType
```python
from malduck import UInt32

buf = b'a'*16

assert type(UInt32.unpack(buf)) is UInt32
assert type(UInt32.unpack(buf, fixed=False)) is int

assert type((UInt32*4).unpack(buf)[0]) is UInt32
# TypeError: unpack() got an unexpected keyword argument 'fixed'
assert type((UInt32*4).unpack(buf, fixed=False)[0]) is int  
```


Discussion / hints:

Fix patch:
diff --git a/malduck/ints.py b/malduck/ints.py
index 6804983..4b41d52 100644
--- a/malduck/ints.py
+++ b/malduck/ints.py
@@ -1,17 +1,6 @@
 from abc import ABCMeta, abstractmethod
 from struct import error, pack, unpack_from
-from typing import (
-    Any,
-    Callable,
-    Generic,
-    Iterator,
-    Optional,
-    Tuple,
-    Type,
-    TypeVar,
-    Union,
-    cast,
-)
+from typing import Any, Callable, Generic, Iterator, Tuple, Type, TypeVar, Union, cast
 
 from .bits import rol
 
@@ -49,7 +38,9 @@ class MultipliedIntTypeBase(IntTypeBase, Generic[T], metaclass=ABCMeta):
 
     @staticmethod
     @abstractmethod
-    def unpack(other: bytes, offset: int = 0) -> Optional[Tuple[T, ...]]:
+    def unpack(
+        other: bytes, offset: int = 0, foxed: bool = False
+    ) -> Union[Tuple[T, ...], int, None]:
         raise NotImplementedError()
 
 
@@ -87,7 +78,9 @@ class MultipliedIntTypeClass(MultipliedIntTypeBase):
             mul = multiplier
 
             @staticmethod
-            def unpack(other: bytes, offset: int = 0) -> Optional[Tuple[T, ...]]:
+            def unpack(
+                other: bytes, offset: int = 0, fixed: bool = True
+            ) -> Union[Tuple[T, ...], int, None]:
                 """
                 Unpacks multiple values from provided buffer
                 :param other: Buffer object containing value to unpack
@@ -99,6 +92,10 @@ def unpack(other: bytes, offset: int = 0) -> Optional[Tuple[T, ...]]:
                     ret = unpack_from(fmt, other, offset=offset)
                 except error:
                     return None
+
+                if not fixed:
+                    return tuple(ret)
+
                 ints: Iterator[T] = map(cls, ret)
                 return tuple(ints)
 


Test patch:
diff --git a/tests/test_ints.py b/tests/test_ints.py
index 6df1d5a..b72a836 100644
--- a/tests/test_ints.py
+++ b/tests/test_ints.py
@@ -113,6 +113,14 @@ def test_multi_unpack():
     assert (Int64 * 3).unpack(b"\x11\x22\x33\x44\xff\xff\xff\xff\x00\x00\x00\x00") is None
 
 
+def test_fixed():
+    assert type(UInt32.unpack(b'A'*16)) is UInt32
+    assert type(UInt32.unpack(b'A'*16, fixed=False)) is int
+
+    assert type((UInt32*4).unpack(b'A'*16)[0]) is UInt32
+    assert type((UInt32*4).unpack(b'A'*16, fixed=False)[0]) is int  
+
+
 def test_unpack_from():
     assert UInt32.unpack(b"\xAA\xAA\xF0\x0F\xF0\x0B\xAA", offset=2) == u32(b"\xF0\x0F\xF0\x0B")
     assert UInt32.unpack(b"\xAA\xAA\xF0\x0F\xF0\x0B", offset=2) == u32(b"\xF0\x0F\xF0\x0B")