Skip to content

Commit 43f5d90

Browse files
committed
docstring and switch to 0.1.1
1 parent a5926c5 commit 43f5d90

File tree

3 files changed

+60
-69
lines changed

3 files changed

+60
-69
lines changed

bindiff/bindiff.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class BinDiff(BindiffFile):
7474
object so after loading the class can be dropped if needed.
7575
7676
.. warning:: the two programs given are mutated into :py:class:`ProgramBinDiff` object
77+
which inherit :py:class:`SimilarityMixin` and :py:class:`DictMatchMixin` which provides
78+
additional attributes and method to the class.
7779
"""
7880

7981
def __init__(self, primary: Union[ProgramBinExport, str], secondary: Union[ProgramBinExport, str], diff_file: str):
@@ -99,7 +101,6 @@ def _convert_program_classes(p: ProgramBinExport) -> None:
99101
Internal method to mutate a ProgramBinExport into ProgramBinDiff.
100102
101103
:param p: program to mutate
102-
:return: None (perform all the side effect on the program)
103104
"""
104105
p.__class__ = ProgramBinDiff
105106
for f in p.values():
@@ -112,10 +113,7 @@ def _convert_program_classes(p: ProgramBinExport) -> None:
112113
def _map_diff_on_programs(self) -> None:
113114
"""
114115
From a diffing result, maps functions, basic blocks and instructions of primary and secondary
115-
116-
:return: None
117116
"""
118-
119117
# Map similarity and confidence on both programs
120118
self.primary.similarity, self.secondary.similarity = self.similarity, self.similarity
121119
self.primary.confidence, self.secondary.confidence = self.confidence, self.confidence
@@ -165,7 +163,7 @@ def raw_diffing(p1_path: Union[Path, str], p2_path: Union[Path, str], out_diff:
165163
:param p1_path: primary file path
166164
:param p2_path: secondary file path
167165
:param out_diff: diffing output file
168-
:return: int (0 if successfull, -x otherwise)
166+
:return: True if successful, False otherwise
169167
"""
170168

171169
# Make sure the bindiff binary is okay before doing any diffing
@@ -248,8 +246,6 @@ def from_binexport_files(p1_binexport: str, p2_binexport: str, diff_out: str) ->
248246
def _configure_bindiff_path() -> None:
249247
"""
250248
Check BinDiff access paths
251-
252-
:return: None
253249
"""
254250
if not _check_environ():
255251
if not _check_default_path():

bindiff/file.py

+55-61
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from pathlib import Path
2-
from typing import List
32
import sqlite3
43
import hashlib
54
from datetime import datetime
65
from dataclasses import dataclass
6+
from typing import Union
77
import ctypes
88

99
from bindiff.types import FunctionAlgorithm, BasicBlockAlgorithm
@@ -12,79 +12,88 @@
1212
@dataclass
1313
class File:
1414
"""
15-
Represent files parsed
15+
File diffed in database.
1616
"""
17-
18-
id: int
19-
filename: str
20-
exefilename: str
21-
hash: str
22-
functions: int
23-
libfunctions: int
24-
calls: int
25-
basicblocks: int
26-
libbasicblocks: int
27-
edges: int
28-
libedges: int
29-
instructions: int
30-
libinstructions: int
17+
id: int #: Unique ID of the file in database
18+
filename: str #: file path
19+
exefilename: str #: file name
20+
hash: str #: SHA256 hash of the file
21+
functions: int #: total number of functions
22+
libfunctions: int #: total number of functions identified as library
23+
calls: int #: number of calls
24+
basicblocks: int #: number of basic blocks
25+
libbasicblocks: int #: number of basic blocks belonging to library functions
26+
edges: int #: number of edges in callgraph
27+
libedges: int #: number of edges in callgraph addressing a library
28+
instructions: int #: number of instructions
29+
libinstructions: int #: number of instructions in library functions
3130

3231

3332
@dataclass
3433
class FunctionMatch:
3534
"""
36-
Class holding a match between two function.
35+
A match between two functions in database.
3736
"""
38-
39-
id: int
40-
address1: int
41-
address2: int
42-
similarity: float
43-
confidence: float
44-
algorithm: FunctionAlgorithm
37+
id: int #: unique ID of function match in database
38+
address1: int #: function address in primary
39+
address2: int #: function address in secondary
40+
similarity: float #: similarity score (0..1)
41+
confidence: float #: confidence of the match (0..1)
42+
algorithm: FunctionAlgorithm #: algorithm used for the match
4543

4644

4745
@dataclass
4846
class BasicBlockMatch:
4947
"""
50-
Class holding a match between two basic blocks
48+
A match between two basic blocks
5149
"""
52-
53-
id: int
54-
function_match: FunctionMatch
55-
address1: int
56-
address2: int
57-
algorithm: BasicBlockAlgorithm
50+
id: int #: ID of the match in database
51+
function_match: FunctionMatch #: FunctionMatch associated with this match
52+
address1: int #: basic block address in primary
53+
address2: int #: basic block address in secondary
54+
algorithm: BasicBlockAlgorithm #: algorithm used to match the basic blocks
5855

5956

6057
class BindiffFile(object):
61-
def __init__(self, file: Path | str, permission: str = "ro"):
58+
"""
59+
Bindiff database file.
60+
The class seemlessly parse the database and allowing retrieving
61+
and manipulating the results.
62+
63+
It also provides some methods to create a database and to add entries
64+
in the database.
65+
"""
66+
def __init__(self, file: Union[Path, str], permission: str = "ro"):
67+
"""
68+
:param file: path to Bindiff database
69+
:param permission: permission to use for opening database (default: ro)
70+
"""
6271
self._file = file
6372

6473
# Open database
65-
self.db = sqlite3.connect(f"file:{file}?mode={permission}", uri=True)
74+
self.db = sqlite3.connect(f"file:{str(file)}?mode={permission}", uri=True)
6675

6776
# Global variables
68-
self.similarity = None
69-
self.confidence = None
70-
self.version = None
71-
self.created = None
72-
self.modified = None
77+
self.similarity: float = None #: Overall similarity
78+
self.confidence: float = None #: Overall diffing confidence
79+
self.version: str = None #: version of the differ used for diffing
80+
self.created: datetime = None #: Database creation date
81+
self.modified: datetime = None #: Database last modification date
7382
self._load_metadata(self.db.cursor())
7483

7584
# Files
76-
self.primary = None
77-
self.secondary = None
85+
self.primary: File = None #: Primary file
86+
self.secondary: File = None #: Secondary file
7887
self._load_file(self.db.cursor())
7988

8089
# Function matches
81-
self.primary_functions_match = {}
82-
self.secondary_functions_match = {}
90+
self.primary_functions_match: dict[int, FunctionMatch] = {} #: FunctionMatch indexed by addresses in primary
91+
self.secondary_functions_match: dict[int, FunctionMatch] = {} #: FunctionMatch indexed by addresses in secondary
8392
self._load_function_match(self.db.cursor())
8493

8594
# Basicblock matches
86-
self.primary_basicblock_match = {}
87-
self.secondary_basicblock_match = {}
95+
self.primary_basicblock_match: dict[int, dict[int, BasicBlockMatch]] = {} #: Basic block match from primary
96+
self.secondary_basicblock_match: dict[int, dict[int, BasicBlockMatch]] = {} #: Basic block match from secondary
8897
self._load_basicblock_match(self.db.cursor())
8998

9099
# Instruction matches
@@ -97,42 +106,35 @@ def unmatched_primary_count(self) -> int:
97106
"""
98107
Returns the number of functions inside primary that are not matched
99108
"""
100-
101109
return self.primary.functions + self.primary.libfunctions - len(self.primary_functions_match)
102110

103111
@property
104112
def unmatched_secondary_count(self) -> int:
105113
"""
106114
Returns the number of functions inside secondary that are not matched
107115
"""
108-
109116
return self.secondary.functions + self.secondary.libfunctions - len(self.primary_functions_match)
110117

111118
@property
112-
def function_matches(self) -> List[FunctionMatch]:
119+
def function_matches(self) -> list[FunctionMatch]:
113120
"""
114121
Returns the list of matched functions
115122
"""
116-
117123
return list(self.primary_functions_match.values())
118124

119125
@property
120-
def basicblock_matches(self) -> List[BasicBlockMatch]:
126+
def basicblock_matches(self) -> list[BasicBlockMatch]:
121127
"""
122128
Returns the list of matched basic blocks in primary (and secondary)
123129
"""
124-
125130
return [x for bb_matches in self.primary_basicblock_match.values() for x in bb_matches.values()]
126-
# return list(self.primary_basicblock_match.values())
127131

128132
def _load_file(self, cursor: sqlite3.Cursor) -> None:
129133
"""
130134
Load diffing file stored in a DB file
131135
132136
:param cursor: sqlite3 cursor to the DB
133-
:return: None
134137
"""
135-
136138
query = "SELECT * FROM file"
137139
self.primary = File(*cursor.execute(query).fetchone())
138140
self.secondary = File(*cursor.execute(query).fetchone())
@@ -142,9 +144,7 @@ def _load_metadata(self, cursor: sqlite3.Cursor) -> None:
142144
Load diffing metadata as stored in the DB file
143145
144146
:param cursor: sqlite3 cursor to the DB
145-
:return: None
146147
"""
147-
148148
query = "SELECT created, modified, similarity, confidence FROM metadata"
149149
self.created, self.modified, self.similarity, self.confidence = cursor.execute(query).fetchone()
150150
self.created = datetime.strptime(self.created, "%Y-%m-%d %H:%M:%S")
@@ -157,9 +157,7 @@ def _load_function_match(self, cursor: sqlite3.Cursor) -> None:
157157
Load matched functions stored in a DB file
158158
159159
:param cursor: sqlite3 cursor to the DB
160-
:return: None
161160
"""
162-
163161
i2u = lambda x: ctypes.c_ulonglong(x).value
164162
fun_query = "SELECT id, address1, address2, similarity, confidence, algorithm FROM function"
165163
for id, addr1, addr2, sim, conf, alg in cursor.execute(fun_query):
@@ -173,9 +171,7 @@ def _load_basicblock_match(self, cursor: sqlite3.Cursor) -> None:
173171
Load matched basic blocks stored in a DB file
174172
175173
:param cursor: sqlite3 cursor to the DB
176-
:return: None
177174
"""
178-
179175
mapping = {x.id: x for x in self.function_matches}
180176
query = "SELECT id, functionid, address1, address2, algorithm FROM basicblock"
181177
for id, fun_id, bb_addr1, bb_addr2, bb_algo in cursor.execute(query):
@@ -199,9 +195,7 @@ def _load_instruction_match(self, cursor: sqlite3.Cursor) -> None:
199195
Load matched instructions stored in a DB file
200196
201197
:param cursor: sqlite3 cursor to the DB
202-
:return: None
203198
"""
204-
205199
i2u = lambda x: ctypes.c_ulonglong(x).value
206200
mapping = {x.id: x for x in self.basicblock_matches}
207201
query = "SELECT basicblockid, address1, address2 FROM instruction"

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name='python-bindiff',
9-
version='0.1.0',
9+
version='0.1.1',
1010
description='Python wrapper to manipulate bindiff files',
1111
author='Robin David',
1212
author_email='[email protected]',
@@ -19,6 +19,7 @@
1919
"Bug Tracker": "https://github.com/quarkslab/python-bindiff/issues",
2020
"Source": "https://github.com/quarkslab/python-bindiff"
2121
},
22+
python_requires='>=3.9',
2223
install_requires=[
2324
'python-magic',
2425
'click',

0 commit comments

Comments
 (0)