Skip to content

Commit b6088d6

Browse files
committed
[WARP] Improved UX and API
- Exposes WARP type objects directly - Adds processor API (for generating warp files directly) - Adds file and chunk API - Misc cleanup - Simplified the amount of commands - Replaced the "Create" commands with a purpose built processor dialog - Added a native QT viewer for WARP files - Simplified committing to a remote with a purpose built commit dialog
1 parent 21f206a commit b6088d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2829
-1676
lines changed

plugins/warp/api/python/generator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,11 @@ int main(int argc, char* argv[])
321321
fprintf(out, "from binaryninja._binaryninjacore import BNType, BNTypeHandle\n");
322322
continue;
323323
}
324+
if (name == "BNDataBuffer")
325+
{
326+
fprintf(out, "from binaryninja._binaryninjacore import BNDataBuffer, BNDataBufferHandle\n");
327+
continue;
328+
}
324329
if (i.second->GetClass() == StructureTypeClass)
325330
{
326331
fprintf(out, "class %s(ctypes.Structure):\n", name.c_str());

plugins/warp/api/python/warp.py

Lines changed: 162 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
from typing import List, Optional, Union
55

66
import binaryninja
7-
from binaryninja import BinaryView, Function, BasicBlock, Architecture, Platform, Type, Symbol, LowLevelILInstruction, LowLevelILFunction
7+
from binaryninja import BinaryView, Function, BasicBlock, Architecture, Platform, Type, Symbol, LowLevelILInstruction, LowLevelILFunction, DataBuffer, Project, ProjectFile
88
from binaryninja._binaryninjacore import BNFreeString, BNAllocString, BNType
99

1010
from . import _warpcore as warpcore
11-
from .warp_enums import WARPContainerSearchItemKind
11+
from .warp_enums import WARPContainerSearchItemKind, WARPProcessorIncludedData, WARPProcessorIncludedFunctions
1212

1313

1414
class WarpUUID:
@@ -74,6 +74,30 @@ def __repr__(self):
7474
return f"<TypeGUID '{str(self)}'>"
7575

7676

77+
class WarpType:
78+
def __init__(self, handle: warpcore.BNWARPType):
79+
self.handle = handle
80+
81+
def __del__(self):
82+
if self.handle is not None:
83+
warpcore.BNWARPFreeTypeReference(self.handle)
84+
85+
def __repr__(self):
86+
return f"<WarpType name: '{self.name}' confidence: '{self.confidence}'>"
87+
88+
@property
89+
def name(self) -> str:
90+
return warpcore.BNWARPTypeGetName(self.handle)
91+
92+
@property
93+
def confidence(self) -> int:
94+
return warpcore.BNWARPTypeGetConfidence(self.handle)
95+
96+
def analysis_type(self, arch: Optional[Architecture] = None) -> Type:
97+
if arch is None:
98+
return Type.create(handle=warpcore.BNWARPTypeGetAnalysisType(None, self.handle))
99+
return Type.create(handle=warpcore.BNWARPTypeGetAnalysisType(arch.handle, self.handle))
100+
77101
@dataclasses.dataclass
78102
class WarpFunctionComment:
79103
text: str
@@ -155,11 +179,12 @@ def get_symbol(self, function: Function) -> Symbol:
155179
symbol_handle = warpcore.BNWARPFunctionGetSymbol(self.handle, function.handle)
156180
return Symbol(symbol_handle)
157181

158-
def get_type(self, function: Function) -> Optional[Type]:
159-
type_handle = warpcore.BNWARPFunctionGetType(self.handle, function.handle)
182+
@property
183+
def type(self) -> Optional[WarpType]:
184+
type_handle = warpcore.BNWARPFunctionGetType(self.handle)
160185
if not type_handle:
161186
return None
162-
return Type(type_handle)
187+
return WarpType(type_handle)
163188

164189
@property
165190
def constraints(self) -> List[WarpConstraint]:
@@ -259,11 +284,12 @@ def source(self) -> Source:
259284
def name(self) -> str:
260285
return warpcore.BNWARPContainerSearchItemGetName(self.handle)
261286

262-
def get_type(self, arch: Architecture) -> Optional[Type]:
263-
ty = warpcore.BNWARPContainerSearchItemGetType(arch.handle, self.handle)
287+
@property
288+
def type(self) -> Optional[WarpType]:
289+
ty = warpcore.BNWARPContainerSearchItemGetType(self.handle)
264290
if not ty:
265291
return None
266-
return Type(ty)
292+
return WarpType(ty)
267293

268294
@property
269295
def function(self) -> Optional[WarpFunction]:
@@ -405,12 +431,12 @@ def add_functions(self, target: WarpTarget, source: Source, functions: List[Func
405431
core_funcs[i] = functions[i].handle
406432
return warpcore.BNWARPContainerAddFunctions(self.handle, target.handle, source.uuid, core_funcs, count)
407433

408-
def add_types(self, view: BinaryView, source: Source, types: List[Type]) -> bool:
434+
def add_types(self, source: Source, types: List[WarpType]) -> bool:
409435
count = len(types)
410-
core_types = (ctypes.POINTER(BNType) * count)()
436+
core_types = (ctypes.POINTER(warpcore.BNWARPType) * count)()
411437
for i in range(count):
412438
core_types[i] = types[i].handle
413-
return warpcore.BNWARPContainerAddTypes(view.handle, self.handle, source.uuid, core_types, count)
439+
return warpcore.BNWARPContainerAddTypes(self.handle, source.uuid, core_types, count)
414440

415441
def remove_functions(self, target: WarpTarget, source: Source, functions: List[Function]) -> bool:
416442
count = len(functions)
@@ -479,11 +505,11 @@ def get_functions_with_guid(self, target: WarpTarget, source: Source, guid: Func
479505
warpcore.BNWARPFreeFunctionList(funcs, count.value)
480506
return result
481507

482-
def get_type_with_guid(self, arch: Architecture, source: Source, guid: TypeGUID) -> Optional[Type]:
483-
ty = warpcore.BNWARPContainerGetTypeWithGUID(arch.handle, self.handle, source.uuid, guid.uuid)
508+
def get_type_with_guid(self, source: Source, guid: TypeGUID) -> Optional[WarpType]:
509+
ty = warpcore.BNWARPContainerGetTypeWithGUID(self.handle, source.uuid, guid.uuid)
484510
if not ty:
485511
return None
486-
return Type(ty)
512+
return WarpType(ty)
487513

488514
def get_type_guids_with_name(self, source: Source, name: str) -> List[TypeGUID]:
489515
count = ctypes.c_size_t()
@@ -503,6 +529,128 @@ def search(self, query: WarpContainerSearchQuery) -> Optional[WarpContainerRespo
503529
return WarpContainerResponse.from_api(response.contents)
504530

505531

532+
class WarpChunk:
533+
def __init__(self, handle: warpcore.BNWARPChunk):
534+
self.handle = handle
535+
536+
def __del__(self):
537+
if self.handle is not None:
538+
warpcore.BNWARPFreeChunkReference(self.handle)
539+
540+
def __repr__(self):
541+
return f"<WarpChunk functions: '{len(self.functions)}' types: '{len(self.types)}'>"
542+
543+
@property
544+
def functions(self) -> List[WarpFunction]:
545+
count = ctypes.c_size_t()
546+
funcs = warpcore.BNWARPChunkGetFunctions(self.handle, count)
547+
if not funcs:
548+
return []
549+
result = []
550+
for i in range(count.value):
551+
result.append(WarpFunction(warpcore.BNWARPNewFunctionReference(funcs[i])))
552+
warpcore.BNWARPFreeFunctionList(funcs, count.value)
553+
return result
554+
555+
@property
556+
def types(self) -> List[WarpType]:
557+
count = ctypes.c_size_t()
558+
types = warpcore.BNWARPChunkGetTypes(self.handle, count)
559+
if not types:
560+
return []
561+
result = []
562+
for i in range(count.value):
563+
result.append(WarpType(warpcore.BNWARPNewTypeReference(types[i])))
564+
warpcore.BNWARPFreeTypeList(types, count.value)
565+
return result
566+
567+
class WarpFile:
568+
def __init__(self, handle: Union[warpcore.BNWARPFileHandle, str]):
569+
if isinstance(handle, str):
570+
self.handle = warpcore.BNWARPNewFileFromPath(handle)
571+
else:
572+
self.handle = handle
573+
574+
def __del__(self):
575+
if self.handle is not None:
576+
warpcore.BNWARPFreeFileReference(self.handle)
577+
578+
def __repr__(self):
579+
return f"<WarpFile chunks: '{len(self.chunks)}'>"
580+
581+
@property
582+
def chunks(self) -> List[WarpChunk]:
583+
count = ctypes.c_size_t()
584+
chunks = warpcore.BNWARPFileGetChunks(self.handle, count)
585+
if not chunks:
586+
return []
587+
result = []
588+
for i in range(count.value):
589+
result.append(WarpChunk(warpcore.BNWARPNewChunkReference(chunks[i])))
590+
warpcore.BNWARPFreeChunkList(chunks, count.value)
591+
return result
592+
593+
def to_data_buffer(self) -> DataBuffer:
594+
return DataBuffer(handle=warpcore.BNWARPFileToDataBuffer(self.handle))
595+
596+
597+
@dataclasses.dataclass
598+
class WarpProcessorState:
599+
cancelled: bool = False
600+
unprocessed_file_count: int = 0
601+
processed_file_count: int = 0
602+
analyzing_files: List[str] = dataclasses.field(default_factory=list)
603+
processing_files: List[str] = dataclasses.field(default_factory=list)
604+
605+
@staticmethod
606+
def from_api(state: warpcore.BNWARPProcessorState) -> 'WarpProcessorState':
607+
analyzing_files = []
608+
processing_files = []
609+
for i in range(state.analyzing_files_count):
610+
analyzing_files.append(state.analyzing_files[i])
611+
for i in range(state.processing_files_count):
612+
processing_files.append(state.processing_files[i])
613+
return WarpProcessorState(
614+
cancelled=state.cancelled,
615+
unprocessed_file_count=state.unprocessed_file_count,
616+
processed_file_count=state.processed_file_count,
617+
analyzing_files=analyzing_files,
618+
processing_files=processing_files
619+
)
620+
621+
class WarpProcessor:
622+
def __init__(self, included_data: WARPProcessorIncludedData = WARPProcessorIncludedData.WARPProcessorIncludedDataAll,
623+
included_functions: WARPProcessorIncludedFunctions = WARPProcessorIncludedFunctions.WARPProcessorIncludedFunctionsAnnotated,
624+
worker_count: int = 1):
625+
self.handle = warpcore.BNWARPNewProcessor(ctypes.c_int(included_data), ctypes.c_int(included_functions), worker_count)
626+
627+
def __del__(self):
628+
if self.handle is not None:
629+
warpcore.BNWARPFreeProcessor(self.handle)
630+
631+
def add_path(self, path: str):
632+
warpcore.BNWARPProcessorAddPath(self.handle, path)
633+
634+
def add_project(self, project: Project):
635+
warpcore.BNWARPProcessorAddProject(self.handle, project.handle)
636+
637+
def add_project_file(self, project_file: ProjectFile):
638+
warpcore.BNWARPProcessorAddProjectFile(self.handle, project_file.handle)
639+
640+
def add_binary_view(self, view: BinaryView):
641+
warpcore.BNWARPProcessorAddBinaryView(self.handle, view.handle)
642+
643+
def start(self) -> Optional[WarpFile]:
644+
file = warpcore.BNWARPProcessorStart(self.handle)
645+
if not file:
646+
return None
647+
return WarpFile(file)
648+
649+
def state(self) -> WarpProcessorState:
650+
state_raw = warpcore.BNWARPProcessorGetState(self.handle)
651+
warpcore.BNWARPFreeProcessorState(state_raw)
652+
return WarpProcessorState.from_api(state_raw)
653+
506654
def run_matcher(view: BinaryView):
507655
warpcore.BNWARPRunMatcher(view.handle)
508656

plugins/warp/api/python/warp_enums.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,16 @@ class WARPContainerSearchItemKind(enum.IntEnum):
66
WARPContainerSearchItemKindFunction = 1
77
WARPContainerSearchItemKindType = 2
88
WARPContainerSearchItemKindSymbol = 3
9+
10+
11+
class WARPProcessorIncludedData(enum.IntEnum):
12+
WARPProcessorIncludedDataSymbols = 0
13+
WARPProcessorIncludedDataSignatures = 1
14+
WARPProcessorIncludedDataTypes = 2
15+
WARPProcessorIncludedDataAll = 3
16+
17+
18+
class WARPProcessorIncludedFunctions(enum.IntEnum):
19+
WARPProcessorIncludedFunctionsSelected = 0
20+
WARPProcessorIncludedFunctionsAnnotated = 1
21+
WARPProcessorIncludedFunctionsAll = 2

0 commit comments

Comments
 (0)