From 3f062dbf20626aa7e3885ec9e1383a526b2199b7 Mon Sep 17 00:00:00 2001 From: Axel Tillequin Date: Fri, 11 Feb 2022 17:09:50 +0100 Subject: [PATCH] version 2.9.7: * drop python <3.8 support * improve structs package for bitfield support and computation of fields' offsets for chosen pointer size (32 or 64 bits) --- .travis.yml | 2 - README.rst | 9 +- amoco/sa/ghidra.py | 21 ++-- amoco/system/baremetal/imx6.py | 14 +-- amoco/system/baremetal/psx.py | 2 +- amoco/system/elf.py | 4 +- amoco/system/fs/ufs.py | 6 +- amoco/system/pe.py | 2 +- amoco/system/structs/__init__.py | 11 +- amoco/system/structs/core.py | 53 ++++++--- amoco/system/structs/fields.py | 192 ++++++++++++++++++++++--------- amoco/system/vm/wasm.py | 46 ++++---- doc/index.rst | 2 +- setup.py | 4 +- tests/test_system_structs.py | 57 ++++++++- 15 files changed, 294 insertions(+), 131 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1043cfe..364df71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: python python: - - '3.6' - - '3.7' - '3.8' branches: only: diff --git a/README.rst b/README.rst index a91b66c..c3cf026 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ Amoco Description =========== -Amoco is a python (>=3.7) package dedicated to symbolic analysis of binaries. +Amoco is a python (>=3.8) package dedicated to symbolic analysis of binaries. It features: @@ -96,6 +96,12 @@ Please see `LICENSE`_. Changelog ========= +- `v2.9.7`_ + + * drop python <3.8 support + * improve structs package for bitfield support and computation of + fields' offsets for chosen pointer size (32 or 64 bits) + - `v2.9.6`_ * merge Qt GUI ImageView @@ -385,6 +391,7 @@ Changelog .. _sqlalchemy: http://www.sqlalchemy.org .. _QDarkStyleSheet: https://github.com/ColinDuquesnoy/QDarkStyleSheet .. _LICENSE: https://github.com/bdcht/amoco/blob/release/LICENSE +.. _v2.9.7: https://github.com/bdcht/amoco/releases/tag/v2.9.7 .. _v2.9.6: https://github.com/bdcht/amoco/releases/tag/v2.9.6 .. _v2.9.5: https://github.com/bdcht/amoco/releases/tag/v2.9.5 .. _v2.9.4: https://github.com/bdcht/amoco/releases/tag/v2.9.4 diff --git a/amoco/sa/ghidra.py b/amoco/sa/ghidra.py index 46d441e..0148fe4 100644 --- a/amoco/sa/ghidra.py +++ b/amoco/sa/ghidra.py @@ -3,7 +3,8 @@ b = ghidra_bridge.GhidraBridge(namespace=__module__.__dict__) def select_range(begin,end): - from ghidra.program.model.address import setCurrentSelection, AddressSet + setCurrentSelection = ghidra.program.model.address.setCurrentSelection + AddressSet = ghidra.program.model.address.AddressSet setCurrentSelection(AddressSet(toAddr(begin),toAddr(end))) def add_memory_block(name,start,size,val=None,access="rw"): @@ -19,21 +20,21 @@ def add_memory_block(name,start,size,val=None,access="rw"): return blk def setPointer(address,size=4): - from ghidra.program.model.data import PointerDataType + PointerDataType = ghidra.program.model.data.PointerDataType if isinstance(address,int): address = toAddr(address) ls = currentProgram.getListing() ls.createData(address, PointerDataType.dataType, size) def setFunctionName(address,name): - from ghidra.program.model.symbol.SourceType import USER_DEFINED + USER_DEFINED = ghidra.program.model.symbol.SourceType.USER_DEFINED if isinstance(address,int): address = toAddr(address) f = getFunctionAt(address) f.setName(name, USER_DEFINED) def create_labels(labels): - from ghidra.program.model.symbol.SourceType import USER_DEFINED + USER_DEFINED = ghidra.program.model.symbol.SourceType.USER_DEFINED sym = currentProgram.symbolTable for a,r in labels.items(): if isinstance(a,int): @@ -41,13 +42,10 @@ def create_labels(labels): sym.createLabel(a, r, USER_DEFINED) def get_decompiled(func_name): - from ghidra.app.decompiler import DecompileOptions - from ghidra.app.decompiler import DecompInterface - from ghidra.util.task import ConsoleTaskMonitor func = getGlobalFunctions(func_name)[0] - options = DecompileOptions() - monitor = ConsoleTaskMonitor() - ifc = DecompInterface() + options = ghidra.app.decompiler.DecompileOptions() + monitor = ghidra.util.task.ConsoleTaskMonitor() + ifc = ghidra.app.decompiler.DecompInterface() ifc.setOptions(options) ifc.openProgram(func.getProgram()) res = ifc.decompileFunction(func, 1000, monitor) @@ -79,11 +77,10 @@ def get_decompiled_symbols(func_name): print(" nameLocked: {}".format(symbol.nameLocked)) def get_ast_nodes(func_name): - from ghidra.app.decompiler import ClangStatement res = get_decompiled(func_name) # get (decompiled) high-function object from res: def walk(node,L): - if type(node) == ClangStatement: + if type(node) == ghidra.app.decompiler.ClangStatement: L.append(node) else: for i in range(node.numChildren()): diff --git a/amoco/system/baremetal/imx6.py b/amoco/system/baremetal/imx6.py index fd0b227..5ba7e21 100644 --- a/amoco/system/baremetal/imx6.py +++ b/amoco/system/baremetal/imx6.py @@ -178,7 +178,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) offset += 12 self.modulus = data[offset : offset + self.nlen] @@ -205,7 +205,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) assert self.header.tag == HAB_TAG_CRT crtend = offset + self.header.length @@ -241,7 +241,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) assert self.header.tag == HAB_TAG_CSF csfend = offset + self.header.length @@ -298,7 +298,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) assert self.cmd == HAB_CMD_CHK_DAT self.flags = self.par >> 3 @@ -355,7 +355,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) assert self.cmd == HAB_CMD_UNLK if self.len > self.size(): @@ -400,7 +400,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) assert self.cmd == HAB_CMD_INS_KEY if self.len > self.size(): @@ -458,7 +458,7 @@ def blks_format(k, x, cls=None): s.append((Token.Literal, ")")) return highlight(s) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) assert self.cmd == HAB_CMD_AUT_DAT if self.len > self.size(): diff --git a/amoco/system/baremetal/psx.py b/amoco/system/baremetal/psx.py index 8be4640..c74a0a3 100644 --- a/amoco/system/baremetal/psx.py +++ b/amoco/system/baremetal/psx.py @@ -38,7 +38,7 @@ def __init__(self, data=None,offset=0): if data: self.unpack(data,offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): super().unpack(data, offset) if self.magic != b"PS-X EXE": raise TypeError("Wrong magic number, not a PS-X EXE file ?") diff --git a/amoco/system/elf.py b/amoco/system/elf.py index ef6a88d..864963b 100644 --- a/amoco/system/elf.py +++ b/amoco/system/elf.py @@ -476,7 +476,7 @@ def __init__(self, data=None): if data: self.unpack(data) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): StructFormatter.unpack(self, data, offset) if self.ELFMAG0 != 0x7F or self.ELFMAG != b"ELF": raise ElfError("Wrong magic number, not an ELF file ?") @@ -543,7 +543,7 @@ def __init__(self, data=None): if data: self.unpack(data) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): f0 = self.fields[0] self._v.e_ident = f0.unpack(data, offset) offset += f0.size() diff --git a/amoco/system/fs/ufs.py b/amoco/system/fs/ufs.py index 57d7f34..203d1b7 100644 --- a/amoco/system/fs/ufs.py +++ b/amoco/system/fs/ufs.py @@ -344,7 +344,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): sz = 0 for f in self.fields[:2]: setattr(self, f.name, f.unpack(data, offset + sz, self.order)) @@ -385,7 +385,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): sz = 0 for f in self.fields[:3]: setattr(self, f.name, f.unpack(data, offset + sz, self.order)) @@ -429,7 +429,7 @@ def __init__(self, data="", offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): sz = 0 for f in self.fields[:-1]: setattr(self, f.name, f.unpack(data, offset + sz, self.order)) diff --git a/amoco/system/pe.py b/amoco/system/pe.py index a594f74..9d2f730 100644 --- a/amoco/system/pe.py +++ b/amoco/system/pe.py @@ -447,7 +447,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): magic = data[offset : offset + 2] if magic == b"\x0b\x01": logger.verbose("PE32 Magic found") diff --git a/amoco/system/structs/__init__.py b/amoco/system/structs/__init__.py index 54a8e2b..96497e6 100644 --- a/amoco/system/structs/__init__.py +++ b/amoco/system/structs/__init__.py @@ -164,13 +164,13 @@ class that inherits StructCore, in which case the previous class is used "H": 2, "i": 4, "I": 4, - "l": 4, - "L": 4, + "l": 0, + "L": 0, "f": 4, "q": 8, "Q": 8, "d": 8, - "P": 8, + "P": 0, } integer = pp.Regex(r"[0-9][0-9]*") integer.setParseAction(lambda r: int(r[0])) @@ -229,6 +229,9 @@ def __init__(self, fmt, **kargs): f_align = self.alignments[f_type] else: f_cls = Field + if isinstance(f_count, list): + f_cls = BitFieldEx + f_name = f_name.split('/') f_type = kargs.get(f_type, f_type) f_align = 0 self.fields.append( @@ -273,7 +276,7 @@ def TypeDefine(newname, typebase, typecount=0, align_value=0): if typecount: t.fields[0].count = typecount if align_value: - t.fields[0].align_value = align_value + t.fields[0]._align_value = align_value return t # ------------------------------------------------------------------------------ diff --git a/amoco/system/structs/core.py b/amoco/system/structs/core.py index 3bab378..3c49c7b 100644 --- a/amoco/system/structs/core.py +++ b/amoco/system/structs/core.py @@ -81,7 +81,7 @@ def format(cls): return cls.fields[cls.union].format() @classmethod - def size(cls): + def size(cls, psize=0): """ This is a class method that basically computes the sum of the sizes of fields (or the largest field if a union) while @@ -89,12 +89,13 @@ def size(cls): It uses the *class* fields instances so that the resulting value is infinite if any of these field is a VarField. """ - A = cls.align_value() + psize = {32:4, 64:8}.get(psize,psize) + A = cls.align_value(psize) sz = 0 for f in cls.fields: if cls.union is False and not cls.packed: - sz = f.align(sz) - fsz = f.size() + sz = f.align(sz, psize) + fsz = f.size(psize) if cls.union is False: sz += fsz elif fsz > sz: @@ -146,15 +147,15 @@ def __eq__(self, other): return False @classmethod - def align_value(cls): - return max([f.align_value for f in cls.fields]) + def align_value(cls,psize=0): + return max([f.align_value(psize) for f in cls.fields]) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): for f in self.fields: if self.union is False and not self.packed: - offset = f.align(offset) + offset = f.align(offset, psize) try: - value = f.unpack(data, offset) + value = f.unpack(data, offset, psize) except Exception: name = self.__class__.__name__ logger.error("error unpacking %s %s"%(name,str(f))) @@ -172,10 +173,10 @@ def unpack(self, data, offset=0): # is a dict with subnames/subvalues: self._v.__dict__.update(value) if self.union is False: - offset += f.size() + offset += f.size(psize) return self - def pack(self, data=None): + def pack(self, data=None, psize=0): if data is None: data = [] for f in self.fields: @@ -190,29 +191,49 @@ def pack(self, data=None): parts = [] offset = 0 for f, v in zip(self.fields, data): - p = f.pack(v) + p = f.pack(v,psize) if not self.packed: - pad = f.align(offset) - offset + pad = f.align(offset,psize) - offset p = b"\0" * pad + p parts.append(p) if self.union is False: res = b"".join(parts) if not self.packed: - res = res.ljust(self.size(), b"\0") + res = res.ljust(self.size(psize), b"\0") return res else: return parts[self.union] - def offset_of(self, name): + def offset_of(self, name, psize=0): if self.union is not False: return 0 o = 0 for f in self.fields: + o = f.align(o,psize) if f.name == name: return o - o = f.align(o) + f.size() + o += f.size(psize) raise AttributeError(name) + def offsets(self,psize=0): + if self.union is not False: + return [(0,f.size(psize)) for f in self.fields] + o = 0 + offsets = [] + for f in self.fields: + o = f.align(o,psize) + if hasattr(f,'subsizes'): + oo = 0 + for x in f.subsizes: + xo = float("%d.%d"%(o,oo)) + so = float(".%d"%x) + oo += x + offsets.append((xo,so)) + else: + offsets.append((o,f.size(psize))) + o += f.size(psize) + return offsets + # ------------------------------------------------------------------------------ diff --git a/amoco/system/structs/fields.py b/amoco/system/structs/fields.py index e07ffcb..09aca30 100644 --- a/amoco/system/structs/fields.py +++ b/amoco/system/structs/fields.py @@ -56,7 +56,7 @@ def __init__(self, ftype, fcount=0, fname=None, forder=None, falign=1, fcomment= self.count = fcount self.name = fname self.order = forder or "<" - self.align_value = falign + self._align_value = falign self.comment = fcomment self.instance = None @@ -79,7 +79,7 @@ def format(self): sz = self.size() return "%ds" % sz - def size(self): + def size(self,psize=0): # if the field belongs to an instance and was unpacked already, # we return the actual byte-length of the resulting struct: try: @@ -87,7 +87,7 @@ def size(self): except: # otherwise we return the natural size of the field's type, # which may be infinite if the type contains a VarField... - sz = self.type.size() + sz = self.type.size(psize) if self.count > 0: sz = sz * self.count return sz @@ -116,26 +116,25 @@ def __eq__(self, other): else: return False - @property - def align_value(self): + def align_value(self,psize=0): if self._align_value: return self._align_value - return self.type.align_value() + if self.type: + return self.type.align_value(psize) + return psize - @align_value.setter - def align_value(self, val): - self._align_value = val - - def align(self, offset): - A = self.align_value + def align(self, offset, psize=0): + A = self.align_value(psize) + if A == 0: + return offset r = offset % A if r == 0: return offset return offset + (A - r) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): "returns a (sequence of count) element(s) of its self.type" - blob = self.type().unpack(data, offset) + blob = self.type().unpack(data, offset, psize) if self.count>0: # since we are not a RawField, blob is normally a StructCore instance, # but it can be a python raw type in case self is a typedef. @@ -144,11 +143,11 @@ def unpack(self, data, offset=0): if isinstance(blob,(bytes,StructCore)): sizeof = lambda b: len(b) else: - sz = self.type.size() + sz = self.type.size(psize) if sz 0: - nextblob = self.type().unpack(data, offset) + nextblob = self.type().unpack(data, offset, psize) blob.append(nextblob) offset += sizeof(nextblob) count -= 1 return blob - def get(self, data, offset=0): - return (self.name, self.unpack(data, offset)) + def get(self, data, offset=0, psize=0): + return (self.name, self.unpack(data, offset, psize)) - def pack(self, value): + def pack(self, value, psize=0): if self.count > 0: - return b"".join([self.type().pack(v) for v in value]) - return self.type.pack(value) + return b"".join([self.type().pack(v,psize) for v in value]) + return self.type.pack(value,psize) def copy(self,obj=None): cls = self.__class__ @@ -221,26 +220,34 @@ def format(self): sz = self.count return "%d%s" % (sz, fmt) - def size(self): - sz = struct.calcsize(self.typename) + def size(self,psize=0): + tn = self.typename + if psize and tn in ('P','L','l'): + tn = {4:'I',8:'Q',32:'I',64:'Q'}.get(psize,tn) + sz = struct.calcsize(tn) if self.count > 0: sz = sz * self.count return sz - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): pfx = "%d" % self.count if self.count > 0 else "" + tn = self.typename + if psize and tn in ('P','L','l'): + tn = {4:'I',8:'Q',32:'I',64:'Q'}.get(psize,tn) res = struct.unpack( - self.order + pfx + self.typename, - data[offset : offset + self.size()] + self.order + pfx + tn, + data[offset : offset + self.size(psize)] ) - if self.count == 0 or self.typename == "s": + if self.count == 0 or tn == "s": return res[0] - if self.typename == "c": + if tn == "c": return b"".join(res) return res - def pack(self, value): + def pack(self, value, psize=0): fmt = self.typename + if psize and fmt in ('P','L','l'): + fmt = {4:'I',8:'Q',32:'I',64:'Q'}.get(psize,fmt) pfx = "%d" % self.count if self.count > 0 else "" order = self.ORDER if hasattr(self, "ORDER") else self.order if fmt=='c' and isinstance(value,bytes): @@ -263,7 +270,7 @@ def __repr__(self): class BitField(RawField): """ A BitField is a 0-count RawField with additional subnames and subsizes to allow - unpack the raw type into several named values each of given bit sizes. + unpack the type into several named values each of given bit sizes. Arguments: - The ftype argument is the one that gets "splitted" into parts of bits. @@ -281,7 +288,7 @@ def __init__(self, ftype, fcount=0, fname=None, forder=None, falign=1, fcomment= self.subnames = fname or [] # other attributes are as usual... - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): value = super().unpack(data,offset) D = {} l = 0 @@ -291,7 +298,7 @@ def unpack(self, data, offset=0): l += sz return D - def pack(self, D): + def pack(self, D, psize=0): value = 0 l = 0 for x,sz in zip(self.subnames,self.subsizes): @@ -299,7 +306,72 @@ def pack(self, D): v = (D[x]&mask)<>l)&mask + l += sz + return D + + def pack(self, D, psize=0): + value = 0 + l = 0 + for x,sz in zip(self.subnames,self.subsizes): + mask = (1<0 else '#' return "vector(%s)[%s]: %s; %s"%(self.typename,cnt,self.name,self.comment) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): "returns a vector of count element(s) of its self.type" n,sz = read_leb128(data,1,offset) offset += sz @@ -132,7 +132,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self, data, offset=0): + def unpack(self, data, offset=0, psize=0): super().unpack(data, offset) if self.magic != b"\0asm": raise WasmError("Wrong magic number, not a binary Wasm file ?") @@ -225,7 +225,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) ft = VectorField("FunctionType : ft") self.update(ft.get(self.content)) @@ -281,7 +281,7 @@ def __init__(self, data=None,offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) im = VectorField("Import : im") self.update(im.get(self.content)) @@ -309,7 +309,7 @@ def __init__(self, data=None,offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): from codecs import decode super().unpack(data,offset) self.mod = decode(self.mod,"UTF-8") @@ -355,7 +355,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) x = VectorField("I*%leb128 : x") self.update(x.get(self.content)) @@ -380,7 +380,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) tt = VectorField("TableType : tt") self.update(tt.get(self.content)) @@ -400,7 +400,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): if len(self.fields)==4: self.fields.pop() super().unpack(data,offset) @@ -439,7 +439,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) mem = VectorField("MemType : mem") self.update(mem.get(self.content)) @@ -462,7 +462,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data,offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): if len(self.fields)==3: self.fields.pop() super().unpack(data,offset) @@ -502,7 +502,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) glob = VectorField("GlobalType : glob") self.update(glob.get(self.content)) @@ -544,7 +544,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) ex = VectorField("Export : ex") self.update(ex.get(self.content)) @@ -572,7 +572,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): from codecs import decode super().unpack(data,offset) self.name = decode(self.name,"UTF-8") @@ -597,7 +597,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) n,sz = read_uleb128(self.content) self.x = n @@ -622,7 +622,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) seg = VectorField("Elem : seg") self.update(seg.get(self.content)) @@ -668,7 +668,7 @@ def add_vec(self,fmt,data,offset): self.update(v.get(data,offset)) return v - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): self.reset() super().unpack(data,offset) offset += 1 @@ -738,7 +738,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) seg = VectorField("Code : code") self.update(seg.get(self.content)) @@ -762,7 +762,7 @@ def reset(self): t = type("container", (object,), {}) self._v = t() - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): self.reset() super().unpack(data,offset) offset += len(self) @@ -813,7 +813,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) seg = VectorField("Data : code") self.update(seg.get(self.content)) @@ -855,7 +855,7 @@ def add_vec(self,fmt,data,offset): return v - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) offset += 1 if self.d&0x02: @@ -888,7 +888,7 @@ def __init__(self, data=None, offset=0): if data: self.unpack(data, offset) - def unpack(self,data,offset=0): + def unpack(self,data,offset=0,psize=0): super().unpack(data,offset) n,sz = read_uleb128(self.content) self.n = n diff --git a/doc/index.rst b/doc/index.rst index 754e304..507a10c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -4,7 +4,7 @@ Amoco documentation =================== -Amoco is a python (>=3.7) package dedicated to the static symbolic +Amoco is a python (>=3.8) package dedicated to the static symbolic analysis of binary programs. It features: diff --git a/setup.py b/setup.py index 878b28e..c16ae57 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setup( name = 'amoco', - version = '2.9.6', + version = '2.9.7', description = 'yet another binary analysis framework', long_description = long_descr, # Metadata @@ -67,7 +67,7 @@ 'pygments', 'z3-solver', 'tqdm', - 'ccrawl>=1.5', + 'ccrawl>=1.6', 'PySide2', 'IPython'], }, diff --git a/tests/test_system_structs.py b/tests/test_system_structs.py index 9cbfb30..b5a1f04 100644 --- a/tests/test_system_structs.py +++ b/tests/test_system_structs.py @@ -2,10 +2,20 @@ from amoco.system.structs import * def test_rawfield(): - f = RawField('I',fcount=2,fname='v') + f = RawField('I',fcount=2,fname='v',falign=4) assert f.format()=='2I' assert f.size()==8 assert f.unpack(b'\0\x01\x02\x03AAAA') == (0x03020100,0x41414141) + assert f.align_value()==4 + +def test_rawfieldPtr(): + f = RawField('P',fname='ptr') + assert f.format()=='P' + assert f.size(psize=32)==4 + assert f.size(psize=4)==4 + assert f.size(psize=64)==8 + assert f.size(psize=8)==8 + assert f.unpack(b'\0\x01\x02\x03',psize=32) == 0x03020100 def test_varfield(): f = VarField('s',fname='string') @@ -96,6 +106,7 @@ class S3(StructFormatter): pass # s uses type S1 to decode its field 'x', # which by default is little-endian: s.unpack(b'\x00\x00\x00\x01') + assert s.align_value()==4 assert s.x.i == 0x01000000 # an S3 instance q, would do the same but # if we modify the S1 type to change its @@ -209,6 +220,50 @@ def test_bitfield2(): assert v['c'] == 0 assert v['d'] == 1 +def test_bitfield3(): + xxx = TypeDefine('int16', 'h') + f = BitFieldEx('int16',fcount=[2,4,3,1,6],fname=['a','b','c','d','e']) + assert f.format()=='2s' + assert f.size()==2 + assert f.type().unpack(b"\x01\x02")==513 + D = f.unpack(b"\x01\x02") + assert D['a'] == D['d'] == 1 + assert D['b'] == D['c'] == D['e'] == 0 + D = f.unpack(b"\x29\x8a") + assert D['b'] == 10 + assert D['e'] == 34 + +def test_bitfield_struct(): + xxx = TypeDefine('int16', 'h') + @StructDefine(""" + int16 *#2/4/3/1/6 : a/b/c/d/e + """) + class stru_bf(StructFormatter): + order = '<' + def __init__(self,data="",offset=0): + if data: + self.unpack(data,offset) + s = stru_bf() + assert s.size()==2 + assert s.offsets() == [(0.0, 0.2), (0.2, 0.4), (0.6, 0.3), (0.9, 0.1), (0.1, 0.6)] + +def test_ptr_struct(): + xxx = TypeDefine('int32', 'I') + @StructDefine(""" + P : ptr + int32 : val + l : lval + """) + class stru_ptrval(StructFormatter): + order = '<' + def __init__(self,data="",offset=0): + if data: + self.unpack(data,offset) + s = stru_ptrval() + assert s.size(psize=4)==12 + assert s.size(psize=8)==24 + assert s.offsets(psize=4) == [(0, 4), (4, 4), (8, 4)] + assert s.offsets(psize=8) == [(0, 8), (8, 4), (16, 8)] def test_bindedfield(): @StructDefine("""