Skip to content

Commit

Permalink
ACTUAL chained fixup processing. Full device/arch/system coverage.
Browse files Browse the repository at this point in the history
Includes a finally fleshed out version of bitfield processing and a hack to make unions work
  • Loading branch information
0cyn committed Oct 28, 2023
1 parent 5e9c03a commit b33886c
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 215 deletions.
13 changes: 13 additions & 0 deletions EXTERNAL_LICENSES/MACHOVIEW_CHAINEDFIXUP_APACHE_LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2021 Vector 35 Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
4 changes: 4 additions & 0 deletions EXTERNAL_LICENSES/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### notes

The Chained Fixup code within this project is based upon code I wrote for the MachO-View plugin
within BinaryNinja, which at time of writing is located at https://github.com/Vector35/view-macho
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@ Tested on:
* Android (Termux)
* WebAssembly
* Brython

#### Credits

Chained fixup processing is currently entirely based on https://github.com/xpcmdshell/bn-chained-fixups

#### Special thanks to

Expand Down
72 changes: 61 additions & 11 deletions src/katlib/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#
# Copyright (c) kat 2022.
#
from typing import List

type_mask = 0xffff0000
size_mask = 0xffff
Expand All @@ -37,22 +38,66 @@


class Bitfield:
""" Horrible class for decoding bitfields. This basically just exists for chained fixups do not write anything
that uses this because I hardly understand what i've even wrote.
Initialize with dict of field name to size in bits.
Load fields with ``myBitfieldInstance.decode_bitfield(myProperlySizedBytearray)``
Access by myBitfieldInstance.field_name
"""
def __init__(self, fields: dict):
self.fields = fields
self.size = sum(self.fields.values()) // 8
self.size_bits = sum(self.fields.values())
self.decoded_fields = {}

def decode_bitfield(self, value):
loc = 0
assert self.size == len(value)
for field_name, field_size in self.fields.items():
field_size = field_size & size_mask
byte = loc // 8
bit = loc % 8
mask = (1 << field_size) - 1
field_value = (value[byte] & mask << bit) >> bit
self.decoded_fields[field_name] = field_value
loc += field_size
# welcom to my night mare
int_value = int.from_bytes(value, 'little')
# print(bin(int_value))
bit_pos = 0
for field_name, bit_size in self.fields.items():
mask = (1 << bit_size) - 1
# print(f'{field_name} - {bin((int_value >> bit_pos) & mask)} {bin(mask)}')
self.decoded_fields[field_name] = (int_value >> bit_pos) & mask
setattr(self, field_name, self.decoded_fields[field_name])
bit_pos += bit_size


class StructUnion:
""" This class is a horrible one;
This struct code was not written with Unions in mind, or much in mind in general;
This implementation of unions has a couple of rules:
* It can only contain structs or other unions
* It will implicitly assume all types are the same size and probably die horribly if that isn't true.
Create one like:
class MySubClass(StructUnion):
def __init__(): # |size | list of Struct types like `mach_header`, etc
super().__init__(8, [my_struct_1, my_structtype_2])
Use like:
unionInst = MySubClass()
unionInst.load_from_bytes(my_epic_bytearray_that_is_properly_sized)
valueIWant = unionInst.my_struct_1.someFieldInIt
"""
SIZE = 0
def __init__(self, size: int, types: List[object]):
self.size = size
self.types = types

def load_from_bytes(self, data):
for t in self.types:
if issubclass(t, Struct):
setattr(self, t.__name__, Struct.create_with_bytes(t, data, "little"))
elif issubclass(t, StructUnion):
setattr(self, t.__name__, t())
getattr(self, t.__name__).load_from_bytes(data)

def __int__(self):
return self.__class__.SIZE


def _bytes_to_hex(data) -> str:
Expand Down Expand Up @@ -140,6 +185,12 @@ def create_with_bytes(struct_class, raw, byte_order="little"):
setattr(instance, f, fv)
field_value = None

elif issubclass(value, StructUnion):
data = raw[current_off:current_off+value.SIZE]
size = value.SIZE
field_value = value()
field_value.load_from_bytes(data)

elif issubclass(value, Struct):
size = value.SIZE
data = raw[current_off:current_off+size]
Expand Down Expand Up @@ -300,7 +351,6 @@ def serialize(self):
return struct_dict

def __init__(self, fields=None, sizes=None, byte_order="little"):

if sizes is None:
raise AssertionError(
"Do not use the bare Struct class; it must be implemented in an actual type; Missing Sizes")
Expand Down
Loading

0 comments on commit b33886c

Please sign in to comment.