Skip to content

Commit 5d40156

Browse files
ElykDeerrssor
authored andcommitted
Various Python 3 support changes
1 parent 3ead1e2 commit 5d40156

35 files changed

Lines changed: 621 additions & 556 deletions

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,17 @@ CTestTestfile.cmake
3131
api-docs/source/python.rst
3232
api-docs/source/index.rst
3333
.coverage
34+
# Make
35+
generator
36+
libcrypto.so.1.0.2
37+
libcurl.so.4
38+
libssl.so.1.0.2
39+
plugins/
40+
python/examples/binaryninja/
41+
python/libcrypto.so.1.0.2
42+
python/libcurl.so.4
43+
python/libssl.so.1.0.2
44+
python/plugins/
45+
python/types/
46+
suite/binaryninja/
47+
types/

Makefile

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ json.o: ./json/jsoncpp.cpp ./json/json.h ./json/json-forwards.h
4343
install: generate $(TARGET).a
4444
@echo "Installing binaryninja API.";
4545
cp -r python/* $(INSTALLPATH)/python/binaryninja
46-
cp $(TARGETDIR)/$(TARGET).a $(INSTALLPATH)
46+
cp $(TARGET).a $(INSTALLPATH)
47+
@echo "Done.";
4748

4849
generator: python/generator.cpp $(TARGET).a
4950
@echo "Building generator...";
@@ -65,43 +66,36 @@ python_test: environment python/_binaryninjacore.py python/enums.py
6566
oracle: environment python/_binaryninjacore.py python/enums.py
6667
python3 suite/generator.py
6768

68-
environment: python/_binaryninjacore.py python/enums.py
69-
@echo "Copying libs to needed locations..."
70-
@cp $(INSTALLPATH)/libbinaryninjacore.so.1 .
71-
@cp $(INSTALLPATH)/libcurl.so.4 .
72-
@cp $(INSTALLPATH)/libcrypto.so.1.0.2 .
73-
@cp $(INSTALLPATH)/libssl.so.1.0.2 .
74-
75-
@mkdir -p api/python/examples
76-
@cp python/examples/bin_info.py api/python/examples/
77-
@cp $(INSTALLPATH)/libbinaryninjacore.so.1 api/python/
78-
@cp $(INSTALLPATH)/libcurl.so.4 api/python/
79-
@cp $(INSTALLPATH)/libcrypto.so.1.0.2 api/python/
80-
@cp $(INSTALLPATH)/libssl.so.1.0.2 api/python/
69+
environment: environment_clean python/_binaryninjacore.py python/enums.py
70+
@echo "Copying over libs..."
71+
cp $(INSTALLPATH)/libbinaryninjacore.so.1 .
72+
cp $(INSTALLPATH)/libbinaryninjacore.so.1 python/
8173

8274
@echo "Building 'binaryninja' Packages..."
83-
@mkdir -p suite/binaryninja/
84-
@cp -r python/* suite/binaryninja/
85-
@mkdir -p api/python/examples/binaryninja/
86-
@cp -r python/* api/python/examples/binaryninja/
75+
mkdir -p suite/binaryninja/
76+
cp -r python/* suite/binaryninja/
77+
cp -r suite/binaryninja/ python/examples/
8778

8879
@echo "Copying Architectures Over..."
89-
@cp -r $(INSTALLPATH)/types/ .
90-
@cp -r $(INSTALLPATH)/plugins/ .
91-
@cp -r $(INSTALLPATH)/plugins/ api/python/
80+
cp -r $(INSTALLPATH)/types/ .
81+
cp -r $(INSTALLPATH)/plugins/ .
82+
cp -r $(INSTALLPATH)/types/ python/
83+
cp -r $(INSTALLPATH)/plugins/ python/
9284

9385
environment_clean:
9486
@echo "Removing 'binaryninja' Packages..."
95-
@rm -r suite/binaryninja/
96-
@rm -r api/
97-
-@rm suite/*.pyc
98-
87+
rm -rf suite/binaryninja/
88+
rm -rf python/examples/binaryninja/
89+
9990
@echo "Removing libs..."
100-
@rm lib*
91+
rm -f libbinaryninjacore.so.1
92+
rm -f python/libbinaryninjacore.so.1
10193

10294
@echo "Removing Architectures..."
103-
@rm -r types/
104-
@rm -r plugins/
95+
rm -rf types/
96+
rm -rf plugins/
97+
rm -rf python/types/
98+
rm -rf python/plugins/
10599

106100
clean:
107101
@echo " Cleaning...";

python/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,32 @@
2525
import ctypes
2626
from time import gmtime
2727

28+
29+
# 2-3 compatibility
30+
try:
31+
import builtins # __builtins__ for python2
32+
except ImportError:
33+
pass
34+
def range(*args):
35+
""" A Python2 and Python3 Compatible Range Generator """
36+
try:
37+
return xrange(*args)
38+
except NameError:
39+
return builtins.range(*args)
40+
41+
42+
def with_metaclass(meta, *bases):
43+
"""Create a base class with a metaclass."""
44+
class metaclass(type):
45+
def __new__(cls, name, this_bases, d):
46+
return meta(name, bases, d)
47+
48+
@classmethod
49+
def __prepare__(cls, name, this_bases):
50+
return meta.__prepare__(name, bases)
51+
return type.__new__(metaclass, 'temporary_class', (), {})
52+
53+
2854
# Binary Ninja components
2955
import binaryninja._binaryninjacore as core
3056
# __all__ = [

python/architecture.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@
2323
import ctypes
2424
import abc
2525

26-
# Binary Ninja components -- additional imports belong in the appropriate class
26+
# Binary Ninja components
2727
from binaryninja import _binaryninjacore as core
2828
from binaryninja.enums import (Endianness, ImplicitRegisterExtend, BranchType,
2929
InstructionTextTokenType, LowLevelILFlagCondition, FlagRole)
3030
import binaryninja
3131
from binaryninja import log
32-
3332
from binaryninja import lowlevelil
3433
from binaryninja import types
3534
from binaryninja import databuffer
3635
from binaryninja import platform
3736
from binaryninja import callingconvention
3837

3938
# 2-3 compatibility
40-
from six import with_metaclass
41-
from six.moves import range
39+
from binaryninja import range
40+
from binaryninja import with_metaclass
41+
4242

4343
class _ArchitectureMetaClass(type):
4444

@@ -624,7 +624,7 @@ def _get_semantic_flag_group_name(self, ctxt, sem_group):
624624

625625
def _get_full_width_registers(self, ctxt, count):
626626
try:
627-
regs = self._full_width_regs.values()
627+
regs = list(self._full_width_regs.values())
628628
count[0] = len(regs)
629629
reg_buf = (ctypes.c_uint * len(regs))()
630630
for i in range(0, len(regs)):
@@ -639,7 +639,7 @@ def _get_full_width_registers(self, ctxt, count):
639639

640640
def _get_all_registers(self, ctxt, count):
641641
try:
642-
regs = self._regs_by_index.keys()
642+
regs = list(self._regs_by_index.keys())
643643
count[0] = len(regs)
644644
reg_buf = (ctypes.c_uint * len(regs))()
645645
for i in range(0, len(regs)):
@@ -654,7 +654,7 @@ def _get_all_registers(self, ctxt, count):
654654

655655
def _get_all_flags(self, ctxt, count):
656656
try:
657-
flags = self._flags_by_index.keys()
657+
flags = list(self._flags_by_index.keys())
658658
count[0] = len(flags)
659659
flag_buf = (ctypes.c_uint * len(flags))()
660660
for i in range(0, len(flags)):
@@ -669,7 +669,7 @@ def _get_all_flags(self, ctxt, count):
669669

670670
def _get_all_flag_write_types(self, ctxt, count):
671671
try:
672-
write_types = self._flag_write_types_by_index.keys()
672+
write_types = list(self._flag_write_types_by_index.keys())
673673
count[0] = len(write_types)
674674
type_buf = (ctypes.c_uint * len(write_types))()
675675
for i in range(0, len(write_types)):
@@ -684,7 +684,7 @@ def _get_all_flag_write_types(self, ctxt, count):
684684

685685
def _get_all_semantic_flag_classes(self, ctxt, count):
686686
try:
687-
sem_classes = self._semantic_flag_classes_by_index.keys()
687+
sem_classes = list(self._semantic_flag_classes_by_index.keys())
688688
count[0] = len(sem_classes)
689689
class_buf = (ctypes.c_uint * len(sem_classes))()
690690
for i in range(0, len(sem_classes)):
@@ -699,7 +699,7 @@ def _get_all_semantic_flag_classes(self, ctxt, count):
699699

700700
def _get_all_semantic_flag_groups(self, ctxt, count):
701701
try:
702-
sem_groups = self._semantic_flag_groups_by_index.keys()
702+
sem_groups = list(self._semantic_flag_groups_by_index.keys())
703703
count[0] = len(sem_groups)
704704
group_buf = (ctypes.c_uint * len(sem_groups))()
705705
for i in range(0, len(sem_groups)):
@@ -938,7 +938,7 @@ def _get_register_stack_name(self, ctxt, reg_stack):
938938

939939
def _get_all_register_stacks(self, ctxt, count):
940940
try:
941-
regs = self._reg_stacks_by_index.keys()
941+
regs = list(self._reg_stacks_by_index.keys())
942942
count[0] = len(regs)
943943
reg_buf = (ctypes.c_uint * len(regs))()
944944
for i in range(0, len(regs)):
@@ -989,7 +989,7 @@ def _get_intrinsic_name(self, ctxt, intrinsic):
989989

990990
def _get_all_intrinsics(self, ctxt, count):
991991
try:
992-
regs = self._intrinsics_by_index.keys()
992+
regs = list(self._intrinsics_by_index.keys())
993993
count[0] = len(regs)
994994
reg_buf = (ctypes.c_uint * len(regs))()
995995
for i in range(0, len(regs)):
@@ -2415,8 +2415,8 @@ def assemble(self, code, addr=0):
24152415
24162416
:param str code: string representation of the instructions to be assembled
24172417
:param int addr: virtual address that the instructions will be loaded at
2418-
:return: the bytes for the assembled instructions or error string
2419-
:rtype: (a tuple of instructions and empty string) or (or None and error string)
2418+
:return: the bytes for the assembled instructions
2419+
:rtype: Python3 - a 'bytes' object; Python2 - a 'str'
24202420
:Example:
24212421
24222422
>>> arch.assemble("je 10")
@@ -2426,8 +2426,11 @@ def assemble(self, code, addr=0):
24262426
result = databuffer.DataBuffer()
24272427
errors = ctypes.c_char_p()
24282428
if not core.BNAssemble(self.handle, code, addr, result.handle, errors):
2429-
return None, errors.value
2430-
return str(result), errors.value
2429+
raise ValueError("Could not assemble")
2430+
if isinstance(str(result), bytes):
2431+
return str(result)
2432+
else:
2433+
return bytes(result)
24312434

24322435
def is_never_branch_patch_available(self, data, addr):
24332436
"""

python/basicblock.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020

2121
import ctypes
2222

23-
# Binary Ninja components -- additional imports belong in the appropriate class
23+
# Binary Ninja components
2424
import binaryninja
2525
from binaryninja import highlight
2626
from binaryninja import _binaryninjacore as core
2727
from binaryninja.enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType
2828

2929
# 2-3 compatibility
30-
from six.moves import range
30+
from binaryninja import range
31+
3132

3233
class BasicBlockEdge(object):
3334
def __init__(self, branch_type, source, target, back_edge):

python/binaryview.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import ctypes
2424
import abc
2525

26-
# Binary Ninja components -- additional imports belong in the appropriate class
26+
# Binary Ninja components
2727
from binaryninja import _binaryninjacore as core
2828
from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType,
2929
Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics)
@@ -38,8 +38,8 @@
3838
from binaryninja import metadata
3939

4040
# 2-3 compatibility
41-
from six import with_metaclass
42-
from six.moves import range
41+
from binaryninja import range
42+
from binaryninja import with_metaclass
4343

4444

4545
class BinaryDataNotification(object):
@@ -98,17 +98,19 @@ def __init__(self, bv, string_type, start, length):
9898

9999
@property
100100
def value(self):
101-
return self.view.read(self.start, self.length)
101+
return self.view.read(self.start, self.length).decode("charmap")
102102

103103
def __repr__(self):
104104
return "<%s: %#x, len %#x>" % (self.type, self.start, self.length)
105105

106106

107+
_pending_analysis_completion_events = {}
107108
class AnalysisCompletionEvent(object):
108109
"""
109110
The ``AnalysisCompletionEvent`` object provides an asynchronous mechanism for receiving
110111
callbacks when analysis is complete. The callback runs once. A completion event must be added
111-
for each new analysis in order to be notified of each analysis completion.
112+
for each new analysis in order to be notified of each analysis completion. The
113+
AnalysisCompletionEvent class takes responcibility for keeping track of the object's lifetime.
112114
113115
:Example:
114116
>>> def on_complete(self):
@@ -122,11 +124,19 @@ def __init__(self, view, callback):
122124
self.callback = callback
123125
self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify)
124126
self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb)
127+
global _pending_analysis_completion_events
128+
_pending_analysis_completion_events[id(self)] = self
125129

126130
def __del__(self):
131+
global _pending_analysis_completion_events
132+
if id(self) in _pending_analysis_completion_events:
133+
del _pending_analysis_completion_events[id(self)]
127134
core.BNFreeAnalysisCompletionEvent(self.handle)
128135

129136
def _notify(self, ctxt):
137+
global _pending_analysis_completion_events
138+
if id(self) in _pending_analysis_completion_events:
139+
del _pending_analysis_completion_events[id(self)]
130140
try:
131141
self.callback(self)
132142
except:
@@ -142,6 +152,9 @@ def cancel(self):
142152
"""
143153
self.callback = self._empty_callback
144154
core.BNCancelAnalysisCompletionEvent(self.handle)
155+
global _pending_analysis_completion_events
156+
if id(self) in _pending_analysis_completion_events:
157+
del _pending_analysis_completion_events[id(self)]
145158

146159

147160
class ActiveAnalysisInfo(object):
@@ -1731,10 +1744,13 @@ def read(self, addr, length):
17311744
"""
17321745
``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``.
17331746
1747+
Note: Python2 returns a str, but Python3 returns a bytes object. str(DataBufferObject) will
1748+
still get you a str in either case.
1749+
17341750
:param int addr: virtual address to read from.
17351751
:param int length: number of bytes to read.
17361752
:return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data.
1737-
:rtype: str
1753+
:rtype: python2 - str; python3 - bytes
17381754
:Example:
17391755
17401756
>>> #Opening a x86_64 Mach-O binary
@@ -2784,7 +2800,9 @@ def get_strings(self, start = None, length = None):
27842800
def add_analysis_completion_event(self, callback):
27852801
"""
27862802
``add_analysis_completion_event`` sets up a call back function to be called when analysis has been completed.
2787-
This is helpful when using asynchronously analysis.
2803+
This is helpful when using ``update_analysis`` which does not wait for analysis completion before returning.
2804+
2805+
The callee of this function is not resposible for maintaining the lifetime of the returned AnalysisCompletionEvent object.
27882806
27892807
:param callable() callback: A function to be called with no parameters when analysis has completed.
27902808
:return: An initialized AnalysisCompletionEvent object.
@@ -3481,7 +3499,7 @@ def get_section_by_name(self, name):
34813499
def get_unique_section_names(self, name_list):
34823500
incoming_names = (ctypes.c_char_p * len(name_list))()
34833501
for i in range(0, len(name_list)):
3484-
incoming_names[i] = name_list[i]
3502+
incoming_names[i] = name_list[i].encode('charmap')
34853503
outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list))
34863504
result = []
34873505
for i in range(0, len(name_list)):

0 commit comments

Comments
 (0)